☰
Toga ProgressBar 进度条组件完全指南:determinate 与 indeterminate 双模式实战
2026/10/13 3:08:24 网站建设 项目流程
  • 跨平台
  • UI组件
  • 桌面应用

【免费下载链接】toga

A Python native, OS native GUI toolkit.

项目地址:https://gitcode.com/gh_mirrors/to/toga
点击查看免费下载

导读

本文以 Toga 官方参考文档 progressbar.md 为主体,系统讲解toga.ProgressBar进度条组件的两种工作模式:max已知的**确定型(determinate)进度条与max=None的不确定型(indeterminate)**进度条。你将掌握其构造参数、start()/stop()/value/max的核心用法、值裁剪与合法性校验规则,并通过源码与测试用例理解 Toga 如何在 GTK、Qt、Cocoa、WinForms、Android、iOS、Web 等平台上统一实现这一跨平台控件。

组件定位与基本用法

toga.ProgressBar是一个用于可视化任务进度的横向条形组件,被监控任务的长度可以是已知的,也可以是不确定的。它在 Toga 中的定位定义于 apis_by_platform.yaml:描述为 "A horizontal bar to visualize task progress",目前 web 平台标记为 beta,textual 平台暂不支持。

创建进度条最简单的方式是直接实例化:

import toga progress = toga.ProgressBar(max=100, value=1) # 启动进度动画(确定型进度条启动后会持续显示进度) progress.start() # 更新进度到 10% progress.value = 10 # 停止进度动画 progress.stop()

核心对象模型定义在 core/src/toga/widgets/progressbar.py,ProgressBar继承自Widget,构造签名如下:

def __init__( self, id: str | None = None, style: StyleT | None = None, max: str | SupportsFloat = 1.0, value: str | SupportsFloat = 0.0, running: bool = False, **kwargs, ):

各参数含义:

参数默认值说明
idNone组件唯一 ID
styleNone样式对象;未提供时应用默认样式(Pack)
max1.0表示任务完成时的最大值,必须> 0.0;传None表示任务长度不确定
value0.0当前进度值,必须位于0.0与max之间,超出范围会被裁剪
runningFalse创建时进度条是否处于运行状态,True时构造完成后立即调用start()
**kwargs—初始 Pack 样式属性,会覆盖style参数中同名属性

构造函数内部执行self.max = max、self.value = value,若running=True则调用self.start(),随后通过self.factory.ProgressBar(interface=self)创建对应平台的原生实现(见 progressbar.py)。

两种模式:determinate 与 indeterminate

确定型(determinate)进度条

只要max为任意正数值,进度条即为确定型。此时组件维护value / max的进度比例,视觉上填充条随value的增大而增长,直观反映任务完成度。max可以是任意正数值(整数、浮点数甚至数字字符串)。

import toga progress = toga.ProgressBar(max=100, value=1) progress.start() progress.value = 10 # 显示 10% progress.value = 50 # 显示 50% progress.stop()

不确定型(indeterminate)进度条

当max为None时,进度条为不确定型。此时对value的任何修改都会被忽略,因为任务长度未知、无法用数值表示进度。启动后,进度条以 "throbbing"(脉冲式跳动)或 "ping pong"(往返来回)动画呈现,表示"任务正在进行中,但不知何时完成"。

import toga progress = toga.ProgressBar(max=None) # 启动动画 progress.start() # 停止动画 progress.stop()

判断当前模式可用is_determinate属性:max is not None时为True,否则为False(见 core/src/toga/widgets/progressbar.py)。

核心属性与行为规则

value:进度值

  • 确定型:value必须位于[0, max]区间,任何超出范围的值都会被裁剪(clip)。源码中通过max(0.0, min(self.max, float(value)))实现裁剪(见 progressbar.py)。
  • 不确定型:设置value是 no-op(无操作),读取时返回None。

max:最大值与模式切换

  • 设置max为None:进度条切换为不确定型。
  • 设置max为> 0.0的数值:进度条切换回确定型。
  • 设置max为0、负数或无法转换为float的值:抛出ValueError,错误信息为"max value must be None, or a numerical value > 0"(见 progressbar.py)。

进度条可以在运行过程中动态切换模式(例如任务由"未知长度"变为"已知长度"),切换后动画行为随之改变。

start() 与 stop()

  • start():启动进度条。确定型下开始显示进度;不确定型下开始脉冲/往返动画。
  • stop():停止进度条。
  • 幂等性:若进度条已在运行,再次start()是 no-op;若已停止,再次stop()也是 no-op(见 progressbar.py)。

运行状态查询

  • is_running:返回True/False,表示当前是否处于运行状态(见 progressbar.py)。
  • is_determinate:返回True/False,表示是否为确定型。

不可禁用

与大多数可交互组件不同,ProgressBar不能被禁用:enabled属性恒为True,任何将其设为False的尝试都会被忽略(见 progressbar.py)。

测试用例验证的行为契约

core/tests/widgets/test_progressbar.py 全面验证了上述行为:

  • 创建默认值:max默认为1.0,value默认为0.0,is_determinate为True,is_running为False。
  • 值裁剪(test_set_value_determinate):max=10时,value=15被裁剪为10.0,value=-1被裁剪为0.0,value=12.0被裁剪为10.0,value=-42.0被裁剪为0.0。
  • 不确定型 value 忽略(test_set_value_indeterminate):max=None后设置value=5,底层实现不会被调用(assert_attribute_not_set)。
  • max 校验(test_set_max/test_invalid_max):None→ 不确定型;42、12.345、字符串"37.42"均合法;-42、0、0.0、-1.234抛ValueError;无法转换为 float 的字符串抛ValueError;无法强制转换的对象(如object())抛TypeError。
  • start/stop 幂等:已运行时再次start()不触发底层动作;已停止时再次stop()不触发。
  • running=True 初始运行:创建时即触发底层start。
  • 模式切换(test_determinate_switch):max=10, value=5→ 设为None后value读取为None→ 再设为15后value恢复为5.0。
  • disable no-op(test_disable_no_op):enabled=False后仍然enabled == True。

集成测试 testbed/tests/widgets/test_progressbar.py 进一步验证了真实平台行为:test_position_change_on_max_change显示max从 100 改为 200 后,value=60不变但显示位置从 60% 变为 30%(即value/max比例随之变化);test_animation_starts_on_max_change验证确定型切换到不确定型时动画自动启动、切回时动画停止,且切换后确定值会丢失(读回0.0)。另外该测试文件对toga_textual后端跳过测试(ProgressBar 未在 Textual 实现),并在 GTK/Android/iOS 上跳过焦点测试。

平台后端实现原理

Toga 通过工厂模式为每个平台提供ProgressBar原生实现。下面以源码注释为依据,展示几个代表性平台的实现要点:

GTK(gtk/src/toga_gtk/widgets/progressbar.py)

  • GTK 的Gtk.ProgressBar本身没有"running"概念,Toga 自行跟踪运行状态以保持 API 一致。
  • 不确定型进度条需要手动动画:启动时创建后台asyncio任务,每 100ms 调用一次pulse()实现脉冲动画;停止时取消任务并将 fraction 归零。
  • GTK 内部使用 0–1 浮点区间,Toga 的max值在内部缩放映射。

Qt(qt/src/toga_qt/widgets/progressbar.py)

  • 基于QProgressBar,内部固定缩放为 0–100 整数区间(value/100、maximum/100)。
  • 不确定型通过setRange(0, 0)触发原生动画;停止时用setRange(0, 1)伪装成"停止的不确定条"。
  • 最小宽度取_MIN_WIDTH(100)与原生sizeHint()的较大者。

Cocoa(cocoa/src/toga_cocoa/widgets/progressbar.py)

  • 基于NSProgressIndicator(Bar 风格),Cocoa 无法判断动画是否运行,Toga 独立跟踪运行状态。
  • 不确定型通过indeterminate = True实现;displayedWhenStopped = True保证停止时仍可见。

WinForms(winforms/src/toga_winforms/widgets/progressbar.py)

  • ProgressBar使用整数跟踪进度,Toga 将浮点值放大TOGA_SCALE = 1000倍,获得 3 位小数精度。
  • WinForms 原生没有"非运行"状态,进度条始终在动画,Toga 独立跟踪运行状态。
  • 不确定型用ProgressBarStyle.Marquee样式,停止时切换回Continuous。

Android(android/src/toga_android/widgets/progressbar.py)

  • 同样使用TOGA_SCALE = 1000实现浮点精度。
  • Android 的setIndeterminate()语义上更接近"是否在跑不确定动画"而非"是否是不确定型",因此启动/停止以及切换模式时都需要调用。
  • 原生没有"停止的不确定条"标识,Toga 用max=0作为内部标记(该值在 Toga 接口层不合法,不会冲突)。

iOS(iOS/src/toga_iOS/widgets/progressbar.py)

  • 基于UIProgressView,本身没有"运行"与"不确定型"概念;Toga 用后台asyncio任务在 1 秒周期内于 0.95 与 0.05 之间往返动画,模拟不确定型进度。
  • 内部使用 0–1 浮点区间,max用于缩放映射。

Web(web/src/toga_web/widgets/progressbar.py)

  • 创建<wa-progress-bar>原生元素,内部维护_value、_max,通过percentage = value / max * 100计算百分比并写入native.value。
  • 不确定型通过native.indeterminate = True实现。

从以上实现可以看出,Toga 在接口层(core)保持统一的行为契约,而各平台后端负责把"运行状态""确定/不确定""值缩放"等抽象概念映射到原生控件能力上,这正是跨平台 GUI 工具包的核心价值所在。

完整实战示例:进度条演示应用

仓库中的 examples/progressbar/progressbar/app.py 提供了一个完整可运行的示例,覆盖了确定型/不确定型、运行/停止、模式切换等全部核心场景:

import asyncio import toga from toga.constants import COLUMN, ROW MAX_PROGRESSBAR_VALUE = 100 class ProgressBarApp(toga.App): auto = False def startup(self): self.main_window = toga.MainWindow(size=(500, 500)) # 用户可通过 +/- 按钮调整进度 self.progress_adder = toga.ProgressBar(max=MAX_PROGRESSBAR_VALUE) self.minus_button = toga.Button("-", on_press=self.decrease_progress, flex=1) self.plus_button = toga.Button("+", on_press=self.increase_progress, flex=1) self.auto_switch = toga.Switch("Toggle auto mode", on_change=self.auto_progress) self.indeterminate_switch = toga.Switch( "Toggle indeterminate mode", on_change=self.toggle_indeterminate ) self.running_switch = toga.Switch( "Start indeterminate mode", on_change=self.toggle_running, enabled=False, ) self.main_window.content = toga.Box( direction=COLUMN, margin=10, children=[ toga.Box( direction=COLUMN, margin=10, children=[ toga.Label("Use the -/+ buttons to change the progress"), self.progress_adder, toga.Box(children=[self.minus_button, self.plus_button]), self.auto_switch, self.indeterminate_switch, self.running_switch, ], ), # 五种状态展示 toga.Box( direction=ROW, margin=10, children=[ toga.Label("Default", flex=1), toga.ProgressBar(width=150), ], ), toga.Box( direction=ROW, margin=10, children=[ toga.Label("Running determinate", flex=1), toga.ProgressBar(value=0.5, running=True, width=150), ], ), toga.Box( direction=ROW, margin=10, children=[ toga.Label("Stopped determinate", flex=1), toga.ProgressBar(value=0.5, running=False, width=150), ], ), toga.Box( direction=ROW, margin=10, children=[ toga.Label("Running indeterminate", flex=1), toga.ProgressBar(max=None, running=True, width=150), ], ), toga.Box( direction=ROW, margin=10, children=[ toga.Label("Stopped indeterminate", flex=1), toga.ProgressBar(max=None, running=False, width=150), ], ), ], ) self.main_window.show() async def auto_progress(self, switch, **kw): """自动模式:每 0.1 秒递增 1%,直到 100% 或被关闭。""" self.minus_button.enabled = not switch.value self.plus_button.enabled = not switch.value self.indeterminate_switch.enabled = not switch.value if switch.value and not self.auto: self.auto = True if self.progress_adder.is_determinate: for i in range(1, 100): self.progress_adder.value = i + 1 await asyncio.sleep(0.1) if not switch.value: break self.auto = False def increase_progress(self, button, **kw): if self.progress_adder.is_determinate: self.progress_adder.value += 0.1 * self.progress_adder.max def decrease_progress(self, button, **kw): if self.progress_adder.is_determinate: self.progress_adder.value -= 0.1 * self.progress_adder.max def toggle_indeterminate(self, switch, **kw): """在确定型与不确定型之间切换 max。""" self.running_switch.enabled = switch.value self.minus_button.enabled = not switch.value self.plus_button.enabled = not switch.value self.auto_switch.enabled = not switch.value if switch.value: self.progress_adder.max = None else: self.progress_adder.max = MAX_PROGRESSBAR_VALUE def toggle_running(self, switch, **kw): """启动或停止不确定型进度条动画。""" if switch.value: switch.text = "Stop indeterminate mode" self.progress_adder.start() else: switch.text = "Start indeterminate mode" self.progress_adder.stop() def main(): return ProgressBarApp("ProgressBar", "org.beeware.toga.examples.progressbar") if __name__ == "__main__": main().main_loop()

示例中值得注意的实战细节:

  • 使用is_determinate作为业务判断:increase_progress/decrease_progress/auto_progress都先判断is_determinate再操作value,避免在不确定型上做无意义操作。
  • 切换模式不丢目标语义:toggle_indeterminate通过改max让进度条在两种模式间往返,与测试test_determinate_switch验证的行为一致。
  • running参数与start()/stop()等价:toga.ProgressBar(max=None, running=True)等价于创建后调用start()。
  • 样式透传:width=150等 Pack 样式属性可直接作为构造参数传入(**kwargs会覆盖style中的同名属性)。

注意事项与平台差异

官方文档的 Notes 明确指出:

进度条的视觉外观因平台而异。Toga 会尽力在"运行中"与"未运行"的确定型进度条之间提供视觉区分,但无法保证这一区分在所有平台都成立。

例如 WinForms 的进度条原生就始终处于动画状态,运行/停止差异只能通过样式(Continuous vs Marquee)间接体现;而 GTK 与 iOS 的确定型进度条在未运行时静态显示当前值,运行时由原生机制处理。因此在编写 UI 测试或依赖视觉状态的场景中,应优先依赖is_running等接口属性而非外观假设。

总结

toga.ProgressBar是 Toga 中实现简单但契约严谨的组件:

  • 两种模式:max为正数即确定型(value/max显示进度),max=None即不确定型(值被忽略、动画呈现)。
  • 强健的输入校验:value超界自动裁剪,max非正数抛ValueError。
  • 幂等的生命周期:start()/stop()可安全重复调用;running参数支持初始状态。
  • 跨平台统一抽象:从 GTK 的pulse()动画、iOS 的 0.95/0.05 往返动画到 Android 的setIndeterminate语义映射,Toga 用统一接口封装了各平台差异。

配合 官方示例 与 核心测试,你可以在自己的应用中放心地把ProgressBar用于下载进度、文件处理、任务加载等各类进度反馈场景。

  • 跨平台
  • UI组件
  • 桌面应用

【免费下载链接】toga

A Python native, OS native GUI toolkit.

项目地址:https://gitcode.com/gh_mirrors/to/toga
点击查看免费下载
上一篇:Apache APISIX 内置 Dashboard(Admin UI)配置实战指南:启用开关、IP 白名单与 Admin API Key
下一篇:Hyperledger Fabric v3.1.4 版本说明全解析:Docker Engine v29+ 兼容、Gossip 稳定性修复与配置实践

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询