본문 바로가기
PyQt5_

PyQt6 and PySide6 —What’s the difference?

by 자동매매 2023. 3. 16.

 

Appendix C: PyQt6 and PySide6 —

What’s the difference?

If you start building Python application with Qt6 you’ll soon discover that there are in fact two packages which you can use to do this — PyQt6 and PySide6.

In this short chapter I’ll run through why exactly this is, whether you need to care (spoiler: you really don’t), what the (few) differences are and how to work around them. By the end you should be comfortable re-using code examples from both PyQt6 and PySide6 tutorials to build your apps, regardless of which package you’re using yourself.

Background

Why are there two libraries?

PyQt is developed by Phil Thompson of Riverbank Computing Ltd. and has existed for a very long time — supporting versions of Qt going back to 2.x. In 2009 Nokia, who owned Qt toolkit at the time, wanted to make the Python bindings for Qt available in a more permissive LGPL license. Unable to come to agreement with Riverbank (who would lose money from this, so fair enough) they then released their own _bindings as PySide.

Z It’s called PySide because "side" is Finnish for "binder".

The two interfaces were basically equivalent, but over time development of PySide lagged behind PyQt. This was particularly noticeable following the release of Qt 5 — the Qt5 version of PyQt (PyQt5) has been available since mid-2016, while the first stable release of PySide2 was 2 years later. With that in mind, it is unsurprising that many Qt5 on Python examples use PyQt5 — if only because it was available.

However, the Qt project has recently adopted PySide as the official Qt for Python release which should ensure its viability going forward. When Qt6 was released,

both Python bindings were available shortly after.

PyQt6 PySide6

First stable release Jan 2021 Dec 2020

Developed by Riverbank Computing

Ltd.

Qt

License GPL or commercial LGPL

Platforms Python 3 Python 3

Which should you use? Well, honestly, it doesn’t really matter.

Both packages are wrapping the same library — Qt6 — and so have 99.9% identical APIs (see below for the few differences). Anything you learn with one library will be easily applied to a project using the other. Also, no matter with one you choose to use, it’s worth familiarizing yourself with the other so you can make the best use of all available online resources — using PyQt6 tutorials to build your PySide6 applications for example, and vice versa.

In this short chapter I’ll run through the few notable differences between the two packages and explain how to write code which works seamlessly with both. After reading this you should be able to take any PyQt6 example online and convert it to work with PySide6.

Licensing

The main notable difference between the two versions is licensing — with PyQt6 being available under a GPL or commercial license, and PySide6 under a LGPL license.

If you are planning to release your software itself under the GPL, or you are developing software which will not be distributed , the GPL requirement of PyQt6 is unlikely to be an issue. However, if you want to distribute your software but not share your source code you will need to purchase a commercial license from

Riverbank for PyQt6 or use PySide6.

Z

Qt itself is available under a Qt Commercial License , GPL 2.0, GPL

3.0 and LGPL 3.0 licenses.

Namespaces & Enums

One of the major changes introduced for PyQt6 is the need to use fully qualified names for enums and flags. Previously, in both PyQt5 and PySide2 you could make use of shortcuts — for example Qt.DecorationRole, Qt.AlignLeft. In PyQt6 these are now Qt.ItemDataRole.DisplayRole and Qt.Alignment.AlignLeft respectively. This change affects all enums and flag groups in Qt. In PySide6 both long and short names remain supported.

UI files

Another major difference between the two libraries is in their handling of loading .ui files exported from Qt Creator/Designer. PyQt6 provides the uic submodule which can be used to load UI files directly, to produce an object. This feels pretty Pythonic (if you ignore the camelCase).

import sys

from PyQt6 import QtWidgets, uic

app = QtWidgets.QApplication(sys.argv)

window = uic.loadUi("mainwindow.ui")

window.show()

app. exec ()

The equivalent with PySide6 is one line longer, since you need to create a QUILoader object first. Unfortunately the API of these two interfaces is different too (.load vs .loadUI).

import sys

from PySide6 import QtCore, QtGui, QtWidgets

from PySide6.QtUiTools import QUiLoader

loader = QUiLoader()

app = QtWidgets.QApplication(sys.argv)

window = loader.load("mainwindow.ui", None)

window.show()

app. exec ()

To load a UI onto an existing object in PyQt6, for example in your QMainWindow. init you can call uic.loadUI passing in self(the existing widget) as the second parameter.

import sys

from PyQt6 import QtCore, QtGui, QtWidgets

from PyQt6 import uic

class MainWindow (QtWidgets.QMainWindow):

def __init__ (self, *args, **kwargs):

super().__init__(*args, **kwargs)

uic.loadUi("mainwindow.ui", self)

app = QtWidgets.QApplication(sys.argv)

window = MainWindow()

window.show()

app. exec ()

The PySide6 loader does not support this — the second parameter to .load is the parent widget of the widget you’re creating. This prevents you adding custom code to the init block of the widget, but you can work around this with a separate function.

import sys

from PySide6 import QtWidgets

from PySide6.QtUiTools import QUiLoader

loader = QUiLoader()

def mainwindow_setup (w):

w.setWindowTitle("MainWindow Title")

app = QtWidgets.QApplication(sys.argv)

window = loader.load("mainwindow.ui", None)

mainwindow_setup(window)

window.show()

app. exec ()

Converting UI files to Python

Both libraries provide identical scripts to generate Python importable modules from Qt Designer .ui files. For PyQt6 the script is named pyuic5 —

pyuic6 mainwindow.ui -o MainWindow.py

You can then import the UI_MainWindow object, subclass using multiple inheritance from the base class you’re using (e.g. QMainWIndow) and then call self.setupUI(self) to set the UI up.

import sys

from PyQt6 import QtWidgets

from MainWindow import Ui_MainWindow

class MainWindow (QtWidgets.QMainWindow, Ui_MainWindow):

def __init__ (self, *args, **kwargs):

super().__init__(*args, **kwargs)

self.setupUi(self)

app = QtWidgets.QApplication(sys.argv)

window = MainWindow()

window.show()

app. exec ()

For PySide6 it is named pyside6-uic —

pyside6-uic mainwindow.ui -o MainWindow.py

The subsequent setup is identical.

import sys

from PySide6 import QtWidgets

from MainWindow import Ui_MainWindow

class MainWindow (QtWidgets.QMainWindow, Ui_MainWindow):

def __init__ (self, *args, **kwargs):

super().__init__(*args, **kwargs)

self.setupUi(self)

app = QtWidgets.QApplication(sys.argv)

window = MainWindow()

window.show()

app.exec_()

ë

For more information in using Qt Designer with either PyQt6 or

PySide6 see the Qt Creator chapter.

exec() or exec_()

The .exec() method is used in Qt to start the event loop of your QApplication or dialog boxes. In Python 2.7 exec was a keyword, meaning it could not be used for variable, function or method names. The solution used in both PyQt4 and PySide was to rename uses of .exec to .exec_() to avoid this conflict.

Python 3 removed the exec keyword, freeing the name up to be used. As a result from Qt6 all .exec() calls are named just as in Qt itself. However, PySide6 still supports .exec_() so don’t be surprised if you see this in some code.

Slots and Signals

Defining custom slots and signals uses slightly different syntax between the two libraries. PySide6 provides this interface under the names Signal and Slot while PyQt6 provides these as pyqtSignal and pyqtSlot respectively. The behavior of them both is identical for defining and slots and signals.

 

 

Appendix C: PyQt6 and PySide6 — What’s the difference?.

my_custom_signal = pyqtSignal() # PyQt6

my_custom_signal = Signal() # PySide6

my_other_signal = pyqtSignal(int) # PyQt6

my_other_signal = Signal(int) # PySide6

Or for a slot —

@pyqtslot

def my_custom_slot ():

pass

@Slot

def my_custom_slot ():

pass

If you want to ensure consistency across PyQt6 and PySide6 you can use the following import pattern for PyQt6 to use the Signal and @Slot style there too.

from PyQt6.QtCore import pyqtSignal as Signal, pyqtSlot as Slot

Z

You could of course do the reverse from PySide6.QtCore import

Signal as pyqtSignal, Slot as pyqtSlot although that’s a bit

confusing.

QMouseEvent

In PyQt6 QMouseEvent objects no longer have the .pos(), .x() or .y() shorthand property methods for accessing the position of the event. You must use the .position() property to get a QPoint object and access the .x() or .y() methods on that. The .position() method is also available in PySide6.

Features in PySide6 but not in PyQt6

As of Qt 6 PySide supports two Python feature flags to help make code more Pythonic with snake_case variable names and the ability to assign and access properties directly, rather than using getter/setter functions. The example below shows the impact of these changes on code —

Listing 311. Standard PySide6 code.

table = QTableWidget()

table.setColumnCount( 2 )

button = QPushButton("Add")

button.setEnabled(False)

layout = QVBoxLayout()

layout.addWidget(table)

layout.addWidget(button)

The same code, but with snake_case and true_property enabled.

Listing 312. PySide6 code with Snake case & properties.

from __feature__ import snake_case, true_property

table = QTableWidget()

table.column_count = 2

button = QPushButton("Add")

button.enabled = False

layout = QVBoxLayout()

layout.add_widget(table)

layout.add_widget(button)

These feature flags are a nice improvement for code readability, however as they are not supported in PyQt6 it makes migration between the libraries more difficult.

Supporting both in libraries

ë

You don’t need to worry about this if you’re writing a standalone

app, just use whichever API you prefer.

If you’re writing a library, widget or other tool you want to be compatible with

both PyQt6 and PySide6 you can do so easily by adding both sets of imports.

import sys

if 'PyQt6' in sys.modules:

# PyQt6

from PyQt6 import QtGui, QtWidgets, QtCore

from PyQt6.QtCore import pyqtSignal as Signal, pyqtSlot as Slot

else :

# PySide6

from PySide6 import QtGui, QtWidgets, QtCore

from PySide6.QtCore import Signal, Slot

This is the approach used in our custom widgets library, where we support for PyQt6 and PySide6 with a single library import. The only caveat is that you must ensure PyQt6 is imported before (as in on the line above or earlier) when importing this library, to ensure it is in sys.modules.

To account for the lack of shorthand enum and flags in PyQt6 you can generate these yourself. For example, the following code will copy references for each of the enum objects elements up to their parent object, making them accessible as in PyQt5, PySide2 & PySide6. The code would only need to be run under PyQt6.

enums = [

(QtCore.Qt, 'Alignment'),

(QtCore.Qt, 'ApplicationAttribute'),

(QtCore.Qt, 'CheckState'),

(QtCore.Qt, 'CursorShape'),

(QtWidgets.QSizePolicy, 'Policy'),

]

# Look up using the long name (e.g. QtCore.Qt.CheckState.Checked, used

# in PyQt6) and store under the short name (e.g. QtCore.Checked, used

# in PyQt5, PySide2 & accepted by PySide6).

for module, enum_name in enums:

for entry in getattr(module, enum_name):

setattr(module, entry.name, entry)

Alternatively, you can define a custom function to handle the namespace lookup.

def _enum (obj, name):

parent, child = name.split('.')

result = getattr(obj, child, False)

if result: # Found using short name only.

return result

obj = getattr(obj, parent) # Get parent, then child.

return getattr(obj, child)

When passed an object and a PyQt6 compatible long-form name, this function will return the correct enum or flag on both PyQt6 and PySide6.

>>> _enum(PySide6.QtCore.Qt, 'Alignment.AlignLeft')

PySide6.QtCore.Qt.AlignmentFlag.AlignLeft

>>> _enum(PyQt6.QtCore.Qt, 'Alignment.AlignLeft')

 

If you’re doing this in multiple files it can get a bit cumbersome. A nice solution to this is to move the import logic and custom shim methods to their own file, e.g. named qt.py in your project root. This module imports the Qt modules (QtCore,

QtGui, QtWidgets, etc.) from one of the two libraries, and then you import into your application from there.

The contents of the qt.py are the same as we used earlier —

import sys

if 'PyQt6' in sys.modules:

# PyQt6

from PyQt6 import QtGui, QtWidgets, QtCore

from PyQt6.QtCore import pyqtSignal as Signal, pyqtSlot as Slot

else :

# PySide6

from PySide6 import QtGui, QtWidgets, QtCore

from PySide6.QtCore import Signal, Slot

def _enum (obj, name):

parent, child = name.split('.')

result = getattr(obj, child, False)

if result: # Found using short name only.

return result

obj = getattr(obj, parent) # Get parent, then child.

return getattr(obj, child)

You must remember to add any other PyQt6 modules you use (browser, multimedia, etc.) in both branches of the if block. You can then import Qt6 into your own application as follows —

from .qt import QtGui, QtWidgets, QtCore, _enum

...and it will work seamlessly across either library.

That’s really it

There’s not much more to say — the two libraries really are that similar. However, if you do stumble across any other PyQt6/PySide6 examples or features which you can’t easily convert, drop me a note.

Appendix D: What next?

This book covers the key things you need to know to start creating GUI applications with Python. If you’ve made it here you should be well on your way to create your own apps!

But there is still a lot to discover while you build your applications. To help with this I post regular tips, tutorials and code snippets on the accompanying website. Like this book all samples are MIT licensed and free to mix into your own apps. You may also be interested in joining my Python GUI Academy where I have video tutorials covering the topics in this book & beyond!

Thanks for reading, and if you have any feedback or suggestions please let me know!

Get access to updates

If you bought this book direct from me, you will receive automatic updates to this book. If you bought this book elsewhere, you can email your receipt to me to get access future updates.

Documentation

Resource

Qt6 Documentation

PyQt6 Library documentation

PySide "Qt for Python" Library documentation

Copyright

This book is ©2022 Martin Fitzpatrick. All code examples in this book are free to use in your own programming projects without license.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

========================

 

 

 

'PyQt5_' 카테고리의 다른 글

Translating C++  (0) 2023.03.16
Installing PyQt6  (0) 2023.03.16
Moonsweeper  (0) 2023.03.16
Mozzarella Ashbadger  (0) 2023.03.16
Using Custom Widgets in QtDesigner  (0) 2023.03.16

댓글