如何暂时断开 PyQt5 信号并在之后重新连接?

How to temporally disconnect PyQt5 signal and reconnect it after?

最低工作代码:

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”是答案,但 finally 发生在 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)

如果想避免递归,可以使用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

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

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 是 return 断开连接的功能(类似于 signal.signal 的工作方式),或者 return None。那么tempdisconnect可以写成

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

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

根据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")