Search code examples
pythonkivykivy-languageencapsulation

Kivy: best way to make a single widget containing different widgets and being able to access their properties in the kv file


I'm trying to make a class that would be a Slider + 2 Labels, one label showing the value of the slider, and one showing the name of the slider. The goal is to reuse this in lieu of Slider when convinient.

I am ending up creating properties in the my class MySlider reflecting the ones I am interested in in Slider. If, for example, I wanted to be able to also specify the color of the value label, I could do the same, add a property "value_label_color" to MySlider and set the label's color in on_value_label_color()... Then in the kv file I would do something like that:

value_label_color: 1,0,1,0.15

This would work but could be very long if I had many widgets in my MySlider class.

I thought there may be a way to avoid all this and directly access the label's color from the kv file, so I could do something like the following?

    MySlider:
        orientation: 'vertical'
        id: slider2
        name: 'hop'
        step: 20
        self.value_label.color: 1,1,1,0.15

But this doesn't work.

Here is my minimal code as of now: .py:

from kivy.config import Config
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label
from kivy.uix.slider import Slider
from kivy.app import App
from kivy.properties import NumericProperty, StringProperty


class MySlider(BoxLayout):
    name = StringProperty('Param')
    min = NumericProperty(0)
    max = NumericProperty(100)
    step = NumericProperty()

    def __init__(self, *args, **kwargs):
        super(MySlider, self).__init__(*args, **kwargs)

        self.value_label = Label()
        self.value_label.id = "value_label"
        self.slider = Slider()
        self.slider.bind(value=self._on_value)
        self.name_label = Label()

        self.value_label.text = str(self.slider.value)
        self.slider.orientation = 'vertical'

        self.add_widget(self.value_label)
        self.add_widget(self.slider)
        self.add_widget(self.name_label)

    def on_name(self, obj, val):
        self.name_label.text = val

    def on_min(self, obj, val):
        self.slider.min = val

    def on_max(self, obj, val):
        self.slider.max = val

    def on_step(self, obj, val):
        self.slider.step = val

    def _on_value(self, obj, val):
        self.value_label.text = str(self.slider.value)


class MyLayout(BoxLayout):
    def __init__(self, **kwargs):
        super(MyLayout, self).__init__(**kwargs)


class test7App(App):
    def build_config(self, config):
        Config.set('kivy', 'exit_on_escape', '0')
        Config.set('input', 'mouse', 'mouse,multitouch_on_demand')
        self.title = 'My Sliders'

    def build(self):
        return MyLayout()


if __name__ == '__main__':
    test7App().run()

and the kv file:

#:kivy 2.0.0


<MyLayout>:
    orientation : 'horizontal'

    canvas:
        Color:
            rgba: 1,1,1,0.15
        Rectangle:
            size: self.size
            pos: self.pos

    MySlider:
        orientation: 'vertical'
        id: slider1
        name: "hip"
        min: -30
        max: +60
        step: 1

    MySlider:
        orientation: 'vertical'
        id: slider2
        name: 'hop'
        step: 20

If you have any suggestion about how to get there in the nicest (and economical) way, I would be grateful.

Thanks a lot. C


Solution

  • You can do that all from the kv file. In fact, you don't even need to define the MySlider class in the python code. You can modify your kv like this:

    #:kivy 2.0.0
    
    <MySlider@BoxLayout>:
        orientation: 'vertical'
        name: 'Param'
        min: 0
        max: 100
        step: 1
        value_label_color: 1,1,1,0.15
        
        Label:
            id: value_label
            text: str(slider.value)
            color: root.value_label_color
        Slider:
            id: slider
            orientation: root.orientation
            min: root.min
            max: root.max
            step: root.step
        Label:
            text: root.name
        
    
    
    <MyLayout>:
        orientation : 'horizontal'
    
        canvas:
            Color:
                rgba: 1,1,1,0.15
            Rectangle:
                size: self.size
                pos: self.pos
    
        MySlider:
            orientation: 'vertical'
            id: slider1
            name: "hip"
            min: -30
            max: +60
            step: 1
    
        MySlider:
            orientation: 'horizontal'
            id: slider2
            name: 'hop'
            step: 20
    

    and your python code simplifies to:

    from kivy.config import Config
    from kivy.lang import Builder
    from kivy.uix.boxlayout import BoxLayout
    from kivy.app import App
    
    
    class MyLayout(BoxLayout):
        def __init__(self, **kwargs):
            super(MyLayout, self).__init__(**kwargs)
    
    
    class test7App(App):
        def build_config(self, config):
            Config.set('kivy', 'exit_on_escape', '0')
            Config.set('input', 'mouse', 'mouse,multitouch_on_demand')
            self.title = 'My Sliders'
    
    
        def build(self):
            return MyLayout()
    
    
    if __name__ == '__main__':
        test7App().run()