00. 目录
文章目录
-
- 00. 目录
- 01. 信 与槽
- 02. 介绍
- 03. 信 与槽
- 04. 信
- 05. 槽
- 06. 一个小例子
- 07. 一个真实的例子
- 08. 信 和槽使用默认参数
- 09. 信 与槽高级用法
- 10. 使用Qt与第三方信 和插槽
01. 信 与槽
? 信 和槽用于对象之间的通信。信 和插槽机制是Qt的核心功能,可能是与其他框架提供的功能最不同的部分。Qt的元对象系统使信 和槽成为可能。
02. 介绍
? 在GUI编程中,当我们更改一个小部件时,我们经常需要通知另一个小部件。更一般地说,我们希望任何类型的对象能够彼此通信。例如,如果用户单击“ **关闭”**按钮,我们可能希望调用窗口的close()函数。
? 其它工具包使用回调实现这种通信。回调是指向函数的指针,因此如果您希望处理函数通知您某些事件,则将指针传递给处理函数的另一个函数(回调)。然后,处理函数在适当时调用回调。虽然确实存在使用此方法的成功框架,但回调可能不直观,并且可能在确保回调参数的类型正确性方面存在问题。
03. 信 与槽
? 在Qt中,我们有一种替代回调技术:我们使用信 和槽。发生特定事件时会发出信 。Qt的小部件有许多预定义信 ,但我们总是可以将部件子类化为了向它们添加我们自己的信 。槽是响应于特定信 而被调用的函数。Qt的部件中有许多预定义的槽函数,但通常的做法是将部件子类化并添加自己的槽,以便您可以处理您感兴趣的信 。

? 信 和槽机制是类型安全的:信 的签名必须与接收槽的签名匹配。(事实上,一个槽可能比它收到的信 具有更短的签名,因为它可以忽略额外的参数。)由于签名是兼容的,编译器可以帮助我们在使用基于函数指针的语法时检测类型不匹配。基于字符串的SIGNAL和SLOT语法将在运行时检测类型不匹配。信 和槽是松散耦合:发出信 的类既不知道也不用关心哪个槽接收该信 。Qt的信 和槽机制确保如果您将信 连接到槽,将在适当的时间使用信 的参数调用槽。信 和槽可以采用任何类型的任意数量的参数。它们完全是类型安全的。
? 从QObject或其子类之一(例如,QWidget)继承的所有类都可以包含信 和槽。当它们以某种方式改变它的状态时, 信 就会被发送, 其它对象可能对该信 感兴趣。这是的话所有的对象都可以通讯。它不知道或关心是否有任何东西正在接收它发出的信 。这是真正的信息封装,并确保该对象可以用作软件组件。
? 槽可用于接收信 ,但它们也是普通的成员函数。就像一个对象不知道是否有任何东西接收到它的信 一样,一个槽函数不知道它是否有任何信 连接到它。这确保了可以使用Qt创建真正独立的组件。
? 可以将任意数量的信 连接到一个槽,并且可以根据需要将信 连接到任意数量的插。甚至可以将信 直接连接到另一个信 。(每当发射第一个信 时,将立即发出第二个信 。)
信 和插共同构成了一个强大的组件编程机制。
04. 信
? 当对象的内部状态以某种可能对对象的客户端或所有者感兴趣的方式发生更改时,对象会发出信 。信 是公共访问函数,可以从任何地方发出,但我们建议只从定义信 及其子类的类中发出它们。
? 当信 发出时,通常会立即执行与其连接的槽函数,就像正常的函数调用一样。发生这种情况时,信 和槽机制完全独立于任何GUI事件循环。发出信 之后, 所有与它关联的槽函数将会被执行。这种情形与使用排队连接时情况略有不同; 在这种情况下,关键字后面的代码将立即继续,并且稍后将执行槽函数。
? 如果多个槽连接到一个信 ,则在发出信 时,槽函数将按照它们已连接的顺序依次执行。
? 信 由moc自动生成,不得在文件中实现。它们永远不会有返回类型(即使用)。
关于参数的说明
我们的经验表明,如果信 和槽不使用特殊类型,则它们可以重复使用。如果QScrollBar :: valueChanged()使用特殊类型,例如假设的QScrollBar :: Range,则它只能连接到专门为QScrollBar设计的槽函数。将不同的输入控件连接在一起是不可能的。
05. 槽
? 当连接的信 被发送时, 对应的槽函数将会被执行。槽函数是普通的C ++函数,可以正常调用; 它们唯一的特点是信 可以连接到它们。
? 由于插槽是普通的成员函数,因此它们在直接调用时遵循正常的C ++规则。但是,作为槽,它们可以通过信 和槽连接由任何组件调用,而不管其访问级别如何。这意味着从任意类的实例发出的信 可以导致在不相关的类的实例中调用私有槽。
? 我们发现这些槽在实践中非常有用时, 还可以将槽函数定义为虚槽函数。
? 与回调相比,信 和槽稍微慢一些,因为它们提供了更大的灵活性,尽管实际应用的差异是微不足道的。通常,发射连接到某些槽的信 比使用非虚函数直接调用槽函数大约慢十倍。这是定位连接对象,安全地遍历所有连接(即检查后续槽函数在发射期间没有被销毁)以及以通用方式编组任何参数所需的开销。虽然十个非虚函数调用可能听起来很多,但它的开销比任何或操作都少得多,例如。只要执行场景后面的字符串,vector或list操作,就需要或,信 和槽开销仅仅是函数调用开销的一小部分。无论何时在槽函数中进行系统调用,情况都是如此; 或间接调用十多个函数。由于信 和槽机制的简单性和灵活性, 这一点开销是非常值得的,用户甚至都不会注意到。
? 请注意,定义变量的其他库在与基于Qt的应用程序一起编译时调用或可能导致编译器警告和错误。要解决这个问题,违规的预处理器符 。
06. 一个小例子
最小的C ++类声明可能是:
一个小的基于QObject的类可能是:
? 基于QObject的版本具有相同的内部状态,并提供访问状态的公共方法,但此外它还支持使用信 和槽进行组件编程。这个类可以通过发出信 告诉外面世界它的状态已经改变了,并且它有一个其它对象可以发送信 的槽。
? 包含信 或槽的所有类必须在其声明的顶部提及Q_OBJECT。它们还必须(直接或间接)从QObject派生。
? 槽函数由应用程序员实现。以下是槽函数的可能实现:
? 该行发出来自对象的信 ,新值作为参数。
? 在下面的代码片段中,我们创建了两个对象,并使用QObject :: connect()将第一个对象的信 连接到第二个对象的槽:
? 调用导致对象发出一个信 ,该信 对象比将会接收, 并且执行槽函数setValue()b.setValue(12)bvalueChanged()bvalueChanged()`信 ,该信 被忽略。
请注意
该函数仅设置值并发出信 。这可以防止在循环连接的情况下无限循环(例如,如果连接到)。
? 默认情况下,对于您所做的每个连接,都会发出一个信 ; 发出两个信 用于重复连接。您可以通过一次disconnect()调用来中断所有这些连接。如果传递Qt :: UniqueConnection 类型,则只有在不是重复的情况下才会建立连接。如果已经有重复(完全相同的信 到相同对象上的完全相同的槽),连接将失败并且connect将返回false
? 此示例说明对象可以一起工作,而无需了解彼此的任何信息。为了实现这一点,对象只需要被连接在一起,并且这可以用一些简单的实现的QObject ::连接()函数调用,或者与的自动连接功能。
07. 一个真实的例子
这是一个小部件的简单评论示例。
通过QFrame和QWidget继承具有大部分信 槽知识的QObject。它有点类似于内置的QLCDNumber小部件。
? Q_OBJECT宏由预处理器扩展来声明由实现的几个成员函数; 如果您在“未定义的vtable引用”中遇到编译器错误,您可能忘记运行moc或在链接命令中包含moc输出。
? 它与moc显然不相关,但是如果你继承了QWidget,你几乎肯定希望在构造函数中使用该参数并将其传递给基类的构造函数。
? 这里省略了一些析构函数和成员函数; 该忽略成员函数。
? 当要求显示不可能的值时发出信 。
? 如果您不关心溢出,或者您知道不会发生溢出,则可以忽略该信 ,即不要将其连接到任何槽。
? 另一方面,如果要在数字溢出时调用两个不同的错误函数,只需将信 连接到两个不同的槽即可。Qt将调用它们(按照它们连接的顺序)。
? 槽是一种接收函数,用于获取有关其他小部件中状态更改的信息。如上面的代码所示,使用它来设置显示的数字。由于该类是与该程序其余部分的接口的一部分,因此该槽是公共的。
? 有几个示例程序将QScrollBar的valueChanged()信 连接到插槽,因此LCDNumber会连续显示滚动条的值。
? 注意重载; 当您将信 连接到槽时,Qt将选择适当的版本。使用回调,您必须找到五个不同的名称并自己跟踪类型。
? 此示例中省略了一些不相关的成员函数。
08. 信 和槽使用默认参数
信 和槽的签名可以包含参数,参数可以具有默认值。考虑QObject :: destroyed():
当QObject的被删除时,它发射这个 QObject::destroyed()信 。我们希望捕获这个信 ,无论我们对删除的QObject有何悬空引用,我们都可以清理它。合适的插函数可能是:
要将信 连接到槽,我们使用QObject :: connect()。有几种方法可以连接信 和插槽。第一个是使用函数指针:
将QObject :: connect()与函数指针一起使用有几个优点。首先,它允许编译器检查信 的参数是否与槽的参数兼容。如果需要,编译器也可以隐式转换参数。
您还可以连接到仿函数或C ++ 11 lambdas:
将信 连接到槽的另一种方法是使用QObject :: connect()和和宏。规则是否包含参数在和宏中,如果参数有默认值,是传递给签名宏必须不能比传递到SLOT()宏签名参数少。
所有这些都可行
但这一个不起作用:
因为槽将期望信 不会发送的QObject。此连接将 告运行时错误。
请注意,使用此QObject :: connect()重载时,编译器不会检查signal和slot参数。
09. 信 与槽高级用法
? 对于您可能需要有关信 发送者的信息的情况,Qt提供QObject :: sender()函数,该函数返回指向发送信 的对象的指针。
? 所述QSignalMapper类提供了一种用于有许多信 被连接到相同的槽和槽需要不同的方式处理每一个信 的情况。
? 假设您有三个按钮,用于确定要打开的文件:“税务文件”,“帐户文件”或“ 告文件”。
? 要打开正确的文件,可以使用QSignalMapper :: setMapping()将所有QPushButton :: clicked()信 映射到QSignalMapper对象。然后将文件的QPushButton :: clicked()信 连接到QSignalMapper :: map()槽。
? 然后,将mapped()信 连接到将打开其他文件的位置,具体取决于按下的按钮。
10. 使用Qt与第三方信 和插槽
可以将Qt与第三方信 /插槽机制一起使用。您甚至可以在同一个项目中使用这两种机制。只需将以下行添加到qmake项目(.pro)文件即可。
它告诉Qt的不要定义moc关键字,和,因为这些名称将由第三方库可以使用,例如boost。然后继续使用带有标志的Qt信 和插槽,只需将源中Qt moc关键字的所有使用替换为相应的Qt宏Q_SIGNALS(或Q_SIGNAL),Q_SLOTS(或Q_SLOT)和Q_EMIT。
另请参阅元对象系统和Qt的属性系统。
声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!