首页 > 解决方案 > PyQt 信号:在断开连接()之后,触发的信号仍然可以工作吗?

问题描述

我正在处理一个应用程序,其中许多信号被触发,然后重新连接。我将详细解释应用程序是如何工作的,以及我的困惑从哪里开始。

 

1. 重新连接信号

在我的应用程序中,我经常重新连接信号。为此,我将使用以下静态函数,取自这篇文章中@ekhumoro 的答案(并稍作修改):PyQt Widget connect() and disconnect()

def reconnect(signal, newhandler):
    while True:
        try:
            signal.disconnect()
        except TypeError:
            break
    if newhandler is not None:
        signal.connect(newhandler)

 

2.申请

想象一下emitterFunc(self)循环遍历对象列表的函数。在每次迭代中,该函数连接mySignal到一个对象,触发信号,然后mySignal在下一个迭代步骤开始时再次断开连接。发射的信号还携带一些有效载荷,例如一个对象Foo()

 
在此处输入图像描述

编辑:

 

3. Signal-Slot 机制的心理形象

随着时间的推移,我对信号槽机制如何工作有了一个心理印象。我想象一个Signal-Slot 引擎吸收所有发射的信号并将它们放入队列中。每个信号都在等待轮到它。当时间准备好时,引擎将给定的信号传递给适当的处理程序。为了正确地做到这一点,引擎有一些“簿记”工作,以确保每个信号最终都在正确的插槽中。

在此处输入图像描述

 

4. Signal-Slot 引擎的行为

假设我们处于第 n迭代步骤。我们连接self.mySignalobject_n。然后我们用它的有效载荷发射信号。几乎在这样做之后,我们立即断开连接并建立与object_n+1的新连接。在我们断开连接的那一刻,触发的信号可能还没有完成它的工作。我可以想象 Signal-Slot 引擎的三种可能行为:

 

5. 我的问题

我的第一个问题现在很明显。什么是正确的信号槽引擎行为?我希望这是第三种选择。

作为第二个问题,我想知道给定的心理形象在多大程度上是正确的。例如,我可以依靠信号按顺序从队列中出来吗?这个问题不太重要——它对我的申请当然不是至关重要的。

第三个问题与时间效率有关。重新连接到另一个处理程序是否耗时?一旦我知道第一个问题的答案,我将继续构建应用程序,我可以自己测量重新连接时间。所以这个问题不是那么重要。但是,如果您无论如何都知道答案,请分享:-)

标签: qtpyqtqt5pyqt5signals-slots

解决方案


我想从你的第二个问题开始,说你的心理形象是部分正确的,因为涉及到队列,但并非总是如此。发出信号时,有三种可能的方式调用连接的插槽,其中两种使用事件队列(aQMetaCallEvent动态实例化并使用QCoreApplication's 方法发布postEvent,其中事件目标是插槽持有者,或信号接收器,如果您愿意)。第三种情况是直接调用,所以发出信号就像调用槽一样,没有任何东西进入队列。

现在回到第一个问题:无论如何,当发出信号时,将遍历连接列表(属于信号发射器),并使用上面提示的三种方法之一一个接一个地调用插槽。每当建立或断开连接时,列表都会更新,但这必然发生在信号发出之前或之后。简而言之:在发出信号后成功阻止对已连接插槽的调用的机会非常小,至少不会中断与 的连接disconnect()。所以我会标记[OPTION 3]为正确。

如果您想进一步挖掘,请从ConnectionType 枚举文档开始,其中很好地解释了三种基本连接类型(直接、排队和阻塞队列)。可以将连接类型指定为QObject's method的第五个参数connect,但是,正如您将从上述链接文档中了解到的那样,Qt 本身通常会选择最适合这种情况的连接类型。剧透:涉及线程:)

关于第三个问题:我手头没有基准测试可以展示,所以我将给出一个所谓的主要基于意见的答案,即以恕我直言开头的那种答案。我认为信号/插槽领域是保持简单规则确实规则的领域之一,并且您的重新连接模式似乎使事情变得比需要的复杂得多。正如我上面所暗示的,当建立一个连接时,一个连接对象被附加到一个列表中。当信号发出时,所有连接的插槽都会以某种方式被调用,一个接一个。因此,与其在循环中的每个循环中断开/重新连接/发出,为什么不先连接所有项目,然后发出信号,然后将它们全部断开?

我希望我的(冗长的,也许是 tldr 的)回答有所帮助。好读。


推荐阅读