Skip to content

ProgressBar

A widget that displays progress on a time-consuming task.

  • Focusable
  • Container

Examples

Progress Bar in Isolation

The example below shows a progress bar in isolation. It shows the progress bar in:

  • its indeterminate state, when the total progress hasn't been set yet;
  • the middle of the progress; and
  • the completed state.

IndeterminateProgressBar ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━--%--:--:--  s Start ^p palette

IndeterminateProgressBar ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━39%00:00:07  s Start ^p palette

IndeterminateProgressBar ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━100%--:--:--  s Start ^p palette

from textual.app import App, ComposeResult
from textual.containers import Center, Middle
from textual.timer import Timer
from textual.widgets import Footer, ProgressBar


class IndeterminateProgressBar(App[None]):
    BINDINGS = [("s", "start", "Start")]

    progress_timer: Timer
    """Timer to simulate progress happening."""

    def compose(self) -> ComposeResult:
        with Center():
            with Middle():
                yield ProgressBar()
        yield Footer()

    def on_mount(self) -> None:
        """Set up a timer to simulate progess happening."""
        self.progress_timer = self.set_interval(1 / 10, self.make_progress, pause=True)

    def make_progress(self) -> None:
        """Called automatically to advance the progress bar."""
        self.query_one(ProgressBar).advance(1)

    def action_start(self) -> None:
        """Start the progress tracking."""
        self.query_one(ProgressBar).update(total=100)
        self.progress_timer.resume()


if __name__ == "__main__":
    IndeterminateProgressBar().run()

Complete App Example

The example below shows a simple app with a progress bar that is keeping track of a fictitious funding level for an organisation.

Funding tracking Funding tracking Funding: ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━0% ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ $$$ Donate  ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁

Funding tracking Funding tracking Funding: ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━35% ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ $$$ Donate  ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ Donation for $15 received! Donation for $20 received!

Funding tracking Funding tracking Funding: ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━100% ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ $$$ Donate  ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ Donation for $15 received! Donation for $20 received! Donation for $65 received!

from textual.app import App, ComposeResult
from textual.containers import Center, VerticalScroll
from textual.widgets import Button, Header, Input, Label, ProgressBar


class FundingProgressApp(App[None]):
    CSS_PATH = "progress_bar.tcss"

    TITLE = "Funding tracking"

    def compose(self) -> ComposeResult:
        yield Header()
        with Center():
            yield Label("Funding: ")
            yield ProgressBar(total=100, show_eta=False)  # (1)!
        with Center():
            yield Input(placeholder="$$$")
            yield Button("Donate")

        yield VerticalScroll(id="history")

    def on_button_pressed(self) -> None:
        self.add_donation()

    def on_input_submitted(self) -> None:
        self.add_donation()