如何暂时断开 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")
最低工作代码:
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")