I am at sea again in trying to refer to widgets both within and outside of the class in which they’re created. I was able to do so before I incorporated screens into my program, having been taught to use the App.get_running_app() method, but that doesn’t seem to work now. In this scaled-down code example, I use two screens, one created within the Python code and one in a kv file. I’ve highlighted three examples of the problem I’m having.
Hoping for guidance. Â Thanks.
Â
kv file:
 <CustButton@Button>:
   font_size: 40
   color: 0, 0, 0, 1
   background_normal: ''
   background_color: .3, .3, 1,1
<FirstScreen>:
   BoxLayout:
       orientation: 'vertical'
       BoxLayout:
           padding: 200
           CustButton:
               id: switch_button
               text: "screen switch
button"
               on_press:
root.manager.current = 'second'
Â
Python:
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.dropdown import DropDown
from kivy.uix.button import Button
from kivy.uix.label import Label
from kivy.uix.textinput import TextInput
Â
Â
user_list = ['alpha','beta','gamma']
class SecondScreen(Screen):
   def __init__(self, **kwargs):
       super(SecondScreen, self).__init__(**kwargs)
       b = BoxLayout(orientation='vertical', padding=500)
       t = TextInput(id='my_text', height='60dp', cursor_width="2sp")
       dropdown = DropDown()
       for index in range(len(user_list)):
           btn = Button(text=user_list[index], size_hint_y=None, height=44)
           btn.bind(on_release=lambda btn: dropdown.select(btn.text))
           dropdown.add_widget(btn)
       mainbutton = Button(text='Names')
       mainbutton.bind(on_release=dropdown.open)
       dropdown.bind(on_select=lambda instance, x: setattr(t, 'text', x))
       b.add_widget(t)
       b.add_widget(mainbutton)
       l=Label(height='60dp') #spacer
       b.add_widget(l)
       j= Button(text ="switch back")
       #j.bind(on_release=outside_function())
       j.bind(on_release=self.switcher)
       b.add_widget(j)
       self.add_widget(b)
Â
   def switcher(self,obj):
       global user
       self.manager.current = 'first'
       # 1) If I try this:
       user = self.t.text
       # I get: AttributeError: 'SecondScreen' object has no attribute 't'
Â
       # 2) If I try this:
       app_ref = App.get_running_app()
       user = app_ref.root.ids.t.text
       # I get: AttributeError: 'super' object has no attribute '__getattr__'
Â
Â
# 3) If I call this function from within SecondScreen class and try to refer to a widget:
def outside_function():
   app_ref = App.get_running_app()
   app_ref.root.ids.switch_button.disabled = True
   # I get: AttributeError: 'NoneType' object has no attribute 'ids'
Â
Â
class FirstScreen(Screen):
   pass
Â
class TestApp(App):
   def build(self):
       sm = ScreenManager()
       sm.add_widget(FirstScreen(name='first'))
       sm.add_widget(SecondScreen(name='second'))
       return sm
Â
if __name__ == '__main__':
   TestApp().run()
user_list = []
try:
with open('user.dat', 'rb') as f:
user_data = pickle.load(f)
for i in user_data:
user_list.append(i[0])
except:
user_list = []
class SecondScreen(Screen):
def __init__(self, **kwargs):
super(SecondScreen, self).__init__(**kwargs)
b = BoxLayout(orientation = 'vertical', padding = '100dp')
t = TextInput(id = 'my_text', size_hint_y = None, halign = 'middle', valign = 'middle', height = '60dp')... (more stuff here)
       for index in range(len(user_list)):
       self.btns = [Button(text=user_list[index], size_hint_y=None, height=44) for index in range(len(user_list)]
       [drop_down.add_widget(btn) for btn in self.btns]
Now you should be able to access those buttons via self.manager.current_screen.btns[0] etc. There are other ways of course, but that should work?
Thanks for your help. What I want is to be able to refer to *any* widget from *any* part of my program, for example, get the text in t, the text widget, regardless of which method is referring to it, whether the method is in the current screen's class or that of another screen, or outside of both. I'd like to be able to do this from any method or function anywhere in the program. This seems fundamental.Before I introduced screens, I was able to do all this with the App.get_running_app() method.  (So, to get the text in t:  user = app_ref.root.ids.t.text). I don't know why this doesn't work now that my classes are within screens (if that's the right way to phrase it). But I'd happily accept anything equivalent!
--
You received this message because you are subscribed to the Google Groups "Kivy users support" group.
To unsubscribe from this group and stop receiving emails from it, send an email to kivy-users+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
To unsubscribe from this group and stop receiving emails from it, send an email to kivy-users+...@googlegroups.com.
To unsubscribe from this group and stop receiving emails from it, send an email to kivy-users+...@googlegroups.com.
main = None
class ButtonOutsideRoot(Button):
   global main
   def callback_btn_outsideroot(self):                              # no args given so the reference to the root interface(ScreenManager) has has to be found otherwise
       if self.text == 'btn2':                                                                     # self refers to the calling class automatically
           main.ids.statlabel.text = 'referenced by global main %s' %self.text           Â
          Â
       elif self.text == 'btn3':           Â
           App.get_running_app().root.ids.statlabel.text = 'referenced by App.get_running_app() %s' %self.text
       if self.text == 'btn4':
           self.parent.parent.parent.ids.statlabel.text = 'refereced by kivy tree %s' %self.text
 Â
class MainInterface(ScreenManager):
  Â
   def callback_btn_insideroot(self,btn):
       if btn.text == 'btn1':             Â
           self.ids.statlabel.text = 'self referecenced by callback %s' %btn.text
           print untersuche(self)
I’ve made good progress, thanks to numerous helpful explanations--I (believe I) can now access any widget from any class.
I have a new problem, though. The code below is a minimal version of my program. I can get either of its two screens to display by setting current screen in the app build. And if I start with SecondScreen, I can switch to FirstScreen. However, I can’t go the other direction—If I start with FirstScreen and its button is clicked, only a black screen appears. What do I have wrong here? (FirstScreen has to be constructed within the Python code rather than in the kv file, because in the actual program I load the button texts from a file.)Â
Another, less serious problem: Â If I start with FirstScreen, the other one (SecondScreen) appears for a fraction of a second before FIrstScreen is displayed. Â Maybe related to the first problem? Â Thanks for any edification.
import kivy
kivy.require('1.9.0')from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.dropdown import DropDown
from kivy.uix.button import Button
from kivy.uix.label import Label
from kivy.uix.textinput import TextInput
from kivy.lang import Builder
Builder.load_string('''
<CustButton@Button>:
font_size: 40
color: 0, 0, 0, 1
background_normal: ''
background_color: .3, .3, 1,1
height: "130dp"
size_hint_y: None
<MainInterface>:
padding: 50
switch_button: switch_button
exit_button: exit_button
SecondScreen:
BoxLayout:
spacing: 20
CustButton:
id: switch_button
text: "Switch to FirstScreen"
on_release: root.current = "first"
CustButton:
id: exit_button
background_color: [.71, .086, .075,1]
text: "Exit"
on_release: app.stop()
''')
class MainInterface(ScreenManager):
pass
class SecondScreen(Screen):
pass
class FirstScreen(Screen):
def __init__(self, **kwargs):
super(FirstScreen, self).__init__(**kwargs)
players_list = ['alpha', 'beta', 'gamma']
b = BoxLayout(orientation='vertical', padding=500)
self.txt = TextInput(id='my_text', height='60dp', cursor_width="2sp")
dropdown = DropDown()
for index in range(len(players_list)):
btn = Button(text=players_list[index], size_hint_y=None, height=44)
btn.bind(on_release=lambda btn: dropdown.select(btn.text))
dropdown.add_widget(btn)
mainbutton = Button(text='Names')
mainbutton.bind(on_release=dropdown.open)
dropdown.bind(on_select=lambda instance, x: setattr(self.txt, 'text', x))
b.add_widget(self.txt)
b.add_widget(mainbutton)
l=Label(text = '')
b.add_widget(l)
j= Button(text ="switch to SecondScreen")
j.bind(on_release=self.fetch_name)
b.add_widget(j)
self.add_widget(b)
def fetch_name(self,obj):
app_ref = App.get_running_app()
app_ref.root.ids.switch_button.text = self.txt.text
main.current = 'second'
class MinimalApp(App):
def build(self):
global main
main = MainInterface()
main.add_widget(FirstScreen(name='first'))
main.add_widget(SecondScreen(name='second'))
main.current = 'first'
return main
if __name__ == '__main__':
MinimalApp().run()