Search code examples
pythonkivykivy-language

Can't reference widget id in Kivy


Very new to Kivy and trying to dynamically add widgets, but despite all the examples I have followed, I can't get mine to work.

My .kv file is...

ScreenManager:
    MainScreen:
    LoginScreen:

<MainScreen>:
   name: 'MainScreen'
   id: ms

   BoxLayout:
        id: rc_display
        orientation: "vertical"
        padding: 10
        spacing: 10

        Label:
            id: ms_label1
            text: 'Oh Hell Yeah'

<LoginScreen>:
    name: 'LoginScreen'
    id: ls

    Button:
        on_release: app.root.current
        text: 'Click to Login'
        font_size: 20

and my python code is...

from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
import requests

class MainScreen(Screen):

   def on_pre_enter(self, *args):

        show_view = self.ids.rc_display # error here
        show_view.clear_widgets()
        buttons = BoxLayout(orientation = 'vertical')
        pr = requests.get('http://127.0.0.1:5000/stageplanning/api/v1.0/shows')
        for show in pr.json():
            buttons.add_widget(Button(text = show['show_title']))

        show_view.add_widgets(buttons)    

class LoginScreen(Screen):
    pass

class ScreenManagement(ScreenManager):
    pass

class StagePlanningApp(App):
    def build(self):

        sm = Builder.load_file('StagePlanning.kv')
        # sm.add_widget(MainScreen())
        # sm.add_widget(LoginScreen())
        return sm

sp_app = StagePlanningApp()
sp_app.run()

When I run the above I get the following error...

File "*pathtoFile*/StagePlanning.py", line 12, in on_pre_enter
 show_view = self.ids.rc_display
File "kivy\properties.pyx", line 839, in kivy.properties.ObservableDict.__getattr__ (kivy\properties.c:12654)
AttributeError: 'super' object has no attribute '__getattr__'

If I add the widgets directly to the Screen object they display, but are on top of each other. Its only when I try and reference the id I get an error.

I have even printed to the console the list of ids, and they are there as expected.


Solution

  • The problem is caused because the on_pre_enter event is done before adding the BoxLayout so it does not exist, a possible solution is to use Clock:

    class MainScreen(Screen):
        def __init__(self, *args, **kwargs):
            Screen.__init__(self, *args, **kwargs)
            Clock.schedule_once(self.finished_init)
    
        def finished_init(self, *args):
            show_view = self.ids.rc_display # error here
            show_view.clear_widgets()
            buttons = BoxLayout(orientation = 'vertical')
            pr = requests.get('http://127.0.0.1:5000/stageplanning/api/v1.0/shows')
            for show in pr.json():
                buttons.add_widget(Button(text = show['show_title']))
    
            show_view.add_widget(buttons) 
    

    Another option is to define it in the BoxLayout constructor:

    *.py

    class MainBoxLayout(BoxLayout):
        def __init__(self, *args, **kwargs):
            BoxLayout.__init__(self, *args, **kwargs)
            self.clear_widgets()
            buttons = BoxLayout(orientation = 'vertical')
            pr = requests.get('http://127.0.0.1:5000/stageplanning/api/v1.0/shows')
            for show in pr.json():
                buttons.add_widget(Button(text = show['show_title']))
            self.add_widget(buttons) 
    

    *.kv

    ScreenManager:
        MainScreen:
        LoginScreen:
    
    <MainScreen>:
        name: 'MainScreen'
        id: ms
    
        MainBoxLayout:
            id: rc_display
            orientation: "vertical"
            padding: 10
            spacing: 10
    
    <LoginScreen>:
        name: 'LoginScreen'
        id: ls
    
        Button:
            on_release: app.root.current
            text: 'Click to Login'
            font_size: 20