""" Components/Slider ================= .. seealso:: `Material Design spec, Sliders `_ .. rubric:: Sliders allow users to make selections from a range of values. .. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/slider.png :align: center With value hint --------------- .. code-block:: python from kivy.lang import Builder from kivymd.app import MDApp KV = ''' Screen MDSlider: min: 0 max: 100 value: 40 ''' class Test(MDApp): def build(self): return Builder.load_string(KV) Test().run() .. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/slider-1.gif :align: center Without value hint ------------------ .. code-block:: kv MDSlider: min: 0 max: 100 value: 40 hint: False .. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/slider-2.gif :align: center Without custom color -------------------- .. code-block:: kv MDSlider: min: 0 max: 100 value: 40 hint: False humb_color_down: app.theme_cls.accent_color .. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/slider-3.png :align: center """ __all__ = ("MDSlider",) from kivy.lang import Builder from kivy.metrics import dp from kivy.properties import ( AliasProperty, BooleanProperty, ListProperty, NumericProperty, ) from kivy.uix.slider import Slider from kivy.utils import get_color_from_hex from kivymd.color_definitions import colors from kivymd.theming import ThemableBehavior Builder.load_string( """ #:import images_path kivymd.images_path id: slider canvas: Clear Color: rgba: self._track_color_disabled if self.disabled \ else (self._track_color_active if self.active \ else self._track_color_normal) Rectangle: size: (self.width - self.padding * 2 - self._offset[0], dp(4)) if \ self.orientation == "horizontal" \ else (dp(4),self.height - self.padding*2 - self._offset[1]) pos: (self.x + self.padding + self._offset[0], self.center_y - dp(4)) \ if self.orientation == "horizontal" else \ (self.center_x - dp(4), self.y + self.padding + self._offset[1]) # If 0 draw circle Color: rgba: (0, 0, 0, 0) if not self._is_off \ else (self._track_color_disabled if self.disabled \ else (self._track_color_active \ if self.active else self._track_color_normal)) Line: width: 2 circle: (self.x + self.padding + dp(3), self.center_y - dp(2), 8 \ if self.active else 6 ) if self.orientation == "horizontal" \ else (self.center_x - dp(2), self.y + self.padding + dp(3), 8 \ if self.active else 6) Color: rgba: (0, 0, 0, 0) if self._is_off \ else (self.thumb_color_down if not self.disabled \ else self._track_color_disabled) Rectangle: size: ((self.width - self.padding * 2) * self.value_normalized, sp(4)) \ if slider.orientation == "horizontal" else (sp(4), \ (self.height - self.padding * 2) * self.value_normalized) pos: (self.x + self.padding, self.center_y - dp(4)) \ if self.orientation == "horizontal" \ else (self.center_x - dp(4), self.y + self.padding) Thumb: id: thumb size_hint: None, None size: (dp(12), dp(12)) if root.disabled else ((dp(24), dp(24)) \ if root.active else (dp(16), dp(16))) pos: (slider.value_pos[0] - dp(8), slider.center_y - thumb.height / 2 - dp(2)) \ if slider.orientation == "horizontal" \ else (slider.center_x - thumb.width / 2 - dp(2), \ slider.value_pos[1] - dp(8)) color: (0, 0, 0, 0) if slider._is_off else (root._track_color_disabled \ if root.disabled else root.thumb_color_down) elevation: 0 if slider._is_off else (4 if root.active else 2) MDCard: id: hint_box size_hint: None, None md_bg_color: (1, 1, 1, 1) if not root.hint_bg_color else slider.hint_bg_color elevation: 0 opacity: 1 if slider.active else 0 background: f"{images_path}transparent.png" radius: [slider.hint_radius,] size: (dp(12), dp(12)) if root.disabled else ((dp(28), dp(28)) \ if root.active else (dp(20), dp(20))) pos: (slider.value_pos[0] - dp(9), slider.center_y - hint_box.height / 2 + dp(30)) \ if slider.orientation == "horizontal" \ else (slider.center_x - hint_box.width / 2 + dp(30), \ slider.value_pos[1] - dp(8)) MDLabel: text: str(int(slider.value)) font_style: "Caption" halign: "center" theme_text_color: "Custom" text_color: (root.thumb_color_down if root.active else (0, 0, 0, 0)) \ if not slider.hint_text_color else slider.hint_text_color """ ) class MDSlider(ThemableBehavior, Slider): active = BooleanProperty(False) """ If the slider is clicked. :attr:`active` is an :class:`~kivy.properties.BooleanProperty` and defaults to `False`. """ hint = BooleanProperty(True) """ If True, then the current value is displayed above the slider. :attr:`hint` is an :class:`~kivy.properties.BooleanProperty` and defaults to `True`. """ hint_bg_color = ListProperty() """ Hint rectangle color in ``rgba`` format. :attr:`hint_bg_color` is an :class:`~kivy.properties.ListProperty` and defaults to `[]`. """ hint_text_color = ListProperty() """ Hint text color in ``rgba`` format. :attr:`hint_text_color` is an :class:`~kivy.properties.ListProperty` and defaults to `[]`. """ hint_radius = NumericProperty(4) """ Hint radius. :attr:`hint_radius` is an :class:`~kivy.properties.NumericProperty` and defaults to `4`. """ show_off = BooleanProperty(True) """ Show the `'off'` ring when set to minimum value. :attr:`show_off` is an :class:`~kivy.properties.BooleanProperty` and defaults to `True`. """ # Internal state of ring _is_off = BooleanProperty(False) # Internal adjustment to reposition sliders for ring _offset = ListProperty((0, 0)) _thumb_color = ListProperty(get_color_from_hex(colors["Gray"]["50"])) def _get_thumb_color(self): return self._thumb_color def _set_thumb_color(self, color, alpha=None): if len(color) == 2: self._thumb_color = get_color_from_hex(colors[color[0]][color[1]]) if alpha: self._thumb_color[3] = alpha elif len(color) == 4: self._thumb_color = color thumb_color = AliasProperty( _get_thumb_color, _set_thumb_color, bind=["_thumb_color"] ) """ Current color slider in ``rgba`` format. :attr:`thumb_color` is an :class:`~kivy.properties.AliasProperty` that returns the value of the current color slider, property is readonly. """ _thumb_color_down = ListProperty([1, 1, 1, 1]) def _get_thumb_color_down(self): return self._thumb_color_down def _set_thumb_color_down(self, color, alpha=None): if len(color) == 2: self._thumb_color_down = get_color_from_hex( colors[color[0]][color[1]] ) if alpha: self._thumb_color_down[3] = alpha else: self._thumb_color_down[3] = 1 elif len(color) == 4: self._thumb_color_down = color _thumb_color_disabled = ListProperty( get_color_from_hex(colors["Gray"]["400"]) ) def _get_thumb_color_disabled(self): return self._thumb_color_disabled def _set_thumb_color_disabled(self, color, alpha=None): if len(color) == 2: self._thumb_color_disabled = get_color_from_hex( colors[color[0]][color[1]] ) if alpha: self._thumb_color_disabled[3] = alpha elif len(color) == 4: self._thumb_color_disabled = color thumb_color_down = AliasProperty( _get_thumb_color_disabled, _set_thumb_color_disabled, bind=["_thumb_color_disabled"], ) """ Color slider in ``rgba`` format. :attr:`thumb_color_down` is an :class:`~kivy.properties.AliasProperty` that returns and set the value of color slider. """ _track_color_active = ListProperty() _track_color_normal = ListProperty() _track_color_disabled = ListProperty() _thumb_pos = ListProperty([0, 0]) def __init__(self, **kwargs): super().__init__(**kwargs) self.theme_cls.bind( theme_style=self._set_colors, primary_color=self._set_colors, primary_palette=self._set_colors, ) self._set_colors() def on_hint(self, instance, value): if not value: self.remove_widget(self.ids.hint_box) def _set_colors(self, *args): if self.theme_cls.theme_style == "Dark": self._track_color_normal = get_color_from_hex("FFFFFF") self._track_color_normal[3] = 0.3 self._track_color_active = self._track_color_normal self._track_color_disabled = self._track_color_normal self.thumb_color = get_color_from_hex(colors["Gray"]["400"]) self.thumb_color_down = get_color_from_hex( colors[self.theme_cls.primary_palette]["200"] ) self.thumb_color_disabled = get_color_from_hex( colors["Gray"]["800"] ) else: self._track_color_normal = get_color_from_hex("000000") self._track_color_normal[3] = 0.26 self._track_color_active = get_color_from_hex("000000") self._track_color_active[3] = 0.38 self._track_color_disabled = get_color_from_hex("000000") self._track_color_disabled[3] = 0.26 self.thumb_color_down = self.theme_cls.primary_color def on_value_normalized(self, *args): """When the ``value == min`` set it to `'off'` state and make slider a ring. """ self._update_is_off() def on_show_off(self, *args): self._update_is_off() def _update_is_off(self): self._is_off = self.show_off and (self.value_normalized == 0) def on__is_off(self, *args): self._update_offset() def on_active(self, *args): self._update_offset() def _update_offset(self): """Offset is used to shift the sliders so the background color shows through the off circle. """ d = 2 if self.active else 0 self._offset = (dp(11 + d), dp(11 + d)) if self._is_off else (0, 0) def on_touch_down(self, touch): if super().on_touch_down(touch): self.active = True def on_touch_up(self, touch): if super().on_touch_up(touch): self.active = False