在 class 实例和另一个 class 实例中的对话之间连接槽和信号
Connecting slots and signals between a class instance and a dialog within another class instance
我正在用 QT 编写一个程序,目前有一个 GameEngine
(数据处理)class 和一个 MainWindow
(GUI)class。
GameEngine
和 MainWindow
class 的单个实例都属于 int main
函数。
MainWindow
实例有一个 User Action
按钮,它将打开一个名为 Dialog_UserAction
的 QDialog class 实例。此 QDialog 的实例由 MainWindow
所有,它也是 QDialog 的父级(在 Dialog_UserAction
实例打开时禁用 MainWindow
GUI)。
我的问题是 QDialog 和 GameEngine
实例之间需要连接许多事件(信号)。
有什么简单的方法可以做到这一点吗?
我已经尝试通过 MainBoard
将信号从 Dialog_UserAction
转发到 GameEngine
,反之亦然。这可行,但对于此任务来说这是一个相当混乱的解决方案。
我也试过让 Dialog_UserAction
归 Main
所有,但我不知道如何在主要上下文中对 User Action Button clicked
事件做出反应。
最后,我还尝试让 Dialog_UserAction
归 GameEngine
实例所有,这将是一个简单的解决方案(除了 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;
然后在 Main
和 Dialog_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) );
}
我正在用 QT 编写一个程序,目前有一个 GameEngine
(数据处理)class 和一个 MainWindow
(GUI)class。
GameEngine
和 MainWindow
class 的单个实例都属于 int main
函数。
MainWindow
实例有一个 User Action
按钮,它将打开一个名为 Dialog_UserAction
的 QDialog class 实例。此 QDialog 的实例由 MainWindow
所有,它也是 QDialog 的父级(在 Dialog_UserAction
实例打开时禁用 MainWindow
GUI)。
我的问题是 QDialog 和 GameEngine
实例之间需要连接许多事件(信号)。
有什么简单的方法可以做到这一点吗?
我已经尝试通过 MainBoard
将信号从 Dialog_UserAction
转发到 GameEngine
,反之亦然。这可行,但对于此任务来说这是一个相当混乱的解决方案。
我也试过让 Dialog_UserAction
归 Main
所有,但我不知道如何在主要上下文中对 User Action Button clicked
事件做出反应。
最后,我还尝试让 Dialog_UserAction
归 GameEngine
实例所有,这将是一个简单的解决方案(除了 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;
然后在 Main
和 Dialog_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) );
}