信号和槽的简介
信号和插槽用于对象之间的通信。信号和插槽机制是Qt的核心特征,可能是不同的部分大部分来自其他框架提供的特性。信号和槽是由Qt的元对象系统实现的。
介绍(来自Qt帮助文档Signals & Slots)
在GUI编程中,当我们更改一个小部件时,我们通常希望通知另一个小部件。更一般地说,我们希望任何类型的对象都能够进行交互相互沟通。例如,如果用户单击Close按钮,我们可能希望调用窗口的Close()函数。其他工具包使用回调实现这种通信。回调可以是一个指向函数的指针(函数指针),所以如果你想要一个处理函数通知你在某些事件中,您将指向另一个函数(回调函数)的指针传递给处理函数。然后,处理函数在适当的时候调用回调。虽然确实存在使用此方法的成功框架,但回调可能不直观,并且可能在确保类型正确性方面存在问题:callback arguments.
对比java的事件处理机制非常类似:
传送门:java事件处理入门http://t.csdnimg.cn/C1SUt
connect函数的使用
以点击按钮关闭窗口来进行演示信号和槽机制:widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include <QPushButton>
Widget::Widget(QWidget *parent) : QWidget(parent) , ui(new Ui::Widget)
{
ui->setupUi(this);
//第二种new方式,省略上面两行
QPushButton *btn2 = new QPushButton("关闭窗口", this);
btn2->move(100,100); //按钮左上点移动到100*100的位置上
connect(btn2, &QPushButton::clicked, this, &QWidget::close);
}
Widget::~Widget()
{
delete ui;
}
【运行结果】
补充知识点:
在GUI(图形用户界面)编程中,不同的界面元素(我们通常称之为“小部件”)之间经常需要相互通信。比如,当你点击一个“关闭”按钮时,你希望这个操作能够通知窗口进行关闭。为了实现这种通信,许多编程框架和库使用了一种叫做“回调”的机制。
什么是“回调”
简单来说,回调就是当某个事件发生时,你告诉另一个函数(或方法)去执行的一种机制。这个被告诉去执行的函数,就是我们所说的“回调函数”。回调允许程序中的不同部分在特定事件发生时相互“交谈”或“通知”。
为什么要用回调?
- 解耦:回调允许我们将事件的发起者和处理者分开,这有助于代码的模块化和重用。
- 灵活性:可以动态地改变或添加新的处理逻辑,而不需要修改事件的发起者。
回调的“例子”
想象一下,你是一家餐厅的服务员。当有顾客点完餐后,你不需要亲自去厨房告诉厨师做什么菜(虽然你也可以这么做,但那样效率很低),而是将订单(事件)交给一个系统(比如一个订单本或者电子点餐系统)。这个系统(或某个厨师助手)会看着订单(监听事件),并在合适的时机(比如厨房准备好做菜时)将订单(事件)交给厨师(回调函数)去处理。
在这个例子中:
- 顾客点餐 相当于触发了某个事件(比如按钮点击)。
- 服务员将订单交给系统 相当于将事件的处理权交给了某个机制(可能是系统、助手或框架)。
- 厨师根据订单做菜 相当于回调函数被调用,执行了具体的处理逻辑。
回调的潜在问题
- 不直观:对于初学者来说,回调的概念可能有些抽象,需要一定的时间来适应和理解。
- 类型安全:在动态类型语言中,回调可能导致类型错误,因为编译器在编译时无法检查回调函数的参数和返回类型是否与预期一致。
- 调试困难:回调可能导致调用栈变得复杂,使得调试更加困难。
尽管存在这些问题,回调仍然是GUI编程和其他许多领域中非常有用和强大的机制。随着现代编程语言和框架的发展,许多工具和库提供了更高级、更直观的替代方案(如事件监听器、信号槽机制、响应式编程等),但它们背后的基本思想仍然与回调紧密相关。
由文心大模型3.5生成
“信号”的原理(来自Qt帮助文档Signals & Slots)
当对象的内部状态以某种方式发生变化,对象的客户端或所有者可能会感兴趣时,对象就会发出信号。信号是公共访问函数,可以从任何地方发但我们建议只从定义信号及其子类的类发出信号。
当发出信号时,连接到它的槽通常立即执行,就像普通的函数调用一样。当这种情况发生时,信号和槽机制完全独立于任何GUI事件循环。一旦所有槽都返回,emit语句之后的代码就会执行。当使用排队连接时,情况略有不同;在这种情况下,emit关键字后面的代码将立即继续执行,而插将稍后执行。
如果多个插槽连接到一个信号,则在信号发出时,这些插槽将按照它们连接的顺序依次执行。
信号是由moc(Meta-Object Compiler,简称moc,处理Qt的C++扩展程序)自动生成的,不能在moc中实现。cpp文件。它们永远不光有返回类型(即使用void)。
关于参数的注意事项:我们的经验表明,如果信号和槽不使用特殊类型它们的可重用性会更好。如果如果QScrollBar::valuechange()使用一个特殊的类型比如假设的QScrollBar::Range,它只能连接到设计的槽专门为QScrollBar。将不同的输入部件连接在一起是不可能的。
“槽”的原理(来自Qt帮助文档Signals & Slots)
当一个连接到插槽的信号被发出时,就会调用这个插槽。Slots是普通的c++函数,可以正常调用;它们唯一的特点是所有的信号都可以连接到它们上面。
由于slots是普通的成员函数,因此在直接调用时遵循普通的c++规则。但是,作为插槽,它们可以被任何组件调用,而不管其访问级别如何,都可以通过信号插槽连接调用。这意味着从任意类的实例发出的信号可以导致在不相关类的实例中调用私有槽。
您还可以将插槽定义为虚拟的,我们发现这在实践中非常有用。
与回调相比,信号和槽稍微慢一些,因为它们提供了更大的灵活性,尽管对于实际应用程序来说差异并不大。一般来说,发出连接到某些插槽的信号比直接调用接收器(使用非虚拟函数调用)慢大约10倍。这是定位连接对象、安全地遍历所有连接(例如检查后续接收器在发射期间没有被销毁)以及以通用方式编组仟何参数所需的开错。虽然10个非虚函数调用听起来很多,但它的开销比任何new或delete操作都要少得多。只要在后台执行需要new或delete的字符串、向量或列表操作,信号和槽开销只占整个函数调用成本的很小一部分。当您在插槽中执行系统调用时也是如此;或者间接调用十多个函数。信号和插槽机制的简单性和灵活性是值得的,你的用户甚至不会注意到
请注意,当与基于qt的应用程序一起编译时,定义称为信号或槽的变量的其他库可能会导致编译器警告和错误。要解决这个问题,请#undef有问题的预处理器符号。
参考文档:http://t.csdnimg.cn/Kl21e
Qt帮助文档