Skip to content Skip to sidebar Skip to footer

Kivy: Get Widgets Ids And Accessing Widgets By Unique Property

I'm new to Kivy and I have this little demo snippet that demonstrates my problem: from kivy.app import App from kivy.uix.boxlayout import BoxLayout from kivy.lang import Builder

Solution 1:

Actually, no. name in your widgets is a variable and id is just a widget reference, weakref according to the docs. Maybe python docs will help you understand how it works. What you did was printing id, not a variable "id" inside a widget.

In the kivy docs it's explained that after kv is parsed, ids are collected into a ObservableDict. The id works like a python dict key id:Widget but only if accessed through the dictionary(ids). I think kv parser just takes all ids into dict and works only with the dict it creates.

Button:
    id: test
    text: 'self.id'
#or
Button:
    id: 'test'
    text: 'self.id'

Even if it's written like a string, nothing changes. So I expect parser to behave like this: grabs whatever whole word is after id:, turns to a string, appends to a ids dictionary <id_string>:Widget_weakref, forgets about id in your .kv or just ignores it if it works with .kv again. Therefore, when id is called directly(not dictionary-like d[key]), it behaves like an empty/None variable. I hope I'm right.


To answer the second and the third one:

If you mean accessing widget by id in MyBox directly for example SimpleLayout, then yes.

python class:

self.ids.simple_layout

kv MyBox rule:

MyBox:
    id: screen_manager
    name: 'screen_manager'
    BoxLayout:
        Label:
            id: my_label
            text: 'test'
        Button:
            text: 'button'
            on_release: self.text = root.ids.my_label.text

However, to access all widgets by their ids in way like python globals work, it's not possible. You'd need to access class/widget first and then its ids dictionary


Solution 2:

Isn't 'id' a property just like 'name'?

No, ids are a special syntax that exist only in kv language, or accessible via the root widget of the rule in python (self.ids.idname). There is an id property, but I'm not sure it's actually used anywhere, it seems to exist mostly for legacy reasons. I think we were considering removing it.

How can I access id for each widget from python side?

Via the ids property of the root widget. For instance, in a MyBox method you can write self.ids.simple_layout to get the SimpleLayout.

I was thinking of creating a dictionary { id : widget object } of all widgets for easy access

This is not generically possible because multiple widgets can have the same id. That's why they are rule-local and accessed via the root widget of the rule.

You can construct such a list yourself if you like, either accounting for duplicates or with the knowledge that you won't create them.


Solution 3:

Recently I have been asking myself a similar question: how to access a property in another widget. I have found the answer myself and I'm posting here as I find it not very intuitive.

The idea of this code is simple: when on click a button, it changes a property which is in a child widget of another class.

Usually when calling inside the same widget tree it's rather easy and can be called by a simple self.ids.simple_layout (or any variation with app, self or root).

from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label
from kivy.uix.button import Button

kv = """
<Test>:
    Button:
        text: "Access label"
        on_press: root.access_label()
    MyWidget:
        id: my_widget


<MyWidget>
    Label:
        id: my_label
        text: "not yet accessed"
"""

Builder.load_string(kv)
class MyWidget(BoxLayout):
    pass

class Test(BoxLayout):
   def access_label(self):
        self.ids.my_widget.ids.my_label.text = 'Accessed!'

class TestApp(App):
    def build(self):
        return Test()

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

The tricky part is self.ids.my_widget.ids.my_label.text and the fact that the id my_widget must be in the root widget and not in the <My_widget> definition.

I'm not sure I fully understand myself, but from what I understand it seems that when defining another widget with another class, it creates another tree, which means:

  • the id has to be assigned in the main tree
  • the two trees are not linked and one has to use twice ids when accessing a property or a function.

Please correct me if I'm wrong


Post a Comment for "Kivy: Get Widgets Ids And Accessing Widgets By Unique Property"