I am writing a program in QT, which currently has a GameEngine (data handling) class and a MainWindow (GUI) class.

The single instances of both GameEngineand MainWindow classes are owned by the int main function.

The MainWindow instance has a User Action-button, which will open an instance of a QDialog class called Dialog_UserAction. The instance of this QDialog is owned by the MainWindow, which is also the parent of the QDialog (to disable the MainWindow GUI while the Dialog_UserAction instance is open).

My issue is that many events (signals) need to be connected between the QDialog and the GameEngine instance.

Is there any simple way that I can achieve this?

I have already tried by forwarding the signals from Dialog_UserAction to GameEngine via the MainBoard and vice versa. This works, but it is quite a messy solution for this task.

I have also tried letting the Dialog_UserAction be owned by Main, but I don't know how to react on the User Action Button clicked-event in main context.

Finally, I have also tried letting the Dialog_UserAction be owned by the GameEngine instance, which would the easy solution (except that the MainBoard GUI will not be disabled, while Dialog_UserAction is opened). But, I would really prefer that all GUI related instances were kept out of the GameEngine context.

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
    }
}
...

So, what I'm really asking is: Is there any simple way I can setup the signal-slot connections in this setup where I can connect Dialog_UserAction with GameEngine without forwarding the signals in the MainBoard context?

If not, do you have any suggestions on how I could approach this in a better way in general? Thanks in advance.

2 Answers

0
Nikos C. On Best Solutions

Since the GameEngine object is a singleton (only one instance exists), you can have the Dialog_UserAction object connect its signals directly to the GameEngine object. You can do that in the Dialog_UserAction constructor.

To get easy access to the GameEngine object, simply add a static member function to it that returns a static GameEngine* member.

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;

You can now connect the Dialog_UserAction signals to GameEngine::instance().

0
Jesperten On

So, just for clarification, I ended using the Singleton design pattern as suggested by Nikos C.

But I implemented the class a little differently, and therefore wanted to share this as a standalone answer.

I found that I needed to trigger the constructor of the the object somehow, and figured that this could be done using the so-called (I believe) Lazy Initialization method. Also, the constructor of the class should be private, such that only the instance itself will be able to call this, and thus making sure that the constructor is only called once.

Furthermore, I made the GameEngine::Instance()method as a const static GameEngine*-type in order to let the access to the object be read-only.

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;

Then in Main and Dialog_UserAction (the clients) the access to the Singleton GameEngine instance is used by creating a const class instance pointer in the each context.

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)                                                                );
}