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

如何通过ScreenManager从.py动态修改Kivy .kv中的MDLabel文本?

解决Kivy多屏幕应用访问控件属性的AttributeError问题+搜索功能实现

错误原因

  • 你在kv文件中用name标识控件,但Kivy只有通过id定义的控件才会被纳入对应屏幕的ids字典,这是触发AttributeError的核心原因。
  • 你将sm(ScreenManager)设为类属性,可能引发多实例共享的潜在问题,建议改为实例属性。

修正步骤

1. 修正kv文件的控件标识

把SearchScreen里MDLabel和MDTextField的name改为id:

<SearchScreen>:
    name: 'searchScreen'

    MDLabel:
        id: searchResult  # 替换原name属性为id
        text: 'Lorem Ipsum Dolor'
        size_hint: 0.8, None
        pos_hint: {'center_x': 0.5, 'top': 0.8}

    MDTextField:
        id: searchText  # 替换原name属性为id
        pos_hint: {'center_x': 0.5, 'top': 1}
        size_hint: 0.8, None
        hint_text: 'Search for...'
        icon_left: 'magnify'
        on_text: app.search_something(self.text)

2. 调整ScreenManager的初始化方式

将sm从类属性改为实例属性,避免共享问题:

class FoodApp(MDApp):

    searchCache = []

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.sm = WindowManager()  # 移至__init__中作为实例属性

3. 正确访问控件并实现搜索功能

修改search_something方法,实现文本更新和动态创建结果标签(最多20个):

def search_something(self, text):
    results = []
    for name in self.searchCache:
        if text.lower() in name.lower():
            results.append(name)
    
    # 获取SearchScreen实例
    search_screen = self.sm.get_screen('searchScreen')
    
    # 更新搜索结果提示文本
    if results:
        search_screen.ids.searchResult.text = f"找到{len(results)}条匹配结果"
    else:
        search_screen.ids.searchResult.text = "未找到任何匹配结果"
    
    # 清空旧结果
    search_screen.ids.results_container.clear_widgets()
    
    # 动态添加最多20个结果标签
    for result in results[:20]:
        result_label = MDLabel(text=result, size_hint_y=None, height=40)
        search_screen.ids.results_container.add_widget(result_label)

4. 给SearchScreen添加结果容器

在kv文件的SearchScreen中加入用于承载动态结果的BoxLayout:

<SearchScreen>:
    name: 'searchScreen'

    MDLabel:
        id: searchResult
        text: 'Lorem Ipsum Dolor'
        size_hint: 0.8, None
        pos_hint: {'center_x': 0.5, 'top': 0.8}

    # 新增结果容器
    BoxLayout:
        id: results_container
        orientation: 'vertical'
        size_hint: 0.8, None
        height: self.minimum_height
        pos_hint: {'center_x': 0.5, 'top': 0.7}
        spacing: 5

    # 其他控件保持不变
    MDTextField:
        id: searchText
        pos_hint: {'center_x': 0.5, 'top': 1}
        size_hint: 0.8, None
        hint_text: 'Search for...'
        icon_left: 'magnify'
        on_text: app.search_something(self.text)

    MDIconButton:
        icon: 'close'
        pos_hint: {'right': 0.99, 'top': 0.99}
        on_release:
            root.manager.transition.direction = 'up'
            root.manager.current = 'welcomeScreen'

完整修正代码

Python文件

from kivymd.app import MDApp
from kivymd.uix.screenmanager import MDScreenManager
from kivymd.uix.screen import MDScreen
from kivymd.uix.label import MDLabel
import requests


class WelcomeScreen(MDScreen):
    pass


class SearchScreen(MDScreen):
    pass


class WindowManager(MDScreenManager):
    pass


class FoodApp(MDApp):

    searchCache = []

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.sm = WindowManager()

    def magnify_callback(self):
        self.sm.transition.direction = "down"
        self.sm.current = "searchScreen"

    def search_something(self, text):
        results = []
        for name in self.searchCache:
            if text.lower() in name.lower():
                results.append(name)
        
        search_screen = self.sm.get_screen('searchScreen')
        # 更新结果提示
        if results:
            search_screen.ids.searchResult.text = f"找到{len(results)}条匹配结果"
        else:
            search_screen.ids.searchResult.text = "未找到任何匹配结果"
        
        # 清空旧结果
        search_screen.ids.results_container.clear_widgets()
        
        # 添加新结果(最多20条)
        for result in results[:20]:
            result_label = MDLabel(text=result, size_hint_y=None, height=40)
            search_screen.ids.results_container.add_widget(result_label)

    def fetch_searchindex(self):
        try:
            request = requests.get("https://api.sampleapis.com/beers/ale")
            request.raise_for_status()
            data = request.json()
            for obj in data:
                self.searchCache.append(obj["name"])
        except Exception as e:
            print(f"获取数据失败: {e}")

    def build(self):
        self.sm.add_widget(WelcomeScreen(name='welcomeScreen'))
        self.sm.add_widget(SearchScreen(name='searchScreen'))
        self.fetch_searchindex()
        return self.sm


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

kv文件

WindowManager:
    WelcomeScreen:
    SearchScreen:

<WelcomeScreen>:
    name: 'welcomeScreen'

    MDTopAppBar:
        title: 'Welcome!'
        right_action_items: [['magnify', lambda x: app.magnify_callback()]]

<SearchScreen>:
    name: 'searchScreen'

    MDLabel:
        id: searchResult
        text: 'Lorem Ipsum Dolor'
        size_hint: 0.8, None
        pos_hint: {'center_x': 0.5, 'top': 0.8}

    BoxLayout:
        id: results_container
        orientation: 'vertical'
        size_hint: 0.8, None
        height: self.minimum_height
        pos_hint: {'center_x': 0.5, 'top': 0.7}
        spacing: 5

    MDTextField:
        id: searchText
        pos_hint: {'center_x': 0.5, 'top': 1}
        size_hint: 0.8, None
        hint_text: 'Search for...'
        icon_left: 'magnify'
        on_text: app.search_something(self.text)

    MDIconButton:
        icon: 'close'
        pos_hint: {'right': 0.99, 'top': 0.99}
        on_release:
            root.manager.transition.direction = 'up'
            root.manager.current = 'welcomeScreen'

关键说明

  • id是Kivy中控件唯一能被ids字典识别的标识,name仅用于标记Screen,二者不可混用。
  • 将ScreenManager改为实例属性可避免多App实例共享同一管理器的问题。
  • 用BoxLayout作为结果容器,通过minimum_height自动适配高度,确保所有结果标签正常显示。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 11:17:15