""" Components/Progress Bar ======================= .. seealso:: `Material Design spec, Progress indicators https://material.io/components/progress-indicators`_ .. rubric:: Progress indicators express an unspecified wait time or display the length of a process. .. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/progress-bar-preview.png :align: center `KivyMD` provides the following bars classes for use: - MDProgressBar_ - Determinate_ - Indeterminate_ .. MDProgressBar: MDProgressBar ------------- .. code-block:: python from kivy.lang import Builder from kivymd.app import MDApp KV = ''' BoxLayout: padding: "10dp" MDProgressBar: value: 50 ''' class Test(MDApp): def build(self): return Builder.load_string(KV) Test().run() .. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/progress-bar.png :align: center Vertical orientation -------------------- .. code-block:: kv MDProgressBar: orientation: "vertical" value: 50 .. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/progress-bar-vertical.png :align: center With custom color ----------------- .. code-block:: kv MDProgressBar: value: 50 color: app.theme_cls.accent_color .. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/progress-bar-custom-color.png :align: center .. Indeterminate: Indeterminate ------------- .. code-block:: python from kivy.lang import Builder from kivy.properties import StringProperty from kivymd.app import MDApp KV = ''' Screen: MDProgressBar: id: progress pos_hint: {"center_y": .6} type: "indeterminate" MDRaisedButton: text: "START" pos_hint: {"center_x": .5, "center_y": .45} on_press: app.state = "stop" if app.state == "start" else "start" ''' class Test(MDApp): state = StringProperty("stop") def build(self): return Builder.load_string(KV) def on_state(self, instance, value): { "start": self.root.ids.progress.start, "stop": self.root.ids.progress.stop, }.get(value)() Test().run() .. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/indeterminate-progress-bar.gif :align: center .. Determinate: Determinate ----------- .. code-block:: kv MDProgressBar: type: "determinate" running_duration: 1 catching_duration: 1.5 .. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/determinate-progress-bar.gif :align: center """ __all__ = ("MDProgressBar",) from kivy.animation import Animation from kivy.clock import Clock from kivy.lang import Builder from kivy.properties import ( BooleanProperty, ListProperty, NumericProperty, OptionProperty, StringProperty, ) from kivy.uix.progressbar import ProgressBar from kivymd.theming import ThemableBehavior Builder.load_string( """ canvas: Clear Color: rgba: self.theme_cls.divider_color Rectangle: size: (self.width, dp(4)) \ if self.orientation == "horizontal" \ else (dp(4), self.height) pos: (self.x, self.center_y - dp(4)) \ if self.orientation == "horizontal" \ else (self.center_x - dp(4),self.y) Color: rgba: self.theme_cls.primary_color if not self.color else self.color Rectangle: size: (self.width * self.value_normalized, sp(4)) \ if self.orientation == "horizontal" \ else (sp(4), self.height * self.value_normalized) pos: (self.width * (1 - self.value_normalized) + self.x \ if self.reversed else self.x + self._x, self.center_y - dp(4)) \ if self.orientation == "horizontal" \ else (self.center_x - dp(4),self.height \ * (1 - self.value_normalized) + self.y if self.reversed \ else self.y) """ ) class MDProgressBar(ThemableBehavior, ProgressBar): reversed = BooleanProperty(False) """Reverse the direction the progressbar moves. :attr:`reversed` is an :class:`~kivy.properties.BooleanProperty` and defaults to `False`. """ orientation = OptionProperty( "horizontal", options=["horizontal", "vertical"] ) """Orientation of progressbar. Available options are: `'horizontal '`, `'vertical'`. :attr:`orientation` is an :class:`~kivy.properties.OptionProperty` and defaults to `'horizontal'`. """ color = ListProperty() """ Progress bar color in ``rgba`` format. :attr:`color` is an :class:`~kivy.properties.OptionProperty` and defaults to `[]`. """ running_transition = StringProperty("in_cubic") """Running transition. :attr:`running_transition` is an :class:`~kivy.properties.StringProperty` and defaults to `'in_cubic'`. """ catching_transition = StringProperty("out_quart") """Catching transition. :attr:`catching_transition` is an :class:`~kivy.properties.StringProperty` and defaults to `'out_quart'`. """ running_duration = NumericProperty(0.5) """Running duration. :attr:`running_duration` is an :class:`~kivy.properties.NumericProperty` and defaults to `0.5`. """ catching_duration = NumericProperty(0.8) """Catching duration. :attr:`running_duration` is an :class:`~kivy.properties.NumericProperty` and defaults to `0.8`. """ type = OptionProperty( None, options=["indeterminate", "determinate"], allownone=True ) """Type of progressbar. Available options are: `'indeterminate '`, `'determinate'`. :attr:`type` is an :class:`~kivy.properties.OptionProperty` and defaults to `None`. """ _x = NumericProperty(0) def __init__(self, **kwargs): self.catching_anim = None self.running_anim = None super().__init__(**kwargs) def start(self): """Start animation.""" if self.type in ("indeterminate", "determinate"): Clock.schedule_once(self._set_default_value) if not self.catching_anim and not self.running_anim: if self.type == "indeterminate": self._create_indeterminate_animations() else: self._create_determinate_animations() self.running_away() def stop(self): """Stop animation.""" Animation.cancel_all(self) self._set_default_value(0) def running_away(self, *args): self._set_default_value(0) self.running_anim.start(self) def catching_up(self, *args): if self.type == "indeterminate": self.reversed = True self.catching_anim.start(self) def _create_determinate_animations(self): self.running_anim = Animation( value=100, opacity=1, t=self.running_transition, d=self.running_duration, ) self.running_anim.bind(on_complete=self.catching_up) self.catching_anim = Animation( opacity=0, t=self.catching_transition, d=self.catching_duration, ) self.catching_anim.bind(on_complete=self.running_away) def _create_indeterminate_animations(self): self.running_anim = Animation( _x=self.width / 2, value=50, t=self.running_transition, d=self.running_duration, ) self.running_anim.bind(on_complete=self.catching_up) self.catching_anim = Animation( value=0, t=self.catching_transition, d=self.catching_duration ) self.catching_anim.bind(on_complete=self.running_away) def _set_default_value(self, interval): self._x = 0 self.value = 0 self.reversed = False