问题背景与描述
我这两天一直想把YOLOv5上加个训练的界面,把控制台的输出重定向到一个PyQT5写的界面上,然后我很顺序的写好了一个简单的测试,发现可以重定向了,一直实时获取输出了。代码如下: 然后我就写了个界面,让控制台显示到界面的texteditor中,我知道训练是一个长耗时的操作过程,所以我单独开启了一个QThread线程来完成,然后在线程中发送获取的控制台输出文本给界面主线程,完成界面刷新。以前我用QT C++就是这样干的,所以这个肯定可以。于是写一个线程类,大致如下:
class TrainTask(QThread): textWritten = QtCore.pyqtSignal(str) def __init__(self, command_txt): QThread.__init__(self) self.cmd_txt = command_txt def __del__(self): self.wait() def run(self): self.textWritten.emit("test refresh ui") for i in range(1000): self.textWritten.emit("test refresh ui") self.msleep(1)
然后在界面类中通过一个按钮点击开启它, 然后它就会开始发送数据到界面类指定方法outputWritten中。绑定按钮响应事件:
self.start_training_btn.clicked.connect(self.start_traning_action)
点击按钮执行:
my_train = TrainTask(cmd_txt + params)my_train.textWritten.connect(self.outputWritten)my_train.start()我以为上述代码是天衣无缝,这样就大功告成了,实际上是界面直接卡死了,还有天理吗?多线程居然阻塞返回之后才刷新?!!!
问题现象与解决
把线程中的for训练语句去掉,单独发消息跟界面类,发现可以成功。一旦放开,在run方法中使用for或while之后,就会一直等到结束才返回刷新界面,说明开启的线程没有异步,还是阻塞执行,导致界面卡死。
01
尝试一:
网上一通搜索,有人告诉我说必须先定义一个work类,然后把work类移到QThread线程实例中,测试了是个锤子,没用!
02
尝试二:
说在线程run方法中导致CPU无法轮询,一定就不是很合理的解释,必须要用线程的sleep方法才行,测试了还是个锤子,没用!
03
解决方法:
之前的卡死界面线程的代码没有错的,错就错在调用方法,错误的调用方式:
my_train = TrainTask(cmd_txt + params) my_train.textWritten.connect(self.outputWritten) my_train.start()
重点来了,正确的调用方式如下:
self.my_train = TrainTask(cmd_txt + params)self.my_train.textWritten.connect(self.outputWritten)self.my_train.finished_signal.connect(self.do_finish_action)self.my_train.finished.connect(self.my_train.deleteLater)self.my_train.start()self.start_training_btn.setEnabled(False)self.stop_training_btn.setEnabled(True)
总结一句话:
一定要把线程变量声明为界面类的成员变量,而不是方法中临时变量,是临时变量必然卡死界面,无法刷新,这个是折腾一天得到的教训!网上搜不到!!! 解决了这个问题之后,我很快写好了一个YOLOv5从界面直接开启训练的演示界面,如下图所示,准备好数据,点几下按钮就可以训练YOLOv5,生成模型了!
后来我又添加了一个高级参数设置,发现更好用了!
从此训练YOLOv5, 只要标注好数据,剩下点点鼠标就好了!
全部0条评论
快来发表一下你的评论吧 !