QProgressBar动画卡顿原因及无需显示%v的平滑实现问询
QProgressBar动画卡顿问题解析与优化方案
问题背景
我想实现QProgressBar的数值变化动画,目前采用将所有数值乘以1000的方式实现过渡——比如数值从5改为4时,进度条实际值从5000变为4000,但界面显示5/最大值而非5000/最大值。但这个实现的动画存在卡顿、不流畅的问题。
意外发现将格式字符串改为f"{displayed_value}/{displayed_total_value} %v"后,动画变得流畅很多,现在有两个疑问:
- 为何添加
%v能提升动画流畅度? - 能否在不显示QProgressBar真实值(
%v)的前提下实现同样的平滑动画?
问题1:添加%v提升流畅度的原因
QProgressBar的默认格式逻辑是:当value属性变化时,会自动根据格式字符串更新显示文本。但如果手动设置固定格式字符串(比如你原来用的"{displayed_value}/{displayed_max_value}"),动画过程中进度条的value在高频变化,可显示文本却被固定死了,Qt内部的渲染优化机制会因为状态不一致出现冲突,导致卡顿。
当加入%v后,格式字符串变成了动态格式,Qt会在value每一次变化时自动同步更新显示文本,让进度条的绘制和文本更新都进入Qt的高效渲染流水线,动画的一致性得到保障,所以流畅度明显提升。
问题2:不显示%v实现平滑动画的方案
核心是让显示文本的更新和value的变化绑定,确保动画过程中文本与进度条状态同步,而不是仅在调用set_value时设置一次固定格式。下面是具体的优化实现:
修改后的完整代码
from PySide6.QtWidgets import QWidget, QProgressBar, QVBoxLayout, QApplication from PySide6.QtCore import Qt, QPropertyAnimation, QEasingCurve, QObject, Signal, QSize from threading import Thread import time STYLE = """ QProgressBar { border-radius: 3px; background-color: rgb(26, 39, 58); } QProgressBar::chunk { border-radius: 3px; background-color: qlineargradient(spread:pad, x1:0, y1:0.54, x2:1, y2:0.5625, stop:0 rgba(0, 85, 255, 255), stop:1 rgba(8, 255, 227, 255)); } """ class CustomProgressBar(QWidget): def __init__(self, parent, curr_value: int, max_value: int): super().__init__(parent=parent) self.MULTIPLIER = 1000 self.displayed_max = max_value self.setStyleSheet(STYLE) layout = QVBoxLayout(self) self.progress_bar = QProgressBar(self) self.progress_bar.setTextVisible(True) self.progress_bar.setAlignment(Qt.AlignmentFlag.AlignCenter) self.progress_bar.setFixedSize(QSize(1000, 100)) self.progress_bar.setMaximum(max_value * self.MULTIPLIER) self.progress_bar.setValue(curr_value * self.MULTIPLIER) # 监听value变化,动态更新格式 self.progress_bar.valueChanged.connect(self.update_display_format) # 初始化显示格式 self.update_display_format(self.progress_bar.value()) layout.addWidget(self.progress_bar) # 动画设置 self.animation = QPropertyAnimation(self.progress_bar, b"value", self.progress_bar) self.animation.setDuration(500) self.animation.setEasingCurve(QEasingCurve.Type.OutCubic) def update_display_format(self, current_raw_value): """每次value变化时自动更新显示格式""" displayed_value = int(current_raw_value / self.MULTIPLIER) self.progress_bar.setFormat(f"{displayed_value}/{self.displayed_max}") # 同步更新文本颜色 if current_raw_value <= self.progress_bar.maximum() / 2: self.progress_bar.setStyleSheet(f"{STYLE} QProgressBar {{color: white;}}") else: self.progress_bar.setStyleSheet(f"{STYLE} QProgressBar {{color: black;}}") def set_value(self, new_value): new_value = new_value * self.MULTIPLIER if new_value > 0 else 0 self.animate_value_change(new_value) def animate_value_change(self, new_value): """执行数值变化动画""" self.animation.stop() self.animation.setStartValue(self.progress_bar.value()) self.animation.setEndValue(new_value) self.animation.start() class MyCounter(QObject): value_changed = Signal(int) def __init__(self, value: int, step: int = 1): super().__init__() self._value = value self.step = step @property def value(self) -> int: return self._value @value.setter def value(self, value: int): self._value = value if value >= 0 else 0 self.value_changed.emit(self.value) def make_step(self): self.value -= self.step def process(self, delay: int | float): time.sleep(3) default_value = self.value while True: while self.value: self.make_step() time.sleep(delay) self.value = default_value time.sleep(delay) def run(self, delay: int | float = 1): Thread(target=self.process, args=(delay, ), daemon=True).start() if __name__ == "__main__": app = QApplication() widget = QWidget() layout = QVBoxLayout(widget) counter = MyCounter(30, 4) progress_bar = CustomProgressBar(widget, 30, 30) counter.value_changed.connect(progress_bar.set_value) layout.addWidget(progress_bar) widget.show() counter.run(1) app.exec()
优化点说明
- 新增
update_display_format方法,通过valueChanged信号绑定,每次进度条的value变化时自动更新显示格式,确保动画过程中文本与进度状态同步。 - 将文本颜色的更新逻辑移到
update_display_format中,保证颜色变化也能跟随动画实时同步。 - 移除了原来在
set_value中手动设置格式的逻辑,避免固定格式导致的渲染冲突。
内容的提问来源于stack exchange,提问作者Gygabrain
相关产品推荐
相关产品推荐

