Search code examples
sliderbindkivytextinput

kivy bind textiput to slider vlaue


I want to create a control panel which provides a slider for quick input but also an textinput to type in the exact value as float number. Additionally there shall be a label, which displays the actual value. The problem is, that I am not quit shure how to link all three widgets to each other AND auto update them if one changes the value... Everything is done in an external .kv file.

Here is my first attempt via id. It works but the textinput does not change its content if I change the sliders value. Does anyone have a better solution for my problem? Thanks for your help :)

That's the content of test.kv:

<MainLayout>:
BoxLayout:
    pos: self.parent.x + self.parent.width*0.1, self.parent.y
    orientation: 'vertical'
    BoxLayout:
        orientation: 'horizontal'
        Label:
            color: 0, 0, 0, 1
            font_size: 20 
            text: 'Position: ' + str(linear_pos_slider.value) + ' mm' 
            size: self.texture_size  
        Slider:
            id: linear_pos_slider
            orientation: 'horizontal'
            max: 50
            min: -50
            padding: 1
            value: float(linear_pos_ti.text)
            step: 0.1
        TextInput:
            id: linear_pos_ti
            size_hint: 0.2, 0.8
            font_size: 20
            text_color: 0, 0, 0, 1
            input_filter: 'float'
            multiline: 'False'
            text: '0'

And here is the content of the test.py to make the application run:

from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.uix.label import Label
from kivy.uix.slider import Slider
from kivy.uix.floatlayout import FloatLayout

from kivy.core.window import Window

Window.clearcolor = (1, 1, 1, 1)


class MainLayout(BoxLayout):
    pass


class Test(App):

    def build(self):
        return MainLayout()


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

Solution

  • You should just be able to bind them to each other:

    Label:
        color: 0, 0, 0, 1
        font_size: 20
        text: 'Position: ' + str(linear_pos_slider.value) + ' mm'
        size: self.texture_size
    Slider:
        id: linear_pos_slider
        orientation: 'horizontal'
        max: 50
        min: -50
        padding: 1
        value: float(linear_pos_ti.text)
        step: 0.1
    TextInput:
        id: linear_pos_ti
        size_hint: 0.2, 0.8
        font_size: 20
        text_color: 0, 0, 0, 1
        input_filter: 'float'
        multiline: 'False'
        text: str(linear_pos_slider.value)
    

    Although in practice, it would be better if you used a NumericProperty (or StringProperty) in the code and bound them to that. This has all the same advantages but it is cleaner and gives you easy access from the code side too.

    Note that since the bindings are one-way (property -> widget), you will need to set it with on_* events:

    Slider:
        id: linear_pos_slider
        orientation: 'horizontal'
        max: 50
        min: -50
        padding: 1
        value: root.value
        on_value: root.value = self.value
        step: 0.1
    TextInput:
        id: linear_pos_ti
        size_hint: 0.2, 0.8
        font_size: 20
        text_color: 0, 0, 0, 1
        input_filter: 'float'
        multiline: 'False'
        text: str(root.value)
        on_text: root.value = float(self.text)
    

    with:

    from kivy.properties import NumericProperty, StringProperty
    
    class MainLayout(BoxLayout):
        value = NumericProperty(0.0)
        pass