Opened 5 years ago
Closed 5 years ago
#4120 closed enhancement (fixed)
Support PySide2 and PyQt5 with a shim qt module
| Reported by: | Tom Goddard | Owned by: | Tom Goddard |
|---|---|---|---|
| Priority: | moderate | Milestone: | |
| Component: | Window Toolkit | Version: | |
| Keywords: | Cc: | ||
| Blocked By: | Blocking: | ||
| Notify when closed: | Platform: | all | |
| Project: | ChimeraX |
Description
At Jan 7, 2021 group meeting we decided to look into using the standard qt.py shim library to easily switch between PySide2 and PyQt5 after a discussion of whether we should switch back to PyQt5 as it currently works better and seems better supported.
We have been using PySide2 in ChimeraX daily builds since December 2020. Two issues have arisen with it so far.
1) The webcam command is broken (ticket #4085) because PySide2 QCamera.setViewFinder() is not properly wrapped. A PySide2 ticket for that bug from August 2018 has not been resolved. I added exact details about the incorrectly generated code on Dec 24, 2020 but so far no response.
2) Qt OpenGL APIs appear not to be available in PySide2. I was considering moving from PyOpenGL to Qt OpenGL APIs because PyOpenGL is poorly supported (currently its builds omit a numpy_formathandler library that causes warnings, many other problems in the past). I posted to the PySide mailing list since the missing OpenGL APIs are not described but got no response.
https://lists.qt-project.org/pipermail/pyside/2020-December/003041.html
While these two issues are small, the lack of response from PySide2 developers is alarming.
Change History (8)
comment:1 by , 5 years ago
comment:2 by , 5 years ago
There are two well-supported shim libraries QtPy developers about their differences and combining their projects in 2016.
https://github.com/spyder-ide/qtpy
https://github.com/mottosso/Qt.py
Here is a discussion between the Qt.py and QtPy developers about their differences and combining their projects in 2016.
https://github.com/spyder-ide/qtpy/issues/69
The Qt.py developer did not want to combine, thought it important to stick to a single file implementation, and seemed to think his audience in the the film industry had different needs than the QtPy audience which was made for Spyder, a scientific python package.
Both packages seem widely used. I did not see any factor that would favor one over the other. The Qt.py package has a bit more commits in 2020 but both have quite few commits. Also Qt.py has ~200 forks versus QtPy ~100. Qt.py considers PySide2 the reference api while QtPy considers Qt the reference and says it follows PyQt5 more closely.
comment:3 by , 5 years ago
I could not find any mention online of a shim library that would handle PySide6/PyQt6 and PySide2/PyQt5.
comment:4 by , 5 years ago
I asked the Qt.py developers if there are PySide6 / PyQt6 plans
https://gitter.im/Qt-py/Lobby
Another person asked if qtpy will support PySide6 here
comment:5 by , 5 years ago
The Qt.py main developer responded in less than a day to by inquiry about support for PySide6 -- basically he will go there if film and game industries need it, but there is no indication they are going to Qt 6 currently.
From Marcus Ottosson
"Good question.
Short answer: Qt.py will support whichever bindings are relevant to the film and games industries.
Long answer: Qt.py was originally and currently made to accommodate Qt bindings in the VFX and games industries. These industries had a hard time moving from Qt 4 to 5 before Qt.py came along. I would wager that when(/if?) there is a move from 5 to 6, then Qt.py would follow suit; deprecating support for Qt 4. On the other hand, if it happens soon (within the next year or two) then odds are the transition would be between 4, 5 and 6, since there are still users of Qt 4 out there.
So in summary, Qt.py will continue to support Qt 4 and 5 for as long as VFX and games make use of it.
That said, I would welcome the addition of Qt 6 to enable support for future bindings, so long as it doesn't interfere with compatibility for Qt 4 and 5. Also bearing in mind that Qt.py must still work interchangeably with all bindings, including 6. It's possible Qt.py will be less about specific versions of Qt, and more about providing compatibility with QtWidgets specifically; as that's what most/all users of Qt in VFX/games currently use it for."
comment:6 by , 5 years ago
Marcus also said that Qt.py differs from qtpy in that Qt.py does not make wrappers around Qt classes like QPushButton, and instead gives you the exact QPushButton from the underlying PySide2 or PyQt5. He felt the wrappers could trip up some uses that mix Qt.py and direct use of PySide2 / PyQt5.
comment:7 by , 5 years ago
I replaced all imports from PySide2 with imports from Qt or from qtpy to test those shim modules.
I tried Qt.py 1.3.3 in ChimeraX -- it does not include QtWebEngine module. Gave up on it.
Then I tried qtpy 1.9.0 module. It does not have QtWebEngineCore, also missing QtWebEngineProfile from QtWebEngineWidgets. Also does not wrap sip.isdeleted() / shiboken2.isValid() for detected deleted C++ objects. Working around those issues by adding the missing stuff to qtpy it worked with PySIde2 and PyQt5.
qtpy has a very simple implementation. It simply has submodules like qtpy/QtWidgets.py that simply import all the methods from PySide2.QtWidgets or PyQt5.QtWidgets, nothing more, just a few lines of code. It has more code to handle differences in old PyQt4 and PySIde.
I think we would be better off making a very simple shim of our own following the pattern of qtpy. This would be very simple and would make it easier to include the missing stuff rather than patching and adding files to the qtpy distribution. Also our own very simple shim would simplify testing PySide6 / PyQt6 support. Also we could give the module the better name of just Qt.
Our own module could also contain the rare weird compatibility problems likes QImage.bits() having different methods in PyQt5 and PySIde2 (asarray() vs tobytes()).
I had to fix dozens of QAction.triggered.connect(lambda ses=session,...: ...) calls mostly in ui/gui.py and toolbar/tool.py so they don't get a bad first argument passed by PyQt5 or PySide2. The recipe that works is to use lambda *, ses=session, ...: ... with the "*" making PyQt5 / PySide2 introspection see that it should not pass an argument to the callback.
comment:8 by , 5 years ago
| Resolution: | → fixed |
|---|---|
| Status: | assigned → closed |
Made very simple Qt shim module (prereqs/qtshim) starting with qtpy and removing all the old PyQt4 / PySide code, and removing just about everything else. Total shim code is less than 100 lines and will allow easy testing of Qt 6 in the future.
Also switched from PySide2 to PyQt5 since it seems to be better supported and works better. This fixes the webcam command.
There were a dozen differences in moving from PyQt5 to PySide2 described in #1635. Need to consider whether all those can be covered by a shim library.