Write a custom widget¶
The built-in widgets cover percentages, timers, and transfer speeds, but not every readout fits that mold – naming the current phase of a job (“starting”, “working”, “finishing”) isn’t something any of them do.
"""Write your own widget by subclassing `WidgetBase`.
A widget is a callable: `__call__(self, progress, data)` returns the text
to render for one redraw. `progress` is the bar itself (read from it, don't
mutate it); `data` is the same snapshot dict the built-in widgets read --
`data['value']`, `data['percentage']`, and so on. This one names the
current phase instead of showing a percentage.
"""
import time
import progressbar
from progressbar.bar import ProgressBarMixinBase
from progressbar.widgets import Data, WidgetBase
STEPS = 24
class Stage(WidgetBase):
"""Names the current phase of the job instead of a percentage."""
def __call__(self, progress: ProgressBarMixinBase, data: Data) -> str:
percentage = data['percentage'] or 0.0
if percentage < 20:
return 'starting'
elif percentage < 90:
return 'working'
else:
return 'finishing'
def main() -> None:
widgets = [
progressbar.Percentage(),
' ',
progressbar.Bar(),
' ',
Stage(),
]
with progressbar.ProgressBar(max_value=STEPS, widgets=widgets) as bar:
for step in range(STEPS):
bar.update(step + 1)
time.sleep(0.005)
if __name__ == '__main__':
main()
A widget is any callable matching WidgetBase.__call__(self, progress,
data), returning the text to render for one redraw. Subclass
WidgetBase and implement __call__: progress is the bar itself
(read from it, don’t mutate it), and data is the same snapshot dict
the built-in widgets read – data['value'], data['percentage'],
and so on. Drop the instance straight into a widgets= list alongside
the built-ins. Nothing distinguishes a custom widget from a shipped one
at that point.