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
相关推荐
西装没钱买2 小时前
QT组播的建立和使用(绑定特定的网卡,绑定特定IP)
网络·c++·qt·udp·udp组播
森G3 小时前
20、元对象系统---------QT基础
qt
Laurence3 小时前
CMake 报错 Failed to find required Qt component WebEngineWidgets
qt·webengine·cmake·找不到
习惯就好zz3 小时前
Qt Quick 系统托盘完整实践
开发语言·qt·qml·系统托盘·system tray·qapplication·qguiapplication
笨笨马甲3 小时前
Qt集成OpenCV
开发语言·qt
笨笨马甲3 小时前
Qt 工业机器视觉开发
开发语言·qt
小灰灰搞电子3 小时前
Qt 打印输出:printf与qDebug的区别
开发语言·qt
火山上的企鹅4 小时前
Qt/QGroundControl 实战:接入 Skydroid(云卓) G20 遥控器 Android SDK 并实时显示摇杆与信号质量
android·开发语言·qt·qgroundcontrol·云卓sdk
白杆杆红伞伞4 小时前
Qt进程间通信
开发语言·qt