Qt5中的信号槽
Qt4中的信号槽
Qt4中的信号槽是通过SIGNAL,SLOT
两个宏,将参数转换成字符串.Qt编译前,会从源码的头文件中提取由signal
和slot
声明的信号和槽的函数,
将其组成一张信号和槽对应的字符串表.connect
函数的作用是,将信号关联的槽字符串,同这张表的信息进行对比.这样信号发出的时候,就可以知道调用哪一个槽函数了.
Qt4信号槽的不足
- 没有编译期的检查:Qt4中的信号槽会被宏转化成字符串处理,而字符串的比较机制是在程序运行的时候检测的.而且,转换成字符串后,信号槽的参数数据类型就会丢失.这就导致,有的时候,信号槽在编译的时候没有问题,在运行的时候,反而出错.
- 无法使用相容类型的参数:因为信号槽的机制使用的是字符串的匹配的方法,所以,槽函数的参数类型的名字,必须和信号参数类型的名字一致,同时,还必须和头文件中声明的类型名字一致,也就是字符串意义上的严格相同.如果使用了
typdef
或者namespace
这样的类型,虽然实际的类型是一样的,但是由于字符串的名字不一样,所以Qt4中是会有错误的.如下伪代码示例(实际类型都是int,但因为按照字符串处理,所以Qt4中,编译前不能通过.)
//head.h file
typedef int MyInt;
typedef int BigInt;
//head.cpp file
connect(Sender,SIGNAL(sigFun(MyInt)),Receiver,SLOT(sltFun(BigInt)));
Qt5中的信号槽
Qt5中不仅解决了上述Qt4中的问题,而且还有一些扩充.
- 支持编译期的检查:拼写错误,槽函数参数个数大于信号参数的个数等;
- 支持相容类型的自动转换;
- 槽允许连接到任意的函数:Qt5中,因为槽使用的是函数指针,所以槽的调用,可以是任意的成员函数,静态函数,还可以是C++11 的lambda表达式;Qt4中槽的声明一般是
private slots
,private
是私有限制,只有把槽函数当作普通函数使用的时候,才会体现私有的性质.而SLOT
,把槽函数转化成了字符串,此时private
是不起作用的.Qt5中,因为使用的是函数指针,所以在类的外部,connect
是无法关联一个类的私有槽的,否则,编译的时候就会报错.
总之,Qt5中,增加了信号槽的灵活性,加强了信号槽的检测性.
Qt5信号槽的语法例子
常用用法
//ClassA.h
signal:
void sigClassA(int num);
void sigStringChanged(QString str);
//ClassB.h
void sltClassB(int num);//任意的成员函数,静态函数都阔以
void sltStringChanged(QVariant str);
//ClassB.cpp
connect(Sender, &ClassA::sigClassA, this, &ClassB::sltClassB);//函数指针关联的时候,不需要指明参数,而且this可以省略[**Update:2016_11_20,注意在这种情况下,可以省略, 在其他的情况就一定了.省略会有错误危险的.**]
connect(Sender, &ClassA::sigClassA, &ClassB::quit);//省略了this,同时静态函数quit作为槽
//QString 可以转化成 QVariant
connect(Sender, &ClassA::sigStringChanged, &ClassB::sltStringChanged);//信号槽的参数类型可以发生隐士类型转化即可
信号槽的重载
解决方法:
- 使用Qt4的方法(不再介绍)
- Qt5显示转换函数指针
//信号的重载和槽的重载都是一样的解决机制
//ClassA.h
signal:
void sigClassA();
void sigClassA(int num);
//ClassB.h
void sltClassB();
void sltClassB(int num);
//ClassB.cpp
connect(Sender, static_cast<void(ClassA::\*)()>(&ClassA::sigClassA),//注意\*为markdown转义
this,static_cast<void(ClassA::\*)()>(&ClassB::sltClassB) );
connect(Sender, static_cast<void(ClassA::\*)(int)>(&ClassA::sigClassA),
this,static_cast<void(ClassA::\*)()>(&ClassB::sltClassB) );
connect(Sender, static_cast<void(ClassA::\*)(int)>(&ClassA::sigClassA),
this,static_cast<void(ClassA::\*)(int)>(&ClassB::sltClassB) );
带默认数值的槽函数
解决方法:
- 进一步的封装函数(不做介绍)
- 采用Qt5的C++11 lambda表达式(表达式规则暂且不做详细介绍)
//ClassA.h
signal:
void sigClassA();
void sigClassA(int num);
//ClassB.h
void sltClassB(int num = 10);
//ClassB.cpp
connect(Sender, static_cast<void(ClassA::\*)(int)>(&ClassA::sigClassA),//注意\*为markdown转义
this,static_cast<void(ClassA::\*)(int)>(&ClassB::sltClassB) );//信号和槽的参数个数对应,是可以的
connect(Sender, static_cast<void(ClassA::\*)()>(&ClassA::sigClassA),
this,static_cast<void(ClassA::\*)()>(&ClassB::sltClassB) );//槽的参数,比信号多,这个会报错误的
//函数参数的默认数值,只有在函数调用的时候,才会有效,取函数地址的时候,是看不到参数的默认数值的.函数指针并不包含默认数值.又因为槽包含默认数值,所以信号可以不提供参数.那么,这就和信号的参数个必须大于槽的参数的个数产生了矛盾.
connect(Sender, static_cast<void(ClassA::\*)()>(&ClassA::sigClassA),
this,[=](int num = 10){//使用了lambda表达式
//...函数体
}] );
[update:2017_10_13]
信号和槽函数使用自定义数据类型
对于自定义的数据类型,使用前去要使用qRegisterMetaType<NEW_TYPE>("NEW_TYPE");
进行一下类型的注册。
[update:2016_11_20]
思考this的省略?
前面提到过connect函数的第三个参数this指针是可以省略的.但是在某些情况下this是绝不可以省略的.甚至我建议大家为了避免不必要的错误, this指针最好不要省略, 还是带上比较好.connect函数基本是如下的原型:
connect( 发送者, 发送者信号, 接收者, 接收者处理方法 ); ///< 一般的四个参数
connect( 发送者, 发送者信号, **发送者**处理方法 ); ///< 如果省略this, 是三个参数, 那么最后一个参数的意义就发生了变化. 此时调用的方法则是发送者自己的方法.
///< 试想一下, 如果此时发送者和接收者又刚好拥有同样的函数名字, 但是内部的方法不同, 那么最后的结果就会让人莫名奇妙的诡异起来.
所以,一定要明确的区分每个参数的具体意义, 马马虎虎最终还是自己填坑.
你也看到connect是可以使用C++的匿名函数的, 也是可以省略this的,但是, 这一步一定要小心了. 尤其是当你在使用线程的时候, 在接收线程信号的时候, 一万个小心.比如:
connect(pThread, &QThread::finished, [=]() { myFun(); } ); ///< 当线程执行完后, 你会惊奇的接收到应用程序的崩溃. 基本的提示内容, 就是, 你的某个线程出了问题.
///< 就上面的问题,你可以在多个地方, 把线程的ID打印出来就知道了.
qDebug() << "ThreadId1:";
connect(pThread, &QThread::finished, [=]()
{
qDebug() << "ThreadId2:";
myFun();
} );
///< 打印出来以后, 你会发现lambda表达式函数里面ID和线程run函数里面的id是一样的. 虽然说, 代码在不同的类里面, 不同的文件里面. 可是运行环境, 运行的线程却是可以在一起的. 解决方法, 加个this, 就可以了. 你懂的.
信号槽的高级应用
Help manual 说的是高级应用,也不是什么特别深奥的东西,面对一下特殊的需求,解决方法有很多种。
特殊需求:
大部分信号和槽的使用,都是少数对象的信号和少数槽的链接,写几个connect就可以了。如果有很多对象,同类的或者不同类的,每个对象对应不同的信号槽,那么写起来就繁琐一些了。这时候官方提供的是使用QSignalMapper
类,进行信号和槽的映射。 民间也有很多其他的好方法;
- 借助 QSignalMapper
- 每一个槽在触发的时候,都可以获得对应的sender对象,可以根据不同的对象,进行不同的操作。
void slotFun(){
QObject* obj = sender();
}
- 或者使用lambda表达式.
connect(m_button, &QPushButton::clicked,[m,this]()->void{HandleButton(m);});
信号和槽的注意
- Strongly advise against deleting the signal object in the slot function.
参考
《Qt 5编程入门》
Qt Signal and Slots
QSignalMapper
QSignalSpy
QSignalBlocker
QSignalTransition
Qt Forum
Qt5中的信号槽的更多相关文章
- QT5中的信号与槽与C++ 11的function的配合
最近将公司的界面开发库从WX转换到了QT,有了信号和槽,原本我们在使用WX的时候,为了使用信号,我们是使用BOOST的signal2库,到了QT有了信号槽,这个就没什么必要了 但是前段时间使用QT发现 ...
- QT中的信号槽
只有继承了QObject类的类,才具有信号槽的能力.所以,为了使用信号槽,必须继承QObject. 凡是QObject类(不管是直接子类还是间接子类),都应该在第一行代码写上Q_OBJECT. 不管是 ...
- VS2008 Qt Designer 中自定义信号槽
一.Qt Designer自定义槽函数 发现:在VS2008 +Qt4.7 中打开ui文件,所用的英文QT Designer工具,没有转到槽函数的功能,不如QtCreator自带的QtDesigne ...
- 【linux】【qt5】【信号槽示例】
什么叫信号槽: 信号槽是 Qt 框架引以为豪的机制之一.所谓信号槽,实际就是观察者模式.当某个事件发生之后,比如,按钮检测到自己被点击了一下,它就会发出一个信号(signal).这种发出是没有目的的, ...
- Qt在多线程中使用信号槽的示例
之前对线程理解得不深入,所以对Qt的线程机制没有搞清楚,今天写一篇文章总结一下,如有错误,欢迎指出. 首先需要理解线程是什么,线程在代码中的表现其实就是一个函数,只不过这个函数和主线程的函数同时运行, ...
- Qt信号槽的一些事(第一次知道信号还有返回值,以及Qt::UniqueConnection)
注:此文是站在Qt5的角度说的,对于Qt4部分是不适用的. 1.先说Qt信号槽的几种连接方式和执行方式. 1)Qt信号槽给出了五种连接方式: Qt::AutoConnection 0 自动连接:默认的 ...
- QT信号槽详解
1 QT信号槽详解 1.1 信号和槽的定义 信号是触发信号,例如按钮的点击触发一个clicked信号,槽是用来接收信号,并处理信号,相当于信号响应函数.一个信号可以关联多个槽函数,信 ...
- Qt5中运行后台网络读取线程与主UI线程互交
项目中有一个需求就是,因为需要请求服务端数据,因为网络的读取会阻塞,所以该过程不能放在Qt中的UI主线程当中,需要用一个后台线程来读取数据,数据准备完毕后 在通过Qt5中的信号槽机制来跨线程的传递数据 ...
- Qt信号槽的一些事 Qt::带返回值的信号发射方式
一般来说,我们发出信号使用emit这个关键字来操作,但是会发现,emit并不算一个调用,所以它没有返回值.那么如果我们发出这个信号想获取一个返回值怎么办呢? 两个办法:1.通过出参形式返回,引用或者指 ...
随机推荐
- js数组操作大全
原文(http://www.cnblogs.com/webhotel/archive/2010/12/21/1912732.html) 用 js有很久了,但都没有深究过js的数组形式.偶尔用用也就是简 ...
- Android进阶系列之源码分析Activity的启动流程
美女镇楼,辟邪! 源码,是一个程序猿前进路上一个大的而又不得不去翻越障碍,我讨厌源码,看着一大堆.5000多行,要看完得啥时候去了啊.不过做安卓的总有这一天,自从踏上这条不归路,我就认命了.好吧,我慢 ...
- 编译原理-词法分析05-正则表达式到DFA-01
编译原理-词法分析05-正则表达式到DFA 要经历 正则表达式 --> NFA --> DFA 的过程. 0. 术语 Thompson构造Thompson Construction 利用ε ...
- btrace使用
btrace使用 目录btracee是btrace的解压目录 btrace/btrace是btrace的项目工程 root@ubuntu:/usr/local/bogon/btrace# tree b ...
- C++: Perfect Forwarding
Link: Rvalue References and Perfect Forwarding in C++0x (https://www.justsoftwaresolutions.co.uk/cpl ...
- Linux下添加apache虚拟主机
一切在确保apache被正确安装的前提下 设置虚拟主机 创建虚拟目录 现在,让我们继续安装虚拟主机.虚拟主机命名为local.gis. 创建一个公用的文件夹来存放这虚拟主机的数据. 首先,让我们为lo ...
- MVC高级编程+C#高级编程
本人今年的目标是学习MVC高级编程和C#高级编程,把自己的基础打的扎实,本文中值是一个开到,定期会在上面记录学习的技术点和心得就,加油吧!!!!!
- PHP+JQUEY+AJAX实现分页【转】
HTML CSS #list{width:680px; height:530px; margin:2px auto; position:relative} #list ul li{float:left ...
- IE6 的兼容相关问题
因为在实习公司要求兼容IE6+,所以将IE6相关的样式兼容问题列出,及解决方案. 1.让页面变丑的透明背景图片问题: HTML都为以下代码: <div class="img-png&q ...
- How Long Does It Take
好长时间没写博客了,真心惭愧啊! 废话少说,原题链接:https://pta.patest.cn/pta/test/1342/exam/4/question/24939 题目如下: Given the ...