Home > Back-end >  pyqt5) GUI still freezing even though using QThread
pyqt5) GUI still freezing even though using QThread

Time:09-07

What I intended

  • when I click 'start' button on Subwindow then loading logo animation starts, and RSC function(Long running function) works separately.

Real work

  • when I click the "start" button, the RSC function works but gui freezes.

I'm trying to use QThread for offloading the RSC function... but it looks not working.

Below is my code ↓

  1. Main Window(1st window)

     # -*- coding: utf-8 -*-
    
     from PyQt5 import QtCore, QtWidgets
     from Sub import SubWindow
     from Last import LastWindow
     from functools import partial
     import sys
    
     class Main_Window(QtWidgets.QMainWindow) :
         def __init__(self):
             super().__init__()
             self.resize(800, 600)
             self.initUI()
    
         def initUI(self):
             # next pushbutton
             self.pb_next = QtWidgets.QPushButton(self)
             self.pb_next.setGeometry(QtCore.QRect(540, 480, 112, 34))
             self.pb_next.setText('Next')
             self.pb_next.clicked.connect(self.CallSub)
             # quit pushbutton
             self.pb_quit = QtWidgets.QPushButton(self)
             self.pb_quit.setGeometry(QtCore.QRect(660, 480, 112, 34))
             self.pb_quit.setText('Quit')
             self.pb_quit.clicked.connect(QtCore.QCoreApplication.instance().quit)
    
         def CallSub(self):
             self.hide()
             self.SW = SubWindow()
             self.SW.show()
             print('call sub')
             self.SW.PB.clicked.connect(self.SW.Loading)
             print('loading')
             self.SW.PB.clicked.connect(self.RSC)
             print('rsc')
    
         def RSC(self):
             print('start rsc')
             self.text1 = 'host.txt'
             self.text2 = 'access.txt'
             self.thread = QtCore.QThread()
             self.SW.moveToThread(self.thread)
             self.thread.started.connect(partial(self.SW.RunSecurityCheck,self.text1,self.text2))
             self.SW.finished.connect(self.thread.quit)
             self.SW.finished.connect(self.SW.deleteLater)
             self.thread.finished.connect(self.thread.deleteLater)
             self.SW.finished.connect(self.CallLast)
             self.thread.start()
    
         def CallLast(self):
             self.LW = LastWindow()
             self.SW.hide()
             self.LW.show()
             self.LW.pb_next.clicked.connect(self.CallMain)
    
         def CallMain(self):
             self.LW.hide()
             self.show()
    
     if __name__ == "__main__":
         app = QtWidgets.QApplication(sys.argv)
         mw = Main_Window()
         mw.show()
         sys.exit(app.exec_())
    
  2. Sub Window(2nd window)

     # -*- coding: utf-8 -*-
    
     from time import sleep
     from PyQt5 import QtCore, QtWidgets, QtGui
    
     class SubWindow(QtWidgets.QWidget) :
         finished = QtCore.pyqtSignal()
    
         def __init__(self):    
             super().__init__()
             self.resize(800, 600)
             self.initUI()
    
         def initUI(self):
             # Quit PushButton
             self.pb_quit = QtWidgets.QPushButton(self)
             self.pb_quit.setGeometry(QtCore.QRect(660, 480, 112, 34))
             self.pb_quit.setText('Quit')
             self.pb_quit.clicked.connect(QtCore.QCoreApplication.instance().quit)
             # start push button
             self.PB = QtWidgets.QPushButton(self)
             self.PB.setGeometry(QtCore.QRect(280, 240, 100, 25))
             self.PB.setText('Start')
             # loading animation
             self.LB_loading = QtWidgets.QLabel(self)
             self.LB_loading.setGeometry(QtCore.QRect(250, 210, 200, 100))
             self.loading = QtGui.QMovie("loading.gif")
             self.LB_loading.setMovie(self.loading)
             self.LB_loading.hide()
    
         def Loading(self):
             self.PB.hide()
             self.LB_loading.show()
             self.loading.start()
    
         def RunSecurityCheck(self,t1,t2):
             sleep(10)
             self.finished.emit()
    
  3. Last window(3rd window)

     # -*- coding: utf-8 -*-
    
     from PyQt5 import QtCore, QtWidgets
    
     class LastWindow(QtWidgets.QWidget) :
         def __init__(self):
             super().__init__()
             self.resize(800, 600)
             self.initUI()
    
         def initUI(self):
             # Next PushButton
             self.pb_next = QtWidgets.QPushButton(self)
             self.pb_next.setGeometry(QtCore.QRect(540, 480, 112, 34))
             self.pb_next.setText('Back to Main')
             # Quit PushButton
             self.pb_quit = QtWidgets.QPushButton(self)
             self.pb_quit.setGeometry(QtCore.QRect(660, 480, 112, 34))
             self.pb_quit.setText('Quit')
             self.pb_quit.clicked.connect(QtCore.QCoreApplication.instance().quit)
             # complete label
             self.LB1 = QtWidgets.QLabel(self)
             self.LB1.setGeometry(QtCore.QRect(140, 220, 221, 18))
             self.LB1.setText('Complete.')
    

and below is my loading.gif file

enter image description here

how could I fix the gui freezing?

CodePudding user response:

As stated in musicamante's comment routines run in a separate thread need to be independent of the GUI. So what probably makes the most sense in your situation would be to subclass QThread and make RunSecurityCheck a method of the QThread, and pass in your two text parameters to the constructor of the thread. Then the code that is responsible for freezing the GUI will be run separately and will no longer cause freezing/stalling.

For Example:

in your 1st window file you can make some minor changes to RSC method and import the Thread subclass

from Sub import SubWindow, Thread

...

    def RSC(self):
        print('start rsc')
        self.text1 = 'host.txt'
        self.text2 = 'access.txt'
        self.thread = Thread(self.text1, self.text2)
        self.thread.finished.connect(self.thread.quit)
        self.thread.finished.connect(self.SW.deleteLater)
        self.thread.finished.connect(self.thread.deleteLater)
        self.thread.finished.connect(self.CallLast)
        self.thread.start()

...

Then in your Sub.py

class SubWindow(QtWidgets.QWidget) :
    finished = QtCore.pyqtSignal()

    def __init__(self):
        super().__init__()
        self.resize(800, 600)
        self.initUI()

    def initUI(self):
        # Quit PushButton
        self.pb_quit = QtWidgets.QPushButton(self)
        self.pb_quit.setGeometry(QtCore.QRect(660, 480, 112, 34))
        self.pb_quit.setText('Quit')
        self.pb_quit.clicked.connect(QtCore.QCoreApplication.instance().quit)
        # start push button
        self.PB = QtWidgets.QPushButton(self)
        self.PB.setGeometry(QtCore.QRect(280, 240, 100, 25))
        self.PB.setText('Start')
        # loading animation
        self.LB_loading = QtWidgets.QLabel(self)
        self.LB_loading.setGeometry(QtCore.QRect(250, 210, 200, 100))
        self.loading = QtGui.QMovie("loading.gif")
        self.LB_loading.setMovie(self.loading)
        self.LB_loading.hide()

    def Loading(self):
        self.PB.hide()
        self.LB_loading.show()
        self.loading.start()


class Thread(QtCore.QThread):

    def __init__(self, t1, t2):
        super().__init__()
        self.t1 = t1
        self.t2 = t2

    def RunSecurityCheck(self,t1,t2):
        sleep(10)

    def run(self):
        self.RunSecurityCheck(self.t1, self.t2)
        self.finished.emit()

Your last file doesn't require any changes.

All you need to do is make those few minor changes and Bob's your uncle. It should work fine.

  • Related