21、信号和槽详解---------QT基础

信号和槽详解

信号和槽机制在元对象系统时简单介绍并使用了,这里详细介绍,包括:

1信号和槽的定义

2如何连接信号和槽

3信号和槽的参数匹配规则

4一个信号连接多个槽函数

5连接信号和信号

6信号和槽的几种连接方式

7发送信号

信号和槽的定义

信号需要声明在signals关键字下面

cpp 复制代码
ClassA {
  signals:
    void mySignal(int value);
    void myPropertyChanged(int value);  
}

信号的定义包括 信号的返回类型,一般都是void,信号的名字,以及信号携带的参数。

例如mySignal这个信号返回类型为void,参数为int类型。

信号不需要写实现逻辑

槽函数的声明放在slots关键字下面,但是槽函数有权限控制,有的槽函数是公有的,有的是私有的,有的是受保护的。

槽函数返回类型一般为void,也可以携带参数。

cpp 复制代码
ClassA {
public slots:
    void publicSlot(int value);
protected slots:
    void protectedSlot(int value);
private slots:
    void privateSlot(int value);
}

槽函数需要写实现逻辑

cpp 复制代码
void ClassA::publicSlot(int value)
{
    qDebug()<<"called public slot, value is " << value;
}

void ClassA::protectedSlot(int value)
{
    qDebug()<<"called protected slot, value is " << value;
}

void ClassA::privateSlot(int value)
{
    qDebug()<<"called private slot, value is " << value;
}

关于访问控制,私有的槽函数只能在其所属的类中连接,比如privateSlot只能在ClassA的函数中连接

比如我们将私有的槽函数和信号连接逻辑放在ClassA的构造函数里

cpp 复制代码
ClassA::ClassA()
{
    //在构造函数中连接自己的信号和槽函数
    connect(this,&ClassA::mySignal,this,&ClassA::privateSlot);
}
如何连接信号和槽

连接信号和槽的写法基本如下

cpp 复制代码
connect(sender, &SenderType::signalName, receiver, &ReceiverType::slotName);

翻译过来就是

cpp 复制代码
connect(信号发出者的指针,信号的指针,接收者的指针,槽函数的指针)

可以在任何函数, 连接ClassA的信号和公有槽函数

cpp 复制代码
void con_sig_slot(){
    ClassA ca;
    //连接ca的mySignal信号和ca的publicSlot槽
    QObject::connect(&ca, &ClassA::mySignal, &ca, &ClassA::publicSlot);
}

练习

练习一下,写一下信号和槽连接的代码

注意

在普通函数和main函数中连接信号和槽,需要使用QObject::connect, 需显示值明使用的是QObject下的connect信号。

如果在支持了元对象系统(1 继承了QObject, 2 声明了Q_OBJECT宏 ) 的类里连接信号和槽,可以直接使用connect进行连接。

注意

部分公司信号和槽的连接写法采用QT4版本之前的写法,使用 SIGNALSLOT 宏:

cpp 复制代码
connect(sender, SIGNAL(signalName()), receiver, SLOT(slotName()));
cpp 复制代码
void con_sig_slot_v4(){
    ClassA ca;
    //连接ca的mySignal信号和ca的publicSlot槽
    QObject::connect(&ca, SIGNAL(mySignal()), &ca, SLOT(publicSlot()));
}

这种写法不推荐,编译器不做检测,很多错误在运行时才爆发,很危险。

C++11 风格

qt信号和槽也支持C++11风格连接

cpp 复制代码
connect(sender, &SenderType::signalName, [=]() {
    // Lambda 表达式中的代码
});

例如

cpp 复制代码
void con_sig_slot_v4(){
    ClassA ca;
    //连接ca的mySignal信号和ca的publicSlot槽
    QObject::connect(&ca, SIGNAL(mySignal()), [=](){
        //Lambda 表达式
    });
}
信号和槽函数匹配规则

在 Qt 的信号和槽机制中,信号的参数可以比槽函数的参数多,但必须满足以下条件:

  1. 参数类型和顺序匹配:槽函数的参数类型和顺序必须与信号的前几个参数类型和顺序一致。
  2. 参数数量:槽函数的参数数量必须小于或等于信号的参数数量。

示例

我们能对ClassA 新增一个携带多个参数的信号paramSignal和一个处理多个参数的槽函数paramSlot

cpp 复制代码
class ClassA:public QObject
{
    Q_OBJECT
public:
    ClassA();
signals:
    void paramSignal(int value, QString str, double dvalue);
public slots:
    void paramSlot(int value, QString str);
};

因为paramSlot的参数比paramSignal的参数少,但是槽函数的参数类型和信号的前几个参数类型顺序一致,所以可以连接

cpp 复制代码
void con_param_sig_slot(){
    ClassA ca;
    //连接ca的paramSignal信号和ca的paramSlot槽
    QObject::connect(&ca, &ClassA::paramSignal, &ca, &ClassA::paramSlot);
}
一个信号匹配多个槽函数

一个信号可以连接到多个槽函数, 比如我们的类中声明并定义了多个槽函数和信号

cpp 复制代码
class ClassA:public QObject
{
    Q_OBJECT
public:
    ClassA();
signals:
    void mySignal(int value);
    void mySignal2(int value);
public slots:
    void publicSlot(int value);
    void publicSlot2(int value);
};

我们可以让信号mySignal连接到publicSlot和publicSlot2

cpp 复制代码
void con_sig_mulslots(){
    ClassA ca;
    //一个信号连接多个槽
    QObject::connect(&ca, &ClassA::mySignal, &ca, &ClassA::publicSlot);
    QObject::connect(&ca, &ClassA::mySignal, &ca, &ClassA::publicSlot2);
}
连接信号和信号

信号可以连接信号,将消息转发给其他信号

cpp 复制代码
void con_sig_sig(){
    ClassA ca;
    //一个信号连接另一个信号
    QObject::connect(&ca, &ClassA::mySignal, &ca, &ClassA::mySignal2);
}
信号和槽的几种连接方式

在 Qt 中,信号和槽的连接方式有几种不同的模式,分别是默认模式(Qt::AutoConnection)、直接连接(Qt::DirectConnection)、队列连接(Qt::QueuedConnection)、阻塞连接(Qt::BlockingQueuedConnection)以及唯一连接(Qt::UniqueConnection)。这些连接方式决定了槽函数在哪个线程中被调用。以下是每种连接方式的详细说明:

  1. 默认模式(Qt::AutoConnection

在连接信号和槽时,我们不写具体的连接方式,

说明:

  • 行为:默认模式下,Qt 会根据信号和槽所在的线程自动选择连接方式。如果信号和槽在同一个线程中,使用直接连接;如果信号和槽在不同线程中,使用队列连接。
  • 槽函数线程:如果信号和槽在同一个线程中,槽函数在发送信号的线程中调用。如果信号和槽在不同线程中,槽函数在接收信号的对象所属的线程中调用。

示例:

cpp 复制代码
QObject::connect(sender, &Sender::signal, receiver, &Receiver::slot, Qt::AutoConnection);
  1. 直接连接(Qt::DirectConnection

说明:

  • 行为:信号发出时,槽函数立即在发出信号的线程中被调用。
  • 槽函数线程:槽函数在发送信号的线程中调用。

示例:

cpp 复制代码
QObject::connect(sender, &Sender::signal, receiver, &Receiver::slot, Qt::DirectConnection);
  1. 队列连接(Qt::QueuedConnection

说明:

  • 行为:信号发出时,槽函数调用被放入接收对象所在线程的事件队列中,槽函数将在接收对象的线程中被调用。
  • 槽函数线程:槽函数在接收信号的对象所属的线程中调用。

示例:

cpp 复制代码
QObject::connect(sender, &Sender::signal, receiver, &Receiver::slot, Qt::QueuedConnection);
  1. 阻塞连接(Qt::BlockingQueuedConnection

说明:

  • 行为:信号发出时,槽函数调用被放入接收对象所在线程的事件队列中,且发送信号的线程会阻塞,直到槽函数执行完毕。仅在多线程环境下有效。
  • 槽函数线程:槽函数在接收信号的对象所属的线程中调用。

示例

cpp 复制代码
QObject::connect(sender, &Sender::signal, receiver, &Receiver::slot, Qt::BlockingQueuedConnection);
  1. 唯一连接(Qt::UniqueConnection

说明:

  • 行为:确保信号和槽之间只有一个连接。如果尝试创建重复连接,连接操作会失败。可以与其他连接类型组合使用。
  • 槽函数线程:根据与其他连接类型组合使用的结果确定。

示例

cpp 复制代码
QObject::connect(sender, &Sender::signal, receiver, &Receiver::slot, Qt::UniqueConnection | Qt::AutoConnection);

以上连接方式不用过多关注,实际开发采用默认连接即可,面试会问到信号和槽的连接方式。

发送信号

发送信号使用emit 关键字,后面跟信号即可

cpp 复制代码
void emit_sig(){
    ClassA ca;
    emit ca.mySignal(100);
    emit ca.paramSignal(1024,"hello hema",3.14);
}
练习

定义一个类ClassA,ClassA包含一个信号sigNotify, 参数为(QString , int)类型。

定义一个类ClassB,ClassB包含一个公有的槽函数slotNofiy, 参数为(QString , int), slotNotify 打印收到的信息

实现函数nofity_func, 函数内部分别定义ca对象(Class A类型)以及cb对象(Class B类型),连接ca的sigNotify和cb的slotNotify。

并且发送ca的sigNotify, 测试cb的槽函数是否触发。

答案

分别定义ClassA和ClassB

cpp 复制代码
class ClassA:public QObject
{
    Q_OBJECT
public:
    ClassA();
signals:
    void sigNotify(QString name, int year);
};

classB

cpp 复制代码
class ClassB: public QObject{
    Q_OBJECT
public:
    ClassB(){}
public slots:
    void slotNotify(QString name, int year);
};

void ClassB::slotNotify(QString name, int year)
{
    qDebug() << "slotNotify called, name is " << name
             << " year is " << year;
}

定义函数连接并发送信号

cpp 复制代码
void con_ca_cb(){
    ClassA ca;
    ClassB cb;
    QObject::connect(&ca, &ClassA::sigNotify, &cb, &ClassB::slotNotify);
    emit ca.sigNotify("zack",28);
}

main函数调用con_ca_cb会看到程序输出

cpp 复制代码
slotNotify called, name is  "zack"  year is  28
相关推荐
女王大人万岁34 分钟前
Golang实战gRPC与Protobuf:从入门到进阶
服务器·开发语言·后端·qt·golang
sycmancia1 小时前
Qt——计算器示例(用户界面与业务逻辑的分离)
开发语言·qt·ui
charlie1145141912 小时前
现代Qt开发——0.1——如何在IDE中配置Qt环境?
开发语言·c++·ide·qt·嵌入式
Dovis(誓平步青云)2 小时前
《QT学习第二篇:QT的常用控件属性与按钮、view系列、Label、输入框》
开发语言·qt·学习
黎相思12 小时前
音乐播放器
qt
森G18 小时前
46、环境配置---------QChart
c++·qt
冉佳驹1 天前
Qt【第六篇】 ——— 事件处理、多线程、网络与文件等操作详解
qt·http·udp·tcp·事件·多线程与互斥锁
用户805533698031 天前
嵌入式Linux驱动开发——模块参数与内核调试:让模块"活"起来的魔法
qt
冉佳驹1 天前
Qt【第七篇】 ——— QSS 样式表与绘图 API 核心用法及 UI 定制功能总结
qt·qbrush·qpainter·qss·paintevent·qpen
森G1 天前
45、QGraphicsScene 与 QGraphicsView 框架---------绘图
c++·qt