How to connect backend classes with the GUI properly?
-
wrote on 20 May 2022, 09:34 last edited by
Hello, I am new to Qt from this year, and am making a board game using Qt 6.
There is no specific code example I can show to make my problem clear, so please bare with me.
My backend classes have already been created with pure C++, that I now added to Qt to create a GUI that interacts with them. My idea is to use my Game class as a public interface, which also has constant references to some helper classes that can be called from within the GUI.
In general, this approach ensures information hiding as the GUI can't access anything non-const in my deeper backend classes. However, when I want to connect signals with slots, I need a pure pointer to the class that holds the signals/slots. So when adding signals to the most logical backend class, I would need a pointer to this class in the GUI, to make the connect. This would however destroy the information hiding principle, as now every method can be called from the GUI.
Is there anything I am doing wrong here? What is a possible solution that doesn't violate GRASP too much?
An example:
My game has a private member that manages 'rounds' in the board game. Whenever a round is over, I want to send a signal from the RoundManager to the MainWindow (this class holds my Game pointer and initializes it).A connect is now needed, so the only solution I can come up with is this:
-
Add a getter in Game that returns a pure pointer to the private RoundManager member (not const anymore).
-
Call the getter in the GUI to pass it to the first parameter of the connect.
What I thought about:
- Have all signals in the public Game class, as I have a pointer to this class in the GUI. (but then where to emit them?)
This however, violates GRASP's information expert (Game shouldn't include all signals that are specific to certain classes)
Thanks in advance for any advice or help.
-
-
Hello, I am new to Qt from this year, and am making a board game using Qt 6.
There is no specific code example I can show to make my problem clear, so please bare with me.
My backend classes have already been created with pure C++, that I now added to Qt to create a GUI that interacts with them. My idea is to use my Game class as a public interface, which also has constant references to some helper classes that can be called from within the GUI.
In general, this approach ensures information hiding as the GUI can't access anything non-const in my deeper backend classes. However, when I want to connect signals with slots, I need a pure pointer to the class that holds the signals/slots. So when adding signals to the most logical backend class, I would need a pointer to this class in the GUI, to make the connect. This would however destroy the information hiding principle, as now every method can be called from the GUI.
Is there anything I am doing wrong here? What is a possible solution that doesn't violate GRASP too much?
An example:
My game has a private member that manages 'rounds' in the board game. Whenever a round is over, I want to send a signal from the RoundManager to the MainWindow (this class holds my Game pointer and initializes it).A connect is now needed, so the only solution I can come up with is this:
-
Add a getter in Game that returns a pure pointer to the private RoundManager member (not const anymore).
-
Call the getter in the GUI to pass it to the first parameter of the connect.
What I thought about:
- Have all signals in the public Game class, as I have a pointer to this class in the GUI. (but then where to emit them?)
This however, violates GRASP's information expert (Game shouldn't include all signals that are specific to certain classes)
Thanks in advance for any advice or help.
wrote on 20 May 2022, 09:50 last edited by JonB@mayfairstrange said in How to connect backend classes with the GUI properly?:
This would however destroy the information hiding principle, as now every method can be called from the GUI.
? Assuming you make your
Game
class(es) derive fromQObject
and haveQ_OBJECT
macro in it/them, so that they canemit
signals, it is only those signals which must bepublic
. You do not have to expose any other methods in them to the UI.At a pinch you could probably make it so you do not have to make your
Game
class(es) "derive fromQObject
and haveQ_OBJECT
macro" by defining a separate class with that as a "wrapper" for signalemit
s which the backend class(es) call instead ofemit
directly, with some judicious method definitions/parameter passing. And use that class in the UI'sconnect()
s. More work though. -
-
wrote on 20 May 2022, 11:04 last edited by mayfairstrange
Hello, thank you for your answer. The classes contained in Game that must emit signals are derived from QObject and have the QObject macro. How do I connect those signals in the UI?
Here is a template of my example:
// RoundManager.h class RoundManager : public QObject{ Q_OBJECT signals: void roundOver(); } //Game.h class Game : public QObject { Q_OBJECT public: inline RoundManager* roundManagerPtr() {return m_roundManager; } // Exposes all methods in roundmanager private: RoundManager* m_roundManager; } // MainWindow.h class MainWindow : public QMainWindow { Q_OBJECT public slots: void handleRoundOver(); private: Game* m_game; } // MainWindow constructor connect(m_game->roundManagerPtr(), &RoundManager::roundOver, this, &MainWindow::handleRoundOver);
How would I connect this signal alone without getting a pointer to the RoundManager class?
-
Hello, thank you for your answer. The classes contained in Game that must emit signals are derived from QObject and have the QObject macro. How do I connect those signals in the UI?
Here is a template of my example:
// RoundManager.h class RoundManager : public QObject{ Q_OBJECT signals: void roundOver(); } //Game.h class Game : public QObject { Q_OBJECT public: inline RoundManager* roundManagerPtr() {return m_roundManager; } // Exposes all methods in roundmanager private: RoundManager* m_roundManager; } // MainWindow.h class MainWindow : public QMainWindow { Q_OBJECT public slots: void handleRoundOver(); private: Game* m_game; } // MainWindow constructor connect(m_game->roundManagerPtr(), &RoundManager::roundOver, this, &MainWindow::handleRoundOver);
How would I connect this signal alone without getting a pointer to the RoundManager class?
wrote on 20 May 2022, 11:29 last edited by mpergand@mayfairstrange said in How to connect backend classes with the GUI properly?:
How would I connect this signal alone without getting a pointer to the RoundManager class?
You can't.
inline RoundManager* roundManagerPtr() {return m_roundManager; } // Exposes all methods in roundmanager
Will expose only public methods.
Now if you do not want RoundManager to be exposed outside the Game class, define the Game class as a proxy, that is an intermediate class between your app and RoundManager.
-
@mayfairstrange said in How to connect backend classes with the GUI properly?:
How would I connect this signal alone without getting a pointer to the RoundManager class?
You can't.
inline RoundManager* roundManagerPtr() {return m_roundManager; } // Exposes all methods in roundmanager
Will expose only public methods.
Now if you do not want RoundManager to be exposed outside the Game class, define the Game class as a proxy, that is an intermediate class between your app and RoundManager.
wrote on 20 May 2022, 11:44 last edited by@mpergand Thank you, a definite answer is what I wanted to see. I will have to solve it that way then indeed.
1/5