PyQt5界面無響應的解決方案
更新時間:2024年05月09日 15:02:48 作者:Lorin洛林
如果在主線程執(zhí)行耗時操作,比如 循環(huán)、sleep、wait 異步線程執(zhí)行 會導致 UI 界面進入無響應狀態(tài),我們可以采用以下兩種方式異步處理:使用QThread 或 QTimer,本文給大家介紹了PyQt5界面無響應的解決方案,需要的朋友可以參考下
前言
- 在PyQt5中,GUI線程通常指的是Qt的主事件循環(huán)線程,也稱為主線程。主線程負責處理GUI事件、更新UI界面等任務。在PyQt5中,主線程和GUI線程是同一個線程,即運行應用程序的線程。
- 當創(chuàng)建一個Qt應用程序時,主線程會啟動,并執(zhí)行QApplication.exec_()方法,進入Qt的事件循環(huán)。在事件循環(huán)中,主線程會不斷地監(jiān)聽并處理用戶的輸入事件、定時器事件、網(wǎng)絡事件等,然后更新UI界面。
- 如果在主線程執(zhí)行耗時操作,比如
循環(huán)、sleep、wait 異步線程執(zhí)行會導致 UI 界面進入無響應狀態(tài),我們可以采用以下兩種方式異步處理:使用QThread 或 QTimer。
版本
- PyQt5
- Python 3.x
案例
- 我們寫一個簡單的進度條填充程序,每 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)建一個按鈕
self.button = QPushButton('點擊我', self)
self.button.clicked.connect(self.on_clicked)
# 創(chuàng)建一個垂直布局,并將按鈕添加到布局中
layout = QHBoxLayout()
layout.addWidget(self.progressBar)
layout.addWidget(self.button)
# 設置窗口的主布局為垂直布局
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_())
- 點擊運行,我們會發(fā)現(xiàn) UI 界面出現(xiàn)無響應且進度條沒有刷新:

解決方案
- 為了避免 UI 界面無響應,我們可以采用以下兩種方式:使用
QThread 或 QTimer。
QThread
- 我們可以通過點擊事件創(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)建一個按鈕
self.button = QPushButton('點擊我', self)
self.button.clicked.connect(self.on_clicked)
# 創(chuàng)建一個垂直布局,并將按鈕添加到布局中
layout = QHBoxLayout()
layout.addWidget(self.progressBar)
layout.addWidget(self.button)
# 設置窗口的主布局為垂直布局
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_())
運行效果:

QTimer
- 我們可以通過點擊事件創(chuàng)建
QTimer定時器異步執(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)建一個按鈕
self.button = QPushButton('點擊我', self)
self.button.clicked.connect(self.on_clicked)
# 創(chuàng)建一個垂直布局,并將按鈕添加到布局中
layout = QHBoxLayout()
layout.addWidget(self.progressBar)
layout.addWidget(self.button)
# 設置窗口的主布局為垂直布局
self.setLayout(layout)
def on_clicked(self):
# 定義一個定時器并啟動定時器
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_())
- 運行效果:

局部變量創(chuàng)建異步線程導致 UI 未響應
- 在使用
QThread的案例中,將on_clicked方法改為如下寫法,同樣會導致 UI 未響應狀態(tài):
def on_clicked(self):
worker = MyWorker()
worker.timeout.connect(self.upgradeProgress)
worker.start()
- 這是因為在Python中,類似于
worker = MyWorker()這樣的語句創(chuàng)建的對象在當前作用域中是局部變量,它的生命周期與當前作用域相關聯(lián)。當當前作用域的代碼執(zhí)行完成后局部變量會被銷毀。 - 如果異步線程的任務還沒有完成,而主線程的事件循環(huán)又需要等待任務完成才能繼續(xù)執(zhí)行,那么就會導致GUI線程無響應。這是因為主線程被阻塞在等待異步任務的過程中,無法處理事件。
- 為了避免這種情況,我們應該將異步線程對象存儲為實例變量(即使用
self.worker = MyWorker()),這樣可以確保異步線程對象的生命周期與主對象相同,直到異步任務完成。這樣即使當前作用域的代碼執(zhí)行完成,異步線程仍然可以繼續(xù)執(zhí)行,并且主線程的事件循環(huán)也不會被阻塞。
如果 QTimer 不使用 self.time 寫法
- 同理,如果不使用
self.time寫法,會被當做當前作用域中的局部變量,當前作用域代碼執(zhí)行完成后就會被銷毀,不再繼續(xù)執(zhí)行。
到此這篇關于PyQt5界面無響應的解決方案的文章就介紹到這了,更多相關PyQt5界面無響應內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
詳解PyQt5中textBrowser顯示print語句輸出的簡單方法
這篇文章主要介紹了詳解PyQt5中textBrowser顯示print語句輸出的簡單方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-08-08
Python 輸入一個數(shù)字判斷成績分數(shù)等級的方法
今天小編就為大家分享一篇Python 輸入一個數(shù)字判斷成績分數(shù)等級的方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-11-11
在?Python?中創(chuàng)建DataFrame的方法
這篇文章主要介紹了教你如何在?Python?中創(chuàng)建DataFrame,我們將學習以多種方式創(chuàng)建DataFrame,DataFrame是數(shù)據(jù)的二維集合,是一種數(shù)據(jù)結構,其中數(shù)據(jù)以表格形式存儲,更多相關資料需要的小伙伴可以參考一下2022-03-03

