Search code examples
pythonmultithreadingpyqtpyqt5qthread

Why my QThread class drastically slows PyQT5 app?


I'm facing the problem with Threads. I'm displaying current CPU usage with progress bar and it seems to be working well but the performance of whole window is terrible. Can't even click the button without laggy behavior. Is there any simple solution to fix it?

Here is my main code

from PyQt5 import QtCore, QtGui,  QtWidgets
from PyQt5.QtCore import QThread, pyqtSignal


import progressBarUI
import sys
import sysnfo


class MainWindow(QtWidgets.QMainWindow, progressBarUI.Ui_MainWindow):
    def __init__(self, parent = None):
        super(MainWindow, self).__init__(parent)
        self.setupUi(self)
        self.threadclass = ThreadClass()
        self.threadclass.start()
        self.threadclass.signal.connect(self.updateProgressBar)

    def updateProgressBar(self):
        current_percentage = sysnfo.getCpuPercentage()
        self.progressBar.setValue(current_percentage)


class ThreadClass(QThread):
    signal = pyqtSignal(int)

    def __init__(self, parent=None):
        super(ThreadClass, self).__init__(parent)

    def run(self):
        while True:
            current_percentage = sysnfo.getCpuPercentage()
            self.signal.emit(current_percentage)


if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    mainAppWin = MainWindow()
    mainAppWin.show()
    app.exec_()

Here is sysnfo module:

import psutil as ps

def getCpuPercentage():
    return ps.cpu_percent(interval=1)

And UI file (converted to .py file):


from PyQt5 import QtCore, QtGui, QtWidgets


class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(787, 203)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")

        self.progressBar = QtWidgets.QProgressBar(self.centralwidget)
        self.progressBar.setGeometry(QtCore.QRect(370, 20, 381, 111))
        self.progressBar.setProperty("value", 0)
        self.progressBar.setObjectName("progressBar")


        self.pushButton = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton.setGeometry(QtCore.QRect(20, 50, 151, 41))
        self.pushButton.setObjectName("pushButton")

        MainWindow.setCentralWidget(self.centralwidget)

        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 787, 21))
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)

        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)

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

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.pushButton.setText(_translate("MainWindow", "Click me"))


if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    MainWindow = QtWidgets.QMainWindow()
    ui = Ui_MainWindow()
    ui.setupUi(MainWindow)
    MainWindow.show()
    sys.exit(app.exec_())


Solution

  • When you use the "interval" parameter you are indicating that it measures the information during that time and calculates the average causing the execution of that function to take "interval" seconds, and the correct thing is to execute it in another thread, but you make the mistake of executing it too in the updateProgressBar method that lives in the main thread blocking it, instead use the information that sends you the signal:

    @QtCore.pyqtSlot(int)
    def updateProgressBar(self, percentage):
        self.progressBar.setValue(percentage)