PyQt5界面無響應(yīng)的解決方案
前言
- 在PyQt5中,GUI線程通常指的是Qt的主事件循環(huán)線程,也稱為主線程。主線程負(fù)責(zé)處理GUI事件、更新UI界面等任務(wù)。在PyQt5中,主線程和GUI線程是同一個(gè)線程,即運(yùn)行應(yīng)用程序的線程。
- 當(dāng)創(chuàng)建一個(gè)Qt應(yīng)用程序時(shí),主線程會(huì)啟動(dòng),并執(zhí)行QApplication.exec_()方法,進(jìn)入Qt的事件循環(huán)。在事件循環(huán)中,主線程會(huì)不斷地監(jiān)聽并處理用戶的輸入事件、定時(shí)器事件、網(wǎng)絡(luò)事件等,然后更新UI界面。
- 如果在主線程執(zhí)行耗時(shí)操作,比如
循環(huán)、sleep、wait 異步線程執(zhí)行
會(huì)導(dǎo)致 UI 界面進(jìn)入無響應(yīng)狀態(tài),我們可以采用以下兩種方式異步處理:使用QThread 或 QTimer
。
版本
- PyQt5
- Python 3.x
案例
- 我們寫一個(gè)簡(jiǎn)單的進(jìn)度條填充程序,每 2 秒填充 1%:
import sys import time from PyQt5.QtWidgets import QApplication, QWidget, QProgressBar, QPushButton, QHBoxLayout class MyWidget(QWidget): def __init__(self): super(MyWidget, self).__init__() self.currentValue = 0 self.progressBar = QProgressBar(self) self.progressBar.resize(200, 50) self.progressBar.move(20, 20) self.progressBar.setValue(self.currentValue) # 創(chuàng)建一個(gè)按鈕 self.button = QPushButton('點(diǎn)擊我', self) self.button.clicked.connect(self.on_clicked) # 創(chuàng)建一個(gè)垂直布局,并將按鈕添加到布局中 layout = QHBoxLayout() layout.addWidget(self.progressBar) layout.addWidget(self.button) # 設(shè)置窗口的主布局為垂直布局 self.setLayout(layout) def on_clicked(self): while True: time.sleep(2) self.currentValue = (self.currentValue + 1) % 101 self.progressBar.setValue(self.currentValue) if __name__ == '__main__': app = QApplication(sys.argv) w = MyWidget() w.resize(500, 300) w.move(300, 300) w.setWindowTitle('Simple') w.show() sys.exit(app.exec_())
- 點(diǎn)擊運(yùn)行,我們會(huì)發(fā)現(xiàn) UI 界面出現(xiàn)無響應(yīng)且進(jìn)度條沒有刷新:
解決方案
- 為了避免 UI 界面無響應(yīng),我們可以采用以下兩種方式:使用
QThread 或 QTimer
。
QThread
- 我們可以通過點(diǎn)擊事件創(chuàng)建
QThread
異步線程執(zhí)行:
import sys import time from PyQt5.QtCore import QThread, pyqtSignal from PyQt5.QtWidgets import QApplication, QWidget, QProgressBar, QPushButton, QHBoxLayout class MyWorker(QThread): timeout = pyqtSignal() def __init__(self): super(MyWorker, self).__init__() def run(self): while True: time.sleep(2) self.timeout.emit() class MyWidget(QWidget): def __init__(self): super(MyWidget, self).__init__() self.worker = None self.currentValue = 0 self.progressBar = QProgressBar(self) self.progressBar.resize(200, 50) self.progressBar.move(20, 20) self.progressBar.setValue(self.currentValue) # 創(chuàng)建一個(gè)按鈕 self.button = QPushButton('點(diǎn)擊我', self) self.button.clicked.connect(self.on_clicked) # 創(chuàng)建一個(gè)垂直布局,并將按鈕添加到布局中 layout = QHBoxLayout() layout.addWidget(self.progressBar) layout.addWidget(self.button) # 設(shè)置窗口的主布局為垂直布局 self.setLayout(layout) def on_clicked(self): self.worker = MyWorker() self.worker.timeout.connect(self.upgradeProgress) self.worker.start() def upgradeProgress(self): self.currentValue = (self.currentValue + 1) % 101 self.progressBar.setValue(self.currentValue) if __name__ == '__main__': app = QApplication(sys.argv) w = MyWidget() w.resize(500, 300) w.move(300, 300) w.setWindowTitle('Simple') w.show() sys.exit(app.exec_())
運(yùn)行效果:
QTimer
- 我們可以通過點(diǎn)擊事件創(chuàng)建
QTimer
定時(shí)器異步執(zhí)行:
import sys from PyQt5.QtCore import QTimer from PyQt5.QtWidgets import QApplication, QWidget, QProgressBar, QPushButton, QHBoxLayout class MyWidget(QWidget): def __init__(self): super(MyWidget, self).__init__() self.currentValue = 0 self.progressBar = QProgressBar(self) self.progressBar.resize(200, 50) self.progressBar.move(20, 20) self.progressBar.setValue(self.currentValue) # 創(chuàng)建一個(gè)按鈕 self.button = QPushButton('點(diǎn)擊我', self) self.button.clicked.connect(self.on_clicked) # 創(chuàng)建一個(gè)垂直布局,并將按鈕添加到布局中 layout = QHBoxLayout() layout.addWidget(self.progressBar) layout.addWidget(self.button) # 設(shè)置窗口的主布局為垂直布局 self.setLayout(layout) def on_clicked(self): # 定義一個(gè)定時(shí)器并啟動(dòng)定時(shí)器 self.time = QTimer() self.time.timeout.connect(self.upgradeProgress) self.time.start(200) def upgradeProgress(self): self.currentValue = (self.currentValue + 1) % 101 self.progressBar.setValue(self.currentValue) if __name__ == '__main__': app = QApplication(sys.argv) w = MyWidget() w.resize(500, 300) w.move(300, 300) w.setWindowTitle('Simple') w.show() sys.exit(app.exec_())
- 運(yùn)行效果:
局部變量創(chuàng)建異步線程導(dǎo)致 UI 未響應(yīng)
- 在使用
QThread
的案例中,將on_clicked
方法改為如下寫法,同樣會(huì)導(dǎo)致 UI 未響應(yīng)狀態(tài):
def on_clicked(self): worker = MyWorker() worker.timeout.connect(self.upgradeProgress) worker.start()
- 這是因?yàn)樵赑ython中,類似于
worker = MyWorker()
這樣的語句創(chuàng)建的對(duì)象在當(dāng)前作用域中是局部變量,它的生命周期與當(dāng)前作用域相關(guān)聯(lián)。當(dāng)當(dāng)前作用域的代碼執(zhí)行完成后局部變量會(huì)被銷毀。 - 如果異步線程的任務(wù)還沒有完成,而主線程的事件循環(huán)又需要等待任務(wù)完成才能繼續(xù)執(zhí)行,那么就會(huì)導(dǎo)致GUI線程無響應(yīng)。這是因?yàn)橹骶€程被阻塞在等待異步任務(wù)的過程中,無法處理事件。
- 為了避免這種情況,我們應(yīng)該將異步線程對(duì)象存儲(chǔ)為實(shí)例變量(即使用
self.worker = MyWorker()
),這樣可以確保異步線程對(duì)象的生命周期與主對(duì)象相同,直到異步任務(wù)完成。這樣即使當(dāng)前作用域的代碼執(zhí)行完成,異步線程仍然可以繼續(xù)執(zhí)行,并且主線程的事件循環(huán)也不會(huì)被阻塞。
如果 QTimer 不使用 self.time 寫法
- 同理,如果不使用
self.time
寫法,會(huì)被當(dāng)做當(dāng)前作用域中的局部變量,當(dāng)前作用域代碼執(zhí)行完成后就會(huì)被銷毀,不再繼續(xù)執(zhí)行。
到此這篇關(guān)于PyQt5界面無響應(yīng)的解決方案的文章就介紹到這了,更多相關(guān)PyQt5界面無響應(yīng)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
詳解PyQt5中textBrowser顯示print語句輸出的簡(jiǎn)單方法
這篇文章主要介紹了詳解PyQt5中textBrowser顯示print語句輸出的簡(jiǎn)單方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-08-08Tensorflow進(jìn)行多維矩陣的拆分與拼接實(shí)例
今天小編就為大家分享一篇Tensorflow進(jìn)行多維矩陣的拆分與拼接實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-02-02Python 輸入一個(gè)數(shù)字判斷成績(jī)分?jǐn)?shù)等級(jí)的方法
今天小編就為大家分享一篇Python 輸入一個(gè)數(shù)字判斷成績(jī)分?jǐn)?shù)等級(jí)的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2018-11-11論文查重python文本相似性計(jì)算simhash源碼
這篇文章主要為大家介紹了python文本相似性計(jì)算simhash源碼來實(shí)現(xiàn)論文的查重,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步2022-02-02python執(zhí)行等待程序直到第二天零點(diǎn)的方法
這篇文章主要介紹了python執(zhí)行等待程序直到第二天零點(diǎn)的方法,涉及Python等待程序的實(shí)現(xiàn)技巧,需要的朋友可以參考下2015-04-04在?Python?中創(chuàng)建DataFrame的方法
這篇文章主要介紹了教你如何在?Python?中創(chuàng)建DataFrame,我們將學(xué)習(xí)以多種方式創(chuàng)建DataFrame,DataFrame是數(shù)據(jù)的二維集合,是一種數(shù)據(jù)結(jié)構(gòu),其中數(shù)據(jù)以表格形式存儲(chǔ),更多相關(guān)資料需要的小伙伴可以參考一下2022-03-03