Search code examples
pythonpython-3.xkivyscreenkivy-language

Kivy App with API Call: Loading Screen Transition and Error


I’m working on a Kivy app with three screens: MainScreen, LoadingScreen, and ContentScreen. Here’s what I’m trying to achieve:

  1. On the MainScreen, I have a “Create” button.

  2. When the button is pressed, I want to display a loading screen (LoadingScreen).

  3. After making an API call, if I receive a successful response (status code 200), I want to transition to the ContentScreen and display the retrieved data.

However, I’m encountering an error.

 Traceback (most recent call last):
   File "kivy\\properties.pyx", line 961, in kivy.properties.ObservableDict.__getattr__
 KeyError: 'content_label'

 During handling of the above exception, another exception occurred:

 Traceback (most recent call last):
   File "C:\Users\graha\Documents\GitHub\kviy-skylr\app.py", line 59, in <module>
     SkylrApp().run()
   File "C:\Users\graha\Documents\GitHub\kviy-skylr\.venv\Lib\site-packages\kivy\app.py", line 956, in run
     runTouchApp()
   File "C:\Users\graha\Documents\GitHub\kviy-skylr\.venv\Lib\site-packages\kivy\base.py", line 574, in runTouchApp
     EventLoop.mainloop()
   File "C:\Users\graha\Documents\GitHub\kviy-skylr\.venv\Lib\site-packages\kivy\base.py", line 339, in mainloop
     self.idle()
   File "C:\Users\graha\Documents\GitHub\kviy-skylr\.venv\Lib\site-packages\kivy\base.py", line 383, in idle
     self.dispatch_input()
   File "C:\Users\graha\Documents\GitHub\kviy-skylr\.venv\Lib\site-packages\kivy\base.py", line 334, in dispatch_input
     post_dispatch_input(*pop(0))
   File "C:\Users\graha\Documents\GitHub\kviy-skylr\.venv\Lib\site-packages\kivy\base.py", line 302, in post_dispatch_input
     wid.dispatch('on_touch_up', me)
   File "kivy\\_event.pyx", line 731, in kivy._event.EventDispatcher.dispatch
   File "C:\Users\graha\Documents\GitHub\kviy-skylr\.venv\Lib\site-packages\kivy\uix\behaviors\button.py", line 179, in on_touch_up
     self.dispatch('on_release')
   File "kivy\\_event.pyx", line 727, in kivy._event.EventDispatcher.dispatch
   File "kivy\\_event.pyx", line 1307, in kivy._event.EventObservers.dispatch
   File "kivy\\_event.pyx", line 1191, in kivy._event.EventObservers._dispatch
   File "C:\Users\graha\Documents\GitHub\kviy-skylr\.venv\Lib\site-packages\kivy\lang\builder.py", line 60, in custom_callback
     exec(__kvlang__.co_value, idmap)
   File "C:\Users\graha\Documents\GitHub\kviy-skylr\mainscreen.kv", line 30, in <module>
     app.load_content(content.text)
   File "C:\Users\graha\Documents\GitHub\kviy-skylr\app.py", line 53, in load_content
     app.root.get_screen('content_screen').ids.content_label.text = data['data']
     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   File "kivy\\properties.pyx", line 964, in kivy.properties.ObservableDict.__getattr__
 AttributeError: 'super' object has no attribute '__getattr__'. Did you mean: '__setattr__'?

Here is my KV file:"

<MainScreen>:
    BoxLayout:
        orientation: 'vertical'
        padding: 10
        spacing: 10

        # Logo at the top
        Image:
            source: 'images/icon.png'
            size_hint: (1, 1)

        # recipe input field
        BoxLayout:
            size_hint_y: None
            height: '30'
            TextInput:
                id: content
                multiline: False
                hint_text: 'What would you like to make?'

        # Button to submit the form
        Button:
            text: 'Create'
            size_hint:(None, None)
            size:(86, 34)
            background_color: (1.0, 0.0, 0.0, 1.0)
            font_size:'16'
            on_release:
                app.root.current = 'loading_screen'
                app.load_content(content.text)
            pos_hint: {"center_x":0.5, "center_y":0.5}

<LoadingScreen>:
    BoxLayout:
        orientation: 'vertical'
        Label:
            text: 'Loading...'  # Customize as needed
            font_size: '20'
            size_hint_y: None
            height: '50'


<ContentScreen>:
ScrollView:
    do_scroll_x: False
    do_scroll_y: True
    BoxLayout:
        orientation: 'vertical'
        padding: 10
        spacing: 10
        Label:
            id: content_label
            pos_hint: {"center_x": 0.5, "center_y": 0.5}
            font_size: '16'
            text_size: self.width, None
        Button:
            text: 'Back'
            size_hint:(None, None)
            size:(86, 34)
            background_color: (1.0, 0.0, 0.0, 1.0)
            font_size:'16'
            on_release:
                app.root.current = 'main_screen'
                app.clear_input()
            pos_hint: {"center_x": 0.5, "center_y": 0.5}

and my app.py:

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

Builder.load_file('mainscreen.kv')


class LoadingScreen(Screen):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)


class ContentScreen(Screen):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)


class MainScreen(Screen):
    pass


class SkylrApp(App):
    def build(self):
        sm = ScreenManager()
        sm.add_widget(MainScreen(name='main_screen'))
        sm.add_widget(ContentScreen(name='content_screen'))
        sm.add_widget(ContentScreen(name='loading_screen'))
        return sm

    def clear_input(self):
        # Access the input field by its ID and clear its text
        self.root.ids.content.text = ''

    def load_content(self, query):
        url = 'http://127.0.0.1:5000/recipes/ask_mobile'
        data = {
            'content': query
        }
        headers = {
            'Content-Type': 'application/json'
        }
        response = requests.post(url, json=data, headers=headers)

        if response.status_code == 200:
            data = response.json()
            app = App.get_running_app()
            app.root.current = 'content_screen'
            app.root.get_screen('content_screen').ids.content_label.text = data['data']
        else:
            print('Failed to retrieve data:', response.status_code)


if __name__ == '__main__':
    SkylrApp().run()

Solution

  • You have a typo in your build() method. Try changing:

    sm.add_widget(ContentScreen(name='loading_screen'))
    

    to:

    sm.add_widget(LoadingScreen(name='loading_screen'))