Qt信号与槽机制及其几种写法

本文介绍了Qt中的信号与槽机制,用于响应控件事件。通过信号与槽的连接,实现了简易加法器的功能。详细讲解了信号与槽的定义、连接方法,包括传统写法、地址指针写法、直接命名槽函数和Lambda表达式,并提供了实例代码。此外,还讨论了信号与槽的一对多连接以及如何解除连接。
摘要由CSDN通过智能技术生成

目录

 

1.何为“信号”

2.何为“槽”

3.连接信号到槽函数

4.实现简易加法器

5.信号与槽的更多用法

5.1带参数的信号与槽

5.2信号与槽连接的几种写法

5.3信号与槽解除连接

6总结


        上一章我们使用Qt Designer进行界面设计,最后设计了个简易加法器界面,这一章我们来学习Qt独有的特性:信号与槽,通过信号与槽,实现控件事件的响应。

1.何为“信号”

        在Qt中,操作一个控件后,会产生和操作对应的信号,对于按钮这个控件来说,鼠标左键点击会产生点击信号,鼠标左键双击会产生双击信号;对于下拉框控件来说,改变下拉框的选中项,会产生当前选项改变的信号。对于一个控件来说,可能不止一个信号产生,这些信号都会被Qt内部机制所捕捉到,留给用户使用,当然,用户也可以自定义信号。

2.何为“槽”

        有“信号”就会有“槽”,信号与槽是互不可缺的天生一对。槽,本意是用来盛东西的器具,那么在Qt中,“槽”就是用来连接“信号”的函数。比如说,点击按钮后,按钮会产生一个点击信号,我们使用槽函数来接收点击信号,然后在槽函数中执行功能代码。

3.连接信号到槽函数

        前面对“信号”和“槽”的解释有些抽象,我们拿具体的例子看下就明白了,使用上很简单。我们在程序中使用connect()将信号与槽连接上:

connect(sender, SIGNAL(signal), receiver, SLOT(slot));

其中,sender是信号的发送者;SIGNAL()是代表信号的宏定义,里面的参数signal是信号;receiver是信号的接收者;SLOT()是代表槽的宏定义,里面的参数slot是槽函数。举个例子:

connect(ui->pushButton1, SIGNAL(clicked()), this, SLOT(func()));

这个connect的作用是,将界面的按钮(按钮命名为pushButton1)的点击信号clicked()连接到当前界面(用this表示)的槽函数func()。当点击界面的pushButton1按钮时,程序就会跑到this类中的func()函数里。

4.实现简易加法器

        在上一章中,我们设计了如下界面:

d7517ba94bcb4410a93a12c86830b7cd.png

         在Qt Designer中设计时,将“计算”按钮的objectName属性设置为calculatePushButton,将加数1数字输入框的objectName属性设置为add1SpinBox,将加数2数字输入框的objectName属性设置为add2SpinBox,将结果标签的objectName属性设置为resultLabel。在Qt Creator将界面编译成代码时,会把控件的变量名命名成objectName属性的值,然后在程序中就可以用控件的变量名来引用控件。这个在后面讲解界面编译的时候再细说,这里我们就把控件的objectName属性值作为控件变量名就行。

        下面是mainwindow.h和mainwindow.cpp的代码,实现简易加法器功能:

mainwindow.h:

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private:
    Ui::MainWindow *ui;

private slots:
    void calculate();  // 槽函数:加法计算
};
#endif // MAINWINDOW_H

 mainwindow.cpp:

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    // 连接信号与槽
    connect(ui->calculatePushButton, SIGNAL(clicked()), this, SLOT(calculate()));
}

MainWindow::~MainWindow()
{
    delete ui;
}

/* 槽函数:加法计算 */
void MainWindow::calculate()
{
    // 获取加数1的值
    int add1 = ui->add1SpinBox->value();
    // 获取加数2的值
    int add2 = ui->add2SpinBox->value();
    // 计算和
    int result = add1 + add2;
    // 将计算和显示出来
    ui->resultLabel->setText(QString::number(result));
}

        在mainwindow.h中,声明了一个槽函数,这个槽函数声明在private slots修饰符后,是私有槽函数,只能在自己的类中使用。当然,也可以声明在public slots修饰符后,那槽函数就变成了公有函数,可以在类外被访问。

        在mainwindow.cpp中,在构造函数中连接了信号与槽:

connect(ui->calculatePushButton, SIGNAL(clicked()), this, SLOT(calculate()));

表示将calculatePushButton按钮的clicked()信号连接到该类(this表示当前类)的槽函数calculate()。

        然后定义槽函数calculate(),在calculate()函数中,先获取加数1和加数2的值,然后两者相加求和,最后将计算和结果显示在结果标签中。程序运行后,显示界面并操作如下:

6e8c2c9d77f44a4580d3782efb0db5e7.gif

         至此,简易加法器就完成了!其实,再复杂的界面程序也大致是这样的开发流程,只不过有的加入了其它一些技巧或语法,但万变不离其宗。

5.信号与槽的更多用法

5.1带参数的信号与槽

        刚才所用到的按钮的点击的信号与接收信号的槽都不带参数,但有的信号是带参数的,比如界面中的控件add1SpinBox和add2SpinBox,有个值改变信号valueChanged(int i),这个信号有个参数,是控件改变后的值。那么,当信号带参数时,用于接收信号的槽函数也必须带着参数,用于接收信号传来的参数。我们修改简易加法器,当加数1或者加数2改变时,直接出来结果值而不用在点击“计算”按钮。修改后的代码如下:

mainwindow.h:

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private:
    Ui::MainWindow *ui;

private slots:
    void add1Calculate(int i);
    void add2Calculate(int i);
};
#endif // MAINWINDOW_H

mainwindow.cpp:

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    // 连接信号与槽
    connect(ui->add1SpinBox, SIGNAL(valueChanged(int)), this, SLOT(add1Calculate(int)));
    connect(ui->add2SpinBox, SIGNAL(valueChanged(int)), this, SLOT(add2Calculate(int)));
}

MainWindow::~MainWindow()
{
    delete ui;
}

/* 用于接收add1SpinBox的valueChanged(int)信号的槽函数 */
void MainWindow::add1Calculate(int i)
{
    // 获取加数1的值
    int add1 = i;
    // 获取加数2的值
    int add2 = ui->add2SpinBox->value();
    // 计算和
    int result = add1 + add2;
    // 将计算和显示出来
    ui->resultLabel->setText(QString::number(result));
}

/* 用于接收add2SpinBox的valueChanged(int)信号的槽函数 */
void MainWindow::add2Calculate(int i)
{
    // 获取加数1的值
    int add1 = ui->add1SpinBox->value();
    // 获取加数2的值
    int add2 = i;
    // 计算和
    int result = add1 + add2;
    // 将计算和显示出来
    ui->resultLabel->setText(QString::number(result));
}




        在上面代码中,我们将信号与槽的连接改成:

connect(ui->add1SpinBox, SIGNAL(valueChanged(int)), this, SLOT(add1Calculate(int)));
connect(ui->add2SpinBox, SIGNAL(valueChanged(int)), this, SLOT(add2Calculate(int)));

当add1SpinBox的值改变后,会触发它的valueChanged(int i)信号,这个信号带着控件改变后的值,然后接收信号的槽同时也接收到了信号带来的参数,并在槽函数中使用这个参数。在槽函数add1Calculate(int i)中,获取加数1的值不再通过ui->add1SpinBox->value()获取,而是直接从槽函数的实参中获取,实参i即是控件改变后的值。对于add1SpinBox,也是一样的道理。程序运行后,界面操作如下:

9db0ac5d3ae84862b77add3e921a1cf3.gif

5.2信号与槽连接的几种写法

①上面用到的信号与槽的连接写法是最传统的写法:

connect(sender, SIGNAL(signal), receiver, SLOT(slot));

这种写法从Qt4就开始有的,这种写法的优点是信号和槽,包括所携带的参数一目了然,缺点是该写法在编译时不做类型检查,如果你把信号写错了,编译器是检查不出来的,只能在运行期间发现。

②有一种新的写法:

connect(sender, &Sender::signal, receiver, &Receiver::slot);

比如说上面我们用到的按钮的点击信号,在这里可以这么写:

connect(ui->calculatePushButton, &QPushButton::clicked, this, &MainWindow::calculate);

这种方法明确指向信号的地址和槽函数地址,在编译时做类型检查,能够在编译期间就发现错误。但这种方法看不到信号与槽的参数,不够直接。

③直接命名槽函数名,在Qt中,按以下规则给槽函数命名可直接将控件的信号关联到槽函数:

void 类名::on_控件名_信号() {...}

比如说上面的“计算”按钮的点击信号所对应的槽函数可写成:

void MainWindow::on_calculatePushButton_clicked() {...}

又比如,加数1的数字输入框的值改变信号对应的槽函数可写成:

void MainWindow::on_add1SpinBox_valueChanged(int i) {...}

当然,这样的槽函数在.h中声明时也必须要用private slots或public slots修饰,否则触发不了槽函数。

④使用Lambda表达式,这在C++11中才支持的特性,这种写法比较简洁,比如以上的简易加法器的按钮的信号与槽函数可以这么写:

connect(ui->calculatePushButton, &QPushButton::clicked, this, [=]() {
        // 加法计算
});

        上面写到的四种信号与槽的写法,前三种用得最多,第四种在某些追求代码简洁的地方可以使用,对于这几种写法,我们都了解一下,如果看到别人写的代码中出现这几种写法,就知道是咋回事了。

        信号与槽并不是只能一对一,可以多个信号对单个槽,单个信号对多个槽,或者其它更灵活的使用,大家在今后的实践中,慢慢就用到了,稍微变通一下就可以了。

5.3信号与槽解除连接

        如果我们使用connect()连接信号与槽后,也可以使用disconnect()解除连接,解除连接后,信号与槽就不再关联了。

6总结

        这一章完成了简易加法器的程序,详细介绍了程序的开发流程,之后介绍了信号与槽的几种写法。学会了上述的开发流程,也就初步地学会了Qt的界面编程,大家也就明白了Qt是怎么开发应用的,熟悉了之后,就可以尝试自己写Qt程序了,剩下的无非就是弄明白Qt有多少种控件、每个控件里有什么变量和函数可供使用、哪个信号需要连接到哪个槽中、Qt有哪些写好的功能类或库,再者就是进一步地研究各种变量类型的使用、如何自定义控件、对界面进行美工等,最后再加入自己的功能代码,就成为了一个完整的Qt应用。这些内容我们在之后的学习中会慢慢抛出,有个对Qt的基本了解,再进行深一步的学习就会变得轻松很多。所以,万丈高楼平地起,打好基础很重要!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值