1、问题的出现
当按钮控件按下后,如果操作比较耗时,就会出现界面假死、卡死的现象,如果要想在这个过程中将运行过程中的内容打印在文本控件上,此时也不会出现内容。
现在模拟一下,在按钮的槽函数里面添加一个休眠函数,添加一个for循环,循环一次休眠1秒,模拟耗时函数,每次循环之后将信息打印在文本控件中,同时信息也在后台打印一下。
本次演示需要两个控件,一个pushButton,一个textEdit(记得将ui文件转化为py文件)
添加信号和槽函数
# 信号
self.pushButton.clicked.connect(self.on_click_start)
# 槽函数
def on_click_start(self):
for i in range(10):
time.sleep(1)
print('第{}次循环'.format(i+1))
self.textEdit.append('第{}次循环'.format(i+1))
运行效果
可以看到,当点击按钮一小会之后,主界面就出现未响应的状态,如果此时不停点击主界面就会卡死。
在10次循环中,每次后台都可以打印信息,但是每次循环的内容却不打印在文本控件上,直到循环结束才出现在文本控件上,这是因为整个程序是一个单线程,只能注意一件事情,在执行按钮点击的事件的时候,主界面就照顾不到了,所以主界面会出现未响应,控件上也显示不出来信息
2、解决办法
使用多线程,开辟一个子线程,将耗时的任务放到子线程,主线程继续刷新主界面
需要使用QThread类
这里我们新建一个py文件,在文件中导入OThread类(多线程类),pyqtSignal(信号类),编写一个类用来继承QThread
Thread.py
from PyQt5.QtCore import QThread, pyqtSignal
import time
class MyThread(QThread):
mySignal = pyqtSignal(int) # 实例化一个信号
def __init__(self):
super(MyThread, self).__init__()
def run(self):
for i in range(10):
time.sleep(1) # 每次循环休眠一秒,模拟耗时操作
self.mySignal.emit(i) # 发射信号
我们将循环的的耗时代码放在run()方法中
mySignal = pyqtSignal(int) 实例化了一个信号,用来将当前的值做为信号发射到主程序中,括号里得类型根据实际需求来,我这里传得是数字,所以用的int
在主程序中接受信号
run.py
from untitled import Ui_MainWindow
from PyQt5.QtWidgets import *
from Thread import *
import sys
class Run(QMainWindow, Ui_MainWindow):
def __init__(self):
super(Run, self).__init__()
self.setupUi(self)
self.myThraed = MyThread() # 实例化多线程类
# 信号
self.pushButton.clicked.connect(self.on_click_start)
self.myThraed.mySignal.connect(self.accept_signal)
# 槽函数
def on_click_start(self):
self.myThraed.start()
# 接受信号的值,将该值显示在控件上
def accept_signal(self, int):
self.textEdit.append('第{}次循环'.format(int+1))
if __name__ == '__main__':
app = QApplication(sys.argv)
run = Run()
run.show()
sys.exit(app.exec_())
运行效果
此时,运行的时候就可以同时显示内容,而且点击主界面也不会卡死
3、在子程序中获取文本控件的内容
有时候我们需要在子线程的run()方法中获取文本控件的内容,此时就需要在实例化线程的时候将主类做为参数传入,在子线程的类中实例化
这里添加一个lineEdit控件,在按钮点击事件中,给这个控件设文本内容,在子线程中读取这个控件的文本内容并后台打印
在上面的代码做修改
在run.py中
在Thread.py中,做如下修改
运行效果
可以看到,给lineEdit设置文本内容后,在子线程的run方法中获取到了控件的内容打印到了后台