【QT学习之路】-信号槽

【QT学习之路】-信号槽

内容摘要:文章中主要介绍了Qt中的信号槽, 主要内容包括: 信号槽的本质, 信号槽的关系, 标准信号槽的使用, 自定义信号槽的使用, 信号槽的拓展, Lambda表达式。

1. 信号和槽概述 信号槽机制是Qt最引以为豪的机制之一,信号槽的本质实际上就是一种观察者模式(发送-订阅)。当某一个事件发生后,例如:有一个按钮组件,当它检测到自己被点击之后,就会发出一个信号(signal),这个信号本身是没有目的性的(类似于广播),但是当有一个对象对这个信号感兴趣时,这个对象就会调用连接(connect)函数,意思是,对象会将自己想要的信号与自己绑定,以此来共同处理这个信号。也就是说,当一个信号产生时,被连接的槽函数会自动回调。

1.1. 信号的本质信号就是当用户对窗口或者组件进行了相关操作,然后窗口和组件就会产生特定的事件,这个时候Qt的对应窗口和组件就会发出信号。

从上面的描述中我们可以得出一个结论:事件就是信号!!!比如:

单击、双击

代码语言:javascript复制女朋友,“我饿了”,于是我带她去吃饭点击刷新

鼠标的点击与释放

键盘的输入

那么信号是以什么样的形式呈现给使用者的呢?

当使用者对某个窗口进行操作时,这个窗口就会捕捉被触发的事件也就是说,我们触发了一个事件,就可以得到Qt框架给我们的一个信号而信号的呈现形式就是函数(某个信号发生了,Qt框架就会调用相应的函数通知我们)1.2. 槽的本质

在Qt中槽函数是一种具有特殊功能的函数,实际上也是普通的成员函数,只不过它们还有一个作用就是对于Qt框架中的信号进行处理。

下面举一个例子进行说明:

代码语言:javascript复制女朋友说,“我饿了”,于是我带她去吃饭。上面这个例子就说明了,女朋友发出了一个信号,我接收到了并且做出处理,带她去吃饭。

实例对象

角色

描述

女朋友

信号发出者

信号携带的信息: 我饿了

信号接收者

处理女朋友发射的信号: 带她去吃饭

注意:Qt中槽函数属于某一个类的实例对象!

1.3. 信号和槽的关系

其实在Qt中,槽和函数本身并没有任何联系。但是由于某种特性需求我们需要将他们两者联系到一起,而我们这里用到的是QObject类中的connect()函数。

连接信号和槽的connect()函数原型如下, 其中PointerToMemberFunction是一个指向函数地址的指针:

代码语言:javascript复制QMetaObject::Connection QObject::connect(

const QObject *sender, PointerToMemberFunction signal,

const QObject *receiver, PointerToMemberFunction method,

Qt::ConnectionType type = Qt::AutoConnection);

参数:

- sender: 发出信号的对象

- signal: 属于sender对象, 信号是一个函数, 这个参数的类型是函数

指针, 信号函数地址

- receiver: 信号接收者

- method: 属于receiver对象, 当检测到sender发出了signal信号,

receiver对象调用method方法,信号发出之后的处理动作

// 参数 signal 和 method 都是函数地址, 因此简化之后的 connect() 如下:

connect(const QObject *sender, &QObject::signal,

const QObject *receiver, &QObject::method);使用connect()函数进行信号槽连接的注意事项:

connect()函数只是做了信号处理的注册,换句话说connect()函数并不会立刻执行,只是在Qt框架中定义了一个“订阅关系”(举个例子:当你订阅了新闻频道,频道也并不会立刻给你发消息,只有当重大新闻发生时,才会收到消息)调用connect()函数的sender对象的信号没有产生,receiver的method方法也不会调用method槽函数是一个回调函数,调用的信号就是信号特定的信号产生之后,由Qt框架来执行如果sender和receiver两个指针必须实例化,否则connect()函数不会成功2. 标准信号槽的使用2.1. 标准信号/槽

在Qt中提供了很多标准类对于特定的事件进行检测,当用户做了这些操作事件被触发类的内部就会产生信号,这些信号都是Qt内部自带的,因此称之为标准信号。同样的,在Qt的很多类内部为我们提供了很多功能函数,并且这些函数可以作为触发信号的处理动作,这些函数被称作标准槽函数。 那么系统自带的信号和槽函数应该怎么寻找呢?这里我们可以借助帮助文档,例如我们试着看一下

按钮的点击信号(QPushButton):

然后我们输入QPushButton:

然后我们看一下右边文档的目录中有没有signals,但是我们发现好像并没有,此时我们再看一下它的父类继承下来的那些信号:

我们可以看见,QPushButton的父类是继承了信号的:

2.2. 使用

掌握了标准信号、标准信号槽的使用以及connect()函数的作用之后,我们来看一看怎么它们的使用方式。

下面举一个例子:

代码语言:javascript复制功能实现: 点击窗口上的按钮, 关闭窗口

功能分析:

- 按钮: 信号发出者 -> QPushButton 类型

- 窗口: 信号的接收者和处理者 -> QWidget 类型需要使用的标准信号、槽函数:

代码语言:javascript复制// 单击按钮发出的信号

[signal] void QAbstractButton::clicked(bool checked = false)

// 关闭窗口的槽函数

[slot] bool QWidget::close();然后我们使用connect()进行连接:

代码语言:javascript复制// 单击按钮关闭窗口

connect(ui->closewindow, &QPushButton::clicked, this, &MainWindow::close);注意:connnect()函数一般写在窗口的构造函数中,这相当于在事件发生之前就在Qt框架中进行注册。这样的话,在程序运行的过程中如果产生了信号,那么就会调用槽函数进行处理,如果没有信号产生的话,槽函数就不会被调用。

3. 自定义槽函数的使用

当Qt提供的一些标准信号和槽函数不能满足我们的需求时,我们可以自己定义信号和槽函数,同样使用connect()函数进行连接。

但是有一些自定义信号和槽函数需要注意的事项:

自定义的类必须让其继承Qt的某些标准类这个新子类必须从QObject或者QObject的子类继承在头文件中派生类中需要引入Q_Object宏代码语言:javascript复制// 在头文件派生类的时候,首先像下面那样引入Q_OBJECT宏:

class MyMainWindow : public QWidget

{

Q_OBJECT

......

}3.1. 自定义信号

信号是类的成员函数返回值必须是viod类型信号的名字可以自定义参数可以也可以自定义(信号也可以重载)信号函数只需要声明不需要定义(就是没有函数体实现)代码语言:javascript复制// 举例: 信号重载

// Qt中的类想要使用信号槽机制必须要从QObject类派生(直接或间接派生都可以)

class Test : public QObject

{

Q_OBJECT

signals:

void testsignal();

// 参数的作用是数据传递, 谁调用信号函数谁就指定实参

// 实参最终会被传递给槽函数

void testsignal(int a);

};3.2. 自定义槽函数

槽函数就是信号的处理动作,本质可以当做普通的成员函数来使用。如果标准槽函数

返回值必须是void类型的槽函数本质上也是函数,所以可以重载槽函数指定多少个参数取决于连接连接信号的参数个数槽函数的参数是用来接收信号传递的数据的,信号传递的数据就是信号的参数总结:

槽函数对应的参数个数应该与信号信号传递的参数个数相等,从左到右类型依次对应信号的参数可以大于槽函数的参数个数(只是会导致数据的丢失)代码语言:javascript复制信号函数: void testsig(int a, double b);

槽函数: void testslot(int a);Qt槽函数的类型可以是多样的,可以是成员函数、全局函数、静态函数、Lambda表达式。槽函数可以使用关键字进行声明: slots (Qt5中slots可以省略不写) public slots:private slots: –> 这样的槽函数不能在类外部被调用protected slots: –> 这样的槽函数不能在类外部被调用代码语言:javascript复制// 槽函数书写格式举例

// 类中的这三个函数都可以作为槽函数来使用

class Test : public QObject

{

public:

void testSlot();

static void testFunc();

public slots:

void testSlot(int id);

};下面举一个例子:

代码语言:javascript复制// class GirlFriend --卸载头文件当中

class GirlFriend : public QObject

{

Q_OBJECT

public:

explicit GirlFriend(QObject *parent = nullptr);

signals:

void hungry(); // 不能表达出想要吃什么

void hungry(QString msg); // 可以通过参数表达想要吃什么

};

// class Me

class Me : public QObject

{

Q_OBJECT

public:

explicit Me(QObject *parent = nullptr);

public slots:

// 槽函数

void eatMeal(); // 不能知道信号发出者要吃什么

void eatMeal(QString msg); // 可以知道信号发出者要吃什么

};然后使用connect()函数进行连接:

代码语言:javascript复制// 写在int main()中

// 连接两个重载信号/槽。Qt5+ 推荐用函数指针语法

QObject::connect(&girl, QOverload<>::of(&GirlFriend::hungry),

&me, QOverload<>::of(&Me::eatMeal)); // 无参版本

QObject::connect(&girl, QOverload::of(&GirlFriend::hungry),

&me, QOverload::of(&Me::eatMeal)); // 有参版本运行结果:

(本篇完)

相关风暴