使用PySide多線程處理圖形界面卡頓問題詳解
在了解pyside多線程之前,如果是初學(xué)者可以先去看看我的另一篇文章Python多線程threading模塊實例詳解,文中有對多線程的概念進行詳細介紹,如果有相關(guān)基礎(chǔ)可直接跳過。
1.為什么要使用多線程
在制作圖形界面時,只用一個線程很容易導(dǎo)致卡頓無響應(yīng),一旦主線程被阻塞,那么整個圖形界面都會無法繼續(xù)使用,為了解決這個問題,就得使用多線程。
多線程的主要目的就是把各種任務(wù)放在子線程里處理,這樣的話就可以保證主界面的流暢使用,尤其是需要我們在主界面上打開多個子界面。
因此在學(xué)習(xí)Pyside/PyQt時,多線程是必定是要學(xué)習(xí)了解的知識點,我們廢話不多說,直接開始!
2.信號與槽
信號與槽是pyside/pyQt中的一個核心概念,學(xué)習(xí)使用pyside/pyQt進行圖形化界面的繪制時必然會使用到,因此我們首先要簡略介紹一下,熟練掌握此處知識點的小伙伴可以直接跳過。
在理解信號與槽這個概念時,我們不用把他想的太復(fù)雜了,只需要知道:信號的作用就是用于觸發(fā)槽函數(shù)。
我們在使用一個軟件例如網(wǎng)易云音樂時,想要播放某首歌,那么點擊播放的按鈕,歌就開始唱,此時點擊這個動作就是一個信號,而歌播放這個過程就是一個函數(shù)。
在編寫界面時也是如此,我們想要把某個觸發(fā)的信號與某個函數(shù)連接在一起,就常常需要使用connect方法,標準的格式是:
對象.對象的方法信號.connect(槽函數(shù))
如果我們點擊一個按鈕對象Mybutton時想要觸發(fā)一個Myfunction函數(shù),那么就寫為:
Mybutton.clicked.connect(Myfunction)
如果到此處仍無法理解沒有關(guān)系,我們繼續(xù)往下看。
3.結(jié)合實例理解
實例1-不使用多線程導(dǎo)致的問題
我們來看第一個實例,該實例想要實現(xiàn)的效果是:點擊圖形界面上的按鈕便開始計時,總共計時10秒,首先來看不使用多線程的情況下發(fā)現(xiàn)的問題:
"""
Pyside6# QThread多線程
實例1:體驗不使用多線程的結(jié)果
"""
from PySide6.QtCore import QThread, Slot, Signal
from PySide6.QtWidgets import QWidget, QApplication, QPushButton, QVBoxLayout, QLabel, QTextEdit
import time
from time import sleep
# 主界面,繼承QWidget類
class MyGUI(QWidget):
def __init__(self): # 重寫構(gòu)造方法
super().__init__()
self.setup() # 創(chuàng)建界面
self.bind() # 綁定函數(shù)
def bind(self):
self.button.clicked.connect(self.timer) # 將按鈕與開啟新線程的槽函數(shù)通過click信號相連
def setup(self):
layout = QVBoxLayout() # 界面布局為垂直布局
self.setLayout(layout) # 設(shè)置layout布局
self.resize(300, 150) # 重新設(shè)置界面大小
self.label = QLabel("點擊按鈕開始計時") # 定義標簽控件
self.button = QPushButton("Click!") # 定義按鈕控件
self.text_edit = QTextEdit() # 定義可編輯文本框控件
layout.addWidget(self.label) # 放置標簽控件
layout.addWidget(self.button) # 放置按鈕控件
layout.addWidget(self.text_edit) # 放置文本框界面
def timer(self): # 定義一個10s的計時器
count = 0
while count < 10:
time.sleep(1)
self.text_edit.setText(f"{count}") # 將當(dāng)前數(shù)值反映到文本框
count += 1
if __name__ == '__main__':
app = QApplication([]) # 定義一個應(yīng)用
window = MyGUI() # 定義一個界面
window.show() # 展示界面
app.exec() # 啟動應(yīng)用運行代碼后,出現(xiàn)以下界面:

點擊按鈕,開始計時:

但是可以發(fā)現(xiàn),事情沒有安裝我們預(yù)想的情況那樣進行,界面直接卡住了,變成“未響應(yīng)”的狀態(tài)。為什么會產(chǎn)生這樣的結(jié)果呢?
原因在于在計時的時候使用了sleep方法來計時,但是sleep實際上是通過阻塞線程的方式實現(xiàn)的延時,如果只使用一個線程,那么自然會把僅有的這一個線程卡住。
解決方法自然是通過多線程,創(chuàng)建子線程來計時,而主線程只負責(zé)主界面的部分,就不會出現(xiàn)這種一直未響應(yīng)的情況。多線程的優(yōu)勢就在于此,可以同時處理多個并發(fā)任務(wù)。
實例2-使用多線程改進上一實例
接下來我們就開始使用多線程對第一個實例進行改進,主線程負責(zé)主界面myGUI,而子線程負責(zé)計時,其使用自定義的timer函數(shù)。
要實現(xiàn)的目標與實例1相同,即點擊按鈕就開始計時,共計時10s并將數(shù)字實時顯示在主界面文本框中。
在實例2中,共有兩處信號和槽函數(shù)的連接:
①點擊按鈕時,觸發(fā)創(chuàng)建子線程槽函數(shù),此處點擊按鈕的信號與子線程槽函數(shù)連接
②子線程啟動后,開始計時,此處計時的信號與主界面在文本框中實時顯示數(shù)字的槽函數(shù)相連接
實例如下:
"""
Pyside6# QThread多線程
實例2:使用多線程改進實例1
"""
from PySide6.QtCore import QThread, Slot, Signal
from PySide6.QtWidgets import QWidget, QApplication, QPushButton, QVBoxLayout, QLabel, QTextEdit
import time
from time import sleep
# 主界面,繼承QWidget類
class MyGUI(QWidget):
def __init__(self): # 重寫構(gòu)造方法
super().__init__()
self.setup() # 創(chuàng)建界面
self.bind() # 綁定函數(shù)
def bind(self):
self.button.clicked.connect(self.start_new_thread) # 將按鈕與開啟新線程的槽函數(shù)通過click信號相連
def setup(self):
layout = QVBoxLayout() # 界面布局為垂直布局
self.setLayout(layout) # 設(shè)置layout布局
self.resize(300, 150) # 重新設(shè)置界面大小
self.label = QLabel("點擊按鈕開始計時") # 定義標簽控件
self.button = QPushButton("Click!") # 定義按鈕控件
self.text_edit = QTextEdit() # 定義可編輯文本框控件
layout.addWidget(self.label) # 放置標簽控件
layout.addWidget(self.button) # 放置按鈕控件
layout.addWidget(self.text_edit) # 放置文本框界面
def start_new_thread(self):
print("啟動新線程")
self.thread1 = myThread() # 創(chuàng)建子線程thread1
self.thread1.signal_int.connect(self.update_number) # 【關(guān)鍵】將信號與槽函數(shù)連接
self.thread1.start() # 啟動子線程thread1
@Slot(int) # 定義一個槽函數(shù), 在函數(shù)前放一個@Slot()表明其是一個槽函數(shù)
def update_number(self, count):
self.text_edit.setText(f"{count}") # 將計時器傳來的信號展示在文本框中
# 子線程類,繼承QThread類
class myThread(QThread):
signal_int = Signal(int) # 定義信號
def __init__(self):
super().__init__()
def run(self):
self.timer() # 重寫run()方法,啟動線程時自動調(diào)用并運行timer函數(shù),開始計時
def timer(self): # 定義一個10s的計時器
print("開始計時")
count = 0
while count < 10:
time.sleep(1)
count += 1
self.signal_int.emit(count) # 【關(guān)鍵】每秒發(fā)送一次信號,將當(dāng)前計數(shù)數(shù)字傳至主界面顯示
print("結(jié)束計時")
# 運行應(yīng)用界面程序
if __name__ == '__main__':
app = QApplication([]) # 定義一個應(yīng)用
window = MyGUI() # 定義一個界面
window.show() # 展示界面
app.exec() # 啟動應(yīng)用實現(xiàn)的效果如下:

可以看到,此時計時正常進行,主界面無響應(yīng)的情況也不再發(fā)生了,這就是多線程的作用。雖然此處只使用了time阻塞線程的這一特例,但是在其他很多情況下都可以使用,尤其是項目較為復(fù)雜時,需要同時進行多并發(fā)的任務(wù),多線程就必不可少了。
感謝各位支持,之后還會繼續(xù)分享更多有用的知識!想要了解更多可以關(guān)注我或關(guān)注本專欄。
以上就是使用PySide多線程處理圖形界面卡頓問題詳解的詳細內(nèi)容,更多關(guān)于PySide處理圖形界面卡頓的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Python-OpenCV實戰(zhàn):利用 KNN 算法識別手寫數(shù)字
K-最近鄰(KNN)是監(jiān)督學(xué)習(xí)中最簡單的算法之一,KNN可用于分類和回歸問題。本文將為大家介紹的是通過KNN算法實現(xiàn)識別手寫數(shù)字。文中的示例代碼介紹詳細,需要的朋友可以參考一下2021-12-12

