Qt4中的信号槽

Qt4中的信号槽是通过SIGNAL,SLOT两个宏,将参数转换成字符串.Qt编译前,会从源码的头文件中提取由signalslot声明的信号和槽的函数,

将其组成一张信号和槽对应的字符串表.connect函数的作用是,将信号关联的槽字符串,同这张表的信息进行对比.这样信号发出的时候,就可以知道调用哪一个槽函数了.

Qt4信号槽的不足

  1. 没有编译期的检查:Qt4中的信号槽会被宏转化成字符串处理,而字符串的比较机制是在程序运行的时候检测的.而且,转换成字符串后,信号槽的参数数据类型就会丢失.这就导致,有的时候,信号槽在编译的时候没有问题,在运行的时候,反而出错.
  2. 无法使用相容类型的参数:因为信号槽的机制使用的是字符串的匹配的方法,所以,槽函数的参数类型的名字,必须和信号参数类型的名字一致,同时,还必须和头文件中声明的类型名字一致,也就是字符串意义上的严格相同.如果使用了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中的问题,而且还有一些扩充.

  1. 支持编译期的检查:拼写错误,槽函数参数个数大于信号参数的个数等;
  2. 支持相容类型的自动转换;
  3. 槽允许连接到任意的函数: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类,进行信号和槽的映射。 民间也有很多其他的好方法;

  1. 借助 QSignalMapper
  2. 每一个槽在触发的时候,都可以获得对应的sender对象,可以根据不同的对象,进行不同的操作。
void slotFun(){
QObject* obj = sender();
}
  1. 或者使用lambda表达式. connect(m_button, &QPushButton::clicked,[m,this]()->void{HandleButton(m);});

信号和槽的注意

  1. Strongly advise against deleting the signal object in the slot function.

参考

《Qt 5编程入门》

Qt Signal and Slots

QSignalMapper

QSignalSpy

QSignalBlocker

QSignalTransition

Qt Forum


Qt Signal and Slot的更多相关文章

  1. 深入了解Qt(三)之元signal和slot

    深入了解Qt主要内容来源于Inside Qt系列,本文做了部分删改,以便于理解.在此向原作者表示感谢! 在Qt 信号和槽函数这篇文章中已经详细地介绍了信号和槽的使用及注意事项.在这里对其使用方面的知识 ...

  2. 【golang-GUI开发】qt之signal和slot(二)

    上一篇文章里我们详细介绍了signal的用法. 今天我们将介绍slot的使用.在qt中slot和signal十分相像,这次我们将实现一个能显示16进制数字的SpinBox,它继承自QSpinbox并重 ...

  3. 【golang-GUI开发】qt之signal和slot(一)

    想了很久,我决定还是先从signal和slot(信号槽)开始讲起. signal和slot大家一定不陌生,先看一段示例(选自文档): class Counter : public QObject { ...

  4. qt的signal和slot机制

    signal和slot是QT中的一大特点 signal/slot是Qt对象以及其派生类对象之间的一种高效通信接口 用户可以将N多个信号和单个槽相连接, 或者将将N个槽和单个信号连接, 甚至是一个信号和 ...

  5. Qt里的slot

    昨天出了一个小bug, 一直调都没调出来, 今天仔细看了下, 发现出错的原因了. 我在用osgEarth的时候, 用到一个类MapCatalogWidget, 觉得它不够用, 就把这个类给改了下, 添 ...

  6. PyQt5 signal and slot

    PyQt5 的 signal 与 slot 有所改变,例如,先定义一个 ZeroSignal 类: class ZeroSignal(QObject): atzero = pyqtSignal(int ...

  7. [C++_QT] Error: Not a signal or slot declaration

    问题: 在Qt工程中添加了一个新的窗口之后 一直报错 如下 单单从错误描述上看 是缺少信号或者槽 但是我确定没有缺少啊 然后第二个错误显示了一个mox_xxxx文件 然后我就去那个目录下去找那个文件 ...

  8. PyQt信号connect连接槽方法时报:native Qt signal is not callable错误

    专栏:Python基础教程目录 专栏:使用PyQt开发图形界面Python应用 专栏:PyQt入门学习 老猿Python博文目录 老猿学5G博文目录 在将一个信号连接到槽方法时,程序异常退出,捕获异常 ...

  9. QT中使用 slot 传递 opencv 中得Mat对象以及 使用多线程集成开源代码。

    关于 slot传递 Mat 对象 以前一直是使用 Qtimer 定时器,设定超时后读取 dialog 对象的 Mat成员实现在 UI 里显示图像,发现这样对以后集成其他面向过程的代码增加了复杂度. 所 ...

随机推荐

  1. (转)Linux查看CPU,硬盘,内存的大小

         分类: linux(21)  在Linux的桌面版本中,查看这些东西的确很方便,有图形化的工具可以使用.但是在Linux服务器版上,或者远程ssh连接的时候,就没有图形化的界面可以操作了.此 ...

  2. SharePoint自动登录问题

    SharePoint使用Windows身份验证,默认会弹出Windows验证登录框,如下图所示: 1.对于已经加域的客户端,可通过如下方式解决 IE安全设置,将站点加信任站点,然后修改信任站点安全设置 ...

  3. 如何使用indexdb

    一.实现步骤 1)获得indexedDB对象 if (!window.indexedDB) { window.indexedDB = window.mozIndexedDB || window.web ...

  4. linux网络编程、系统编程

    http://blog.csdn.net/lianghe_work/article/category/2871247

  5. VBA7种遍历方法

    Sub 在选定文档最后加入一句话() '遍历文件 Dim MyDialog As FileDialog On Error Resume NextApplication.ScreenUpdating = ...

  6. Java 中的this关键字

    1.this关键字代表当前对象 this.属性 操作当前对象的属性 this.方法 调用当前对象的方法 2.封装对象的属性的时候,经常会使用this关键字 public class Telphone ...

  7. 北京师范大学第十六届程序设计竞赛决赛 C萌萌哒身高差

    链接:https://www.nowcoder.com/acm/contest/117/C来源:牛客网 萌萌哒身高差 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 32768K,其他 ...

  8. Windows2012使用笔记

    一.介绍 win 2012的名字于北京时间2012年4月18日公布,全称Windows Server 2012(下面简称win 2012),正式版于2012年9月4日发布.这是一套基于Windows ...

  9. (转)使用 linux tar 命令压缩与解压文件

    原文链接 http://www.cnblogs.com/qq78292959/archive/2011/07/06/2099427.html tar -c: 建立压缩档案-x:解压-t:查看内容-r: ...

  10. 2017百度之星初赛B-1002(HDU-6115)

    一.思路 这题“看似”比较难搞的一点是,一个节点上有多个办公室,这怎么求?其他的,求树中任意两个节点的距离(注意:没有最远或最最进这一说法,因为树上任意两个节点之间有且仅有一条路径.不然就有回路了,对 ...