pyqt 信号用对象实例或 None 发出

问题描述 投票:0回答:3

我想要一个 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”。但似乎都不起作用。我需要做什么才能完成这项工作?

python pyqt pyside
3个回答
5
投票

一个变通的解决方法是定义你的信号发射

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

2
投票

您不需要指定第二个选项 - 它将接受您的类类型的指针,它已经可以是 None 值:

valueChanged = pyqtSignal(MyClass)

# both will work as is
inst.valueChanged.emit(MyClass())
inst.valueChanged.emit(None)

1
投票

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
.

© www.soinside.com 2019 - 2024. All rights reserved.