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_())
推薦閱讀:
