Come to the PySide!

4 min read
Come to the PySide!

Due to the increasing number of tools that can create Python bindings of popular libraries written in another programming language, it became easy to extend and use these libraries from a Python code. Tools like SWIG, Shiboken, Cython, among others, made it so easy to write Desktop GUIs (Graphical User Interfaces). One of these libraries is PySide2, a port of Qt5 to Python.

PySide2 is the official Python module from the Qt for Python project, which provides access to the complete Qt 5.12+ framework.  - https://pypi.org/project/PySide2/

In this article, we will discuss how to install and use PySide2 by writing a desktop application.

Installation

To install PySide2 using pip, run the command pip install pyside2.

A simple window

PySide2 or Qt user interfaces are widget-based. The base class for all user interfaces is the QWidget class. To create a simple window using QWidget, simply initialize a QWidget object and call show() function for the widget to appear. In the example below, we used QApplication which controls the GUI's control flow and main settings and called exec_() to start the main loop.

#!/usr/bin/env python
import sys

from PySide2.QtWidgets import QApplication, QWidget

if __name__ == '__main__':
    application = QApplication(sys.argv)
    window = QWidget()
    window.setWindowTitle("Example Window")
    window.show()
    sys.exit(application.exec_())

After executing the above code, we should be able to see a blank window.

The QMainWindow

As a base class, QWidget is very extensible if we want to write a custom widget. However, if we want a complete window application, the QMainWindow class has almost all the basic features we need for a user interface. A QMainWindow provides the following layout which includes a menu bar, a location for toolbars, dock widgets, a status bar and a central widget,

QMainWindow Layout

To write a simple mainwindow, simply change QWidget to QMainWindow from the previous example. Now we can easily add an action, for example, terminating the application.

#!/usr/bin/env python
import sys

from PySide2.QtWidgets import QApplication, QMainWindow, QMenuBar, QMenu

if __name__ == '__main__':
    application = QApplication(sys.argv)
    window = QMainWindow()
    window.setWindowTitle("Example MainWindow")
    menu_bar = window.menuBar()
    file_menu = menu_bar.addMenu("File")
    file_menu.addAction("Exit", application.quit)
    window.show()
    sys.exit(application.exec_())

The above example code will result to a window with an Exit action in the File menu.

Refactoring

The above example code base becomes larger as we add more widgets to it. To solve this, we can refactor our code by inheriting the widget classes and implementing the functionalities inside these classes.

#!/usr/bin/env python
import sys

from PySide2.QtWidgets import QApplication, QMainWindow, QMenuBar, QMenu


class MyWindow(QMainWindow):

    def __init__(self, *args, **kwargs):
        super(MyWindow, self).__init__(*args, **kwargs)
        self.setWindowTitle("Example MainWindow")
        menu_bar = self.menuBar()
        file_menu = menu_bar.addMenu("File")
        file_menu.addAction("Exit", QApplication.quit)


if __name__ == '__main__':
    application = QApplication(sys.argv)
    window = MyWindow()
    window.show()
    sys.exit(application.exec_())

Writing a complete example

We will be creating an image viewer that has a menu and a viewer as the central widget. First step is to create a Viewer class by painting the image directly to the widget.

#!/usr/bin/env python
from PySide2.QtGui import QImage, QPainter, QPixmap
from PySide2.QtWidgets import QWidget


class Viewer(QWidget):

    def __init__(self, *args, **kwargs):
        super(Viewer, self).__init__(*args, **kwargs)
        self.image = QImage()

    def set_image(self, file_name):
        self.image.load(file_name)

    def paintEvent(self, event):
        painter = QPainter(self)
        if self.image:
            painter.drawPixmap(0, 0, QPixmap.fromImage(self.image))

The last step is to create the main window and set a Viewer object as the central widget. The Open menu action will acquire the image path using QFileDialog.getOpenFileName.

#!/usr/bin/env python
import os

from PySide2.QtWidgets import QMainWindow, QAction, QFileDialog

from viewer import Viewer


class MainWindow(QMainWindow):
    def __init__(self, *args, **kwargs):
        super(MainWindow, self).__init__(*args, **kwargs)
        self.setWindowTitle("Image Viewer")
        self.setMinimumSize(600, 400)
        menu_bar = self.menuBar()
        file_menu = menu_bar.addMenu("File")
        file_menu.addAction(QAction("Open", self, triggered=self.open))
        self.viewer = Viewer()
        self.setCentralWidget(self.viewer)

    def open(self):
        file_name, filter_ = QFileDialog.getOpenFileName(
            self,
            caption="Open Image",
            dir=os.path.expanduser("~/Pictures/ron.sh"),
            filter="Images (*.png *jpg)",
        )
        self.viewer.set_image(file_name)
        self.update()

We should be able to open PNG and JPG files as defined in the filter.

Image by NickyPe from Pixabay

To wrap up

There is another popular Python library for Qt which is called PyQt. However, PySide2 has less restrictions as it is licensed under LGPL compared to PyQt's GPL license. In addition, PySide2 is the official Python binding for Qt5. In terms of maturity, PyQt definitely takes the spot.

Qt/C++ is a popular cross-platform library for C++. With the introduction of Python bindings like PySide2 and PyQt, developing Qt applications became easier and faster.

Read more articles like this in the future by buying me a coffee!

Buy me a coffeeBuy me a coffee