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! If you buy my PyQt6 ebook you get both PyQt5 & PyQt6 editions included. That means you'll have everything you need, whichever you end up choosing.
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
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.
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
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() 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.
In Qt6 the
QAction class, which is used for creation toolbars and menus, has been moved from the QtWidgets to the QtGui module.
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_UseHighDpiPixmaps have been deprecated because high DPI is enabled by default in PyQt6 and can’t be disabled.
QMouseEvent.globalPos() methods returning a
QPoint object as well as
QMouseEvent.y() returning an
int object have been deprecated – use
QMouseEvent.globalPosition() returning a
QPointF object instead, so like
Qt.MidButton has been renamed to
Finally, platform-specific methods in the
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.
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
QDesktopWidgethas been removed – use
QScreeninstead, which can be retrieved using
QFontMetricshas been renamed to
QOpenGLVersionFunctionsFactory()has been recommended to be used instead of
QOpenGLContext()when obtaining any functions of the Open GL library.
QWidget.mapFromGlobal()now accept and return a
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.
To support developers in [[ countryRegion ]] I give a [[ localizedDiscount[couponCode] ]]% discount with the code [[ couponCode ]] — Enjoy!
For [[ activeDiscount.description ]] I'm giving a [[ activeDiscount.discount ]]% discount with the code [[ couponCode ]] — Enjoy!
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.
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.
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.