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_())
QThread
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_())
View Code
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_())
View Code
线程休眠唤醒
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_())
View Code
线程挂起和唤醒
#!/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_())
View Code
线程的休眠唤醒用的是python线程的方法;线程的挂起和唤醒用的是C++线程方法