目录
1、UI与逻辑分离
2、线程的创建
3、动态创建多线程
4、停止指定线程
两者分离,一个是代码结构更清晰;另外,将复杂的逻辑放到线程中,不会造成UI的卡顿
网上很多方法和教程都是直接继承QThread,重写run方法,这样用起来的确简单方便,但是再翻下网上的资料,据说这是原设计者一直在骂的,不提倡的用法,正确的使用方法是继承QObject,复杂的工作放在work(名字自定义)方法中,如下:
class Parser(QObject):
signal = pyqtSignal(int, str) # 括号里填写信号传递的参数
def __init__(self, id: int):
super(Parser, self).__init__()
self.__id = id
@pyqtSlot()
def work(self):
# do something
然后在需要创建线程的地方:
parserObj = Parser()
parserThd = QThread()
parserObj.moveToThread(parserThd)
parserObj.signal.connect(self.msgBrowserCb)
# connect只能连接一次,否则会出现多个slot函数工作
# connect可以放在线程启动之后无影响
parserThd.started.connect(parserObj.work)
parserThd.start()
这样就完成了线程的创建和使用。
参考了别人的示例:https://www.zchen.info/archives/multithreading-with-pyqt5-and-qthread.html
3的示例中也包含了线程的停止,是通过发信号的方式来实现,但在测试中发现的,主线程中发出信号后,线程的槽函数并没有收到,不知为何,这里也遇到了相同的问题,并从中找到了解决方法:https://stackoverflow.com/questions/39976322/pyqt-how-to-send-a-stop-signal-into-a-thread-where-an-object-is-running-a-condi
在主线程中直接调用创建的QObject对象的方法,使其工作任务正常结束,再调用线程的quit();恢复时反之。
class Parser(QObject):
signal = pyqtSignal(int, str) # 括号里填写信号传递的参数
def __init__(self, id: int):
super(Parser, self).__init__()
self.__id = id
self.__abort = False
@pyqtSlot()
def work(self):
# do something
thread_name = QThread.currentThread().objectName()
thread_id = int(QThread.currentThreadId())
msgid = None
self.__abort = False
while 1:
if self.__abort:
break
print(' running worker #{} from {}(#{})'.format(self.__id,
thread_name,
thread_id))
self.signal.emit(thread_id, thread_name)
time.sleep(2)
# break
def abort(self, finish_id=0):
print(' worker #{} notified to abort'.format(self.__id))
self.__abort = True
多线程创建时:
self.__threads = []
for i in range(self.model.rowCount()):
print(self.model.data(self.model.index(i, 1)))
parserObj = Parser(int(self.model.data(self.model.index(i, 0))))
parserThd = QThread()
parserThd.setObjectName('thread_' + str(i))
self.__threads.append((parserThd, parserObj)) # 放到list列表中,便于管理
parserObj.moveToThread(parserThd)
parserObj.signal.connect(self.msgBrowserCb)
# self.notice_signal.connect(parserObj.abort) # 此方法没效果
parserThd.started.connect(parserObj.work)
parserThd.start()
停止时点击按钮方法:
def stopBtnCb(self):
# 获取tableView的当前行号
worker_id = self.tableView.currentIndex().row()
parserThd, parserObj = self.__threads[worker_id]
# self.notice_signal.emit(worker_id) # 此方法没效果
parserObj.abort(worker_id) #直接调用对象的abort
parserThd.quit()
# parserThd.wait()