You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Kivy应用API调用异常:加载界面切换与AttributeError求助

Kivy应用界面切换与API加载错误修复方案

错误根源分析

从报错信息和代码来看,问题出在四个地方:

  • ScreenManager中错误将LoadingScreen注册为ContentScreen实例,导致界面逻辑混乱
  • KV文件中ContentScreen的布局缩进错误,内部控件的id无法被Screen实例捕获
  • 同步API调用阻塞UI线程,LoadingScreen来不及渲染就完成跳转
  • clear_input方法错误访问root的ids,应该直接获取MainScreen实例的控件

修复步骤

1. 修正ScreenManager的屏幕注册

在app.py的build方法中,把loading_screen的实例从ContentScreen改成LoadingScreen:

def build(self):
    sm = ScreenManager()
    sm.add_widget(MainScreen(name='main_screen'))
    sm.add_widget(ContentScreen(name='content_screen'))
    # 此处修复:把ContentScreen改为LoadingScreen
    sm.add_widget(LoadingScreen(name='loading_screen'))
    return sm

2. 修复KV文件中ContentScreen的缩进

ScrollView作为ContentScreen的子控件,必须缩进对齐,否则内部控件的id不会被Screen识别:

<ContentScreen>:
    # 此处添加缩进,ScrollView是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}

3. 改为异步API调用避免阻塞UI

同步的requests.post会卡住UI线程,导致LoadingScreen无法显示。改用线程处理API请求,通过Clock更新UI:
在app.py中导入threading和kivy.clock.Clock:

import threading
from kivy.clock import Clock

修改load_content方法:

def load_content(self, query):
    def fetch_data():
        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)
        
        # 用Clock调度UI更新操作,确保在主线程执行
        if response.status_code == 200:
            res_data = response.json()
            Clock.schedule_once(lambda dt: self.update_content_screen(res_data['data']))
        else:
            print('Failed to retrieve data:', response.status_code)
            # 加载失败也切换回主界面
            Clock.schedule_once(lambda dt: setattr(self.root, 'current', 'main_screen'))
    
    # 启动线程执行API调用
    threading.Thread(target=fetch_data).start()

def update_content_screen(self, content_text):
    # 更新ContentScreen的内容并切换界面
    self.root.current = 'content_screen'
    self.root.get_screen('content_screen').ids.content_label.text = content_text

4. 修复clear_input方法的控件访问

clear_input方法需要获取MainScreen实例,再访问其内部的content输入框:

def clear_input(self):
    # 获取MainScreen实例,再访问其ids下的content
    main_screen = self.root.get_screen('main_screen')
    main_screen.ids.content.text = ''

完整修改后的代码

app.py

from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
import requests
import threading
from kivy.clock import Clock

Builder.load_file('mainscreen.kv')


class LoadingScreen(Screen):
    pass


class ContentScreen(Screen):
    pass


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(LoadingScreen(name='loading_screen'))
        return sm

    def clear_input(self):
        main_screen = self.root.get_screen('main_screen')
        main_screen.ids.content.text = ''

    def load_content(self, query):
        def fetch_data():
            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:
                res_data = response.json()
                Clock.schedule_once(lambda dt: self.update_content_screen(res_data['data']))
            else:
                print('Failed to retrieve data:', response.status_code)
                Clock.schedule_once(lambda dt: setattr(self.root, 'current', 'main_screen'))
        
        threading.Thread(target=fetch_data).start()

    def update_content_screen(self, content_text):
        self.root.current = 'content_screen'
        self.root.get_screen('content_screen').ids.content_label.text = content_text


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

mainscreen.kv

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

        Image:
            source: 'images/icon.png'
            size_hint: (1, 1)

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

        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...'
            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}

内容的提问来源于stack exchange,提问作者Graham Patrick

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 03:29:50