写点什么

Qt | 信号和槽的一些总结

作者:YOLO.
  • 2022 年 7 月 28 日
  • 本文字数:2190 字

    阅读完需:约 7 分钟

Qt | 信号和槽的一些总结

前言:

信号和槽用于两个对象之间的通信,信号和槽机制是 Qt 的核心特征。在使用信号和槽时,一个信号可以关联到多个槽上,多个信号也可以关联到同一个槽上,一个信号还可以关联到另一个信号上。如果是一个信号关联多个槽,这些槽会一个接一个地执行,执行顺序与关联顺序相同。

之前遇到一个面试题

  • 如果同一个信号和同一个槽函数进行了多次连接(connect),是否会造成崩溃?

  • 答案是不会,如果同一个信号和同一个槽函数进行多次连接,那么信号在发出后,会多次执行槽函数。

信号和槽详解:

  • 声明一个信号需要使用 signals 关键字,在 signals 前面不能添加 public、private、protected 等限定符。信号默认是 public 的,可以在任何地方进行发射(emit),但是建议只在定义该信号的类及其子类中进行发射改信号。

  • 信号只用声明,不需要实现。而且,信号没有返回值,只能是 void 类型的。

  • 只有 QObject 类及其子类派生的类才可以使用信号和槽机制。使用信号和槽还必须在类声明的最开始处添加 Q_OBJECT 宏。

  • 槽就是普通的 C++函数,可以像一般函数一样使用。声明槽要使用 slots 关键字,一个槽可以是 private、public 或者 protected 类型的,也可以被声明为虚函数。

  • 槽函数中的参数的类型和信号参数的类型要相对应,且不能比信号的参数多。

connect 详解:

  • 第一个参数为发射信号的对象。

  • 第二个参数为要发射的信号。

  • 第三个参数为接收信号的对象。

  • 第四个参数为要执行的槽。

  • 第五个参数 type 为关联的方式。

Qt::AutoConnection: 自动关联,默认值。如果 receiver 于发射信号在同一线程,则该类型表示 Qt::DirectConnection;否则,该类型表示 Qt::QueuedConnection。在信号被发射时决定表示那种类型。

Qt::DirectConnection: 直接关联。发射完信号后立即调用槽函数,只有槽函数执行完成返回后,发射信号后面的代码才会执行。

Qt::QueuedConnection: 队列关联。当控制返回 receiver 所在线程的事件循环后再执行槽函数,无论槽函数是否执行,发射信号后面的代码都会立即执行。

Qt::BlockingQueuedConnection: 阻塞队列关联。类似 Qt::QueuedConnection,不过,信号线程会一直阻塞,知道槽函数返回。当 receiver 存在于信号线程时不能使用该类型,不然程序会死锁。

Qt::UniqueConnection: 唯一关联。这是一个标志,可以结合其他几种连接类型,使用按位或操作。这时两个对象间的相同的信号和槽只能有唯一的关联。使用这个类型主要是为了防止重复关联。

  • Qt5 之前的形式: connect(dlg, SIGNAL(funcSignal(int)), this, SLOT(funcSlot(int)));

  • Qt5 加入的一种形式: connect(dlg, &MyDialog::funcSignal, this, &Widget::funcSlot);

使用 Qt5 的这种形式的一个好处就是可以再编译时进行检查,信号或槽的拼写错误、槽函数参数数目多于信号的参数数目等错误都会在编译时发现。

另外,这种形式还支持 C++11 中的 Lambda 表达式,可以在关联时直接编写信号发射后要执行的代码,例如:

connect(dlg, &MyDialog::funcSignal, [=](int value){ui->label->setText(QString::number(value))});

信号和槽的自动关联:

槽函数:on_"部件的 objectName"_"信号名称"。

  • 以这种方式命名的槽函数可以直接和信号关联,不用再使用 connect()函数。

  • connectSlotsByName()函数是用来支持信号和槽自动关联的,因为 setupUi()函数中自动调用了 connectSlotsByName()函数,所以要使用自动关联的部件的定义都要放在 setupUi()函数调用之前,且必须使用 setObjectName()指定它们的 objectName,只有这样才能正常使用自动关联。

断开关联 disconnect:

  • 断开与一个对象多有信号的所有关联:disconnect(myObject, 0, 0, 0); 等价于 myObject->disconnect();

  • 断开与一个指定信号的所有关联:disconnect(myObject, SIGNAL(mySignal()), 0, 0);等价于 myObject->disconnect(SIGNAL(mySignal()));

  • 断开与一个指定的 receiver 的所有关联:disconnect(myObject, 0, myReceiver, 0); 等价于 myObject->disconnect(myReceiver);

  • 断开一个指定信号和槽的关联:disconnect(myObject, SIGNAL(mySignal()), myReceiver, SLOT(mySlot())); 等价于 myObject->disconnect(SIGNAL(mySignal()), myReceiver, SLOT(mySlot()));还等价于 disconnect(myConnection);//myConnection 是进行关联时 connect()的返回值。

disconnect 也可以使用基于函数指针的重载形式,及 Qt5 后添加的不需要 SIGNAL()和 SLOT()宏的形式。

关于信号槽的高级应用:

Qt 提供了 QObject::sender() 函数来返回发送该信号的对象的指针

有时存在多个信号关联到同一个槽上的情况,此时如果想在槽函数中对某个信号进行特殊处理,就可以通过 QObject::sender()来获取信号的对象指针,来进行判断是否是我们需要特殊处理的对象。

如果在多个信号关联到同一个槽上,而在该槽中需要对每一个信号进行不同的处理,可以使用 QSignalMapper 类。QSignalMapper 被叫做信号映射器,可以实现对多个相同部件的相同信号进行映射,为其添加字符串或者数值参数,然后再发射出去。

QSignalMapper 使用举例:

QSignalMapper *signalMapper = new QSignalMapper(this);for (i = 0; i < 5; i++) {    QPushButton *button = new QPushButton(this);    connect(button, SIGNAL(clicked()), signalMapper, SLOT(map()));    signalMapper->setMapping(button, i);}
connect(signalMapper, SIGNAL(mapped(int)), this, SLOT(handle(int)));
然后槽中可以这样写:switch(i) //i表示是哪个button发生了clicked信号{ //自定义操作}
复制代码


发布于: 1 小时前阅读数: 6
用户头像

YOLO.

关注

还未添加个人签名 2022.05.06 加入

还未添加个人简介

评论

发布
暂无评论
Qt | 信号和槽的一些总结_qt_YOLO._InfoQ写作社区