
目录
内容摘要:文章中主要介绍了Qt中的信号槽, 主要内容包括: 信号槽的本质, 信号槽的关系, 标准信号槽的使用, 自定义信号槽的使用, 信号槽的拓展, Lambda表达式。
1. 信号和槽概述
信号槽机制是Qt最引以为豪的机制之一,信号槽的本质实际上就是一种观察者模式(发送-订阅)。当某一个事件发生后,例如:有一个按钮组件,当它检测到自己被点击之后,就会发出一个信号(signal),这个信号本身是没有目的性的(类似于广播),但是当有一个对象对这个信号感兴趣时,这个对象就会调用连接(connect)函数,意思是,对象会将自己想要的信号与自己绑定,以此来共同处理这个信号。也就是说,当一个信号产生时,被连接的槽函数会自动回调。
1.1. 信号的本质
信号就是当用户对窗口或者组件进行了相关操作,然后窗口和组件就会产生特定的事件,这个时候Qt的对应窗口和组件就会发出信号。
从上面的描述中我们可以得出一个结论:事件就是信号!!!比如:
- 单击、双击
女朋友,“我饿了”,于是我带她去吃饭- 点击刷新
- 鼠标的点击与释放
- 键盘的输入
那么信号是以什么样的形式呈现给使用者的呢?
- 当使用者对某个窗口进行操作时,这个窗口就会捕捉被触发的事件
- 也就是说,我们触发了一个事件,就可以得到Qt框架给我们的一个信号
- 而信号的呈现形式就是函数(某个信号发生了,Qt框架就会调用相应的函数通知我们)
1.2. 槽的本质
在Qt中槽函数是一种具有特殊功能的函数,实际上也是普通的成员函数,只不过它们还有一个作用就是对于Qt框架中的信号进行处理。
下面举一个例子进行说明:
女朋友说,“我饿了”,于是我带她去吃饭。上面这个例子就说明了,女朋友发出了一个信号,我接收到了并且做出处理,带她去吃饭。
实例对象
角色 描述 女朋友 信号发出者 信号携带的信息: 我饿了 我 信号接收者 处理女朋友发射的信号: 带她去吃饭 注意:Qt中槽函数属于某一个类的实例对象!
1.3. 信号和槽的关系
其实在Qt中,槽和函数本身并没有任何联系。但是由于某种特性需求我们需要将他们两者联系到一起,而我们这里用到的是QObject类中的connect()函数。
连接信号和槽的connect()函数原型如下, 其中PointerToMemberFunction是一个指向函数地址的指针:
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()函数的作用之后,我们来看一看怎么它们的使用方式。
下面举一个例子:
功能实现: 点击窗口上的按钮, 关闭窗口 功能分析: - 按钮: 信号发出者 -> QPushButton 类型 - 窗口: 信号的接收者和处理者 -> QWidget 类型需要使用的标准信号、槽函数:
// 单击按钮发出的信号 [signal] void QAbstractButton::clicked(bool checked = false) // 关闭窗口的槽函数 [slot] bool QWidget::close();然后我们使用connect()进行连接:
// 单击按钮关闭窗口 connect(ui->closewindow, &QPushButton::clicked, this, &MainWindow::close);注意:connnect()函数一般写在窗口的构造函数中,这相当于在事件发生之前就在Qt框架中进行注册。这样的话,在程序运行的过程中如果产生了信号,那么就会调用槽函数进行处理,如果没有信号产生的话,槽函数就不会被调用。
3. 自定义槽函数的使用
当Qt提供的一些标准信号和槽函数不能满足我们的需求时,我们可以自己定义信号和槽函数,同样使用connect()函数进行连接。
但是有一些自定义信号和槽函数需要注意的事项:
- 自定义的类必须让其继承Qt的某些标准类
- 这个新子类必须从QObject或者QObject的子类继承
- 在头文件中派生类中需要引入Q_Object宏
// 在头文件派生类的时候,首先像下面那样引入Q_OBJECT宏: class MyMainWindow : public QWidget { Q_OBJECT ...... }
3.1. 自定义信号
- 信号是类的成员函数
- 返回值必须是viod类型
- 信号的名字可以自定义
- 参数可以也可以自定义(信号也可以重载)
- 信号函数只需要声明不需要定义(就是没有函数体实现)
// 举例: 信号重载 // Qt中的类想要使用信号槽机制必须要从QObject类派生(直接或间接派生都可以) class Test : public QObject { Q_OBJECT signals: void testsignal(); // 参数的作用是数据传递, 谁调用信号函数谁就指定实参 // 实参最终会被传递给槽函数 void testsignal(int a); };
3.2. 自定义槽函数
槽函数就是信号的处理动作,本质可以当做普通的成员函数来使用。如果标准槽函数
- 返回值必须是void类型的
- 槽函数本质上也是函数,所以可以重载
- 槽函数指定多少个参数取决于连接连接信号的参数个数
- 槽函数的参数是用来接收信号传递的数据的,信号传递的数据就是信号的参数
总结:
- 槽函数对应的参数个数应该与信号信号传递的参数个数相等,从左到右类型依次对应
- 信号的参数可以大于槽函数的参数个数(只是会导致数据的丢失)
信号函数: void testsig(int a, double b); 槽函数: void testslot(int a);
- Qt槽函数的类型可以是多样的,可以是成员函数、全局函数、静态函数、Lambda表达式。
- 槽函数可以使用关键字进行声明: slots (Qt5中slots可以省略不写)
- public slots:
- private slots: –> 这样的槽函数不能在类外部被调用
- protected slots: –> 这样的槽函数不能在类外部被调用
// 槽函数书写格式举例 // 类中的这三个函数都可以作为槽函数来使用 class Test : public QObject { public: void testSlot(); static void testFunc(); public slots: void testSlot(int id); };下面举一个例子:
// 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()函数进行连接:
// 写在int main()中 // 连接两个重载信号/槽。Qt5+ 推荐用函数指针语法 QObject::connect(&girl, QOverload<>::of(&GirlFriend::hungry), &me, QOverload<>::of(&Me::eatMeal)); // 无参版本 QObject::connect(&girl, QOverload<QString>::of(&GirlFriend::hungry), &me, QOverload<QString>::of(&Me::eatMeal)); // 有参版本运行结果:
(本篇完)
Qt信号槽机制详解





1463

被折叠的 条评论
为什么被折叠?



