python GUI库图形界面开发之PyQt5 UI主线程与耗时线程分离详细方法实例

Sylvia ·
更新时间:2024-09-20
· 724 次阅读

在做界面开发时,无论是移动端的Android,还是我们这里讲的PyQt5,经常会有一个界面开发准则,那就是UI主线程与耗时子线程一定要分开,主线程负责刷新界面,耗时操作,如网络交互、磁盘IO等,都应该放在子线程里执行,它们各司其职,保证系统正常运行,提升整体用户体验。

软硬件环境

windows 10 64bit

PyQt5

Anaconda3 with python 3.6.5

实例代码

首先看下工程目录结构

main.py,这是工程入口文件,它负责创建app

# -*- coding: utf-8 -*- import sys from PyQt5.QtWidgets import QApplication from gui.mainwindow import MainWindow if __name__ == '__main__': app = QApplication(sys.argv) main_window = MainWindow() main_window.show() sys.exit(app.exec_())

ui_mainwindow.py,负责界面的绘制,这个文件通过designer图形化工具作图然后使用pyuic工具生成对应的python代码

# -*- coding: utf-8 -*- # Form implementation generated from reading ui file '.\mainwindow.ui' # # 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.setObjectName("MainWindow") MainWindow.resize(800, 600) MainWindow.setMinimumSize(QtCore.QSize(800, 600)) MainWindow.setMaximumSize(QtCore.QSize(800, 600)) self.centralwidget = QtWidgets.QWidget(MainWindow) self.centralwidget.setObjectName("centralwidget") self.button_ok = QtWidgets.QPushButton(self.centralwidget) self.button_ok.setGeometry(QtCore.QRect(260, 220, 230, 140)) self.button_ok.setMinimumSize(QtCore.QSize(230, 140)) self.button_ok.setMaximumSize(QtCore.QSize(230, 140)) font = QtGui.QFont() font.setPointSize(50) self.button_ok.setFont(font) self.button_ok.setFocusPolicy(QtCore.Qt.TabFocus) self.button_ok.setObjectName("button_ok") MainWindow.setCentralWidget(self.centralwidget) self.statusbar = QtWidgets.QStatusBar(MainWindow) self.statusbar.setObjectName("statusbar") MainWindow.setStatusBar(self.statusbar) self.menubar = QtWidgets.QMenuBar(MainWindow) self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 23)) self.menubar.setObjectName("menubar") self.menuFile = QtWidgets.QMenu(self.menubar) self.menuFile.setObjectName("menuFile") self.menuHelp = QtWidgets.QMenu(self.menubar) self.menuHelp.setObjectName("menuHelp") MainWindow.setMenuBar(self.menubar) self.actionExit = QtWidgets.QAction(MainWindow) self.actionExit.setObjectName("actionExit") self.actionCopy = QtWidgets.QAction(MainWindow) self.actionCopy.setObjectName("actionCopy") self.actionPaste = QtWidgets.QAction(MainWindow) self.actionPaste.setObjectName("actionPaste") self.actionCut = QtWidgets.QAction(MainWindow) self.actionCut.setObjectName("actionCut") self.actionHelp = QtWidgets.QAction(MainWindow) self.actionHelp.setObjectName("actionHelp") self.actionAbout = QtWidgets.QAction(MainWindow) self.actionAbout.setObjectName("actionAbout") self.action_query = QtWidgets.QAction(MainWindow) self.action_query.setObjectName("action_query") self.action_backupDB = QtWidgets.QAction(MainWindow) self.action_backupDB.setObjectName("action_backupDB") self.action_reset_mac = QtWidgets.QAction(MainWindow) self.action_reset_mac.setObjectName("action_reset_mac") self.menuFile.addSeparator() self.menuFile.addAction(self.actionExit) self.menuFile.addSeparator() self.menuHelp.addSeparator() self.menuHelp.addSeparator() self.menuHelp.addAction(self.actionAbout) self.menuHelp.addSeparator() self.menubar.addAction(self.menuFile.menuAction()) self.menubar.addAction(self.menuHelp.menuAction()) self.retranslateUi(MainWindow) QtCore.QMetaObject.connectSlotsByName(MainWindow) def retranslateUi(self, MainWindow): _translate = QtCore.QCoreApplication.translate MainWindow.setWindowTitle(_translate("MainWindow", "分离UI主线程和工作线程")) self.button_ok.setText(_translate("MainWindow", "确定")) self.menuFile.setTitle(_translate("MainWindow", "File")) self.menuHelp.setTitle(_translate("MainWindow", "Help")) self.actionExit.setText(_translate("MainWindow", "退出")) self.actionHelp.setText(_translate("MainWindow", "软件使用说明")) self.actionAbout.setText(_translate("MainWindow", "关于"))

mainwindow.py,主要负责界面上控件的事件处理

import time from PyQt5.QtWidgets import QMainWindow from gui.ui_mainwindow import * class MainWindow(QMainWindow, Ui_MainWindow): def __init__(self, parent=None): super(MainWindow, self).__init__(parent) self.setupUi(self) # 绑定点击事件 self.button_ok.clicked.connect(self.button_start) def button_start(self): self.button_ok.setChecked(True) self.button_ok.setDisabled(True) time.sleep(20)

这里我们使用time.sleep(20)来模拟耗时任务,执行python main.py后一会,界面就会出现无响应,假死的现象,等到20秒过后,界面又恢复了正常,用户体验非常差。

其实要解决这个问题,也非常简单。我们将UI主线程中的time.sleep(20)移动到子线程中就可以了。PyQt5中提供了线程类QThread,我们继承它并重写它的run方法,新建一个新的文件threads.py

# -*- coding: utf-8 -*- import time from PyQt5.QtCore import QThread, pyqtSignal class WorkThread(QThread): # 使用信号和UI主线程通讯,参数是发送信号时附带参数的数据类型,可以是str、int、list等 finishSignal = pyqtSignal(str) # 带参数示例 def __init__(self, ip, port, parent=None): super(WorkThread, self).__init__(parent) self.ip = ip self.port = port def run(self): ''' 重写 ''' print('=============sleep======ip: {}, port: {}'.format(self.ip, self.port)) time.sleep(20) self.finishSignal.emit('This is a test.') return

注意到这里我们使用了pyqtSignal,我们使用它来跟UI主线程通讯,一般用于界面元素的刷新,在子线程的最后,我们发送这个信号。

对应的mainwindow.py,需要进行如下修改

from gui.threads import WorkThread # 其它部分省略 def button_start(self): print('button_start clicked.') # 设置按钮不可用 self.button_ok.setChecked(True) self.button_ok.setDisabled(True) self.th = WorkThread(ip='192.168.1.1', port=4000) # 将线程th的信号finishSignal和UI主线程中的槽函数button_finish进行连接 self.th.finishSignal.connect(self.button_finish) # 启动线程 self.th.start() def button_finish(self, msg): print('msg: {}'.format(msg)) # 设置按钮可用 self.button_ok.setChecked(False) self.button_ok.setDisabled(False)

一顿操作之后,再次执行python main.py,界面就再也不会出现No Resonding的提示了,可以在子线程执行过程中可以随意操作界面上的其它控件

更多相关知道请查看下面的相关链接

您可能感兴趣的文章:python3.x+pyqt5实现主窗口状态栏里(嵌入)显示进度条功能python GUI库图形界面开发之PyQt5计数器控件QSpinBox详细使用方法与实例python GUI库图形界面开发之PyQt5切换按钮控件QPushButton详细使用方法与实例python GUI库图形界面开发之PyQt5多行文本框控件QTextEdit详细使用方法实例python GUI库图形界面开发之PyQt5单选按钮控件QRadioButton详细使用方法与实例python GUI库图形界面开发之PyQt5复选框控件QCheckBox详细使用方法与实例python GUI库图形界面开发之PyQt5滑块条控件QSlider详细使用方法与实例python GUI库图形界面开发之PyQt5工具栏控件QToolBar的详细使用方法与实例python GUI库图形界面开发之PyQt5菜单栏控件QMenuBar的详细使用方法与实例python GUI库图形界面开发之PyQt5日期时间控件QDateTimeEdit详细使用方法与实例python GUI库图形界面开发之PyQt5下拉列表框控件QComboBox详细使用方法与实例python GUI库图形界面开发之PyQt5访问系统剪切板QClipboard类详细使用方法与实例python GUI库图形界面开发之PyQt5控件数据拖曳Drag与Drop详细使用方法与实例python GUI库图形界面开发之PyQt5图片显示控件QPixmap详细使用方法与实例python GUI库图形界面开发之PyQt5打开保存对话框QFileDialog详细使用方法与实例python GUI库图形界面开发之PyQt5输入对话框QInputDialog详细使用方法与实例python GUI库图形界面开发之PyQt5单行文本框控件QLineEdit详细使用方法与实例python GUI库图形界面开发之PyQt5窗口布局控件QStackedWidget详细使用方法python GUI库图形界面开发之PyQt5状态栏控件QStatusBar详细使用方法实例



方法 pyqt5 pyqt gui 图形界面 python gui 线程 Python 界面

需要 登录 后方可回复, 如果你还没有账号请 注册新账号