【问题标题】:Connecting slots and signals between a class instance and a dialog within another class instance在一个类实例和另一个类实例中的对话框之间连接槽和信号
【发布时间】:2019-04-21 16:39:39
【问题描述】:

我正在用 QT 编写一个程序,目前有一个GameEngine(数据处理)类和一个MainWindow(GUI)类。

GameEngineMainWindow 类的单个实例都归 int main 函数所有。

MainWindow 实例有一个User Action 按钮,它将打开一个名为Dialog_UserAction 的QDialog 类的实例。此 QDialog 的实例归 MainWindow 所有,它也是 QDialog 的父级(在 Dialog_UserAction 实例打开时禁用 MainWindow GUI)。

我的问题是 QDialog 和 GameEngine 实例之间需要连接许多事件(信号)。

有什么简单的方法可以实现吗?

我已经尝试通过MainBoard 将信号从Dialog_UserAction 转发到GameEngine,反之亦然。这可行,但对于这项任务来说,这是一个相当混乱的解决方案。

我也尝试让Dialog_UserActionMain 所有,但我不知道如何在主要上下文中对User Action Button clicked-事件作出反应。

最后,我还尝试让GameEngine 实例拥有Dialog_UserAction,这是一个简单的解决方案(除了MainBoard GUI 不会被禁用,而Dialog_UserAction 是打开的)。但是,我真的希望所有与 GUI 相关的实例都被排除在 GameEngine 上下文之外。

GameEngine.h:

class GameEngine : public QObject
{
    Q_OBJECT

signals:
    void someSignalToDialog(void);

public slots:
    void on_someSignalFromDialog();
}

Dialog_UserAction.h:

class Dialog_UserAction: public QObject
{
    Q_OBJECT

signals:
    void someSignalToGameEngine(void);

public slots:
    void on_someSignalFromGameEngine();
}

Main.cpp:

int main(int argc, char *argv[]) {
    QApplication a(argc, argv);
    QApplication::setWindowIcon(QIcon(":/images/MageKnightLogo.jpg"));
    GameEngine gameEngine;
    Window_MainBoard mainBoard;

    mainBoard.showFullScreen();

    return a.exec();
}

主板.cpp:

#include "Dialog_UserAction.h"

...

void Window_MainBoard::on_pushButton_enterUserAction_clicked() {
    Dialog_UserAction actionDialog(this);

    // connect signals and slots here?

    if (actionDialog.exec() == QDialog::Accepted)
    {
        // Send signal with data to GameEngine
    }
}
...

所以,我真正要问的是: 有什么简单的方法可以在此设置中设置信号槽连接,我可以将Dialog_UserActionGameEngine 连接起来,而无需在MainBoard 上下文中转发信号?

如果没有,您对我如何以更好的方式解决这个问题有什么建议吗?提前致谢。

【问题讨论】:

  • 听起来你真的不希望游戏引擎担心对话框的存在。因此,只需将您需要的信号放入主窗口,然后将它们连接到游戏引擎一次。然后在创建对话框时,将其信号连接到主窗口的信号。为此编写辅助函数将有助于您的代码保持可读性。我真的不明白你对这种方法的问题是什么。

标签: c++ qt hierarchy


【解决方案1】:

由于GameEngine 对象是单例(仅存在一个实例),您可以让Dialog_UserAction 对象将其信号直接连接到GameEngine 对象。您可以在 Dialog_UserAction 构造函数中执行此操作。

要轻松访问GameEngine 对象,只需向其添加一个静态成员函数,该函数返回一个静态GameEngine* 成员。

GameEngine.h

class GameEngine
{
public:
    GameEngine()
    {
        Q_ASSERT(instance_ == nullptr); // Only one instance allowed.
        instance_ = this;
    }

    static GameEngine* instance() noexcept
    { return instance_; }

private:
    static GameEngine* instance_;
};

GameEngine.cpp

GameEngine* GameEngine::instance_ = nullptr;

您现在可以将Dialog_UserAction 信号连接到GameEngine::instance()

【讨论】:

  • 非常感谢您的回答。 Singleton 确实是我需要的关键字。以前从未听说过。我是一名电子工程师,只在业余时间编程。我认为您上面的答案中唯一缺少的就是对 GameEngine 构造函数的调用在哪里!?我通过在 instance() 方法中执行“== nullptr”检查来结束使用延迟初始化方法。
  • @Jesperten 我通常在main() 中创建这样的对象,就在QApplication::exec() 调用之前。
  • 好吧,我没想到。我在下面发布了我自己的答案。如果有什么我误解的地方,我希望您能花一些时间通读并发表评论:-) 无论如何,我已经让它运行起来了。再次感谢您的回答。
【解决方案2】:

所以,为了澄清起见,我结束了使用 Nikos C 建议的 Singleton 设计模式。

但我实现该类的方式略有不同,因此想将其作为独立答案分享。

我发现我需要以某种方式触发对象的构造函数,并认为这可以使用所谓的(我相信)惰性初始化方法来完成。此外,类的构造函数应该是私有的,这样只有实例本身才能调用 this,从而确保构造函数只被调用一次。

此外,我将GameEngine::Instance()方法设置为const static GameEngine*-type,以便让对对象的访问为只读。

GameEngine.h

class GameEngine
{

public:
    const static GameEngine* instance() { // Singleton instance reference getter
        if (instance_ == nullptr)
            instance_ = new GameEngine(); // This triggers the constructor of the object
        return instance_;
    }

private:
    GameEngine(); // The constructor is made private!
};

GameEngine.cpp

// Place this declaration in the top of the file to create the global class instance pointer
// The initial value of the pointer must be nullptr to let the constructor be correctly triggered at the first instance()-call
GameEngine* GameEngine::instance_ = nullptr;

然后在MainDialog_UserAction(客户端)中,通过在每个上下文中创建一个 const 类实例指针来使用对 Singleton GameEngine 实例的访问。

Main.cpp

#include "GameEngine.h"

int main(int argc, char *argv[]) {
    QApplication a(argc, argv);
    Window_MainBoard mainBoard;

    const GameEngine* gameEngine = GameEngine::instance(); // Const pointer to the Singleton instance of the GameEngine class (global object)

    QObject::connect(gameEngine, SIGNAL(someSignalToDialog), &mainBoard, SLOT(on_someSignalFromGameEngine));
}

Dialog_UserAction.cpp

#include "GameEngine.h"

// Constructor
Dialog_UserAction::Dialog_UserAction(QWidget *parent) : QDialog(parent), ui(new Ui::Dialog_UserAction) {
    ui->setupUi(this);

    // Const pointer to the Singleton instance of the GameEngine class (global object)
    const GameEngine* gameEngine = GameEngine::instance();

    connect(this, SIGNAL(someSignalToGameEngine), gameEngine, SLOT(on_someSignalFromDialog)                                                                );
}

【讨论】:

  • 指针没有理由成为const。此外,您正在使用的 connect() 调用已被弃用(它们来自 Qt 4,只是为了与非常旧的代码兼容。)您应该使用 Qt 5 connect() 函数:connect(this, &Dialog_UserAction::someSignalToGameEngine, gameEngine, &GameEngine::on_someSignalFromDialog)); 新函数允许编译期间要检查的信号/插槽连接。旧的函数是基于字符串的,并且只在运行时检查连接(这意味着错误的连接将编译得很好,只有在应用程序运行时才会失败。)
  • 如果您使用 Qt Creator 作为您的 IDE,您可以右键单击 connect() 调用并选择“Refactor->Convert connect to Qt 5 style”。这可能并非在所有情况下都有效,因此有时您需要手动完成。
猜你喜欢
  • 1970-01-01
  • 2022-07-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-28
  • 2012-02-27
  • 2015-04-07
相关资源
最近更新 更多