PythonQt properties shadowing methods

Hi all, I’ve noticed a strange behavior that I could not understand if it is a bug or intended.
It seems to me that a property can sometimes shadow methods from qt objects. Example:

qgw = qt.QGraphicsWidget()

qgw.layout is None

qgw.setLayout(qt.QGraphicsLayout())

This runs ok with no errors, and even emit the signal layoutChanged
but qgw.layout continues to be None

if I try to

qgw.layout = qt.QGraphicsLayout()

I get an error:

Traceback (most recent call last):
  File "<console>", line 1, in <module>
AttributeError: Property 'layout' of type 'QGraphicsLayout*' does not accept an object of type QGraphicsLayout (QGraphicsLayout (C++ object at: 0x000002187FBE0B90))

Which I can understand, because setting an object to a pointer variable can only result in a error. But the layout() method is not accessible, which is also understandable from the python point of view.

That is not the case for QWidget, which I can access the layout via layout() slot. Can you help me to understand what is happening? I know it may be more related to PythonQt than Slicer, but I wanted to use QGraphicsWidget with another lib. Thanks

For reference, discussions on github:

There are hundreds of graphing libraries for Python. pyqtgraph may make sense if you already use the same Qt wrapping as it does, but otherwise bringing in an entire new set of Qt is probably not worth it.

1 Like

Thanks Andras, your input is always greatly appreciated. We’ve managed to use matplotlib inside slicer to improve plotting capabilities, but we need a lib that allow us to plot things more dynamically and interactively. Also we have another project outside slicer that we built based on pyqtgraph that we would like to include into slicer.

I’ve managed to install PySide2 in Slicer and run pyqtgraph with it, but as expected the python wrapped qt objects are not compatible with pyqhtonqt wrapped ones. i.e. I cannot add a PySide2.QtCore.QWidget to a PythonQt.QtGui.QLayout. This way I can only use pyqtgraph as a separate window. A wrapper of some kind comes to mind to make pythonqt accept a pyside2 widget, but I don’t know how to start looking into it.

It sounds like you are into a potential dangerzone here by mixing PythonQt with PySide2, but if it’s working and matches your use case, I guess we can’t stop you ; )

If you just need a workaround, you should be able to get the pixel data from any of your widgets as numpy arrays that you can then display in the “other” qt.

For example in SlicerWeb I do:

    slicer.qMRMLUtils().qImageToVtkImageData(timeImage, vtkTimeImage)

then I get a numpy array from the vtkImageData. It should be a very fast operation, even if it is a “creative” (hacky) workaround.

Thanks @pieper that is a nice workaround to know as well.

I’ve come up with this other hack so far:

from PySide2.QtWidgets import QVBoxLayout
import shiboken2
pysidelayout = shiboken2.wrapInstance(hash(self.layout), QVBoxLayout)
import numpy as np
from pyqtgraph.Qt import QtGui, QtCore
import pyqtgraph as pg
cw = pg.GraphicsLayoutWidget()
pysidelayout.addWidget(cw)

This way I can wrap a pythonqt qt object with shiboken and use it as it were a pyside2 object. Events are working, nicely and I got to add a pyside2 widget to a slicer layout. I will report if I find any issues, so far so good.

The other way around is still a mystery (pyside2 → pythonqt).

1 Like