Search code examples
pythonkivy

Referencing a variable from a different screen in kivy


I need to reference the variable name_input in TalkBot screen. Here is the code:

import kivy
kivy.require('2.2.1')
import pyttsx3
from kivymd.app import MDApp
from kivy.uix.screenmanager import Screen, ScreenManager, FadeTransition
from kivymd.uix.boxlayout import MDBoxLayout
from kivy.uix.video import Video
from kivy.uix.image import Image
from kivy.uix.textinput import TextInput
from kivymd.uix.button import MDRaisedButton
from kivymd.uix.label import MDLabel


class homeScreen(Screen):
    def __init__(self, **kwa):
        super().__init__(**kwa)

#bacgkround gradient
        bgbox = MDBoxLayout()
        bgbox.add_widget(Image(source='background.png', allow_stretch=True, keep_ratio=False, size_hint_y=1))
        self.add_widget(bgbox)

#page title
        topic = MDLabel(text="Personal Information", font_style="H4", pos_hint = {'x': 0.03, 'y': 0.45})
        topic_info = MDLabel(text="Please input the following information. This will be used to generate your report", font_style="H6", pos_hint = {'x':0.06, 'y':0.37})
#name input
        name_prompt = MDLabel(text="Name:", pos_hint = {'x': 0.1, 'y': 0.3}, _text_color_str="white")
        name_input = TextInput(size_hint_x = 0.3, size_hint_y = 0.05, pos_hint = {'x':0.1, 'y': 0.73}, multiline = False)        #I want to reference this variable in talkbot screen

        self.add_widget(topic)
        self.add_widget(topic_info)
        self.add_widget(name_prompt)
        self.add_widget(name_input)


class TalkBot(Screen):
    def __init__(self, **kwa):
        super(TalkBot, self).__init__(**kwa)

        hs = self.manager.get_screen('homeScreen') #need to reference here but the error
        name = hs.name_input.text()         

        bgimg = Image(source="background.png", allow_stretch=True, keep_ratio=False, size_hint_y=1)
        self.add_widget(bgimg)

        def playvid(self):
            bgvid = Video(source="speechvid.avi", allow_stretch=True, keep_ratio=False, size_hint_y=1, state="play")
            box.add_widget(bgvid)

               
            engine=pyttsx3.init()
            engine.say(f"Hello {name}, welcome to Mind Magic!")
            print(name)
            engine.runAndWait()

        btn = MDRaisedButton(text="start", pos_hint={'x':0.49, 'y':0.49}, on_release=playvid)
        self.add_widget(btn)

        box=MDBoxLayout()
        self.add_widget(box)


class MindMagic(MDApp):
    def build(self):
        self.theme_cls.material_style = "M3"
        self._app_name = "MindMagic"
        self.icon = "logo.png"
        sm = ScreenManager(transition = FadeTransition())
        sm.add_widget(winvid(name="winvid"))
        sm.add_widget(homeScreen(name="homeScreen"))
        sm.add_widget(TalkBot(name="talkbot"))
        return sm

if __name__ == "__main__":
    MindMagic().run()

I have tried some solutions, and logically I think self.manager.get_screen should work. I have also tried MDApp.get_running_app().root. But both throw the error AttributeError: 'NoneType' object has no attribute 'get_screen'


Solution

  • The kivy widget tree is not accessible at the time the objects get initialized. Your options are:

    1. Move all operations that must acces the widget tree into a separate function and set it to execute on first render with Clock.schedule_once:

    class TalkBot(Screen):
        def __init__(self, **kwa):
            super(TalkBot, self).__init__(**kwa)
            Clock.schedule_once(self.init)
    
        def init(self, _):
            hs = self.manager.get_screen('homeScreen')  # need to reference here but the error
            name = hs.name_input.text
    
            bgimg = Image(source="background.png", allow_stretch=True, keep_ratio=False, size_hint_y=1)
            self.add_widget(bgimg)
    
            def playvid(self):
                bgvid = Video(source="speechvid.avi", allow_stretch=True, keep_ratio=False, size_hint_y=1, state="play")
                box.add_widget(bgvid)
    
                engine = pyttsx3.init()
                engine.say(f"Hello {name}, welcome to Mind Magic!")
                print(name)
                engine.runAndWait()
    
            btn = MDRaisedButton(text="start", pos_hint={'x': 0.49, 'y': 0.49}, on_release=playvid)
            self.add_widget(btn)
    
            box = MDBoxLayout()
            self.add_widget(box)
    

    (Note that this was still crashing for me at first because name_input is not assigned to an instance variable in homeScreen and because the TextInput's text is an attribute and not a callable and you were using hs.name_input.text().)

    2. Initialize TalkBot with self.name = None, then set it to the homeScreen's current value whenever you do the screen transition:

    class homeScreen(Screen):
        ...
        def go_to_talkbot_screen(self):
            self.manager.get_screen("talkbot").name = self.name_input.text
            self.manager.current = "talkbot"