Skip to content

Slider

Usage

The range is shown as a horizontal line, and the selected value is shown as a draggable marker.

A slider can either be continuous (allowing any value within the range), or discrete (allowing a fixed number of equally-spaced values). For example:

import toga

def my_callback(slider):
    print(slider.value)

# Continuous slider, with an event handler.
toga.Slider(min=-5, max=10, value=7, on_change=my_callback)

# Discrete slider, accepting the values [0, 1.5, 3, 4.5, 6, 7.5].
toga.Slider(min=0, max=7.5, tick_count=6)

Reference

Bases: Widget

Source code in core/src/toga/widgets/slider.py
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
class Slider(Widget):
    _MIN_WIDTH = 100

    def __init__(
        self,
        id: str | None = None,
        style: StyleT | None = None,
        value: float | None = None,
        min: float = 0.0,
        max: float = 1.0,
        tick_count: int | None = None,
        on_change: toga.widgets.slider.OnChangeHandler | None = None,
        on_press: toga.widgets.slider.OnPressHandler | None = None,
        on_release: OnReleaseHandler | None = None,
        enabled: bool = True,
        **kwargs,
    ):
        """Create a new Slider widget.

        :param id: The ID for the widget.
        :param style: A style object. If no style is provided, a default style will be
            applied to the widget.
        :param value: Initial [`value`][toga.Slider.value] of the slider. Defaults to
            the mid-point of the range.
        :param min: Initial minimum value of the slider. Defaults to 0.
        :param max: Initial maximum value of the slider. Defaults to 1.
        :param tick_count: Initial [`tick_count`][toga.Slider.tick_count] for the
            slider. If [`None`][], the slider will be continuous.
        :param on_change: Initial [`on_change`][toga.Slider.on_change] handler.
        :param on_press: Initial [`on_press`][toga.Slider.on_press] handler.
        :param on_release: Initial [`on_release`][toga.Slider.on_release] handler.
        :param enabled: Whether the user can interact with the widget.
        :param kwargs: Initial style properties.
        """
        super().__init__(id, style, **kwargs)

        # Set a dummy handler before installing the actual on_change, because we do not
        # want on_change triggered by the initial value being set
        self.on_change = None
        self.min = min
        self.max = max
        self.tick_count = tick_count
        if value is None:
            value = (min + max) / 2
        self.value = value

        self.on_change = on_change
        self.on_press = on_press
        self.on_release = on_release

        self.enabled = enabled

    def _create(self) -> Any:
        return self.factory.Slider(interface=self)

    # Backends are inconsistent about when they produce events for programmatic changes,
    # so we deal with those in the interface layer.
    @contextmanager
    def _programmatic_change(self) -> float:
        old_value = self.value
        on_change = self._on_change
        self.on_change = None
        yield old_value

        self._on_change = on_change
        if self.value != old_value:
            on_change()

    @property
    def value(self) -> float:
        """Current value.

        If the slider is discrete, setting the value will round it to the nearest tick.

        :raises ValueError: If set to a value which is outside of the [`range`][].
        """
        return self._impl.get_value()

    @value.setter
    def value(self, value: float) -> None:
        if value < self.min:
            value = self.min
        elif value > self.max:
            value = self.max
        with self._programmatic_change():
            self._set_value(value)

    def _set_value(self, value: SupportsFloat) -> None:
        self._impl.set_value(self._round_value(float(value)))

    def _round_value(self, value: float) -> float:
        step = self.tick_step
        if step is not None:
            # Round to the nearest tick.
            value = self.min + round((value - self.min) / step) * step
        return value

    @property
    def min(self) -> float:
        """Minimum allowed value.

        When setting this property, the current [`value`][toga.Slider.value] and
        [`max`][toga.Slider.max] will be
        clipped against the new minimum value.
        """
        return self._impl.get_min()

    @min.setter
    def min(self, value: SupportsFloat) -> None:
        with self._programmatic_change() as old_value:
            # Some backends will clip the current value within the range automatically,
            # but do it ourselves to be certain. In discrete mode, setting self.value
            # also rounds to the new positions of the ticks.
            _min = float(value)
            _max = self.max
            if _max < _min:
                _max = _min
                self._impl.set_max(_max)

            self._impl.set_min(_min)
            self._set_value(max(_min, min(_max, old_value)))

    @property
    def max(self) -> float:
        """Maximum allowed value.

        When setting this property, the current [`value`][toga.Slider.value] and
        [`min`][toga.Slider.min] will be
        clipped against the new maximum value.
        """
        return self._impl.get_max()

    @max.setter
    def max(self, value: SupportsFloat) -> None:
        with self._programmatic_change() as old_value:
            # Some backends will clip the current value within the range automatically,
            # but do it ourselves to be certain. In discrete mode, setting self.value
            # also rounds to the new positions of the ticks.
            _min = self.min
            _max = float(value)
            if _min > _max:
                _min = _max
                self._impl.set_min(_min)

            self._impl.set_max(_max)
            self._set_value(max(_min, min(_max, old_value)))

    @property
    def tick_count(self) -> int | None:
        """Number of tick marks to display on the slider.

        * If this is `None`, the slider will be continuous.
        * If this is an `int`, the slider will be discrete, and will have the given
          number of possible values, equally spaced within the [`range`][].

        Setting this property to an `int` will round the current value to the nearest
        tick.

        :raises ValueError: If set to a count which is not at least 2 (for the min and
            max).

        /// note | Note

        On iOS, tick marks are not currently displayed, but discrete mode will
        otherwise work correctly.

        ///
        """
        return self._impl.get_tick_count()

    @tick_count.setter
    def tick_count(self, tick_count: float | None) -> None:
        if (tick_count is not None) and (tick_count < 2):
            raise ValueError("tick count must be at least 2")
        with self._programmatic_change() as old_value:
            # Some backends will round the current value to the nearest tick
            # automatically, but do it ourselves to be certain. Some backends also
            # require the value to be refreshed when moving between discrete and
            # continuous mode, because this causes a change in the native range.
            self._impl.set_tick_count(tick_count)
            self.value = old_value

    @property
    def tick_step(self) -> float | None:
        """Step between adjacent ticks.

        * If the slider is continuous, this property returns `None`
        * If the slider is discrete, it returns the difference in value between adjacent
          ticks.

        This property is read-only, and depends on the values of
        [`tick_count`][toga.Slider.tick_count] and [`range`][].
        """
        if self.tick_count is None or self.max == self.min:
            return None
        return (self.max - self.min) / (self.tick_count - 1)

    @property
    def tick_value(self) -> float | None:
        """Value of the slider, measured in ticks.

        * If the slider is continuous, this property returns `None`.
        * If the slider is discrete, it returns an integer between 1 (representing
          [`min`][toga.Slider.min]) and [`tick_count`][toga.Slider.tick_count]
          (representing [`max`][toga.Slider.max]).

        :raises ValueError: If set to anything inconsistent with the rules above.
        """
        if self.tick_count is not None and self.tick_step is not None:
            return round((self.value - self.min) / self.tick_step) + 1
        else:
            return None

    @tick_value.setter
    def tick_value(self, tick_value: int | None) -> None:
        if self.tick_count is None:
            if tick_value is not None:
                raise ValueError("cannot set tick value when tick count is None")
        else:
            if tick_value is None or self.tick_step is None:
                raise ValueError(
                    "cannot set tick value to None when tick count is not None"
                )
            self.value = self.min + (tick_value - 1) * self.tick_step

    @property
    def on_change(self) -> OnChangeHandler:
        """Handler to invoke when the value of the slider is changed, either by the user
        or programmatically.

        Setting the widget to its existing value will not call the handler.
        """
        return self._on_change

    @on_change.setter
    def on_change(self, handler: toga.widgets.slider.OnChangeHandler) -> None:
        self._on_change = wrapped_handler(self, handler)

    @property
    def on_press(self) -> OnPressHandler:
        """Handler to invoke when the user presses the slider before changing it."""
        return self._on_press

    @on_press.setter
    def on_press(self, handler: toga.widgets.slider.OnPressHandler) -> None:
        self._on_press = wrapped_handler(self, handler)

    @property
    def on_release(self) -> OnReleaseHandler:
        """Handler to invoke when the user releases the slider after changing it."""
        return self._on_release

    @on_release.setter
    def on_release(self, handler: OnReleaseHandler) -> None:
        self._on_release = wrapped_handler(self, handler)

max property writable

Maximum allowed value.

When setting this property, the current value and min will be clipped against the new maximum value.

min property writable

Minimum allowed value.

When setting this property, the current value and max will be clipped against the new minimum value.

on_change property writable

Handler to invoke when the value of the slider is changed, either by the user or programmatically.

Setting the widget to its existing value will not call the handler.

on_press property writable

Handler to invoke when the user presses the slider before changing it.

on_release property writable

Handler to invoke when the user releases the slider after changing it.

tick_count property writable

Number of tick marks to display on the slider.

  • If this is None, the slider will be continuous.
  • If this is an int, the slider will be discrete, and will have the given number of possible values, equally spaced within the [range][].

Setting this property to an int will round the current value to the nearest tick.

:raises ValueError: If set to a count which is not at least 2 (for the min and max).

Note

On iOS, tick marks are not currently displayed, but discrete mode will otherwise work correctly.

tick_step property

Step between adjacent ticks.

  • If the slider is continuous, this property returns None
  • If the slider is discrete, it returns the difference in value between adjacent ticks.

This property is read-only, and depends on the values of tick_count and [range][].

tick_value property writable

Value of the slider, measured in ticks.

  • If the slider is continuous, this property returns None.
  • If the slider is discrete, it returns an integer between 1 (representing min) and tick_count (representing max).

:raises ValueError: If set to anything inconsistent with the rules above.

value property writable

Current value.

If the slider is discrete, setting the value will round it to the nearest tick.

:raises ValueError: If set to a value which is outside of the [range][].

__init__(id=None, style=None, value=None, min=0.0, max=1.0, tick_count=None, on_change=None, on_press=None, on_release=None, enabled=True, **kwargs)

Create a new Slider widget.

:param id: The ID for the widget. :param style: A style object. If no style is provided, a default style will be applied to the widget. :param value: Initial value of the slider. Defaults to the mid-point of the range. :param min: Initial minimum value of the slider. Defaults to 0. :param max: Initial maximum value of the slider. Defaults to 1. :param tick_count: Initial tick_count for the slider. If [None][], the slider will be continuous. :param on_change: Initial on_change handler. :param on_press: Initial on_press handler. :param on_release: Initial on_release handler. :param enabled: Whether the user can interact with the widget. :param kwargs: Initial style properties.

Source code in core/src/toga/widgets/slider.py
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
def __init__(
    self,
    id: str | None = None,
    style: StyleT | None = None,
    value: float | None = None,
    min: float = 0.0,
    max: float = 1.0,
    tick_count: int | None = None,
    on_change: toga.widgets.slider.OnChangeHandler | None = None,
    on_press: toga.widgets.slider.OnPressHandler | None = None,
    on_release: OnReleaseHandler | None = None,
    enabled: bool = True,
    **kwargs,
):
    """Create a new Slider widget.

    :param id: The ID for the widget.
    :param style: A style object. If no style is provided, a default style will be
        applied to the widget.
    :param value: Initial [`value`][toga.Slider.value] of the slider. Defaults to
        the mid-point of the range.
    :param min: Initial minimum value of the slider. Defaults to 0.
    :param max: Initial maximum value of the slider. Defaults to 1.
    :param tick_count: Initial [`tick_count`][toga.Slider.tick_count] for the
        slider. If [`None`][], the slider will be continuous.
    :param on_change: Initial [`on_change`][toga.Slider.on_change] handler.
    :param on_press: Initial [`on_press`][toga.Slider.on_press] handler.
    :param on_release: Initial [`on_release`][toga.Slider.on_release] handler.
    :param enabled: Whether the user can interact with the widget.
    :param kwargs: Initial style properties.
    """
    super().__init__(id, style, **kwargs)

    # Set a dummy handler before installing the actual on_change, because we do not
    # want on_change triggered by the initial value being set
    self.on_change = None
    self.min = min
    self.max = max
    self.tick_count = tick_count
    if value is None:
        value = (min + max) / 2
    self.value = value

    self.on_change = on_change
    self.on_press = on_press
    self.on_release = on_release

    self.enabled = enabled

Bases: Protocol

Source code in core/src/toga/widgets/slider.py
13
14
15
16
17
18
19
class OnChangeHandler(Protocol):
    def __call__(self, widget: Slider, **kwargs: Any) -> None:
        """A handler to invoke when the value is changed.

        :param widget: The Slider that was changed.
        :param kwargs: Ensures compatibility with arguments added in future versions.
        """

__call__(widget, **kwargs)

A handler to invoke when the value is changed.

:param widget: The Slider that was changed. :param kwargs: Ensures compatibility with arguments added in future versions.

Source code in core/src/toga/widgets/slider.py
14
15
16
17
18
19
def __call__(self, widget: Slider, **kwargs: Any) -> None:
    """A handler to invoke when the value is changed.

    :param widget: The Slider that was changed.
    :param kwargs: Ensures compatibility with arguments added in future versions.
    """

Bases: Protocol

Source code in core/src/toga/widgets/slider.py
22
23
24
25
26
27
28
class OnPressHandler(Protocol):
    def __call__(self, widget: Slider, **kwargs: Any) -> None:
        """A handler to invoke when the slider is pressed.

        :param widget: The Slider that was pressed.
        :param kwargs: Ensures compatibility with arguments added in future versions.
        """

__call__(widget, **kwargs)

A handler to invoke when the slider is pressed.

:param widget: The Slider that was pressed. :param kwargs: Ensures compatibility with arguments added in future versions.

Source code in core/src/toga/widgets/slider.py
23
24
25
26
27
28
def __call__(self, widget: Slider, **kwargs: Any) -> None:
    """A handler to invoke when the slider is pressed.

    :param widget: The Slider that was pressed.
    :param kwargs: Ensures compatibility with arguments added in future versions.
    """

Bases: Protocol

Source code in core/src/toga/widgets/slider.py
31
32
33
34
35
36
37
class OnReleaseHandler(Protocol):
    def __call__(self, widget: Slider, **kwargs: Any) -> None:
        """A handler to invoke when the slider is pressed.

        :param widget: The Slider that was released.
        :param kwargs: Ensures compatibility with arguments added in future versions.
        """

__call__(widget, **kwargs)

A handler to invoke when the slider is pressed.

:param widget: The Slider that was released. :param kwargs: Ensures compatibility with arguments added in future versions.

Source code in core/src/toga/widgets/slider.py
32
33
34
35
36
37
def __call__(self, widget: Slider, **kwargs: Any) -> None:
    """A handler to invoke when the slider is pressed.

    :param widget: The Slider that was released.
    :param kwargs: Ensures compatibility with arguments added in future versions.
    """