Third party cookies may be stored when visiting this site. Please see the cookie information.

PenguinTutor YouTube Channel

Python PySide6 - Creating a responsive application with Pyside 6 and QThreadPool

Creating graphical applications using PySide. PySide is a python library for using the Qt Framework. Pyside provides a convenient way of creating GUI applications. Note that much of this applies to other GUI applications, but the implementation would be different if using different programming languages or GUI frameworks. There are some other things that are specific to Python and there are some pitfalls which I explain in the video.

Video Explanation

The aim in this video is to show how you can use QThreadPool to ensure the application continues to be responsive when performing other operations. This is not the only option but some way of using an alternative thread or process is needed for most GUI applications.

There are documents online that explain some of this, but I found that they only give very basic examples, and I found it difficult to apply those in my project. So that’s why I thought it would be useful to provide an example how these can be applied.

About the code

Key parts of the code example

  • Import the following from Pyside6.QtCore
    • QThreadPool - Used for the Threadpool
    • Signal, Slot - Used for passing (emit) signals
    • QRunnable - Used for the worker class

Create a Signal as a class variable of the MainWindow using:



progress_signal = Signal(int)

Connect the signal to a method using:



progress_signal = Signal(int)

The worker class is created as below



class Worker (QRunnable):

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

        super().__init__()

        self.fn = fn

        self.args = args

        self.kwargs = kwargs

        

    @Slot() # Pyside6.QtCore.Slot

    def run(self):

        self.fn(*self.args, **self.kwargs)

Signals are sent using:



self.progress_signal.emit(self.count)

A thread can be added to the threadpool using:



    def button_pressed(self):

        worker = Worker(self.run_demo, 5)

        self.threadpool.start(worker)

No time.sleep()

The example code uses time.sleep() to simulate a task that takes a while to run. Note that you would not normally use time.sleep() in a program and that should be replaced with the code you need to run.

Prerequisite installing Pyside 6 using virtual environments

The prerequisite for this is Python 3 with Pyside 6. As Pyside 6 is not normally installed it will need to be installed manually. The Raspberry Pi, and some other operating systems, still provide only PySide2 packages in their repositories, therefore you may need to install PySide 6. It's fairly easy, but due to PEP 704 this should normally be done using a virtual environment. The following will setup the environment.



mkdir ~/.venv

python3 -m venv ~/.venv/pyside6

source ~/.venv/pyside6/bin/activate

pip install pyside6

After the packages are installed the code can be run using:



source ~/.venv/pyside6/bin/activate

python3 demo.py

Replace the program file with the file you save it. You can also use an IDE / programming editor such as Thonny or Mu.

Source code example

In the video I explain how the code is updated from a single threaded application to one using QThreadPool. I have not included the initial code here as the code is deliberately very bad.

Below is the final example which uses QThreadPool, Signals and Slots and creates a worker class to handle the passing of arguments to the thread.



from PySide6.QtCore import Qt, QThreadPool, Signal, Slot, QRunnable

from PySide6.QtWidgets import QApplication, QMainWindow, QWidget, QLabel, QPushButton, QVBoxLayout



# Only needed for access to command line arguments

import sys

import time





class MainWindow(QMainWindow):

    

    progress_signal = Signal(int)

    

    def __init__(self):

        super().__init__()

        

        self.count = 0

        

        self.threadpool = QThreadPool()

        

        self.progress_signal.connect(self.update_display)



        self.setWindowTitle("Threaded example")

        layout = QVBoxLayout()

        

        self.status_label = QLabel(str(self.count))

        self.status_label.setAlignment(Qt.AlignCenter)

        

        self.start_button = QPushButton("Start demo")

        

        self.start_button.pressed.connect(self.button_pressed)

        

        layout.addWidget(self.status_label)

        layout.addWidget(self.start_button)

        

        central_widget = QWidget()

        central_widget.setLayout(layout)

        

        self.setCentralWidget(central_widget)

       

    def button_pressed(self):

        worker = Worker(self.run_demo, 5)

        self.threadpool.start(worker)

           

    def run_demo (self, length=10):

        for i in range (0, length):

            print (f"Sleeping {self.count}")

            time.sleep (2)

            self.update_count()

            

    def update_display (self, new_num):

        self.status_label.setText(str(new_num))

        

    def update_count (self):

        self.count += 1

        self.progress_signal.emit(self.count)

        

class Worker (QRunnable):

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

        super().__init__()

        self.fn = fn

        self.args = args

        self.kwargs = kwargs

        

    @Slot() # Pyside6.QtCore.Slot

    def run(self):

        self.fn(*self.args, **self.kwargs)



# Create application

app = QApplication(sys.argv)



# Create a Qt widget for the window

window = MainWindow()

window.show()  



# Start the event loop.

app.exec()

PySide6 vs PySide2 on the Raspberry Pi

At the time of writing the Raspberry Pi includes PySide2, but not the current version which is PySide6. It is likely that the code will work in PySide2, but you will need to change the names for the imported modules (libraries). To run using PySide6 on the Raspberry Pi (and other Debian based Linux distributions) then you can follow the instructions below.



mkdir ~/venv

python -m venv ~/venv/pyside6

source ~/venv/pyside6/bin/activate

pip install pyside6

Then to run use:



source ~/venv/pyside6/bin/activate

python3 demo.py

Future plans

These are the projects I'm currently working on, although at the time of writing it's early days for both programs.

Graphical program designing in Python PySide 6

More information

See my other programming guides at:

Blog links and videos on programming

Previous Learn GUI programming
Learn GUI programming
Next Python QGraphicsScene
Python QGraphicsScene