If you are already developing Python GUI apps with PyQt5, you might be asking yourself whether it's time to upgrade to PyQt6 and use the latest version of the Qt library. In this article we'll look at the main differences between PyQt5 and PyQt6, benefits of upgrading and problems you might encounter when doing so.
If you're starting out and having trouble deciding, don't worry! I have both a PyQt5 ebook and PyQt6 ebook available. If you get both, you get a heavy discount since they're fundamentally the same.
Background
Qt is a GUI framework written in the C++ programming language created by Trolltech, now developed by The Qt Company. There are two Python bindings: PySide and PyQt. The former is developed in-house by The Qt Company while PyQt is developed independently by Riverbank Computing Ltd. The first version of PyQt6 was released on January 4th, 2021, just one month after the release of Qt6 itself.
For more information on the differences between the latest versions of the two bindings, take a look at PyQt6 vs PySide6.
Upgrading from PyQt5 to PyQt6
The upgrade path from PyQt5 to PyQt6 is fairly straightforward, with one main gotcha. For some applications, just renaming the imports from PyQt5
to PyQt6
will be enough to convert your application to work with the new library. For most however, you will need to account for changes in both PyQt and Qt itself.
Let’s get acquainted with a few differences between the two versions to know how to write code that works seamlessly with both. After reading this, you should be able to take any PyQt5 example online and convert it to work with PyQt6.
Enums
The change mostly likely to impact your projects is the removal of the short-form names for enum members. In PyQt6 all enum members must be named using their fully qualified names. This applies to all enums and flags, including those in the QtCore.Qt
namespace. For example the Qt.Checked
flag in PyQt6 becomes Qt.CheckState.Checked
.
Create GUI Applications with Python & Qt5 by Martin Fitzpatrick — (PyQt5 Edition) The hands-on guide to making apps with Python — Over 10,000 copies sold!
- PyQt5
- PyQt6
widget = QCheckBox("This is a checkbox")
widget.setCheckState(Qt.Checked)
widget = QCheckBox("This is a checkbox")
widget.setCheckState(Qt.CheckState.Checked)
There are too many updated values to mention them all here. But if you're converting a codebase you can usually just search online for the short-form and the longer form will be in the results.
The good news is that the change is backwards compatible: the fully-qualified names also work in PyQt5. So you can make the changes in your PyQt5 code before beginning the upgrade progress.
.exec()
or .exec_()
?
The .exec()
method in Qt starts the event loop of your QApplication
or dialog boxes. In Python 2.7, exec
was a keyword, meaning that it could not be used as a variable name, a function name, or a method name. In earlier versions of PyQt the method was renamed as .exec_()
– adding a trailing underscore – to avoid a conflict.
Python 3.0 removed the exec
keyword, freeing up the name to be used. And since PyQt6 targets only Python 3.x versions, the underscored names have been removed. These were previously deprecated in PyQt5, and the .exec()
method will work there too.
No more QResources
PyQt6 has removed support for Qt's Resource Framework. For packaging data files with your applications, you can use PyInstaller's data file support.
Differences in Qt6
In addition to the changes above there are many other minor changes that reflect underlying differences in Qt6 vs. Qt5 and aren't unique to PyQt itself.
QAction
moved
In Qt6 the QAction
class, which is used for creation toolbars and menus, has been moved from the QtWidgets to the QtGui module.
- PyQt5
- PyQt6
from PyQt5.QtWidgets import QAction
from PyQt6.QtGui import QAction
This may seem strange, but the move makes sense since actions can also be used in QML (non-widgets) applications.
High DPI Scaling
The high DPI (dots per inch) scaling attributes Qt.AA_EnableHighDpiScaling
, Qt.AA_DisableHighDpiScaling
and Qt.AA_UseHighDpiPixmaps
have been deprecated because high DPI is enabled by default in PyQt6 and can’t be disabled.
QMouseEvent
QMouseEvent.pos()
and QMouseEvent.globalPos()
methods returning a QPoint
object as well as QMouseEvent.x()
and QMouseEvent.y()
returning an int
object have been deprecated – use QMouseEvent.position()
and QMouseEvent.globalPosition()
returning a QPointF
object instead, so like QMouseEvent.position().x()
and QMouseEvent.position().y()
.
Qt.MidButton
has been renamed to Qt.MiddleButton
Platform specific
Finally, platform-specific methods in the QtWin
and QtMac
modules have been deprecated, in favor of using the native calls instead. In PyQt applications the only likely consequence of this will be the setCurrentProcessExplicitAppUserModelID
call to set an application ID, for taskbar icon grouping on Windows.
- QtWin
- Native
try:
# Include in try/except block if you're also targeting Mac/Linux
from PyQt5.QtWinExtras import QtWin
myappid = 'com.learnpyqt.examples.helloworld'
QtWin.setCurrentProcessExplicitAppUserModelID(myappid)
except ImportError:
pass
try:
# Include in try/except block if you're also targeting Mac/Linux
from ctypes import windll # Only exists on Windows.
myappid = 'mycompany.myproduct.subproduct.version'
windll.shell32.SetCurrentProcessExplicitAppUserModelID(myappid)
except ImportError:
pass
Miscellaneous
QDesktopWidget
has been removed – useQScreen
instead, which can be retrieved usingQWidget.screen()
,QGuiApplication.primaryScreen()
, orQGuiApplication.screens()
..width()
ofQFontMetrics
has been renamed to.horizontalAdvance()
..get()
ofQOpenGLVersionFunctionsFactory()
has been recommended to be used instead of.versionFunctions()
ofQOpenGLContext()
when obtaining any functions of the Open GL library.QRegularExpression
has replacedQRegExp
.QWidget.mapToGlobal()
andQWidget.mapFromGlobal()
now accept and return aQPointF
object.
Missing modules
This isn’t a concern anymore, but when Qt6 was new, not all of the Qt modules had been ported yet, and so were not available in PyQt. If you needed any of these modules, the upgrade to PyQt6 was not previously workable. Fast forward to Qt 6.2 and PyQt 6.2, the good news is that all of those missing modules are now back, so you can upgrade without problems.
Is it time to upgrade?
Whether or not it's time to upgrade depends on your project. If you're starting out learning PyQt (or GUI programming in general), you may prefer to stick with PyQt5 for the time being as there are far more examples available online. While the differences are relatively minor, anything not working is confusing when you are learning something new. Anything you learn with PyQt5 will carry over to PyQt6, so you can switch once you're a bit more confident.
If you're starting a new project however, or are reasonably familiar with PyQt, I'd recommend jumping into PyQt6 now.
If you want to get started with PyQt6, the PyQt6 book is available with all code examples updated for this latest edition of PyQt.
PyQt
Backwards compatibility
If you're developing software that's targeting both PyQt5 and PyQt6 you can use conditional imports to import the classes from whichever module is loaded.
try:
from PyQt6 import QtWidgets, QtGui, QtCore # ...
except ImportError:
from PyQt5 import QtWidgets, QtGui, QtCore # ...
If you add these imports to a file in the root of your project named qt.py
. You can then, in your own code files import use from qt import QtWidgets
and the available library will be imported automatically.
If you use the fully-qualified names for enums and exec()
then many applications previously written in PyQt5 will work as-is. However, importing in this way won't work around any of the differences between Qt5 and Qt6 mentioned above. For that, we recommend using the QtPy library.
Universal compatibility
If you need to support all Python Qt libraries (PyQt5, PyQt6, PySide2, PySide6) or are dependent on features which have changed between versions of Qt, then you should consider using QtPy. This package is a small abstraction layer around all versions of the Qt libraries, which allows you to use them interchangeably (as far as possible) in your applications.
If you're developing Python libraries or applications that need to be portable across different versions it is definitely worth a look.
Conclusion
As we've discovered, there are no major differences between PyQt5 and PyQt6. The changes that are there can be easily worked around. If you are new to Python GUI programming with Qt you may find it easier to start with PyQt5 still, but for any new project I'd suggest starting with PyQt6.
Packaging Python Applications with PyInstaller by Martin Fitzpatrick — This step-by-step guide walks you through packaging your own Python applications from simple examples to complete installers and signed executables.