pyqt的线程的使用非常简单-建立一个自定义的类(如thread),使它继承自QThread,并实现其run()方法即可; 在使用线程时可以直接得到thread实例,调用其start()函数即可启动线程。线程启动后,会自动调用其实现run方法,该方法就是线程的执行函数。
业务的线程任务就写在run()函数中,当run()退出之后线程基本就结束了。
常用方法:strat():启动线程
wait():阻值线程
sleep():强制当前线程睡眠毫秒
常用信号:started:在开始执行run()函数前,从相关线程发射此信号
finished:当程序完成业务逻辑时,从相关线程发射此信号
步骤:1.建立一个线程实例
2.在主线程类创建一个属性连接子线程
3.使用strat()开始子线程
from PyQt5.QtCore import * from PyQt5.Qt import * from PyQt5.QtGui import * import sys import cgitb sys.excepthook = cgitb.Hook(1, None, 5, sys.stderr, 'text') class mythread(QThread): # 步骤1.创建一个线程实例 mysignal = pyqtSignal(tuple) # 创建一个自定义信号,元组参数 def __init__(self): super(mythread, self).__init__() def run(self): a = (1, 2) self.mysignal.emit(a) # 发射自定义信号 class Window(QWidget): def __init__(self): super(Window, self).__init__() self.setWindowTitle('信号传输') self.resize(500,500) self.move(50,50) self.setup_ui() self.my_thread = mythread() # 步骤2. 主线程连接子线 self.my_thread.mysignal.connect(self.zhi) # 自定义信号连接 self.my_thread.start() # 步骤3 子线程开始执行run函数 def setup_ui(self): self.line1 = QLineEdit(self) self.line1.move(0,0) self.line1.resize(50,50) self.line2 = QLineEdit(self) self.line2.move(50, 50) self.line2.resize(50, 50) def zhi(self, zhi): a, b = zhi self.line1.setText(str(a)) self.line2.setText(str(b)) if __name__ == '__main__': app = QApplication(sys.argv) window = Window() window.show() sys.exit(app.exec_())
UI主线程传递数据给子线程:
from PyQt5.QtCore import * from PyQt5.Qt import * from PyQt5.QtGui import * import sys import cgitb sys.excepthook = cgitb.Hook(1, None, 5, sys.stderr, 'text') class mythread(QThread): # 步骤1.创建一个线程实例 mysignal = pyqtSignal(tuple) # 创建一个自定义信号,元组参数 def __init__(self, a): #通过初始化赋值的方式实现UI主线程传递值给子线程 super(mythread, self).__init__() self.a = a def run(self): a = (1, 2) self.mysignal.emit(a) # 发射自定义信号 class Window(QWidget): mysignal2 = pyqtSignal(tuple) def __init__(self): super(Window, self).__init__() self.setWindowTitle('信号传输') self.resize(500,500) self.move(50,50) self.setup_ui() def setup_ui(self): self.line1 = QLineEdit(self) self.line1.move(0,0) self.line1.resize(50,50) self.line2 = QLineEdit(self) self.line2.move(50, 50) self.line2.resize(50, 50) self.btn = QPushButton('按钮',self) self.btn.resize(50,50) self.btn.move(150,0) self.btn.clicked.connect(self.cao1) def zhi(self, zhi): a, b = zhi self.line1.setText(str(a)) self.line2.setText(str(b)) def cao1(self): self.my_thread = mythread(1) # 步骤2. 主线程连接子线,同时传递一个值给子线程 self.my_thread.mysignal.connect(self.zhi) # 自定义信号连接 self.my_thread.start() # 步骤3 子线程开始执行run函数 if __name__ == '__main__': app = QApplication(sys.argv) window = Window() window.show() sys.exit(app.exec_())
moveToThread:在主线程中将程序送到子线程中运行
# -*- coding: utf-8 -*- import sys from PyQt5.QtWidgets import * from PyQt5.QtCore import * import time import cgitb sys.excepthook = cgitb.Hook(1, None, 5, sys.stderr, 'text') # 需要工作在子线程的程序继承的类必须是QObject class Work(QObject): count = int(0) count_signal = pyqtSignal(int) def __init__(self, parent=None): super(Work, self).__init__(parent) self.run = True def work(self): print('current id', int(QThread.currentThreadId())) self.run = True while self.run: # print(str(self.count)) self.count += 1 self.count_signal.emit(self.count) time.sleep(1) def work_stop(self): self.run = False class Window(QWidget): def __init__(self): super(Window, self).__init__() self.setup_ui() print('main id', int(QThread.currentThreadId())) self.thread = QThread() # 这里设定的一个多线程是一个管理者 self.worker = Work() # 不能有父类 self.worker.count_signal.connect(self.flush) self.worker.moveToThread(self.thread) # 将耗时的类在设定的子线程中运行 self.thread.finished.connect(self.finished) def setup_ui(self): self.setWindowTitle('movetothread') self.resize(500,500) self.btn1 = QPushButton('开始', self) self.btn1.resize(50,50) self.btn1.clicked.connect(self.workStart) self.btn2 = QPushButton('结束',self) self.btn2.resize(50, 50) self.btn2.move(0, 50) self.btn2.clicked.connect(self.workStop) self.label = QLabel(self) self.label.resize(100,100) self.label.move(100, 100) def flush(self, count): self.label.setText(str(count)) def workStart(self): print('开始') self.btn1.setEnabled(False) self.thread.start() self.thread.started.connect(self.worker.work) # 当子线程启动时,调用需要运行类的方法 def workStop(self): print('结束') self.worker.work_stop() self.thread.quit() def finished(self): print('finish') self.btn1.setEnabled(True) if __name__ == '__main__': app = QApplication(sys.argv) w = Window() w.show() sys.exit(app.exec_())
线程休眠唤醒
from PyQt5.QtCore import QThread, QWaitCondition, QMutex, pyqtSignal from PyQt5.QtWidgets import QWidget, QVBoxLayout, QPushButton, QProgressBar class Thread(QThread): valuechange = pyqtSignal(int) def __init__(self, *args, **kwargs): super(Thread, self).__init__(*args, **kwargs) self._isPause = False self._value = 0 self.cond = QWaitCondition() # QWaitCondition用于多线程的同步,一个线程调用QWaitCondition.wait()阻塞等待,直到另一个线程调用QWaitCondition.wake()唤醒才继续往下执行. self.mutex = QMutex() # 互斥锁 def pause(self): self._isPause = True def resume(self): self._isPause = False self.cond.wakeAll() # 唤醒所有线程 def run(self): while 1: self.mutex.lock() # 加锁只能一个线程使用 if self._isPause: self.cond.wait(self.mutex) # QWaitCondition.wait()在使用时必须传入一个上锁的QMutex对象 # 在wait()执行过程中,mutex一直保持上锁状态,直到调用操作系统的wait_block在阻塞的一瞬间把mutex解锁(严格说 # 来应该是原子操作,即系统能保证在真正执行阻塞等待指令时才解锁).另一线程唤醒后,wait()函数将在第一时间重新给 # mutex上锁,直到显示调用mutex.unlock()解锁. # 由此可见,通过mutex把有严格时序要求的代码保护起来,同时把wakeAll()也用同一个mutex保护起来,这样能保证: # 一定先有wait(),再有wakeAll(),不管什么情况,都能保证这先后关系,而不至于摆乌龙. if self._value >100: self._value = 0 self._value += 1 self.valuechange.emit(self._value) self.msleep(100) self.mutex.unlock() # 解锁给接下来的线程使用 class Window(QWidget): def __init__(self, *args, **kwargs): super(Window, self).__init__(*args, **kwargs) layout = QVBoxLayout(self) self.progressBar = QProgressBar(self) layout.addWidget(self.progressBar) layout.addWidget(QPushButton('休眠', self, clicked=self.doWait)) layout.addWidget(QPushButton('唤醒', self, clicked=self.doWake)) self.t = Thread(self) self.t.valuechange.connect(self.progressBar.setValue) self.t.start() def doWait(self): self.t.pause() def doWake(self): self.t.resume() if __name__ == '__main__': import sys import cgitb sys.excepthook = cgitb.enable(1, None, 5, '') from PyQt5.QtWidgets import QApplication app = QApplication(sys.argv) w = Window() w.show() sys.exit(app.exec_())
线程挂起和唤醒
#!/usr/bin/env python # -*- coding: utf-8 -*- import ctypes from PyQt5.QtCore import QThread, pyqtSignal from PyQt5.QtWidgets import QWidget, QVBoxLayout, QProgressBar, QPushButton import win32con from win32process import SuspendThread, ResumeThread class Worker(QThread): valueChanged = pyqtSignal(int) # 值变化信号 handle = -1 def run(self): try: self.handle = ctypes.windll.kernel32.OpenThread( win32con.PROCESS_ALL_ACCESS, False, int(QThread.currentThreadId())) # 利用该方法得到线程的句柄,然后就可以通过SuspendThread和ResumeThread两个函数对其进行挂起与恢复 except Exception as e: print('get thread handle failed', e) print('thread id', int(QThread.currentThreadId())) for i in range(1, 101): print('value', i) self.valueChanged.emit(i) QThread.sleep(1) class Window(QWidget): def __init__(self, *args, **kwargs): super(Window, self).__init__(*args, **kwargs) layout = QVBoxLayout(self) self.progressBar = QProgressBar(self) self.progressBar.setRange(0, 100) layout.addWidget(self.progressBar) self.startButton = QPushButton('开启线程', self, clicked=self.onStart) layout.addWidget(self.startButton) self.suspendButton = QPushButton( '挂起线程', self, clicked=self.onSuspendThread, enabled=False) layout.addWidget(self.suspendButton) self.resumeButton = QPushButton( '恢复线程', self, clicked=self.onResumeThread, enabled=False) layout.addWidget(self.resumeButton) self.stopButton = QPushButton( '终止线程', self, clicked=self.onStopThread, enabled=False) layout.addWidget(self.stopButton) # 当前线程id print('main id', int(QThread.currentThreadId())) # 子线程 self._thread = Worker(self) self._thread.finished.connect(self._thread.deleteLater) self._thread.valueChanged.connect(self.progressBar.setValue) def onStart(self): print('main id', int(QThread.currentThreadId())) self._thread.start() # 启动线程 self.startButton.setEnabled(False) self.suspendButton.setEnabled(True) self.stopButton.setEnabled(True) def onSuspendThread(self): if self._thread.handle == -1: return print('handle is wrong') ret = SuspendThread(self._thread.handle) print('挂起线程', self._thread.handle, ret) self.suspendButton.setEnabled(False) self.resumeButton.setEnabled(True) def onResumeThread(self): if self._thread.handle == -1: return print('handle is wrong') ret = ResumeThread(self._thread.handle) print('恢复线程', self._thread.handle, ret) self.suspendButton.setEnabled(True) self.resumeButton.setEnabled(False) def onStopThread(self): self.startButton.setEnabled(False) self.suspendButton.setEnabled(False) self.resumeButton.setEnabled(False) ret = ctypes.windll.kernel32.TerminateThread( self._thread.handle, 0) # 终止线程,不推荐 print('终止线程', self._thread.handle, ret) self.stopButton.setEnabled(False) def closeEvent(self, event): if self._thread.isRunning(): self._thread.quit() # 强制 # self._thread.terminate() del self._thread super(Window, self).closeEvent(event) if __name__ == '__main__': import sys import os print('pid', os.getpid()) from PyQt5.QtWidgets import QApplication app = QApplication(sys.argv) w = Window() w.show() sys.exit(app.exec_())
线程的休眠唤醒用的是python线程的方法;线程的挂起和唤醒用的是C++线程方法