当前位置:  开发笔记 > 编程语言 > 正文

Qt事件和信号/插槽

如何解决《Qt事件和信号/插槽》经验,为你挑选了6个好方法。

在Qt世界中,事件和信号/插槽的区别是什么?

有人替换另一个吗?事件是信号/插槽的抽象吗?



1> Stefan Monov..:

在Qt中,信号和事件都是Observer模式的实现.它们用于不同的情况,因为它们具有不同的优点和缺点.

首先让我们确切地定义'Qt事件'的含义:Qt类中的虚函数,如果你想处理事件,你应该在你的基类中重新实现.它与模板方法模式有关.

请注意我是如何使用" 句柄 " 一词的.实际上,这是信号和事件的意图之间的基本区别:

你" 处理 "事件

您" 收到 "信号发射的通知

不同之处在于,当您"处理"事件时,您承担责任"回应"在课堂外有用的行为.例如,考虑一个应用程序,其上有一个带有数字的按钮.应用程序需要让用户关注按钮并通过按"向上"和"向下"键盘键来更改数字.否则按钮应该像正常一样运行QPushButton(可以点击等).在Qt中,这是通过创建自己的小可重用"组件"(子类QPushButton)来完成的,该组件重新实现QWidget::keyPressEvent.伪代码:

class NumericButton extends QPushButton
    private void addToNumber(int value):
        // ...

    reimplement base.keyPressEvent(QKeyEvent event):
        if(event.key == up)
            this.addToNumber(1)
        else if(event.key == down)
            this.addToNumber(-1)
        else
            base.keyPressEvent(event)

看到?此代码提供了一个新的抽象:一个小部件,其作用类似于按钮,但具有一些额外的功能.我们非常方便地添加了此功能:

由于我们重新实现了虚拟,我们的实现自动封装在我们的类中.如果Qt的设计人员发出keyPressEvent信号,我们需要决定是继承QPushButton还是外部连接信号.但那将是愚蠢的,因为在Qt中,你总是希望在编写具有自定义行为的小部件时继承(有充分的理由 - 可重用性/模块化).因此,通过制作keyPressEvent一个事件,他们传达的意图keyPressEvent只是功能的基本构建块.如果它是一个信号,它看起来像一个面向用户的东西,当它不打算.

由于该函数的基类实现可用,我们通过处理我们的特殊情况(向上和向下键)并将其余部分留给基类,轻松实现责任链模式.如果keyPressEvent是信号,你可以看到这几乎是不可能的.

Qt的设计经过深思熟虑 - 它们让我们很容易做出正确的事情并且很难做错事(通过使keyPressEvent成为事件)而使我们陷入成功的陷阱.

另一方面,考虑最简单的用法QPushButton- 只是实例化它并在点击时收到通知:

button = new QPushButton(this)
connect(button, SIGNAL(clicked()), SLOT(sayHello())

这显然是由班级用户完成的:

如果我们QPushButton每次想要一些按钮通知我们点击时都必须进行子类化,那么就没有充分的理由需要很多子类!messagebox单击时始终显示"Hello world"的小部件仅在单个案例中有用 - 因此它完全不可重用.同样,我们别无选择,只能通过外部连接来做正确的事情.

我们可能想要连接几个插槽clicked()- 或连接几个信号sayHello().信号没有大惊小怪.通过子类化,你必须坐下来思考一些类图,直到你决定一个合适的设计.

请注意,其中一个位置是在其实现中QPushButton发出的.这并不意味着和是可以互换的-只不过他们相关.clicked()mousePressEvent()clicked()mousePressEvent()

所以信号和事件有不同的目的(但是相关的是两者都让你"订阅"通知发生的事情).



2> Robert Sieme..:

到目前为止,我不喜欢这些答案. - 让我集中讨论这部分问题:

事件是信号/插槽的抽象吗?

简答:不.长答案提出了一个"更好"的问题:信号和事件如何相关?

空闲主循环(例如Qt)通常在操作系统的select()调用中"卡住".该调用使应用程序"休眠",同时它将一堆套接字或文件传递给内核,要求:如果这些内容发生了变化,请让select()调用返回. - 作为世界的主人,内核知道何时发生这种情况.

select()调用的结果可能是:套接字上的新数据连接到X11,一个数据包到我们监听的UDP端口,等等. - 这些东西既不是Qt信号,也不是Qt事件,而且Qt主循环决定它是否将新数据转换为另一个,另一个或忽略它.

Qt可以调用一个方法(或几个)像keyPressEvent(),有效地将其转换为Qt事件.或者Qt发出一个信号,它实际上会查找为该信号注册的所有功能,并一个接一个地调用它们.

这两个概念的一个不同之处在于:一个插槽没有对是否会调用注册到该信号的其他插槽进行投票. - 事件更像是一个链,事件处理程序决定它是否中断该链.在这方面,信号看起来像星星或树.

事件可以触发或完全变成信号(只发出一个,不要调用"super()").信号可以变成事件(调用事件处理程序).

什么取决于具体情况的抽象:clicked() - 信号抽象鼠标事件(一个按钮向下和向上再次上升而没有太多移动).键盘事件是来自较低级别的抽象(诸如果或é之类的东西是我系统上的几个关键击键).

也许focusInEvent()是相反的一个例子:它可以使用(并因此抽象)clicked()信号,但我不知道它是否确实如此.


我发现这一部分:"一个插槽没有投票,是否会注册到该信号的其他插槽是否会被调用"对于信号和事件之间的差异非常有启发性.但是,我有一个相关的问题:什么是消息以及消息与信号和事件有何关系?(如果它们是相关的).消息是信号和事件的抽象吗?它们可以用来描述QObjects(或其他对象?)之间的这种交互吗?
@axeoth:那你的问题是无稽之谈.它写着:"什么是事件以及事件与信号和事件有何关系".

3> Harald Schei..:

在Qt文档可能是最好的解释吧:

在Qt中,事件是从抽象QEvent类派生的对象,它们表示在应用程序中发生的事件或者应用程序需要了解的外部活动的结果.事件可以由QObject子类的任何实例接收和处理,但它们与小部件特别相关.本文档描述了在典型应用程序中如何传递和处理事件.

因此事件和信号/槽是实现相同事物的两种并行机制.通常,事件将由外部实体(例如,键盘或鼠标滚轮)生成,并将通过事件循环传递QApplication.通常,除非您设置代码,否则您将不会生成事件.您可以QObject::installEventFilter()通过覆盖相应的函数来过滤它们或处理子类对象中的事件.

信号和插槽更容易生成和接收,您可以连接任何两个QObject子类.它们通过Metaclass处理(有关更多信息,请查看您的moc_classname.cpp文件),但您将生成的大多数类间通信可能会使用信号和插槽.信号可以立即传递或通过队列延迟(如果您使用线程).

可以生成信号.


@Raphael对你接受这个答案感到羞耻. - 引用的段落甚至不包含"信号"或"插槽"字样!
@neuronet,如果你完全删除引用,它将改善我眼中的答案. - 如果删除整个答案,它将改善整个QA.

4> firescreamer..:

事件循环调度事件.每个GUI程序都需要一个事件循环,无论您使用Qt,Win32还是任何其他GUI库编写Windows或Linux.每个线程都有自己的事件循环.在Qt"GUI事件循环"(这是所有Qt应用程序的主循环)是隐藏的,但你启动它美其名曰:

QApplication a(argc, argv);
return a.exec();

消息发送到程序的OS和其他应用程序将作为事件发送.

信号和插槽是Qt机制.在使用moc(元对象编译器)的编译过程中,它们被更改为回调函数.

事件应该有一个接收器,应该发送它.没有人应该得到那个事件.

将执行连接到发射信号的所有插槽.

您不应该将Signals视为事件,因为您可以在Qt文档中阅读:

发出信号时,通常会立即执行与其连接的插槽,就像正常的函数调用一样.发生这种情况时,信号和插槽机制完全独立于任何GUI事件循环.

发送事件时,它必须等待一段时间,直到事件循环调度之前发生的所有事件.因此,在发送事件或信号之后执行代码是不同的.发送事件后的代码将立即运行.信号和插槽机制取决于连接类型.通常它将在所有插槽之后执行.使用Qt :: QueuedConnection,它将立即执行,就像事件一样.检查Qt文档中的所有连接类型.



5> eric..:

有一篇文章详细讨论了事件处理:http://www.packtpub.com/article/events-and-signals

它讨论了事件和信号之间的区别:

事件和信号是用于完成同样事情的两种并行机制.作为一般差异,信号在使用窗口小部件时很有用,而事件在实现窗口小部件时很有用.例如,当我们使用像QPushButton这样的小部件时,我们对它的clicked()信号比对导致信号发出的低级鼠标按下或按键事件更感兴趣.但是如果我们实现QPushButton类,我们对鼠标和键事件代码的实现更感兴趣.此外,我们通常处理事件,但通过信号发射得到通知.

这似乎是谈论它的常用方式,因为接受的答案使用了一些相同的短语.


请注意,请参阅下面有关Kuba Ober的回答的有用评论,这让我想知道它是否有点过分简单.


我是说报价单的第一句话是泛泛的,以至于毫无用处。从技术上讲,这是不正确的,但这仅仅是因为,如果您愿意,可以使用事件传递来完成信号时隙机制的工作,反之亦然。这会很麻烦。因此,不,信号时隙与事件“不一样”,它们并不能完成相同的事情,而且两者的存在对Qt的成功至关重要。引用的按钮示例完全忽略了信号插槽系统的一般功能。

6> Reinstate Mo..:

TL; DR:信号和插槽是间接方法调用。事件是数据结构。因此它们是完全不同的动物。

它们唯一的在一起就是跨越线程边界进行插槽调用。插槽调用参数打包在数据结构中,并作为事件发送到接收线程的事件队列。在接收线程中,该QObject::event方法将对参数进行解压缩,执行调用,并可能在阻塞连接的情况下返回结果。

如果我们愿意泛化为遗忘,则可以将事件视为调用目标对象event方法的一种方式。这是一种间接的方法调用,是一种流行的方式-但我认为这不是思考的有用方法,即使它是真实的声明也是如此。

推荐阅读
重庆制造漫画社
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有