Search code examples
pythonpython-3.xkivykivy-language

Kivy label code from kivy language to python


I have the following simple application created with kivy gui framework. It is not the simplest one since label_1 has background color and its size is modified according to the text of the label. This is my very first experience with kivy. Unfortunately, kivy documentation and most of the examples accessible via google heavily use kivy language. My qestion is: how can I get the same result without kivy language using only python 3?

Code:

from kivy.config import Config
from kivy.core.window import Window

from kivy.app import App

from kivy.lang import Builder


MainScreen = Builder.load_string('''
BoxLayout:
    orientation: 'vertical'
    Label:
        text: 'label_1'
        font_size: 18
        color: (0, 0, 0, 1)
        size_hint: None, None
        size: self.texture_size
        canvas.before:
            Color:
                rgba: 1, .5, 0, 1
            Rectangle:
                pos: self.pos
                size: self.size
    Label:
        text: 'label_2'
        color: (0, 0, 0, 1)
''')


class MyApp(App):
    def build(self):
        return MainScreen


if __name__ == '__main__':

    Config.set('input', 'mouse', 'mouse,multitouch_on_demand')
    Window.clearcolor = (1, 1, 1, 1)

    MyApp().run()

How it looks:

enter image description here


Solution

  • The implementation of the code that you require is:

    from kivy.config import Config
    from kivy.core.window import Window
    from kivy.app import App
    
    from kivy.uix.boxlayout import BoxLayout
    from kivy.uix.label import Label
    from kivy.graphics import Rectangle, Color
    
    mainscreen = BoxLayout(orientation='vertical')
    
    label1 = Label(text='label_1', font_size=18, color=(0, 0, 0, 1), size_hint=(None, None))
    label1.bind(texture_size=label1.setter('size'))
    def update_rect(instance, *args):
        rect.pos = instance.pos
        rect.size = instance.size
    with label1.canvas.before:
        Color(1, .5, 0, 1)
        rect = Rectangle(pos=label1.pos, size=label1.size)
    label1.bind(pos=update_rect, size=update_rect)
    
    label2 = Label(text='label_2', color=(0, 0, 0, 1))
    
    mainscreen.add_widget(label1)
    mainscreen.add_widget(label2)
    
    class MyApp(App):
        def build(self):
            return mainscreen
    
    if __name__ == '__main__':
        Config.set('input', 'mouse', 'mouse,multitouch_on_demand')
        Window.clearcolor = (1, 1, 1, 1)
        MyApp().run()
    

    IMHO the implementation in kv is more readable and more flexible when done binding as in the case of the label that fits the size.


    from kivy.config import Config
    from kivy.core.window import Window
    from kivy.app import App
    
    from kivy.uix.boxlayout import BoxLayout
    from kivy.uix.label import Label
    from kivy.graphics import Rectangle, Color
    
    from kivy.properties import ListProperty
    
    
    class CustomLabel(Label):
        bgcolor = ListProperty([0, 0, 0, 1])
        def __init__(self, **kwargs):
            if kwargs.get('bgcolor'):
                self.bgcolor = kwargs['bgcolor']
                kwargs.pop('bgcolor')
            super(CustomLabel, self).__init__(**kwargs)
            self.bind(texture_size=self.setter('size'))
    
            with self.canvas.before:
                self.p = Color(*self.bgcolor)
                self.rect = Rectangle(pos=self.pos, size=self.size)
    
            self.on_bgcolor()
            self.bind(pos=self.geometry_bind, size=self.geometry_bind)
    
        def on_bgcolor(self, *args):
            self.p.rgba = self.bgcolor
    
        def geometry_bind(self, *args):
            self.rect.pos = self.pos
            self.rect.size = self.size
    
    
    class MyApp(App):
        def build(self):
            mainscreen = BoxLayout(orientation='vertical')
            label1 = CustomLabel(text='label_1', font_size=18, color=(0, 0, 0, 1), size_hint=(None, None), bgcolor=(1, .5, 0, 1))
            label2 = Label(text='label_2', color=(0, 0, 0, 1))
            mainscreen.add_widget(label1)
            mainscreen.add_widget(label2)
            return mainscreen
    
    if __name__ == '__main__':
        Config.set('input', 'mouse', 'mouse,multitouch_on_demand')
        Window.clearcolor = (1, 1, 1, 1)
        MyApp().run()
    

    Explanation:

    • bind: the bind(foo_property = callback) function is responsible for calling the callback when foo_property changes.

    • setter: the setter('foo_property') function generates a callback that allows you to set a value.

    If you join both functions:

    class FooClass(Foo_EventDispatcher):
        property_a = FooProperty(initial_value_a)
        property_b = FooProperty(initial_value_b)
    
        def __init__(self, **kwargs):
           super(FooClass, self).__init__(**kwargs)
           self.bind(property_a=self.setter('property_b'))
    

    equivalent to the following instruction in .kv:

    <FooClass>:
        property_b: self.property_a