信号和槽
当我们需要一个界面通知另一个界面时,可以采用信号和槽机制。通过链接信号和槽,当一个界面发送信号时,链接该信号的槽会被响应,从而达到消息传递的目的。
我们先创建两个界面并为两个界面添加一个按钮,通过按钮的点击来实现界面的切换
主界面
先实现主界面的点击按钮功能,并在终端打印一条信息”show child dialog”
现在MainWindow的构造函数中添加信号和槽的逻辑连接
1
| connect(ui->showChildButton, SIGNAL(clicked()), this, SLOT(showChildDialog()));
|
然后在MainWindow的头文件中添加槽函数,槽函数需要slots
声明
1 2
| public slots: void showChildDialog();
|
接下来实现该函数
1 2 3
| void MainWindow::showChildDialog() { qDebug() << "show child dialog" << endl; }
|
子界面
不同的连接方式
上面我们使用的是qt4的连接方式,用SIGNAL和SLOT将信号和槽转化为字符串。但是这种方式存在一个问题,qt要求槽函数的参数不能超过信号定义的参数,比如我们用到的信号clicked(bool)参数就是bool,我们定义的槽函数showChildDialog()参数是不带参数的,可以连接成功,如果我们在连接的时候将showChildDialog()的参数写成三个,也可以连接成功
1 2
| connect(ui->showChildButton, SIGNAL(clicked(bool)), SLOT(showChildDialog(1, 2, 3)));
|
但是点击会没有反应,说明qt4 这种连接信号和槽的方式不做编译检查,只是将信号和槽函数转译成字符串。
推荐qt5的连接方式
1 2 3 4 5
| connect(_child_dialog, &ChildDialog::showMainSig, this, &MainWindow::showMainDialog); connect(_child_dialog, &ChildDialog::showMainSig, this, [this]() { this->_child_dialog->hide(); this->show(); });
|
实现界面切换
在MainWindow类中,加入一个子界面类的指针,方便访问
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| class MainWindow : public QMainWindow { Q_OBJECT
public: MainWindow(QWidget *parent = nullptr); ~MainWindow(); public slots: void showChildDialog(); void showMainDialog();
private: Ui::MainWindow *ui; ChildDialog *_child_dialog; };
|
然后改写槽函数,当单击按钮时弹出对话框
1 2 3 4 5
| void MainWindow::showChildDialog() { _child_dialog->show(); this->hide(); }
|
这么做有一个问题就是可能会重复创建子窗口,但是Qt的对象树机制会保证父窗口回收时才回收子窗口,所以关闭子窗口只是隐藏了。
那么随着点击,久而久之窗口会越来越多。
我们想到的一个避免重复创建的办法就是在MainWindow的构造函数里创建好子界面,在槽函数中只控制子界面的显示即可。
但同时要注意在MainWindow的析构函数里回收子界面类对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) { ui->setupUi(this); _child_dialog = new ChildDialog(this); connect(ui->showChildButton, &QPushButton::clicked, this, &MainWindow::showChildDialog); connect(_child_dialog, &ChildDialog::showMainSig, this, [this]() { this->_child_dialog->hide(); this->show(); }); }
MainWindow::~MainWindow() { delete ui; if (_child_dialog != nullptr) { delete _child_dialog; _child_dialog = nullptr; } }
|
这样我们频繁点击显示子界面按钮就不会重复创建窗口了。
那接下来实现点击主界面的按钮,显示子界面,并隐藏主窗口。
实现点击子界面的按钮,显示主窗口,并隐藏子界面。
先实现点击子界面按钮显示主窗口,我们可以在ChildDialog类修改下构造函数,使其接受一个QWidget指针,这个指针指向父窗口也就是MainWindow.
我们新增成员_parent用来存储MainWindow。
新增槽函数showMainWindow用来显示主窗口。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| class ChildDialog : public QDialog { Q_OBJECT signals: void showMainSig();
public: explicit ChildDialog(QWidget *parent = nullptr); ~ChildDialog(); void showMainWindow();
private: Ui::ChildDialog *ui; QWidget *_parent; };
|
实现槽函数
1 2 3 4 5
| void ChildDialog::showMainWindow() { this->hide(); emit showMainSig(); }
|
在MainWindow连接这个信号
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) { ui->setupUi(this); _child_dialog = new ChildDialog(this); connect(ui->showChildButton, &QPushButton::clicked, this, &MainWindow::showChildDialog); connect(_child_dialog, &ChildDialog::showMainSig, this, [this]() { this->_child_dialog->hide(); this->show(); }); }
|
连接信号
上面的程序还可以进一步优化,因为Qt提供了信号连接信号的方式,也就是说我们可以把子界面的按钮点击信号和showMainSig信号连接起来。
1 2 3 4 5 6 7 8
| ChildDialog::ChildDialog(QWidget *parent) : QDialog(parent) , ui(new Ui::ChildDialog) , _parent(parent) { ui->setupUi(this); connect(ui->showMainWindow, &QPushButton::clicked, this, &ChildDialog::showMainSig); }
|
将clicked和showMainSig两个信号连接起来,也可以实现消息的传递。