【问题标题】:How to temporally disconnect PyQt5 signal and reconnect it after?如何暂时断开 PyQt5 信号并在之后重新连接?
【发布时间】:2021-07-15 15:46:24
【问题描述】:

最低工作代码:

step1_failed = False
try:
    print("step #1")
except:
    step1_failed = True
print("step #2")  # always appening after step #1 but before step #3 regardless of if step #1 failed or not
if not step1_failed:
    print("step #3") # only if step #1 execute without error

我的问题是:有没有更好的我看不到的方法?

最好没有像 step1_failed 这样的虚拟变量。

我认为也许“finally”和“else”是答案,但最终发生在 else 之后,我需要在 else 语句之前做一些事情。

这个用例是针对 PyQt5,我想断开一个信号并在做某事后重新连接它以避免不必要的递归。 但是我需要重新连接它,前提是它最初是连接的。

这是我的 PyQt5 代码,以了解我为什么需要它:

def somefunction():
    connected_at_first = True  # assuming it was connected
    try:
        lineedit1.textChanged.disconnect(somefunction)  # throw a TypeError if it is not connected
    except TypeError:
        connected_at_first = False  # happen only if lineedit1 wasn't connected

    lineedit1.setText("always happening !")

    # reconnecting lineedit1 if it was connected at the beginning
    if connected_at_first:
        lineedit1.textChanged.connect(somefunction)

【问题讨论】:

  • 我不能把第 2 步放在前面的最后一行,除非因为第 1 步失败,第 2 步将不会执行。无论第 1 步如何,我都需要始终执行第 2 步。
  • @not_speshal OP 说第 2 步必须在第 3 步之前进行
  • stackoverflow.com/q/21586643/1126841 是相关的,如果不是重复的话。
  • 我在发布这个问题之前找到了 stackoverflow.com/q/21586643/1126841,但这并不是我真正想要的。这个问题更多的是关于如何将连接从一个更改为另一个,而不是在中间做某事。

标签: python pyqt5 try-except


【解决方案1】:

我不知道是否有更清洁的方法,但您的方法可以包装在上下文管理器中。

from contextlib import contextmanager

def tempdisconnect(o, f)
    connected = True
    try:
        o.disconnect(f)
    except TypeError:
        connected = False

    yield

    if connected:
        o.connect(f)

with tempdisconnect(lineedit1.textChanged, somefunction):
    lineedit1.setText("always happening !")

disconnect 的更好 API 是返回被断开连接的函数(类似于 signal.signal 的工作方式),或返回 None。那么tempdisconnect就可以写成

def tempdisconnect(o, f):
    old = o.disconnect(f)
    yield
    o.connect(old)

这也假设o.connect(None) 是一个空操作,因此它在with 语句的主体前后保持未连接状态。

【讨论】:

    【解决方案2】:

    如果想避免递归,可以使用blockSignals():

    def somefunction():
        blocked = lineedit1.blockSignals(True)
        lineedit1.setText("always happening !")
        lineedit1.blockSignals(blocked)
    

    否则,使用简单的标志:

    class SomeClass(QtWidgets.QWidget):
        signalFlag = False
        # ...
        def somefunction(self):
            if self.signalFlag:
                return
            self.signalFlag = True
            self.lineEdit.setText("always happening !")
            self.signalFlag = False
    

    【讨论】:

    • 我想问一下递归使用 somefunction 是否是故意的。我假设相同的名称只是被意外地重复用于两个不同的功能。
    • @chepner 我不确定你的意思,你能澄清一下吗?
    • @chepner 哦,你指的是 OP,对吧?
    • Right :) 并不真正了解 PyQT 的任何具体内容,我假设这可能是理想的解决方案。
    【解决方案3】:

    根据 chepner 的回答,我修改了他的代码,以便能够删除同一函数的重复连接并处理多个函数。

    from contextlib import contextmanager
    
    @contextmanager
    def tempdisconnect(signal, func):
        if not isinstance(func, (tuple, list)):
            func = (func,)
        connected = [True] * len(func)
    
        for i in range(len(func)):
            a = 0
            try:
                while True:
                    signal.disconnect(func[i])
                    a += 1
            except TypeError:
                if a == 0:
                    connected[i] = False
    
        yield
    
        if connected != False:
            for i in range(len(func)):
                if connected[i]:
                    signal.connect(func[i])
    
    

    用法:

    # Disconnect somefunction (even if it was accidently connected multiple times)
    with tempdisconnect(lineEdit1.textChanged, somefunction):
        lineEdit1.setText("hello")
    

    # Disconnect somefunc1, somefunc2, somefunc3
    with tempdisconnect(lineEdit1.textChanged, (somefunc1, somefunc2, somefunc3)):
        lineEdit1.setText("hello")
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-01-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多