前言
PyQt的所有窗口都在UI主线程中,也就是main函数中执行了QApplication.exec_()的线程中,在该线程中执行耗时较长的操作时,会导致当前窗口停止响应。为了避免上述情况发生,需要用QThread开启一个子线程去完成耗时的操作。
问题描述
在实现数据清洗工具时,数据量通常很大,清洗过程会比较耗时,当点击执行后,当前窗口会卡住,有个圈不停再转,直到清洗任务执行完成,用户体验非常不好。
问题解决
通过QThread为清洗操作创建一个线程,在主线程中触发该线程执行。
子线程创建:
from PyQt5.QtCore import QThread, pyqtSignal
from PyQt5.QtWidgets import QMessageBox
from .data_clean import filter_bad, filter_blurred, filter_similar, classify_vehcolor, classify_day_or_night
class WorkThread(QThread):
finish_trigger = pyqtSignal(int)
def __init__(self, dir_path, task_index):
super(WorkThread, self).__init__()
self.dir_path = dir_path
self.task_index = task_index
def run(self):
self.run_task()
def run_task(self):
print(self.task_index, self.dir_path)
# 去除损坏图片
if self.task_index == 1:
filter_bad_number = filter_bad(self.dir_path)
self.finish_trigger.emit(filter_bad_number)
# 去除模糊图片
elif self.task_index == 2:
filter_blurred_number = filter_blurred(self.dir_path)
self.finish_trigger.emit(filter_blurred_number)
# 去除相似度较高图片
elif self.task_index == 3:
filter_similar_number = filter_similar(self.dir_path)
print(filter_similar_number)
self.finish_trigger.emit(filter_similar_number)
# 机动车ORI图像颜色分类
elif self.task_index == 4:
classify_number = classify_vehcolor(self.dir_path)
print(classify_number)
self.finish_trigger.emit(classify_number)
# 昼夜分类
elif self.task_index == 5:
classify_number = classify_day_or_night(self.dir_path)
print(classify_number)
self.finish_trigger.emit(classify_number)
注意自定义信号带参数的书写格式:
finish_trigger = pyqtSignal(int) # 自定义信号
self.finish_trigger.emit(classify_number) # 发送信号
主线程中实例化子线程并触发执行,截取部分代码如下:
class CleanDataDialog(QWidget, Ui_Dialog):
def __init__(self, parent=None):
super(CleanDataDialog, self).__init__(parent)
self.setupUi(self)
self.pushButton_runClean.clicked.connect(self.run_clean)
def closeEvent(self, event):
event.accept()
with open('./log.txt', 'a+', encoding='utf-8') as f:
print(self.textEdit_dataInfo.toPlainText())
f.writelines(self.textEdit_dataInfo.toPlainText())
self.lineEdit_showDir.setText('')
self.textEdit_dataInfo.setText('')
# self.pushButton_chooseDir.setDisabled(False)
self.comboBox_cleanItem.setCurrentIndex(0)
self.pushButton_runClean.setDisabled(False)
def run_clean(self):
if self.comboBox_cleanItem.currentIndex() == 0:
QMessageBox.warning(self, '提示', '请先选择清洗选项!', QMessageBox.Ok)
return
if self.lineEdit_showDir.text() == '':
QMessageBox.warning(self, '提示', '请先选择路径!', QMessageBox.Ok)
return
self.pushButton_runClean.setDisabled(True)
reply = QMessageBox.question(self, '提示', '该操作将需要一定的时间,确定开始吗?', QMessageBox.Yes | QMessageBox.No,
QMessageBox.Yes)
if reply == QMessageBox.No:
self.pushButton_runClean.setDisabled(False)
return
else:
my_thread = WorkThread(self.lineEdit_showDir.text(), self.comboBox_cleanItem.currentIndex())
my_thread.finish_trigger.connect(self.msg)
my_thread.run()
def msg(self, number):
self.pushButton_runClean.setDisabled(False)
# 去除损坏图片
if self.comboBox_cleanItem.currentIndex() == 1:
self.textEdit_dataInfo.append('损坏图片已去除,共 %d 张,并放入到filter_bad目录下!\r\n' % number)
QMessageBox.information(self, '提示', '损坏图片已去除,共 %d 张,已放入到filter_bad目录下!' % number,
QMessageBox.Ok)
主要代码:
my_thread = WorkThread(self.lineEdit_showDir.text(), self.comboBox_cleanItem.currentIndex())
my_thread.finish_trigger.connect(self.msg)
my_thread.run()