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
class KivyGuiApp(App):
def build(self):
return root_widget
class MyBox(BoxLayout):
def print_ids(self, *args):
print("
ids:")
for widget in self.walk():
print("{} -> {}".format(widget, widget.id))
def print_names(self, *args):
print("
names:")
for widget in self.walk():
print("{} -> {}".format(widget, widget.name))
root_widget = Builder.load_string("""
MyBox:
id: screen_manager
name: 'screen_manager'
SimpleLayout:
id: simple_layout
name: 'simple_layout'
<SimpleLayout@BoxLayout>:
id: simple_layout_rule
name: 'simple_layout_rule'
Button:
id: button_ids
name: 'button_ids'
text: 'print ids to console'
on_release: app.root.print_ids()
Button:
id: button_names
name: 'button_names'
text: 'print names to console'
on_release: app.root.print_names()
""")
if __name__ == '__main__':
KivyGuiApp().run()
So When you run the code there will be two buttons:
- first to walk over all widgets and print their names (which works as expected - returns 'name' for each widget),
- second button to walk over all widgets as well, but instead of names print their ids (which doesn't work - returns None for each id).
My questions are:
- Isn't 'id' a property just like 'name'?
- How can I access id for each widget from python side?
Bonus question:
- Can I access a widget "globally" by it's id (assuming all id's are unique)? By "globally" I mean for example accessing (in code above) ids from 'MyBox:' widget without referencing parent-child, but just by ids (or maybe any other property that would be unique to every widget). I was thinking of creating a dictionary { id : widget object } of all widgets for easy access, unless there is another way I'm not aware of?
To emphasize - I'm trying to avoid referencing by children-parent way (which is rather messy when you want to change your widget tree later) and I'd like to generate widgets in .kv language.
So what would be the best way to do that?
EDIT:
So here's the easiest way I could think of to reference widgets by unique "global" id.
First I created a class which will be inherited by my App class:
class KivyWidgetInterface():
''' interface for global widget access '''
global_widgets = {}
def register_widget(self, widget_object):
''' registers widget only if it has unique gid '''
if widget_object.gid not in self.global_widgets:
self.global_widgets[widget_object.gid] = widget_object
def get_widget(self, widget_gid):
''' returns widget if it is registered '''
if widget_gid in self.global_widgets:
return self.global_widgets[widget_gid]
else:
return None
So the widget will be registered only if it has gid - a widget class variable - and it is unique. This way I can store only vital widgets in this dict. Also, it is easily accessible from both .kv and python side.
Now i create the gid variables and register them to the dict in .kv:
<PickDirectory>:
gid: 'pick_directory'
on_pos: app.register_widget(self)
on_selection: app.get_widget('filelist').some_func()
<FileListView>:
gid: 'filelist'
on_pos: app.register_widget(self)
Button:
name: 'not important'
Button:
gid: 'tab_browse_button1'
on_pos: app.register_widget(self)
Thing that bothers me actually is that I register my widgets in this "global" dictionary with the 'on_pos' event... which I really don't like, but I was unable to find any reliable way of calling a register method after the widget was initialized (on_pos is called right after the init phase, when widget is positioned and later very rarely, so... seemed like the least bothering way of doing that with my knowledge of kivy api, the order widgets are initialized with .kv language etc; so if there is a better way I'd be very grafeul for any pointers).
Anyhow, this way I can easy bind any event to any method in any class right from the .kv
One thing to remember is that the gid (global id's) need to be unique globally, but I don't find that any more disturbing than keeping ids unique locally(which could be equally or even more confusing for me). And as I said - I'd like to register the widgets differently, but I couldn't find any other reliable way of doing this (and I don't find Clock to be reliable for such things).
See Question&Answers more detail:
os