在 class 实例和另一个 class 实例中的对话之间连接槽和信号

Connecting slots and signals between a class instance and a dialog within another class instance

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

GameEngineMainWindow class 的单个实例都属于 int main 函数。

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

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

有什么简单的方法可以做到这一点吗?

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

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

最后,我还尝试让 Dialog_UserActionGameEngine 实例所有,这将是一个简单的解决方案(除了 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();
}

MainBoard.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_UserAction 连接到 GameEngine 而无需在 MainBoard 上下文中转发信号?

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

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

要轻松访问 GameEngine 对象,只需向其添加一个静态成员函数即可 returns 一个静态 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()

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

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

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

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

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 class 实例指针来使用对 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)                                                                );
}