不管网上搜索QThread用法还是网上授课老师的教程,对QThread的介绍都是最简单最基本的功能,如下:
创建线程类:
class Thread(QThread):
sinOut = pyqtSignal(str) # 定义信号便于传递数据
def __init __(self):
super(Thread,self).__ init __()
def run(self):
#线程相关的代码
...
# 发射信号
self.sinOut.emit(file_str)
创建一个新的线程
thread = Thread()
thread.start()
举例的代码也是上面这几行代码的反复使用,比如弄个定时器显示个时间什么的。但是,再我们平时开发中,并不会这么简单,那么坑就来了。
在UI中调用线程一般是因为一些比较耗时的工作会导致界面假死,但是往往这些耗时工作需要一些现有数据,那么就需要给线程传参,上面线程内定义的信号传递数据是把线程内的数据传递出去,但是我这里是想说把数据传递进来,让线程进行一些耗时运算,运算完了再把运算结果数据传递出去。这里只要注意,传参只能在线程类的 init(self,agrs)中传参,run(self)是不能传参的!传参的类型很多,可以是数据也可以是对象。
比如我定义了一个类进行数据运算,运算耗时很长,我需要把数据运算类的工作使用多线程处理, 数据运算类:
class ModifyData(object):
def __init__(self, data):
self.data = data # 接收原始数据
def analyze(self):
... # 数据处理过程省略
return self.data # 返回数据处理结果
那么在UI窗口中就要使用多线程了,好吧,先定义一个线程类,上面第一个坑提到了传参的问题,我们直接在线程类初始化时加入参数 argument_,这个参数是用来接受上面定义的数据处理类ModifyData():
class MoreThreadUse(QtCore.QThread):
update_date = QtCore.pyqtSignal(pandas.DataFrame) # 定义信号
def __init__(self, argument_):
super().__init__()
self.fuc = argument_
def run(self):
a = self.fuc.analyze()
self.update_date.emit(a)
time.sleep(10)
print("")
这里还需要一个UI的工作界面类,我们是在这个UI中使用线程进行数据分析的,但是在这个Ui类中,我们如何把前面的数据分析类交给多线程来处理呢?直接把类名ModifyData传递过去吗?对不起!当你用start()启动线程的时候会直接崩溃!有两种传递方式:第一、先把数据处理类ModifyData实例化,再把实例化对象传参过去!第二, 把原始数据传参给自定义多线程类,在多线程类的初始化中方法中实例化数据处理类ModifyData;下面是UI的工作界面类,使用第一种方法开启多线程:
class ThreadUI(QtWidgets.QDialog):
def __init__(self):
super().__init__()
self.setWindowTitle("UI")
self.resize(500, 300)
self.input_line = QtWidgets.QLineEdit(self)
self.input_line.resize(400, 200)
def abc(self):
path = "E:\\*****\\***.xls"
data = pandas.read_excel(path)
ann = ModifyData(data)
self.thread_1 = MoreThreadUse(ann)
self.thread_1.update_date.connect(self.handle_display)
self.thread_1.start()
def handle_display(self, data):
self.input_line.setText(data)
大家都在说,在线程类种定义一个信号,方便与传递数据,虽然可以传递很多常见数据,但是并非所有数据都可以传递,上面的ui类种可以看到,要传递的数据是pandas打开的一个excel表格,其实这个表格有几万条数据,上面的往线程内传参解决了,那使用线程数据处理完了,如何把数据传递出来呢?用信号吗?不可以,直接崩溃了!因为数据格式是pandas.DataFrame ,信号不能传递这类数据,肯定还有其他类型不能传递,这里不列举了,只要知道这回事就可以了,那么怎么处理呢?信号是肯定不能用了,我暂时没想到更好的办法,我这里是使用了写入文件处理的,因为我的数据分析非常耗时,所有分析过程种可以做很多其他事情,直接把分析结果写入一个文件就ok了。正确的代码逻辑如下:
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
import pandas
import time
class MoreThreadUse(QtCore.QThread):
# 多线程,传递参数为一个类的实例,不用信号传递数据,直接把数据写入文件
def __init__(self, argument_):
super().__init__()
self.fuc = argument_
def run(self):
self.fuc = ModifyData(self.fuc)
data = self.fuc.analyze()
data.to_excel("D:\\****.xls") # 直接把数据处理结果写入文件,使用pandas.to_xxx写文件很方便
time.sleep(10)
print(a)
class ThreadUI(QtWidgets.QDialog):
def __init__(self):
super().__init__()
self.setWindowTitle("UI")
self.resize(500, 300)
self.input_line = QtWidgets.QLineEdit(self)
self.input_line.resize(400, 200)
def abc(self):
path = "E:\\*****\\***.xls"
data = pandas.read_excel(path)
ann = ModifyData(data)
self.thread_1 = MoreThreadUse(data)
self.thread_1.start()
def handle_display(self, data):
self.input_line.setText(data)
class ModifyData(object):
def __init__(self, data):
self.data = data
def analyze(self):
... # 数据处理过程省略
return self.data # 返回数据处理结果
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
main = ThreadUI()
main.abc()
main.show()
sys.exit(app.exec_())
上面的例子种可以看到,线程类实例化时变量是 self.thread_1 一定要加上self,如果不加,thread_1就是一个局部变量,当其所在方法运行结束的时候,它的生命周期也都结束了,但是这个线程里的程序很有可能还没有运行完!可能会报错:QThread :Destroyed while thread is still running!