PyQt开发中,往往遇到UI事件处理时间过长,导致界面出现无响应,用户体验很差。
解决的办法很简单,UI与逻辑处理线程分离即可,UI线程只负责更新UI,逻辑代码新开线程进行处理即可,这样子,界面就不会出现卡死无响应状态。
以一个简单的例子讲解。
开发工具:Eric 6, PyQt 4.8, Python 3.4.3
1:用Eric设计好一个窗口,添加一个按钮,通过Eric自动生成代码;
2:自动生成代码,代码如下:
# -*- coding: utf-8 -*- """ Module implementing MainWindow. """ from PyQt4.QtCore import pyqtSlot from PyQt4.QtGui import QMainWindow from PyQt4 import QtCore, QtGui import time from Ui_main import Ui_MainWindow class MainWindow(QMainWindow, Ui_MainWindow): """ Class documentation goes here. """ def __init__(self, parent=None): """ Constructor @param parent reference to the parent widget @type QWidget """ super(MainWindow, self).__init__(parent) self.setupUi(self) @pyqtSlot() def on_pushButton_login_clicked(self): """ Slot documentation goes here. """ # TODO: not implemented yet pass @pyqtSlot(list) def LoginEnd(self, words): for i in words: print(i) self.pushButton_login.setDisabled(False) pass if __name__ == "__main__": import sys app = QtGui.QApplication(sys.argv) win = MainWindow() win.show() sys.exit(app.exec_())
3:处理登陆按钮点击事件,我们在UI线程里直接写:
@pyqtSlot() def on_pushButton_login_clicked(self): """ Slot documentation goes here. """ # TODO: not implemented yet self.pushButton_login.setDisabled(True) #采用单线程操作 time.sleep(6) print("hello, world") self.pushButton_login.setDisabled(False)
这样子,运行起来后,用户体验比较差,因为同在一个线程里,当sleep的时候,UI线程也无法操作,故出现无响应状态。如下状态:
这种体验非常糟糕,而且很容易由于用户多次点击程序挂掉。下面提供解决办法:
1:将点击登录按钮之后的操作,我们通过线程进行操作即可,声明一个线程操作类如下:
#coding='utf-8' from PyQt4 import QtCore, QtGui import time class LoginHandler(QtCore.QThread): finishSignal = QtCore.pyqtSignal(list) def __init__(self, parent=None): super(LoginHandler, self).__init__(parent) pass def run(self): time.sleep(6) self.finishSignal.emit(['hello,','world','!'])
2:点击按钮时调用该线程类:
@pyqtSlot() def on_pushButton_login_clicked(self): """ Slot documentation goes here. """ # TODO: not implemented yet self.pushButton_login.setDisabled(True) #采用单线程操作 #time.sleep(6) #print("hello, world") #self.pushButton_login.setDisabled(False) #采用多线程操作 from LoginHandler import LoginHandler self.login_process = LoginHandler() #登陆完成的信号绑定到登陆结束的槽函数 self.login_process.finishSignal.connect(self.LoginEnd) #启动线程 self.login_process.start() pass
通过这种方式处理后,界面就不会出现无响应的状态。
附所有代码:
Ui_main.py:
# -*- coding: utf-8 -*- # Form implementation generated from reading ui file 'E:\demo_project\pyqt_demo\main.ui' # # Created by: PyQt4 UI code generator 4.11.4 # # WARNING! All changes made in this file will be lost! from PyQt4 import QtCore, QtGui try: _fromUtf8 = QtCore.QString.fromUtf8 except AttributeError: def _fromUtf8(s): return s try: _encoding = QtGui.QApplication.UnicodeUTF8 def _translate(context, text, disambig): return QtGui.QApplication.translate(context, text, disambig, _encoding) except AttributeError: def _translate(context, text, disambig): return QtGui.QApplication.translate(context, text, disambig) class Ui_MainWindow(object): def setupUi(self, MainWindow): MainWindow.setObjectName(_fromUtf8("MainWindow")) MainWindow.resize(475, 264) self.centralWidget = QtGui.QWidget(MainWindow) self.centralWidget.setObjectName(_fromUtf8("centralWidget")) self.pushButton_login = QtGui.QPushButton(self.centralWidget) self.pushButton_login.setGeometry(QtCore.QRect(102, 66, 257, 95)) self.pushButton_login.setStyleSheet(_fromUtf8("font: 18pt \"Adobe Devanagari\";")) self.pushButton_login.setObjectName(_fromUtf8("pushButton_login")) MainWindow.setCentralWidget(self.centralWidget) self.retranslateUi(MainWindow) QtCore.QMetaObject.connectSlotsByName(MainWindow) def retranslateUi(self, MainWindow): MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow", None)) self.pushButton_login.setText(_translate("MainWindow", "点击我登陆", None)) if __name__ == "__main__": import sys app = QtGui.QApplication(sys.argv) MainWindow = QtGui.QMainWindow() ui = Ui_MainWindow() ui.setupUi(MainWindow) MainWindow.show() sys.exit(app.exec_())
main.py:
# -*- coding: utf-8 -*- """ Module implementing MainWindow. """ from PyQt4.QtCore import pyqtSlot from PyQt4.QtGui import QMainWindow from PyQt4 import QtCore, QtGui import time from Ui_main import Ui_MainWindow class MainWindow(QMainWindow, Ui_MainWindow): """ Class documentation goes here. """ def __init__(self, parent=None): """ Constructor @param parent reference to the parent widget @type QWidget """ super(MainWindow, self).__init__(parent) self.setupUi(self) @pyqtSlot() def on_pushButton_login_clicked(self): """ Slot documentation goes here. """ # TODO: not implemented yet self.pushButton_login.setDisabled(True) #采用单线程操作 #time.sleep(6) #print("hello, world") #self.pushButton_login.setDisabled(False) #采用多线程操作 from LoginHandler import LoginHandler self.login_process = LoginHandler() #登陆完成的信号绑定到登陆结束的槽函数 self.login_process.finishSignal.connect(self.LoginEnd) #启动线程 self.login_process.start() pass @pyqtSlot(list) def LoginEnd(self, words): for i in words: print(i) self.pushButton_login.setDisabled(False) pass if __name__ == "__main__": import sys app = QtGui.QApplication(sys.argv) win = MainWindow() win.show() sys.exit(app.exec_())
LoginHandler.py:
#coding='utf-8' from PyQt4 import QtCore, QtGui import time class LoginHandler(QtCore.QThread): finishSignal = QtCore.pyqtSignal(list) def __init__(self, parent=None): super(LoginHandler, self).__init__(parent) pass def run(self): time.sleep(6) self.finishSignal.emit(['hello,','world','!'])
文章的脚注信息由WordPress的wp-posturl插件自动生成