Various Issues with PyQt6

RoadrunnerWMC roadrunnerwmc at gmail.com
Fri Feb 26 04:13:47 GMT 2021


Hi again Phil,

I just installed PyQt 6.0.2. The problems from my previous email are fixed
now (thanks!) and my application is able to launch. I am having a few new
issues now, though.

Here's the main one:

    from PyQt6 import QtCore, QtGui, QtWidgets
    app = QtWidgets.QApplication([])

    mbox = QtWidgets.QMessageBox()
    mbox.setStandardButtons(QtWidgets.QMessageBox.StandardButtons.Yes |
QtWidgets.QMessageBox.StandardButtons.No)
    ret = mbox.exec()

    # The return value is an integer, but
QtWidgets.QMessageBox.StandardButtons
    # can't be converted or compared to integers.

    # This prints False no matter which button you clicked:
    print(ret == QtWidgets.QMessageBox.StandardButtons.Yes)

    # TypeError: int() argument must be a string, a bytes-like object or a
number, not 'StandardButtons'
    int(QtWidgets.QMessageBox.StandardButtons.Yes)

    # TypeError: unsupported operand type(s) for &: 'int' and
'StandardButtons'
    print(ret & QtWidgets.QMessageBox.StandardButtons.Yes)

As far as I can tell, there's no way to check the return value of
`QtWidgets.QMessageBox.exec()` without hardcoding integer values (yuck).
One way to fix it would be to change `.exec()`'s return type to
`QtWidgets.QMessageBox.StandardButtons`, but since the integer values of
the enum are officially publicly documented (
https://doc.qt.io/qt-6/qmessagebox.html#StandardButton-enum), my guess is
that adding support for int comparisons to the `StandardButtons` class
would be a better solution. Or maybe there already is a way to do this
comparison and I just don't see it?

A second issue:

    from PyQt6 import QtCore, QtGui, QtWidgets
    app = QtWidgets.QApplication([])
    folder = QtWidgets.QFileDialog.getExistingDirectory(None, 'Select a
folder')
    print(folder)

For whatever reason, this actually creates a *file*-selection dialog, which
doesn't let me choose a folder. (Platform is KDE Neon (Ubuntu 20.04), if it
helps.)

One more issue I found on 6.0.1 after my previous email -- though
unfortunately without a way to trigger it consistently -- is that sometimes
`QMainWindow.restoreState()` or `QMainWindow.restoreGeometry()` (not sure
which) crashes if the pre-existing state (or geometry) was saved by PyQt5.

The best workaround I can think of is to append the PyQt version name
("PyQt5"/"PyQt6") to the application name passed to the QSettings
constructor, thus preventing PyQt6 from ever trying to read PyQt5's
settings and vice versa. Luckily, my application has very few settings and
I don't expect anyone to switch back and forth between PyQt versions, so
this won't be a big problem. It's still not really ideal, though.

Is this crash considered a bug? If not, is there a better way for me to
handle this situation?

Thanks again!

On Wed, Feb 10, 2021 at 12:11 PM RoadrunnerWMC <roadrunnerwmc at gmail.com>
wrote:

> Thank you! I'll look forward to the next release.
>
>
> On Tue, Feb 9, 2021 at 7:45 AM Phil Thompson
> <phil at riverbankcomputing.com> wrote:
> >
> > 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
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://www.riverbankcomputing.com/pipermail/pyqt/attachments/20210225/837563fc/attachment-0001.htm>


More information about the PyQt mailing list