如何为PyQt耗时线程添加圆形等待指示器?
PyQt 实现圆形等待指示器(配合耗时线程)
嗨,我来给你分享两个实用的方案,不用进度条或启动屏就能实现圆形等待指示器,完美适配你的耗时线程场景~
方案一:用 QMovie 加载 GIF 指示器(简单快捷)
这种方法最省心,找个现成的圆形加载GIF,用QMovie就能直接驱动,不用自己写动画逻辑。
步骤:
- 准备一个圆形加载的GIF图片(比如常见的旋转圆圈样式)
- 在主窗口中添加一个
QLabel作为指示器容器,初始隐藏 - 把GIF加载到
QMovie,关联到QLabel - 通过线程的
started()和finished()信号,控制指示器的显示/隐藏以及动画的启停
代码示例:
from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton, QVBoxLayout, QWidget, QLabel from PyQt5.QtCore import QThread, pyqtSignal, QMovie import sys import time # 耗时任务线程 class WorkerThread(QThread): finished = pyqtSignal() def run(self): # 模拟耗时操作 time.sleep(3) self.finished.emit() class MainWindow(QMainWindow): def __init__(self): super().__init__() self.init_ui() def init_ui(self): self.setWindowTitle("圆形等待指示器示例") central_widget = QWidget() self.setCentralWidget(central_widget) layout = QVBoxLayout(central_widget) # 启动任务按钮 self.start_btn = QPushButton("开始耗时任务") self.start_btn.clicked.connect(self.start_task) layout.addWidget(self.start_btn) # 圆形等待指示器 self.loading_label = QLabel(self) # 加载GIF,替换成你的GIF路径 self.movie = QMovie("loading_circle.gif") self.loading_label.setMovie(self.movie) self.loading_label.hide() # 初始隐藏 layout.addWidget(self.loading_label) # 初始化线程 self.worker_thread = WorkerThread() self.worker_thread.started.connect(self.show_loading) self.worker_thread.finished.connect(self.hide_loading) def start_task(self): self.start_btn.setEnabled(False) # 防止重复点击 self.worker_thread.start() def show_loading(self): self.loading_label.show() self.movie.start() def hide_loading(self): self.movie.stop() self.loading_label.hide() self.start_btn.setEnabled(True) if __name__ == "__main__": app = QApplication(sys.argv) window = MainWindow() window.show() sys.exit(app.exec_())
方案二:自定义绘制圆形动画(纯代码,更灵活)
如果不想依赖外部GIF,可以自己绘制一个旋转的圆形指示器,完全用PyQt的绘图API实现,样式还能自定义。
步骤:
- 继承
QWidget自定义一个圆形指示器组件 - 用
QTimer定时更新旋转角度,重写paintEvent绘制旋转的圆弧 - 同样通过线程信号控制组件的显示/隐藏和动画启停
代码示例:
from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton, QVBoxLayout, QWidget from PyQt5.QtCore import QThread, pyqtSignal, QTimer, Qt from PyQt5.QtGui import QPainter, QPen import sys import time # 耗时任务线程 class WorkerThread(QThread): finished = pyqtSignal() def run(self): time.sleep(3) self.finished.emit() # 自定义圆形等待指示器 class CircularProgress(QWidget): def __init__(self, parent=None): super().__init__(parent) self.setFixedSize(40, 40) # 设置指示器大小 self.angle = 0 self.timer = QTimer(self) self.timer.timeout.connect(self.update_angle) def update_angle(self): self.angle = (self.angle + 10) % 360 self.update() # 触发重绘 def start_animation(self): self.timer.start(30) # 每30ms更新一次角度 self.show() def stop_animation(self): self.timer.stop() self.hide() def paintEvent(self, event): painter = QPainter(self) painter.setRenderHint(QPainter.Antialiasing) painter.translate(self.width()/2, self.height()/2) # 移到中心 painter.rotate(self.angle) # 绘制旋转的圆弧 pen = QPen(Qt.blue, 4) pen.setCapStyle(Qt.RoundCap) painter.setPen(pen) # 绘制270度的圆弧,留出缺口 painter.drawArc(-self.width()/2 + 2, -self.height()/2 + 2, self.width()-4, self.height()-4, 0, 270*16) class MainWindow(QMainWindow): def __init__(self): super().__init__() self.init_ui() def init_ui(self): self.setWindowTitle("自定义圆形等待指示器") central_widget = QWidget() self.setCentralWidget(central_widget) layout = QVBoxLayout(central_widget) self.start_btn = QPushButton("开始耗时任务") self.start_btn.clicked.connect(self.start_task) layout.addWidget(self.start_btn) # 添加自定义指示器 self.circular_progress = CircularProgress(self) self.circular_progress.hide() layout.addWidget(self.circular_progress, alignment=Qt.AlignCenter) self.worker_thread = WorkerThread() self.worker_thread.started.connect(self.circular_progress.start_animation) self.worker_thread.finished.connect(self.circular_progress.stop_animation) self.worker_thread.finished.connect(lambda: self.start_btn.setEnabled(True)) def start_task(self): self.start_btn.setEnabled(False) self.worker_thread.start() if __name__ == "__main__": app = QApplication(sys.argv) window = MainWindow() window.show() sys.exit(app.exec_())
注意事项:
- 不管用哪种方案,都要确保耗时任务在子线程中执行,绝对不能阻塞主线程,否则指示器会卡顿甚至不显示
- 自定义指示器的样式可以随意调整:比如修改颜色、圆弧长度、旋转速度,甚至添加渐变效果
- 如果需要在多个地方使用指示器,可以把它封装成单独的组件,复用更方便
内容的提问来源于stack exchange,提问作者0Nicholas
相关产品推荐
相关产品推荐

