Various Issues with PyQt6

Phil Thompson phil at riverbankcomputing.com
Tue Feb 9 12:45:55 GMT 2021


On 07/02/2021 20:33, RoadrunnerWMC wrote:
> Hello. I'm trying to port a PyQt5 application to PyQt6, but am running
> into a few issues, which I think are bugs in PyQt6 itself. I've
> distilled them down to minimum reproducible examples, all of which
> work fine on PyQt5. My environment is Ubuntu 20.04 (KDE Neon), using
> the system-provided Python 3.8.5.
> 
> Here's an issue with signals/slots signature matching:
> 
>     from PyQt6 import QtCore, QtGui, QtWidgets
> 
>     class MainWindow(QtWidgets.QMainWindow):
>         def __init__(self):
>             super().__init__()
>             self.myList = QtWidgets.QListWidget()
>             
> self.myList.itemActivated.connect(self.itemActivatedHandler)
> 
>         @QtCore.pyqtSlot(QtWidgets.QListWidgetItem)
>         def itemActivatedHandler(self, item):
>             print('Item activated')
> 
>     app = QtWidgets.QApplication([])
>     mw = MainWindow()
>     mw.show()
>     app.exec()
> 
> On PyQt 6.0.1, this results in the following traceback:
> 
>     Traceback (most recent call last):
>       File "/path/to/pyqt6_test.py", line 14, in <module>
>         mw = MainWindow()
>       File "/path/to/pyqt6_test.py", line 7, in __init__
>         self.myList.itemActivated.connect(self.itemActivatedHandler)
>     TypeError: decorated slot has no signature compatible with
> itemActivated(QListWidgetItem*)

That's fixed in the current PyQt6 snapshot.

> This seems to affect every slot that takes one or more pointers to Qt
> objects. If there's a way to denote pointers in the decorator,
> something like 
> `@QtCore.pyqtSlot(pointerTo(QtWidgets.QListWidgetItem))`,
> it doesn't seem to be explained in the documentation
> (https://www.riverbankcomputing.com/static/Docs/PyQt6/signals_slots.html).
> 
> Here's a different issue, related to 
> `QtWidgets.QGraphicsItem.itemChange()`:
> 
>     from PyQt6 import QtCore, QtGui, QtWidgets
> 
>     class CustomGraphicsItem(QtWidgets.QGraphicsItem):
>         def boundingRect(self):
>             return QtCore.QRectF(0, 0, 10, 10)
> 
>         def itemChange(self, change, value):
>             return QtWidgets.QGraphicsItem.itemChange(self, change, 
> value)
> 
>     class MainWindow(QtWidgets.QMainWindow):
>         def __init__(self):
>             super().__init__()
>             self.scene = QtWidgets.QGraphicsScene(0, 0, 100, 100, self)
>             self.view = QtWidgets.QGraphicsView(self.scene, self)
> 
>             self.scene.addItem(CustomGraphicsItem())
> 
>     app = QtWidgets.QApplication([])
>     mw = MainWindow()
>     mw.show()
>     app.exec()
> 
> On PyQt 6.0.1, this results variously in one of the following three
> outcomes (most often the second):
> 
>     Traceback (most recent call last):
>       File "/path/to/pyqt6_test_2.py", line 8, in itemChange
>         return QtWidgets.QGraphicsItem.itemChange(self, change, value)
>     TypeError: itemChange(self, QGraphicsItem.GraphicsItemChange,
> Any): argument 2 has unexpected type 'GraphicsItemChange'
> 
>     Traceback (most recent call last):
>       File "/usr/lib/python3.8/enum.py", line 309, in __call__
>         return cls.__new__(cls, value)
>     RecursionError: maximum recursion depth exceeded while calling a
> Python object
> 
>     Segmentation fault
> 
> I notice this example *does* work fine if I replace
> `QtWidgets.QGraphicsItem` in `itemChange()`'s body with `super()` or
> `super(CustomGraphicsItem, self)`. Maybe that syntax is required now?
> But even if so, it shouldn't really be segfaulting if you do it the
> wrong way.

That's a SIP code generator bug that is fixed in the next snapshot.

> Here's a third issue:
> 
>     from PyQt6 import QtCore, QtGui, QtWidgets
> 
>     class CustomGraphicsView(QtWidgets.QGraphicsView):
>         def mouseMoveEvent(self, event):
>             print(event.x())
> 
>     class MainWindow(QtWidgets.QMainWindow):
>         def __init__(self):
>             super().__init__()
>             self.scene = QtWidgets.QGraphicsScene(0, 0, 100, 100, self)
>             self.view = CustomGraphicsView(self.scene, self)
>             self.view.setMouseTracking(True)
> 
>             self.setCentralWidget(self.view)
> 
>     app = QtWidgets.QApplication([])
>     mw = MainWindow()
>     mw.show()
>     app.exec()
> 
> On PyQt 6.0.1, running this and then moving your cursor over the
> graphics view results in the following traceback:
> 
>     Traceback (most recent call last):
>       File "/path/to/pyqt6_test_3.py", line 6, in mouseMoveEvent
>         print(event.x())
>     AttributeError: 'QMouseEvent' object has no attribute 'x'
> 
> I don't really have any idea about this one. The same thing happens if
> you try to access `.pos()`, so that's not a workaround.

PyQt6 does not support API elements that are deprecated (ie. x(), y(), 
pos() etc.). However the Qt docs are wrong in that these methods aren't 
marked as such.

The exact equivalent of event.x() is event.position().toPoint().x()

> PyQt is by far my favorite GUI framework in any language, so I'm
> really looking forward to being able to use PyQt6. Thanks in advance
> for any help you can provide!

The SIP bug is quite serious so I may make new releases sooner rather 
than later.

Thanks,
Phil


More information about the PyQt mailing list