When doing interface development, whether it is mobile Android, or PyQt5 we are talking about here, there is often a guideline for interface development, that is, the main UI thread and time-consuming sub-threads must be separated, the main thread is responsible for refreshing the interface, and time-consuming operations, such as network interactions, disk IO, etc. should be placed in sub-threads to execute, and each of them should have its own role to ensure that the system operates normally and improve the overall user experience.
software and hardware environment
windows 10 64bit
PyQt5
Anaconda3 with python 3.6.5
sample code (computing)
First look at the project directory structure
This is the project entry file, which is responsible for creating the app
# -*- coding: utf-8 -*- import sys from import QApplication from import MainWindow if __name__ == '__main__': app = QApplication() main_window = MainWindow() main_window.show() (app.exec_())
ui_mainwindow.py, responsible for the interface drawing, this file through the designer graphical tools for drawing and then use the pyuic tool to generate the corresponding python code
# -*- coding: utf-8 -*- # Form implementation generated from reading ui file '.\' # # Created by: PyQt5 UI code generator 5.6 # # WARNING! All changes made in this file will be lost! from PyQt5 import QtCore, QtGui, QtWidgets class Ui_MainWindow(object): def setupUi(self, MainWindow): ("MainWindow") (800, 600) ((800, 600)) ((800, 600)) = (MainWindow) ("centralwidget") self.button_ok = () self.button_ok.setGeometry((260, 220, 230, 140)) self.button_ok.setMinimumSize((230, 140)) self.button_ok.setMaximumSize((230, 140)) font = () (50) self.button_ok.setFont(font) self.button_ok.setFocusPolicy() self.button_ok.setObjectName("button_ok") () = (MainWindow) ("statusbar") () = (MainWindow) ((0, 0, 800, 23)) ("menubar") = () ("menuFile") = () ("menuHelp") () = (MainWindow) ("actionExit") = (MainWindow) ("actionCopy") = (MainWindow) ("actionPaste") = (MainWindow) ("actionCut") = (MainWindow) ("actionHelp") = (MainWindow) ("actionAbout") self.action_query = (MainWindow) self.action_query.setObjectName("action_query") self.action_backupDB = (MainWindow) self.action_backupDB.setObjectName("action_backupDB") self.action_reset_mac = (MainWindow) self.action_reset_mac.setObjectName("action_reset_mac") () () () () () () () (()) (()) (MainWindow) (MainWindow) def retranslateUi(self, MainWindow): _translate = (_translate("MainWindow", "Separate the main UI thread from the worker thread.")) self.button_ok.setText(_translate("MainWindow", "OK.")) (_translate("MainWindow", "File")) (_translate("MainWindow", "Help")) (_translate("MainWindow", "Exit.")) (_translate("MainWindow", "Instructions for the use of the software")) (_translate("MainWindow", "About"))
It is mainly responsible for the event handling of the controls on the interface
import time from import QMainWindow from gui.ui_mainwindow import * class MainWindow(QMainWindow, Ui_MainWindow): def __init__(self, parent=None): super(MainWindow, self).__init__(parent) (self) # Bind the click event self.button_ok.(self.button_start) def button_start(self): self.button_ok.setChecked(True) self.button_ok.setDisabled(True) (20)
Here we use (20) to simulate a time-consuming task. After executing python for a while, the interface will be unresponsive and fake-dead, and after 20 seconds, the interface will be back to normal, which is a very poor user experience.
In fact, it is very simple to solve this problem. We move the (20) in the main UI thread to a sub-thread and we're done. PyQt5 provides a thread class QThread, we inherit it and override its run method, create a new file
# -*- coding: utf-8 -*- import time from import QThread, pyqtSignal class WorkThread(QThread): # Use signals to communicate with the main UI thread, the parameter is the data type of the parameter attached when sending the signal, it can be str, int, list, etc. finishSignal = pyqtSignal(str) # Examples with parameters def __init__(self, ip, port, parent=None): super(WorkThread, self).__init__(parent) = ip = port def run(self): ''' Rewrite ''' print('=============sleep======ip: {}, port: {}'.format(, )) (20) ('This is a test.') return
Notice here that we use pyqtSignal, which we use to communicate with the main UI thread, typically for refreshing interface elements, and at the end of the subthread, we send this signal.
Correspondingly, the following changes need to be made
from import WorkThread # Other parts omitted def button_start(self): print('button_start clicked.') # Setup button not available self.button_ok.setChecked(True) self.button_ok.setDisabled(True) = WorkThread(ip='192.168.1.1', port=4000) # Connect the signal finishSignal in thread th to the slot function button_finish in the main UI thread (self.button_finish) # Starting threads () def button_finish(self, msg): print('msg: {}'.format(msg)) # Setup button available self.button_ok.setChecked(False) self.button_ok.setDisabled(False)
After one operation, execute python again, and the interface will no longer show the No Resonding prompt, so you can manipulate other controls on the interface as you wish while the subthread is executing.
For more on this know please check out the related links below