PyQt5界面無(wú)響應(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)聽(tīng)并處理用戶的輸入事件、定時(shí)器事件、網(wǎng)絡(luò)事件等,然后更新UI界面。
- 如果在主線程執(zhí)行耗時(shí)操作,比如
循環(huán)、sleep、wait 異步線程執(zhí)行會(huì)導(dǎo)致 UI 界面進(jìn)入無(wú)響應(yīng)狀態(tài),我們可以采用以下兩種方式異步處理:使用QThread 或 QTimer。
版本
- PyQt5
- Python 3.x
案例
- 我們寫(xiě)一個(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)無(wú)響應(yīng)且進(jìn)度條沒(méi)有刷新:

解決方案
- 為了避免 UI 界面無(wú)響應(yīng),我們可以采用以下兩種方式:使用
QThread 或 QTimer。
QThread
- 我們可以通過(guò)點(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
- 我們可以通過(guò)點(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方法改為如下寫(xiě)法,同樣會(huì)導(dǎo)致 UI 未響應(yīng)狀態(tài):
def on_clicked(self):
worker = MyWorker()
worker.timeout.connect(self.upgradeProgress)
worker.start()
- 這是因?yàn)樵赑ython中,類(lèi)似于
worker = MyWorker()這樣的語(yǔ)句創(chuàng)建的對(duì)象在當(dāng)前作用域中是局部變量,它的生命周期與當(dāng)前作用域相關(guān)聯(lián)。當(dāng)當(dāng)前作用域的代碼執(zhí)行完成后局部變量會(huì)被銷(xiāo)毀。 - 如果異步線程的任務(wù)還沒(méi)有完成,而主線程的事件循環(huán)又需要等待任務(wù)完成才能繼續(xù)執(zhí)行,那么就會(huì)導(dǎo)致GUI線程無(wú)響應(yīng)。這是因?yàn)橹骶€程被阻塞在等待異步任務(wù)的過(guò)程中,無(wú)法處理事件。
- 為了避免這種情況,我們應(yīng)該將異步線程對(duì)象存儲(chǔ)為實(shí)例變量(即使用
self.worker = MyWorker()),這樣可以確保異步線程對(duì)象的生命周期與主對(duì)象相同,直到異步任務(wù)完成。這樣即使當(dāng)前作用域的代碼執(zhí)行完成,異步線程仍然可以繼續(xù)執(zhí)行,并且主線程的事件循環(huán)也不會(huì)被阻塞。
如果 QTimer 不使用 self.time 寫(xiě)法
- 同理,如果不使用
self.time寫(xiě)法,會(huì)被當(dāng)做當(dāng)前作用域中的局部變量,當(dāng)前作用域代碼執(zhí)行完成后就會(huì)被銷(xiāo)毀,不再繼續(xù)執(zhí)行。
到此這篇關(guān)于PyQt5界面無(wú)響應(yīng)的解決方案的文章就介紹到這了,更多相關(guān)PyQt5界面無(wú)響應(yīng)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
使用Python獲取JS加載的數(shù)據(jù)的多種實(shí)現(xiàn)方法
在當(dāng)今的互聯(lián)網(wǎng)時(shí)代,網(wǎng)頁(yè)數(shù)據(jù)的動(dòng)態(tài)加載已經(jīng)成為一種常見(jiàn)的技術(shù)手段,許多現(xiàn)代網(wǎng)站通過(guò)JavaScript(JS)動(dòng)態(tài)加載內(nèi)容,這使得傳統(tǒng)的靜態(tài)網(wǎng)頁(yè)爬取方法難以奏效,所以本文將詳細(xì)介紹如何使用Python來(lái)爬取JavaScript加載的數(shù)據(jù),需要的朋友可以參考下2025-05-05
tensorflow入門(mén):TFRecordDataset變長(zhǎng)數(shù)據(jù)的batch讀取詳解
今天小編就為大家分享一篇tensorflow入門(mén):TFRecordDataset變長(zhǎng)數(shù)據(jù)的batch讀取詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-01-01
VS Code中Python交互式環(huán)境的完整配置流程
VS Code 作為輕量且強(qiáng)大的代碼編輯器,憑借豐富的插件生態(tài)成為 Python 開(kāi)發(fā)的熱門(mén)選擇,交互式環(huán)境能大幅提升開(kāi)發(fā)效率,尤其適合數(shù)據(jù)分析、算法調(diào)試、代碼片段測(cè)試等場(chǎng)景,本文詳解 VS Code 中 Python 交互式環(huán)境的完整配置流程,需要的朋友可以參考下2026-05-05
對(duì)python中的six.moves模塊的下載函數(shù)urlretrieve詳解
今天小編就為大家分享一篇對(duì)python中的six.moves模塊的下載函數(shù)urlretrieve詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-12-12
把Python列表中的元素移動(dòng)到開(kāi)頭的三種方法
在Python編程中,我們經(jīng)常需要對(duì)列表(list)進(jìn)行操作,有時(shí),我們希望將列表中的某個(gè)元素移動(dòng)到最前面,使其成為第一項(xiàng),本文給大家介紹了把Python列表中的元素移動(dòng)到開(kāi)頭的三種方法,需要的朋友可以參考下2025-08-08
關(guān)于Python函數(shù)的定義和參數(shù)
這篇文章主要介紹了關(guān)于Python函數(shù)的定義和參數(shù),Python中的函數(shù)我們可以理解成是一種具有功能的包裝塊,也就是封裝具有某一種功能的代碼塊,需要的朋友可以參考下2023-04-04

