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

Kivy/KivyMD中如何从另一个类设置类变量以更新按钮文本?

问题描述

在Kivy/KivyMD项目中尝试从另一个类设置按钮文本失败。查看set_people函数,使用PosScreen.people = name修改变量后按钮文本并未改变,尝试通过ids访问组件但不知道如何从其他类实现。

代码片段

test.py

from kivy.properties import StringProperty
from kivy.uix.boxlayout import BoxLayout
from kivymd.app import MDApp
from kivy.uix.screenmanager import ScreenManager, Screen
from kivymd.uix.button import MDFlatButton
from kivymd.uix.dialog import MDDialog

pos_dialog = None

class PosScreen(Screen):
    people = StringProperty("")

    def __init__(self, **kwargs):
        super(PosScreen, self).__init__(**kwargs)

    def on_pre_enter(self, *args):
        self.people = "No one"

    def close_dialog(self, *obj):
        global pos_dialog
        pos_dialog.dismiss()
        pos_dialog = None

    def open_people_dialog(self):
        global pos_dialog
        if not pos_dialog:
            pos_dialog = MDDialog(
                title="Choose customer",
                type="custom",
                content_cls=DialogContentPosAddPeople(),
                buttons=[
                    MDFlatButton(
                        text="Close",
                        theme_text_color="Custom",
                        on_release=self.close_dialog
                    )
                ]
            )
        pos_dialog.open()


class DialogContentPosAddPeople(BoxLayout):
    def __init__(self, **kwargs):
        super(DialogContentPosAddPeople, self).__init__(**kwargs)
        self.set_list_people()

    def set_list_people(self, text="", search=False):
        peeps = ["Brian", "AJ", "Nick", "Kevin", "Howie"]

        def add_name_item(name):
            self.ids.rv.data.append(
                {
                    "viewclass": "OneLineListItem",
                    "text": name,
                    "on_release": lambda x=name: self.set_people(x),
                }
            )

        self.ids.rv.data = []
        for name in peeps:
            if search:
                if text.lower() in name.lower():
                    add_name_item(name)
            else:
                add_name_item(name)

    def set_people(self, name):
        global pos_dialog
        PosScreen.people = name # it changes the variable but button text doesn't change
        PosScreen.ids.people_button.text = name # Not accessible NoneType
        pos_dialog.dismiss()
        pos_dialog = None
        return


class testApp(MDApp):

    def build(self):
        self.theme_cls.primary_palette = "Teal"

        sm = ScreenManager()
        sm.add_widget(PosScreen(name="pos"))
        return sm


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

test.kv

PosScreen:

<PosScreen>:
    name: "pos"
    people_button: people_button
    canvas.before:
        Color:
            rgba: 72/255, 72/255, 72/255, 1
        Rectangle:
            pos: self.pos
            size: self.size
    MDBoxLayout:
        orientation: "vertical"
        RelativeLayout:
            orientation: "vertical"
            MDRectangleFlatIconButton:
                id: people_button
                icon: "account-circle-outline"
                text: root.people
                pos_hint:{'x': 0.05, 'center_y': 0.9}
                md_bg_color: 0, 0, 0, 1
                on_release: root.open_people_dialog()


<DialogContentPosAddPeople>:
    rv: rv
    orientation: "vertical"
    spacing: dp(10)
    padding: dp(20)
    size_hint_y: None
    height: "240dp"
    MDBoxLayout:
        adaptive_height: True
        MDIconButton:
            icon: 'magnify'
        MDTextField:
            id: search_field
            hint_text: 'Search name'
            on_text: root.set_list_people(self.text, True)
    RecycleView:
        id: rv
        key_viewclass: 'viewclass'
        key_size: 'height'
        RecycleBoxLayout:
            padding: dp(10)
            default_size: None, dp(48)
            default_size_hint: 1, None
            size_hint_y: None
            height: self.minimum_height
            orientation: 'vertical'

预期效果

点击列表中的任意名称,关闭对话框并将按钮文本改为所选名称。


解决方案

问题核心是你直接操作了类属性而非实例属性,且没有获取到PosScreen的实例引用,以下是具体修复步骤:

1. 传递PosScreen实例给对话框内容类

修改PosScreen的open_people_dialog方法,创建DialogContentPosAddPeople时传入当前屏幕实例:

def open_people_dialog(self):
    global pos_dialog
    if not pos_dialog:
        pos_dialog = MDDialog(
            title="Choose customer",
            type="custom",
            # 传入当前PosScreen实例
            content_cls=DialogContentPosAddPeople(pos_screen=self),
            buttons=[
                MDFlatButton(
                    text="Close",
                    theme_text_color="Custom",
                    on_release=self.close_dialog
                )
            ]
        )
    pos_dialog.open()

2. 修改DialogContentPosAddPeople接收实例

在DialogContentPosAddPeople的初始化方法中保存传入的屏幕实例:

class DialogContentPosAddPeople(BoxLayout):
    def __init__(self, pos_screen, **kwargs):
        self.pos_screen = pos_screen
        super(DialogContentPosAddPeople, self).__init__(**kwargs)
        self.set_list_people()

3. 修改set_people方法操作实例属性

通过保存的实例修改people属性,Kivy的StringProperty会自动同步更新UI:

def set_people(self, name):
    global pos_dialog
    # 修改实例的people属性,而非类属性
    self.pos_screen.people = name
    pos_dialog.dismiss()
    pos_dialog = None

无效原因说明

  • PosScreen.people = name修改的是类的静态属性,而绑定UI的是当前屏幕实例的people属性,所以不会触发UI更新。
  • PosScreen.ids不存在,ids是每个组件实例独有的属性,必须通过实例才能访问。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 15:40:29