pyqt5學習之QThread


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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM