我想要一个 pyqt 信号,它可以使用 python“对象”子类参数或 None 发出。例如,我可以这样做:
valueChanged = pyqtSignal([MyClass], ['QString'])
但不是这个:
valueChanged = pyqtSignal([MyClass], ['None'])
TypeError: C++ type 'None' is not supported as a pyqtSignal() type argument type
或者这个:
valueChanged = pyqtSignal([MyClass], [])
TypeError: signal valueChanged[MyObject] has 1 argument(s) but 0 provided
我还尝试了不带引号的 None 和 c++ 等效的“NULL”。但似乎都不起作用。我需要做什么才能完成这项工作?
一个变通的解决方法是定义你的信号发射
object
。由于 None
和 MyClass
都继承自 object
它按预期工作。这不需要在插槽中进行铸造。
在 PyQt5 5.11.3 和 Python 3.5.6 中工作
信号
valueChanged = QtCore.pyqtSignal(object)
valueChanged.emit(MyClass())
valueChanged.emit(None)
插槽
valueChanged.connect(slot)
def slot(my_class_instance):
if my_class_instance:
my_class_instance.some_method()
else:
# my_class_instance is None
您不需要指定第二个选项 - 它将接受您的类类型的指针,它已经可以是 None 值:
valueChanged = pyqtSignal(MyClass)
# both will work as is
inst.valueChanged.emit(MyClass())
inst.valueChanged.emit(None)
TLDR;这个问题没有真正的解决方案。 PyQt 在定义信号重载时以特殊方式处理
None
,因此最简单的解决方案是使用 object
代替。有关确切原因的解释,请参见下文。
您尝试的方法无效有几个原因。
首先,
type
的 pyqtSignal
参数接受 python type-object 或给出 C++ 对象(即 Qt 类)名称的字符串。字符串不能用于指定 python 类型 - 在任何情况下,None
是一个 value,而不是一个类型。
其次,
None
是 PyQt 的特例,允许在不显式选择签名的情况下使用信号的默认重载。所以,即使在定义信号过载时使用type(None)
,仍然不能解决问题.
(事后看来,似乎更好的设计选择是 PyQt 默认使用内部哨兵对象,而不是特殊外壳
None
。但是,现在改变它可能为时已晚。行为仍然存在PyQt5 和 PyQt6 中没有变化 [截至 13-03-2023])。
为了更清楚地说明这一切,这里有一些示例代码:
class MyClass: pass
class X(QtCore.QObject):
valueChanged = QtCore.pyqtSignal([MyClass], [type(None)])
x = X()
x.valueChanged.connect(
lambda *a: print('valueChanged[]:', a))
x.valueChanged[MyClass].connect(
lambda *a: print('valueChanged[MyClass]:', a))
x.valueChanged[type(None)].connect(
lambda *a: print('valueChanged[type(None)]:', a))
print('emit: valueChanged[]')
x.valueChanged.emit(MyClass())
print('\nemit: valueChanged[type(None)]')
x.valueChanged[type(None)].emit(MyClass())
print('\nemit: valueChanged[MyClass]')
x.valueChanged[MyClass].emit(MyClass())
输出:
emit: valueChanged[]
valueChanged[]: (<__main__.MyClass object at 0x7f37ab0db8e0>,)
valueChanged[MyClass]: (<__main__.MyClass object at 0x7f37ab0db8e0>,)
valueChanged[type(None)]: (<__main__.MyClass object at 0x7f37ab0db8e0>,)
emit: valueChanged[type(None)]
valueChanged[]: (<__main__.MyClass object at 0x7f37ab0db8e0>,)
valueChanged[MyClass]: (<__main__.MyClass object at 0x7f37ab0db8e0>,)
valueChanged[type(None)]: (<__main__.MyClass object at 0x7f37ab0db8e0>,)
emit: valueChanged[MyClass]
valueChanged[]: (<__main__.MyClass object at 0x7f37ab0db940>,)
valueChanged[MyClass]: (<__main__.MyClass object at 0x7f37ab0db940>,)
valueChanged[type(None)]: (<__main__.MyClass object at 0x7f37ab0db940>,)
如您所见,当
type(None)
用作重载时,除了 default 重载(即参数列表中指定的第一个)之外,不可能选择任何其他内容。这三者的行为方式完全相同,因为它们都选择了相同的MyClass
过载。
这也意味着尝试发出
None
将失败,因为该值永远无法匹配所选重载的类型(默认为 MyClass
)。
一个简单的解决方法是使用
object
而不是 type(None)
- 但这有一个缺点,即允许传递字面上的 any 值。因此,如果保留类型很重要,另一种可能的解决方案可能是定义您自己的单例哨兵类来代替None
.