TLDR; There's no real solution to this problem. PyQt treats None
in a special way when defining signal overloads, so the simplest solution is to use object
instead. For an explanation of exactly why this is, see below.
There are a couple of reasons why what you tried didn't work.
Firstly, the type
argument of pyqtSignal
accepts either a python type-object or a string giving the name of a C++ object (i.e. a Qt class). Strings can't be used to specify python types - and in any case, None
is a value, not a type.
Secondly, None
is special-cased by PyQt to allow the default overload of the signal to be used without explicitly selecting the signature. So, even if type(None)
were used when defining signal overloads, it still wouldn't solve the problem.
(With hindsight, it appears that a better design choice would have been for PyQt to use an internal sentinel object for the default, rather than special-casing None
. However, it's possibly too late to change that now. The behaviour remains unchanged in PyQt5 & PyQt6 [as of 13-03-2023]).
Just to spell this all out more clearly, here's some example code:
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())
Output:
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>,)
As you can see, when type(None)
is used as an overload, it becomes impossible to select anything other than the default overload (i.e. the first one specified in the list of arguments). All three behave in exactly the same way, because they all select the same MyClass
overload.
This also means that attempting to emit None
will fail, because that value can never match the type of the selected overload (which defaults to MyClass
).
A simple work-around would be to use object
instead of type(None)
- but this has the downside of allowing literally any value to be passed. So if preserving type is important, another possible solution might be to define your own singleton sentinel class to use instead of None
.