You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

QProgressBar动画卡顿原因及无需显示%v的平滑实现问询

QProgressBar动画卡顿问题解析与优化方案

问题背景

我想实现QProgressBar的数值变化动画,目前采用将所有数值乘以1000的方式实现过渡——比如数值从5改为4时,进度条实际值从5000变为4000,但界面显示5/最大值而非5000/最大值。但这个实现的动画存在卡顿、不流畅的问题。

意外发现将格式字符串改为f"{displayed_value}/{displayed_total_value} %v"后,动画变得流畅很多,现在有两个疑问:

  1. 为何添加%v能提升动画流畅度?
  2. 能否在不显示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()

优化点说明

  1. 新增update_display_format方法,通过valueChanged信号绑定,每次进度条的value变化时自动更新显示格式,确保动画过程中文本与进度状态同步。
  2. 将文本颜色的更新逻辑移到update_display_format中,保证颜色变化也能跟随动画实时同步。
  3. 移除了原来在set_value中手动设置格式的逻辑,避免固定格式导致的渲染冲突。

内容的提问来源于stack exchange,提问作者Gygabrain

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 07:13:10