PyQt5 - Tutorial 009. Using QThread with MoveToThread

PyQt5, QObject, Qt, moveToThread, QThread

Content

Based on one of the questions on the forum, I wrote an example on using QThread in PyQt5, as well as using the moveToThread method to move the class object of the inherited QObject to another thread.

In this example, a certain algorithm is executed, which returns the text through the signal, as well as the color of the text to the main GUI. This data is added to the QTextBrowser with a color setting.

The program will look as follows

Introduction

There are two main approaches for using QThread in Qt :

  1. Create a new class that inherits from QThread and override the run method
  2. Create a new class that inherits from QObject , write a run method that will execute some code, and transfer the instance of this class to another thread using the moveToThread method

The first method is recommended to be used only if you really need to override the stream class in order to create special functionality in the stream class. If you need to execute some code in another thread, then for this you need to create a separate class that will be transferred to another thread using the moveToThread method.

Also, I want to note right away that you cannot transfer GUI objects to other threads. Qt programs have two kinds of threads:

  • Main stream. GUI thread
  • Workflows. Worker threads

All GUI objects should create and work only in a GUI thread, while various other algorithms can be executed in worker threads.

I quote the documentation

As mentioned, each program has one thread when it is started. This thread is called the "main thread" (also known as the "GUI thread" in Qt applications). The Qt GUI must run in this thread. All widgets and several related classes, for example QPixmap, don't work in secondary threads. A secondary thread is commonly referred to as a "worker thread" because it is used to offload processing work from the main thread.

That is, if, even if something works for you in another thread, it will be only an accident that will sooner or later make itself felt. And your program will stop working. Never pass other GUI objects to other threads.

Program

Now consider the code of our program. Please note that the algorithm of actions will be as follows

  1. We write a class that inherits from QObject and has a run method for executing code in another thread
  2. In the window constructor, create a stream object
  3. In the window constructor, create an object that will be transferred to another thread
  4. Transfer the object to another stream
  5. We connect signals and slots
  6. Run the thread

It is advisable to perform all the initialization of the object and stream in this sequence, if you do not already have sufficient experience.
However, then you would not read this article.
If you swap steps 4 and 5, then you first connect the signals and slots, and then transfer the object to another stream, which will break the signal / slot connection. The application window will stop working. Or the application may just crash.

import sys
import time

from PyQt5 import QtCore, QtWidgets
from PyQt5.QtGui import QColor


class Ui_Form(object):
    def setupUi(self, Form):
        Form.setObjectName("Form")
        Form.resize(453, 408)
        self.verticalLayout = QtWidgets.QVBoxLayout(Form)
        self.verticalLayout.setObjectName("verticalLayout")
        self.verticalLayout_2 = QtWidgets.QVBoxLayout()
        self.verticalLayout_2.setObjectName("verticalLayout_2")
        self.textBrowser = QtWidgets.QTextBrowser(Form)
        self.textBrowser.setObjectName("textBrowser")
        self.verticalLayout_2.addWidget(self.textBrowser)
        self.verticalLayout.addLayout(self.verticalLayout_2)
        self.horizontalLayout = QtWidgets.QHBoxLayout()
        self.horizontalLayout.setObjectName("horizontalLayout")
        spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
        self.horizontalLayout.addItem(spacerItem)
        self.pushButton = QtWidgets.QPushButton(Form)
        self.pushButton.setObjectName("pushButton")
        self.horizontalLayout.addWidget(self.pushButton)
        spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
        self.horizontalLayout.addItem(spacerItem1)
        self.verticalLayout.addLayout(self.horizontalLayout)

        self.retranslateUi(Form)
        QtCore.QMetaObject.connectSlotsByName(Form)

    def retranslateUi(self, Form):
        _translate = QtCore.QCoreApplication.translate
        Form.setWindowTitle(_translate("Form", "Example"))
        self.pushButton.setText(_translate("Form", "Input"))


# Object, which will be moved to another thread
class BrowserHandler(QtCore.QObject):
    running = False
    newTextAndColor = QtCore.pyqtSignal(str, object)

    # method which will execute algorithm in another thread
    def run(self):
        while True:
            # send signal with new text and color from aonther thread
            self.newTextAndColor.emit(
                '{} - thread 2 variant 1.\n'.format(str(time.strftime("%Y-%m-%d-%H.%M.%S", time.localtime()))),
                QColor(0, 0, 255)
            )
            QtCore.QThread.msleep(1000)

            # send signal with new text and color from aonther thread
            self.newTextAndColor.emit(
                '{} - thread 2 variant 2.\n'.format(str(time.strftime("%Y-%m-%d-%H.%M.%S", time.localtime()))),
                QColor(255, 0, 0)
            )
            QtCore.QThread.msleep(1000)


class MyWindow(QtWidgets.QWidget):

    def __init__(self, parent=None):
        super().__init__()
        self.ui = Ui_Form()
        self.ui.setupUi(self)
        # use button to invoke slot with another text and color
        self.ui.pushButton.clicked.connect(self.addAnotherTextAndColor)

        # create thread
        self.thread = QtCore.QThread()
        # create object which will be moved to another thread
        self.browserHandler = BrowserHandler()
        # move object to another thread
        self.browserHandler.moveToThread(self.thread)
        # after that, we can connect signals from this object to slot in GUI thread
        self.browserHandler.newTextAndColor.connect(self.addNewTextAndColor)
        # connect started signal to run method of object in another thread
        self.thread.started.connect(self.browserHandler.run)
        # start thread
        self.thread.start()

    @QtCore.pyqtSlot(str, object)
    def addNewTextAndColor(self, string, color):
        self.ui.textBrowser.setTextColor(color)
        self.ui.textBrowser.append(string)

    def addAnotherTextAndColor(self):
        self.ui.textBrowser.setTextColor(QColor(0, 255, 0))
        self.ui.textBrowser.append('{} - thread 2 variant 3.\n'.format(str(time.strftime("%Y-%m-%d-%H.%M.%S", time.localtime()))))


if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    window = MyWindow()
    window.show()
    sys.exit(app.exec())

We recommend hosting TIMEWEB
We recommend hosting TIMEWEB
Stable hosting, on which the social network EVILEG is located. For projects on Django we recommend VDS hosting.
- company blog
Support the author Donate
c

Огромное спасибо!

Comments

Only authorized users can post comments.
Please, Log in or Sign up
Donate

Hello, Dear Users of EVILEG!!!

If the site helped you, then support the development of the site financially, please.

You can do it by following ways:

Thank you, Evgenii Legotckoi

A
March 29, 2020, 12:14 p.m.
Alexanderv66

C++ - Тест 003. Условия и циклы

  • Result:71points,
  • Rating points1
A
March 29, 2020, 12:05 p.m.
Alexanderv66

C ++ - Test 004. Pointers, Arrays and Loops

  • Result:80points,
  • Rating points4
DE
March 28, 2020, 3:13 p.m.
Denis Erokhin

C++ - Test 005. Structures and Classes

  • Result:100points,
  • Rating points10
Last comments
March 27, 2020, 2:40 p.m.
Evgenij Legotskoj

Добрый день. В конце пятой статьи скачать можете.
March 27, 2020, 2:28 p.m.
mkdir _

Здравствуйте, а можно, пожалуйста, ссылку на целые исходники, если есть?
March 27, 2020, 4:36 a.m.
Evgenij Legotskoj

Скорее всего также, как и для установки всех остальных переменых в CMake, через использование set
March 27, 2020, 2:47 a.m.
Andrej Jankovich

Ошибка с ярлыками в Windows исправлена в версиии 1.4.0.4
March 26, 2020, 1:37 p.m.
Andrej Jankovich

но появятся ярлыки, сейчас поправим
Now discuss on the forum
March 30, 2020, 4:16 a.m.
Evgenij Legotskoj

Попробуйте запустить через плейлист, а не через setMedia. Пример есть в этой статье
March 30, 2020, 3:25 a.m.
Evgenij Legotskoj

Добрый день. Почитайте документацию, в исходниках этой библиотеки есть исходнные классы логгеров. Вам наверняка достаточно только правильно вызвать пару функций, чтобы задать имя файла…
March 30, 2020, 3:17 a.m.
Evgenij Legotskoj

Нет, не нужно ничего умножать, если у вас включена поддержа High DPI в приложении а QML, то достаточно держать в памяти, что все значения задаются в DPI, и просто задаёте значения равные DPI сра…
March 27, 2020, 10 a.m.
Mihailll

Оказывается нужно сделать столбец уникальным ALTER TABLE public.drivers ADD UNIQUE (agregator1_id); Потом так работает INSERT INTO drivers( name_driver, surname, middle_name, agr…
s
March 27, 2020, 8:25 a.m.
shuric

Спасибо за ответы. Скорее всего оставлю на неопределенное время под macOS. Хоть будет полезно другим начинающим разработчикам.
EVILEG
About
Services
© EVILEG 2015-2019
Recommend hosting TIMEWEB