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++線程方法