Skip to main content

QT信号和槽

·258 words·2 mins· loading
Raven005
Author
Raven005
A little bit about you
Table of Contents

信号和槽
#

当我们需要一个界面通知另一个界面时,可以采用信号和槽机制。通过链接信号和槽,当一个界面发送信号时,链接该信号的槽会被响应,从而达到消息传递的目的。

我们先创建两个界面并为两个界面添加一个按钮,通过按钮的点击来实现界面的切换

主界面

mainpage.png

先实现主界面的点击按钮功能,并在终端打印一条信息"show child dialog"
现在MainWindow的构造函数中添加信号和槽的逻辑连接

connect(ui->showChildButton, SIGNAL(clicked()), this, SLOT(showChildDialog()));

然后在MainWindow的头文件中添加槽函数,槽函数需要slots声明

public slots:
    void showChildDialog();

接下来实现该函数

void MainWindow::showChildDialog() {
    qDebug() << "show child dialog" << endl;
}

子界面

subpage.png

不同的连接方式
#

上面我们使用的是qt4的连接方式,用SIGNAL和SLOT将信号和槽转化为字符串。但是这种方式存在一个问题,qt要求槽函数的参数不能超过信号定义的参数,比如我们用到的信号clicked(bool)参数就是bool,我们定义的槽函数showChildDialog()参数是不带参数的,可以连接成功,如果我们在连接的时候将showChildDialog()的参数写成三个,也可以连接成功

 // qt4 style 连接信号和槽
 connect(ui->showChildButton, SIGNAL(clicked(bool)), SLOT(showChildDialog(1, 2, 3)));

但是点击会没有反应,说明qt4 这种连接信号和槽的方式不做编译检查,只是将信号和槽函数转译成字符串。

推荐qt5的连接方式

connect(_child_dialog, &ChildDialog::showMainSig, this, &MainWindow::showMainDialog);
connect(_child_dialog, &ChildDialog::showMainSig, this, [this]() {
    this->_child_dialog->hide();
    this->show();
});

实现界面切换
#

在MainWindow类中,加入一个子界面类的指针,方便访问

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

然后改写槽函数,当单击按钮时弹出对话框

void MainWindow::showChildDialog()
{
    _child_dialog->show();
    this->hide();
}

这么做有一个问题就是可能会重复创建子窗口,但是Qt的对象树机制会保证父窗口回收时才回收子窗口,所以关闭子窗口只是隐藏了。 那么随着点击,久而久之窗口会越来越多。 我们想到的一个避免重复创建的办法就是在MainWindow的构造函数里创建好子界面,在槽函数中只控制子界面的显示即可。 但同时要注意在MainWindow的析构函数里回收子界面类对象。


MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    _child_dialog = new ChildDialog(this);
    // qt4 style 连接信号和槽
    // connect(ui->showChildButton, SIGNAL(clicked(bool)), SLOT(showChildDialog()));
    // qt5 style(recommend)
    connect(ui->showChildButton, &QPushButton::clicked, this, &MainWindow::showChildDialog);
    // connect(_child_dialog, &ChildDialog::showMainSig, this, &MainWindow::showMainDialog);
    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用来显示主窗口。

class ChildDialog : public QDialog
{
    Q_OBJECT
signals:
    void showMainSig();

public:
    explicit ChildDialog(QWidget *parent = nullptr);
    ~ChildDialog();
    void showMainWindow();

private:
    Ui::ChildDialog *ui;
    QWidget *_parent;
};

实现槽函数

void ChildDialog::showMainWindow()
{
    this->hide();
    emit showMainSig(); //发出信号
}

在MainWindow连接这个信号

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    _child_dialog = new ChildDialog(this);
    // qt4 style 连接信号和槽
    // connect(ui->showChildButton, SIGNAL(clicked(bool)), SLOT(showChildDialog()));
    // qt5 style(recommend)
    connect(ui->showChildButton, &QPushButton::clicked, this, &MainWindow::showChildDialog);
    // connect(_child_dialog, &ChildDialog::showMainSig, this, &MainWindow::showMainDialog);
    connect(_child_dialog, &ChildDialog::showMainSig, this, [this]() {
        this->_child_dialog->hide();
        this->show();
    });
}

连接信号
#

上面的程序还可以进一步优化,因为Qt提供了信号连接信号的方式,也就是说我们可以把子界面的按钮点击信号和showMainSig信号连接起来。

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两个信号连接起来,也可以实现消息的传递。