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

如何从Kivy的Spinner获取变量并在RecycleView中使用?

问题分析与解决方案

你的核心问题在于:

  • RecycleView的data仅在初始化时加载一次,Spinner选择变化后不会自动更新
  • Spinner的文本是大写,但freezer函数的字典键是小写,存在大小写不匹配
  • 未正确绑定Spinner的选择值到可监听的Kivy属性,导致无法联动更新RecycleView

修正步骤

1. 修正MyLayout的属性定义

将label_current定义为StringProperty,确保它是可监听的Kivy属性,而非普通变量:

class MyLayout(Widget):
    cheminImage = StringProperty('assets/32/type.png')
    value = StringProperty('viande')
    label_current = StringProperty("viande")  # 新增可监听属性

    def on_spinner_select(self, text):
        self.label_current = text.lower()  # 转换为小写,匹配freezer的键
        self.changeImage()
        self.ids.rv.update_data(self.label_current)  # 触发RecycleView更新

    def changeImage(self):
        img = 'assets/32/' + self.label_current + ".png"
        self.cheminImage = img

2. 给RecycleView添加id以便访问

在kv语言中为MyRecycleView添加id,方便从MyLayout中调用其方法:

<MyLayout>:
    # ... 其他代码 ...
    MyRecycleView:
        id: rv  # 添加id用于后续访问

3. 修改RecycleView,添加更新数据的方法

让RecycleView可以接收外部参数,动态更新data:

class MyRecycleView(RecycleView):
    rvtest = StringProperty('type')

    def __init__(self, **kwargs):
        super(MyRecycleView, self).__init__(**kwargs)
        self.update_data("viande")  # 初始化时加载默认数据

    def update_data(self, type_aliment):
        # 根据传入的类型更新RecycleView数据
        self.data = [{'text': str(x)} for x in freezer(type_aliment)]

4. 简化Spinner的on_text事件

合并Spinner的重复事件处理,避免逻辑分散:

Spinner:
    size_hint: 1, .1
    id: spinner_id
    text: "Type"
    values: ["ACCOMPAGNEMENT", "AROMATIQUE", "DESSERT", "PLAT", "VIANDE", "POISSON", "SAUCE", "SOUPE"]
    on_text: root.on_spinner_select(self.text)

5. 处理大小写匹配

Spinner选项为大写,而freezer字典键为小写,通过text.lower()转换后再传入,避免KeyError。

完整修正后的代码

from kivy.app import App
from kivy.properties import ObjectProperty, StringProperty, ListProperty
from kivy.uix.boxlayout import BoxLayout
from kivy.lang import Builder
from kivy.uix.recycleview import RecycleView
from kivy.uix.recycleview.views import RecycleDataViewBehavior
from kivy.uix.widget import Widget
from kivy.uix.image import Image
from kivy.core.window import Window

Builder.load_string("""
<MyRecycleView>:
    id: rv
    viewclass: 'Label'
    RecycleBoxLayout:
        default_size: None, dp(56)
        default_size_hint: 1, None
        size_hint_y: None
        height: self.minimum_height
        orientation: 'vertical'

<MyLayout>:
    label_current: "type"
    BoxLayout:
        orientation: 'vertical'
        size: root.width, root.height

        Button:
            text: 'Ajouter/Supprimer/Modifier'
            size_hint: 1, .1
            on_release: root.ajoutSuppMod()

        Spinner:
            size_hint: 1, .1
            id: spinner_id
            text: "Type"
            values: ["ACCOMPAGNEMENT", "AROMATIQUE", "DESSERT", "PLAT", "VIANDE", "POISSON", "SAUCE", "SOUPE"]
            on_text: root.on_spinner_select(self.text)

        Label:
            id: click_label
            text: "CONGELO Liste"
            font_size: 32
            size_hint: 1, .2

        MyImage:
            id: img
            source: root.cheminImage

        MyRecycleView:
            id: rv
""")


def freezer(type_aliment):
    dict_freez = {"accompagnement": [1, 2], "aromatique": [3, 4], "dessert": [1, 2], "soupe": [5, 2],
                    "plat": [1, 2], "poisson": [1, 2], "sauce": [1, 2], "viande": [1, 2],
                    "divers": [1, 2]}    
    return dict_freez[type_aliment]


class MyImage(Image):
    pass


class MyLayout(Widget):
    cheminImage = StringProperty('assets/32/type.png')
    value = StringProperty('viande')
    label_current = StringProperty("viande")

    def ajoutSuppMod(self):
        # 可在此添加按钮逻辑
        pass

    def on_spinner_select(self, text):
        self.label_current = text.lower()
        self.changeImage()
        self.ids.rv.update_data(self.label_current)

    def changeImage(self):
        img = 'assets/32/' + self.label_current + ".png"
        self.cheminImage = img


class MyViewClass(RecycleDataViewBehavior, BoxLayout):
    text = StringProperty("")
    index = None

    def refresh_view_attrs(self, rv, index, data):
        self.index = index
        return super(MyViewClass, self).refresh_view_attrs(rv, index, data)


class MyRecycleView(RecycleView):
    rvtest = StringProperty('type')

    def __init__(self, **kwargs):
        super(MyRecycleView, self).__init__(**kwargs)
        self.update_data("viande")

    def update_data(self, type_aliment):
        self.data = [{'text': str(x)} for x in freezer(type_aliment)]
        

class SpinApp(App):
    def build(self):
        Window.clearcolor = (0, 0, 0, 1)
        return MyLayout()


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

关键说明

  • Kivy的属性(如StringProperty)自带监听机制,值变化时可触发关联操作,这是普通Python变量不具备的特性
  • RecycleView的data不会自动响应外部变量变化,需在Spinner选择时主动调用更新方法
  • 务必注意字符串大小写匹配,避免字典查找时出现KeyError

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 03:47:05