一、环境
- 系统:Window10 64企业版
- Qt:qt5.12.12
- VS:vs2017企业版
- addin:2.8.1.6
二、信号和槽
2.1信号
- Qt通过类中声明的信号和槽函数实现类(同一个类或不同类)的对象之间信息流的联动。
- 信号:
- 自定义声明关键字:signal;
- 信号类似void返回值函数的声明方法;
- 信号没有实现只有声明;
- 信号的触发方式在指定函数位置emit signalName();
- 信号可以不加参数,也可以添加参数。当添加参数时参数分为系统默认支持的数据类型和自定义数据类型的两种,信号需要传递自定义参数需要手动注册这种数据类型,注册方法:类的声明部分需要添加:
1 使用方法(声明和注册自定义数据类型) 2 3 1)引入头文件:#include<QMetaType> 4 5 6 2)添加声明(一般在.h文件):利用宏 Q_DECLARE_METATYPE 7 8 //这个函数一定要用在调用或者绑定信号槽前 9 3)注册(一般在.cpp文件构造函数中):利用方法 qRegisterMetaType
2.2槽函数
- 槽函数:
- 当信号被触发响应信号的响应处理;
- 返回值为void类型,参数和信号一致,可以接收信号传递过来的值(自定义参数需要注册);
- 可以跨线程;
- 信号和槽的关联方式一般分为两种:系统根据规则自动关联信号和槽;手动声明信号和槽函数关联关系。
- 信号和槽根据名称自动关联的原因是下面这个语句:
1 //在ui文件生成的.h文件会发现下面这个语句 2 3 QMetaObject::connectSlotsByName(xxxClass);
2.3关联信号和槽的方法
信号和槽的关联方式:
- QObject::connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type = Qt::AutoConnection)
- 参数说明:第一个参数是信号发送对象的地址指针(注意是对象不是类),第二个参数发送的信号地址,第三个接受者对象地址,第四个接收者处理方法,第五个参数关联类型。
具体使用过程有两种方式:
- Qt4.0中常用,支持信号和槽函数重载,例如:connect(ui->pushButton,SIGNAL(clicked()),this,SLOT(pushButon1_clicked()));
- Qt5以后使用的新的绑定方法,不支持信号和槽的重载。但是这种新的方法去掉了使用的宏,改为信号和槽的类,例如:
connect(ui->pushButton_2,&QPushButton::clicked,this,&::MainWindow::pushButon2_clicked);
类似lamda表达式,槽函数直接实现:connect(ui->pushButton_4, QOverload<bool>::of(&QPushButton::clicked),[=](bool check){ ui->textBrowser->setText("按钮4信号绑定成功");});
connect第五个参数:
- connect具体使用过程经常只用四个参数,第五个参数用默认值。
第五个参数使用过程几个值及相关意义:
- Qt::AutoConnection: 默认值,使用这个值则连接类型会在信号发送时决定。如果接收者和发送者在同一个线程,则自动使用Qt::DirectConnection类型。如果接收者和发送者不在一个线程,则自动使用Qt::QueuedConnection类型。
- Qt::DirectConnection:槽函数会在信号发送的时候直接被调用,槽函数运行于信号发送者所在线程。效果看上去就像是直接在信号发送位置调用了槽函数。这个在多线程环境下比较危险,可能会造成奔溃。
- Qt::QueuedConnection:槽函数在控制回到接收者所在线程的事件循环时被调用,槽函数运行于信号接收者所在线程。发送信号之后,槽函数不会立刻被调用,等到接收者的当前函数执行完,进入事件循环之后,槽函数才会被调用。多线程环境下一般用这个。
- Qt::BlockingQueuedConnection:槽函数的调用时机与Qt::QueuedConnection一致,不过发送完信号后发送者所在线程会阻塞,直到槽函数运行完。接收者和发送者绝对不能在一个线程,否则程序会死锁。在多线程间需要同步的场合可能需要这个。
- Qt::UniqueConnection:这个flag可以通过按位或(|)与以上四个结合在一起使用。当这个flag设置时,当某个信号和槽已经连接时,再进行重复的连接就会失败。也就是避免了重复连接。
关于线程:
- QThread 是用来管理线程的,它所依附的线程和它管理的线程并不是同一个线程;
- QThread 所依附的线程,就是执行 QThread t(0) 或 QThread * t=new QThread(0) 的线程。也可以看作主线程
- QThread 管理的线程,就是 run 启动的线程。也就是子线程,只有run函数中创建的对象,才属于子线程(注意qudpsocket跨线程报错),继承自QThread的线程类的成员函数和成员变量都不在子线程;
- 因为QThread的对象依附在主线程中,所以他的slot函数会在主线程中执行,而不是子线程。除非:QThread 对象依附到子线程中(通过movetoThread)slot 和信号是直接连接,且信号在次线程中发射
但上两种解决方法都不好,因为QThread不是这么用的(Bradley T. Hughes)
2.4取消信号和槽的关联
使用方法和connect类似,关键字用disconnect即可。此过程不需要第五个参数。
取消方式:
bool QObject::disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *method)
三、自定义类型注册
注册方法及步骤:
- 自定义类型(结构体/类),声明文件包含QMetaType:#include <QMetaType>;
- 自定义类型结尾通过宏Q_DECLARE_METATYPE,注册:Q_DECLARE_METATYPE(MyClassType);
- 建立信号槽关联connect之前用qRegisterMetaType注册(这个类文件也需要包含头文件QMetaType:#include <QMetaType>)
注意:Q_DECLARE_METATYPE、qRegisterMetaType注册过程对象、引用、指针是不一样的。
示例:
1 //自定义类型文件 2 #include <QMetaType> 3 4 // 5 Q_DECLARE_METATYPE(Custom) 6 7 8 //关联信号槽的类文件 9 qRegisterMetaType<Custom>("Custom&");
四、相关参考
官方参考: