I am writing a Kivy project where my code will dynamically set the text
property within a Screen
class. Below is a MRE that recreates the problem, but my real code is much more complicated.
When I first wrote it, I tried something like self.ids.my_label.text = 'This no worky'
but that threw an error:
AttributeError: 'super' object has no attribute 'getattr'
I did a print(self.ids)
and it is empty. Based on some other SO posts (such as here) I added the line s = self.root.get_screen('screen_one')
but that also throws an error:
AttributeError: 'ScreenOne' object has no attribute 'root'
I'm assuming the problem is that I'm calling the ids
within the screen class, whereas every example I am finding is doing it from some other spot. I sort of get that calling self.root
doesn't work because the ids
doesn't live within the root, but every combo I could think of also doesn't work. I also tried this solution, but either I didn't implement it right or it isn't my problem.
What's the solution?
import kivy
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
class ScreenOne(Screen):
def __init__(self, **kwargs):
super().__init__(**kwargs)
s = self.root.get_screen('screen_one')
s.ids.my_label.text = 'This no worky'
class ScreenTwo(Screen):
pass
class ScreenThree(Screen):
pass
class MyScreenMgr(ScreenManager):
pass
class MultiApp(App):
def build(self):
return MyScreenMgr()
sample_app = MultiApp()
sample_app.run()
And multi.kv:
#:import NoTransition kivy.uix.screenmanager.NoTransition
<MyScreenMgr>:
transition: NoTransition()
ScreenOne:
ScreenTwo:
ScreenThree:
<ScreenOne>:
name:'screen_one'
BoxLayout:
Label:
id: my_label
Button:
text: "Go to Screen 2"
background_color : 0, 0, 1, 1
on_press:
root.manager.current = 'screen_two'
<ScreenTwo>:
name:'screen_two'
BoxLayout:
Button:
text: "Go to Screen 3"
background_color : 1, 1, 0, 1
on_press:
root.manager.current = 'screen_three'
<ScreenThree>:
name:'screen_three'
BoxLayout:
Button:
text: "Go to Screen 1"
background_color : 1, 0, 1, 1
on_press:
root.manager.current = 'screen_one'
The problem is that the ids
of a widget are not set until after __init__()
is executed. So the solution is to delay using the ids
. Here is a modified version of your ScreenOne
class that does this:
class ScreenOne(Screen):
def __init__(self, **kwargs):
super().__init__(**kwargs)
Clock.schedule_once(self.set_text)
def set_text(self, dt):
self.ids.my_label.text = 'This worky'