Custom Signal/Slots with PythonQt

Hi all,

the signal/slot system of Qt is a very helpful mechanism for implementing callbacks. I was wondering if I can create my own signals and slots with PythonQt but had no success so far.
In other projects where I use e.g. pySide I can subclass from “QObject” and then define my custom signals which then can be connected to my callbacks. When searching in PythonQt threads I found a similar code snippet here:

https://sourceforge.net/p/pythonqt/discussion/631393/thread/bbc98b8f/

Unfortunately I am not able to get a PythonQt.QtCore.QObject object in the Python version of Slicer. Is this some wanted restriction?
If this is not possible, is there a similar mechansim available? As an example: I have an IGTL connection which receives data. Based on the data I want to update some UI elements. The connection class and the Widget class are two separate classes though. I am trying to keep the logic separated from the UI.

Thanks for any help!

In addition of being available in the PythonQt.QtXXX namespace, for convenience, all qt objects are also imported in the qt namespace.

Any of these currently work:

import qt
qt.Signal("QVariant")

or

import PythonQt
PythonQt.QtCore.Signal("QVariant")

That said, QObject is not available. We should probably update the version of PythonQt bundled in Slicer. It is currently based on r455. More details here: GitHub - commontk/PythonQt: CMake-ified version of PythonQt

It would be good to know if PythonQt supports creating signals directly and if it does we can add it to Slicer. There are limits compared with PySide, but it gets better over time. Did you look at the PythonQt page and check with the folks there?

Thanks for your suggestions. I reviewed the PythonQt page which says:

QObject.emit to emit Qt signals from Python is not yet implemented but PythonQt allows to just emit a signal by calling it like a normal slot

So I will now try to create a signal and emit it directly as jcfr suggested. Just to be sure, would something like this work then?

 import qt
 
 class ReceiverClass:
 
     new_parameters_received = qt.Signal("QVariant")
 
     def _on_reveive(self, params):
         new_parameters_received.emit(params)

 class UserInterface:
     
    def __init__(self, receiver):
       receiver.new_parameters_received.connect(self._on_new_params)

    def _on_new_params(self, data):
        self._bt_foo.setText(data[0])
        ....

Thanks again!

Hi again,

I did some more tests and still have some problems using the signal-slot concept via Qt. To beginn with I forgot to mention that I was using the stable release of Slicer. Therefore, there was no qt.Signal or qt.Slot object available, only a qt.SIGNAL which would not work.

After updating to a nightly release version of Slicer, the qt.Signal and qt.Slot classes were available. I was still not able though to connect a simple signal with a slot. Everytime I try to connect, the whole application just crashes, e.g.:

import qt

test_sig = qt.Signal("QVariant")

@qt.Slot("QVariant")
def test_slot(data):
    print(data)

test_sig.connect(test_slot)
test_sig.emit("Foo")

Other similar tries (e.g. without qt.Slot decorator) lead to a crash or a non-working connection. In the end I just tried to wrap the signal-slot connection into a class which derives from QWidget (since QObject is not available). This seems to solve the issue. The signal is connected and also correctly triggered.

It still though does not feel right to subclass from QWidget for every class that needs signal-slot feature. Therefore I might use an alternative solution, e.g. the PySignals module. Or are there any better proposals for me?

Thanks!

@jcfr I looked through the commits after r455, and didn’t see anything that was obviously related… I wonder if something from CTK is shadowing/removing QObject, because it is registered the same way as many other classes which are available under PythonQt.QtCore in the repl (e.g. QMimeData).

1 Like

Hi all,
I’m interested in emitting a signal and, if some object is registered on this “channel”, in receiving it.
Any idea about which qt tool can I use?
I also read this and I got a crash too.

Thanks a lot.
Paolo

1 Like

We have signal/slot mechanism in Qt and observer mechanism in VTK, and you can implement custom plugin or callback mechanisms. These are all available to be used from both C++ and Python.

Probably the most universal way (that is available from all Slicer classes) is to observe/invoke custom modified events on a MRML node.

Thanks Andras,
I used the observe/invoke mechanism of MRML node.

Paolo

1 Like