PyQT5線程:多線程(QThread),線程鎖(QMutex)

在編寫GUI界面中,通常用會有一些按鈕,點擊後觸發事件,比如去下載一個文件或者做一些操作,這些操作會耗時,如果不能及時結束,主線程將會阻塞,這樣界面就會出現未響應的狀態,因此必須使用多線程來解決這個問題。

兩個按鈕,分別在控制台列印不同的內容

執行文件,分別點擊兩個按鈕後,控制台會依次列印內容,

多次點擊按鈕,程序會先循環完上一次的點擊,然後再執行下一次的點擊

並且窗口可能會出現假死狀態

下面將這兩個循環使用多線程來寫,在PyQT5中,使用QThread

from PyQt5.Qt import (QApplication, QWidget, QPushButton,
QThread)
import sys
import time

# 繼承QThread
class Thread_1(QThread): # 線程1
def __init__(self):
super().__init__()

def run(self):
values = [1, 2, 3, 4, 5]
for i in values:
print(i)
time.sleep(0.5) # 休眠

class Thread_2(QThread): # 線程2
def __init__(self):
super().__init__()

def run(self):
values = ["a", "b", "c", "d", "e"]
for i in values:
print(i)
time.sleep(0.5)

再次執行文件,不管我們點擊哪個按鈕,點擊多少次

在控制台會立刻列印內容

且窗口不會出現卡頓,假死。

這裡又出現了一個新的問題,當重複點擊相同按鈕的時候,會多一個循環

例如,點擊按鈕1,循環列印1,2,3。此時再次點擊按鈕1,在控制台會開啟一個新的循環

需要在點擊之後開始循環,在循環沒有結束之前,此線程不允許使用,

有兩種解決辦法:線程鎖和信號

第一種:線程鎖(QMutex)

創建兩個線程鎖,然後在run裡面加鎖和解鎖

運行程序

點擊不同的按鈕可以同步運行,可以同步循環列印

點擊相同的按鈕,先列印完一次循環後,在列印第二次循環

並且主界面不會假死

這種辦法還是不夠完善,想要的結果是,點擊按鈕後,開啟循環,當循環沒有結束時,不允許點擊按鈕,這裡使用信號

第二種:信號(Signal)

按鈕1使用線程鎖,按鈕2使用信號

注意兩者的區別,按鈕1可以無限點擊,按鈕2在點擊之後,開啟循環,按鈕呈不可點擊狀態,只有當循環結束後,才能被再次點擊


附上代碼

from PyQt5.Qt import (QApplication, QWidget, QPushButton,
QThread,QMutex,pyqtSignal)
import sys
import time

qmut_1 = QMutex() # 創建線程鎖
qmut_2 = QMutex()
# 繼承QThread
class Thread_1(QThread): # 線程1
def __init__(self):
super().__init__()

def run(self):
qmut_1.lock() # 加鎖
values = [1, 2, 3, 4, 5]
for i in values:
print(i)
time.sleep(0.5) # 休眠
qmut_1.unlock() # 解鎖

class Thread_2(QThread): # 線程2
_signal =pyqtSignal()
def __init__(self):
super().__init__()

def run(self):
# qmut_2.lock() # 加鎖
values = ["a", "b", "c", "d", "e"]
for i in values:
print(i)
time.sleep(0.5)
# qmut_2.unlock() # 解鎖
self._signal.emit()

class MyWin(QWidget):
def __init__(self):
super().__init__()
# 按鈕初始化
self.btn_1 = QPushButton(按鈕1, self)
self.btn_1.move(120, 80)
self.btn_1.clicked.connect(self.click_1) # 綁定槽函數

self.btn_2 = QPushButton(按鈕2, self)
self.btn_2.move(120, 120)
self.btn_2.clicked.connect(self.click_2) # 綁定槽函數

def click_1(self):
self.thread_1 = Thread_1() # 創建線程
self.thread_1.start() # 開始線程

def click_2(self):
self.btn_2.setEnabled(False)
self.thread_2 = Thread_2()
self.thread_2._signal.connect(self.set_btn)
self.thread_2.start()

def set_btn(self):
self.btn_2.setEnabled(True)

if __name__ == "__main__":
app = QApplication(sys.argv)
myshow = MyWin()
myshow.show()
sys.exit(app.exec_())

推薦閱讀:

TAG:PyQt5 | | Python | 圖形用戶界面 |