qt 信号槽_QObject功能及应用介绍:信号-槽

QObject功能及应用介绍:信号-槽

一、QObject概要

QObject是Qt 对象模型机制的核心类,在Qt中QObject类集成了Qt元对象系统,实现了标准C++对象模型没有提供的功能,从而实现了一系列便于用户开发使用的功能,其中最重要的就是信号-槽功能。除此之外,QObject还实现了子节点的内存管理,事件过滤,文本翻译等功能。

在Qt框架中,绝大多数类都是继承自QOjbect类,继承自QObject的类,需要在类头文件代码中添加Q_OBJECT宏,才能使用Qt元对象系统提供的功能,才能使用信号-槽机制,对于很多刚刚开始使用Qt进行开发的用户,如果信号-槽机制正常运行,可能就是没有在自己的类的头文件中添加该宏。在模型情况下,不需要利用Qt的元对象系统,可以不添加该宏,不过Qt的推荐方法,无论是不是需要使用元对象系统,对于继承自QObject类的子类都添加该宏。

二、信号和槽机制

1、 信号-槽概念

信号-槽是Qt提供的一种用于QObject及其子类对象间进行通信的一种机制,这是Qt和其他框架之间重要区别,信号就是信息源,槽就是处理信息的响应函数,信号和槽之间,必须进行连接,连接上以后,当信号被抛出以后,与之相连接的槽,就会按连接顺序一一执行(跨线程情况会有所不同)。

2、 信号-槽申明

信号从代码申明的形式上看,是只有申明,没有定义的函数,所有的信号申明,必须放在“signals:”之后。槽则是一个函数,既有申明,也有定义,所有的槽,必须放在“slots:”申明之后,槽,可以是public,protected,private等访问权限的函数,槽除了和信号相连接,用于响应信号之外,还可以作为单独的函数调用,调用的时候,和普通的函数一样,没有什么额外的限制。

参数问题,当信号和槽的参数个数一样时,信号申明的参数类型必须和槽申明的参数类型一致,类型不一致的时候,会存在连接错位,连接不上;当信号的参数个数,少余槽的参数个数的时候,信号和槽是连接不上的;当信号的参数个数多余槽的参数个数时,以槽的参数个数为基准,信号前面的参数类型,必须和槽的参数类型一致,否则,信号和槽也是连接不上的。通常情况下,信号和槽的参数个数相等,参数类型一致。

3、 信号-槽连接

前文已经说明过信号和槽之间必须连接起来,槽才能响应信号。连接信号和槽的函数是QObject类的connect静态成员函数,这个函数是线程安全的。下面介绍信号和槽的连接几种方法,和几种连接模式,这里的连接方式是指connect函数的重载方法;连接模式,是指connect函数最后一个参数的取值,取值不同连接模式不同。先介绍几种连接方法,再统一介绍最后一个连接模式参数。

第一个连接函数:

connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)

sender参数表示发送信号的源对象地址,即信号是从哪个对象发出来的,该地址必须是QObject类或者其子类的对象地址。

signal 参数是表示信号名称的字符串,Qt提供了SIGNAL()宏函数来生存字符串,该函数的参数是我们已经申明过的信号,直接将整个信号的申明填进SIGNAL()宏函数作为参数即可。

receiver 参数表示槽的对象地址,即由哪个对象来响应信号,和sender一样该地址必须是QObject类或者其子类的对象地址。

method 参数是表示槽名称的字符串,和信号类似,Qt提供了SLOT()宏函数,使用的时候,直接将槽的申明 填进SLOT()宏函数作为参数即可。

下面我们来看一个简单的Demo,也是本系列第一个完整的Demo,主要介绍信号槽的第一种连接方式。本系列教程开发环境采用VS2017 + Qt5.13.2,具体安装VS2017和配置Qt5.13.2的方法,网上有很多教程,这里就不重复介绍了。本教程约定所有信号都已sig字母开头,所有槽以slot字母开头,这样就很容易一眼区分开信号,槽和其他函数,另外其他自己定义的函数都以大写字母开发,这样和Qt定义函数,也就容易区分了。

Demo里面包含了3一个文件,一个main.cpp文件,另外两个是SignalSlot.h, SignalSlot.cpp,在这两个文件里实现信号-槽机制。首先,定义了一个能抛出信号的类SignalObjec,SignalObjec定义了一个信号sigSendParam,该信号带两个整形参数,定义了一个常规函数EmitSignal,当调用这个函数的时候sigSendParam信号就会被抛出。抛出信号是调用Qt 提供的emit宏加信号的函数调用形式。另外定义了一个能响应信号的类SlotObject,SlotObject定义了一个槽函数slotAcceptParam,函数的参数个数,类型和sigSendParam信号一致,在slotAcceptParam的实现里面简单的输出对应的函数参数,用以验证槽函数是不是被自动的调换以及信号传递过来的参数是不是正确的。

在main函数里面,先分别申明了SignalObjec sigObj和SlotObject slotObj两个对象,然后使用QObject的connect函数实现了信号和槽的连接,连接后,调用了sigObj对象的EmitSignal函数抛出了一个信号,通过控制台输出,我们看到正确的实现了信号和槽的连接。代码和控制台输出如下:

#pragma once

#include <QObject>

class SignalObject : public QObject

{

Q_OBJECT

public:

SignalObject(QObject *parent);

~SignalObject();

void EmitSignal();

signals:

void sigSendParam(int a, int b);

};

class SlotObject : public QObject

{

Q_OBJECT

public:

SlotObject(QObject *parent);

~SlotObject();

public slots:

void slotAcceptParam(int a, int b);

};

#include "SignalSlot.h"

#include <QDebug>

SignalObject::SignalObject(QObject *parent)

: QObject(parent)

{

}

SignalObject::~SignalObject()

{

}

void SignalObject::EmitSignal()

{

emit sigSendParam(3, 4);

}

SlotObject::SlotObject(QObject *parent)

: QObject(parent)

{

}

SlotObject::~SlotObject()

{

}

void SlotObject::slotAcceptParam(int a, int b)

{

qInfo() << "a = " << a << " b = " << b;

}

#include <QtCore/QCoreApplication>

#include "SignalSlot.h"

int main(int argc, char *argv[])

{

QCoreApplication a(argc, argv);

SignalObject sigObj(nullptr);

SlotObject slotObj(nullptr);

QObject::connect(&sigObj, SIGNAL(sigSendParam(int, int)), &slotObj, SLOT(slotAcceptParam(int, int)));

sigObj.EmitSignal();

return a.exec();

}

8ee9f4ec7906f88afe24fe8b022be556.png

第二个连接函数:

connect(const QObject *sender, const char *signal, const char *method, Qt::ConnectionType type) const

与第一个连接函数相比,少了receiver参数,暗示receiver就是sender,表示表示信号和对象自身的槽函数进行连接。

第三个连接函数:

connect(const QObject *sender, PointerToMemberFunction signal, const QObject *receiver, PointerToMemberFunction method, Qt::ConnectionType type)

与第一个连接函数相比,第2和第4个参数类型不再是字符指针类型信号和槽,而是PointerToMemberFunction类型,该类型是成员函数指针,使用该方法进行连接的时候只需要写上函数指针,不需要带上形参类型。上面的main()函数可以换成如下代码的连接方式:QObject::connect(&sigObj, &SignalObject::sigSendParam, &slotObj, &SlotObject::slotAcceptParam)。需要提出来的是,采用这种连接方式的时候,响应信号的函数不必须是槽函数,普通的C++成员函数就可以了。

第四个连接函数:

connect(const QObject *sender, PointerToMemberFunction signal, Functor functor),函数的参数变成了三个,没有了槽函数所属的对象地址,第三个参数的类型变成了Functor,它是一个可调用对象,在C++11以及后期的C++标准中,可调用对象的范围比较多了,介绍下两种常见的方式,普通函数方式和Lamada表达式方式,我们还是简单的连接信号后,直接输出接到的参数。代码和输出如下:

函数形式:

void FunctionAcceptParam(int a, int b)

{

qInfo() <<"Function " << "a = " << a << " b = " << b;

}

QObject::connect(&sigObj, &SignalObject::sigSendParam, FunctionAcceptParam);

Lamada表达式形式:QObject::connect(&sigObj, &SignalObject::sigSendParam, [](int a, int b)

{

qInfo() << "Lamada " << "a = " << a << " b = " << b;

});

4167f087bddeef06b7cf18c20f23acc3.png

第五个连接函数:

Connection QObject::connect(const QObject *sender, PointerToMemberFunction signal, const QObject *context, Functor functor, Qt::ConnectionType type = Qt::AutoConnection)与第四个连接函数相比多了一个context参数,该参数表示,连接函数将会在context对象所在的事件循环中执行,关于这个参数的细节描述会比较复杂,再后面介绍了事件循环和Qt多线程以后,再来阐述改连接函数,会比较清晰,目前想放一放细节阐述。

信号-槽的连接方式

本小节介绍下connect函数的最后一个参数type,这是一个ConnectionType枚举类型参数,一共有5个取值,分别是:Qt::AutoConnection,Qt::DirectConnection,Qt::QueuedConnection,Qt::BlockingQueuedConnection,Qt::UniqueConnection。

Qt::AutoConnection表示自动连接,是type申明的默认值,当槽对象和信号对象在同一个线程的时候使用Qt::DirectConnection进行连接,不在同一个线程的时候使用Qt::QueuedConnection方式进行连接。

Qt::DirectConnection表示直接连接,信号抛出后,槽函数立即在信号所在的线程里面执行。

Qt::QueuedConnection表达队列连接,信号抛出后,槽函数在它自己所在的线程里面,等到事件循环到来时再执行。

Qt::BlockingQueuedConnection表示阻塞队列连接,槽函数的执行方式和Qt::QueuedConnection一致,但是会阻塞信号线程,信号线程必须在槽函数得到执行以后才能继续执行。

Qt::UniqueConnection练级方式,是其它几种连接方式的组合,这种形式在实际是使用过程中比较少。

4、 信号-槽断开

信号和槽连接起来的目的是进行信息传递,将信号所在的对象的信息传递给槽所在的对象,有些对象在已经封装好的函数里面抛出信号,当这些函数被调用,信号就会被抛出来,与之相连接的槽函数就会执行,然后用户并不是每次都需要去响应这些信号,这个时候,就需要先将信号和槽断开,断开的函数是disconnect,它的参数及其参数含义与connect完全一致,就不在赘述了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值