在 kivyMD 中选择 MDCheckbox 时出现应用错误

Posted

技术标签:

【中文标题】在 kivyMD 中选择 MDCheckbox 时出现应用错误【英文标题】:App error while selecting a MDCheckbox in kivyMD 【发布时间】:2022-01-14 00:22:44 【问题描述】:

我有一个MDListMDCheckbox,我需要选择列表中的一些项目,但例如当我选择第一个项目时,最后一个项目也被选中,当我选择第二个项目时,倒数第二个项目也被选中,反之亦然,如果我选择最后一个元素,那么第一个元素也会被选中。这只发生在列表的第一个和最后两个元素上,当我在它们之间选择一个元素时,它可以正常工作。我怎样才能防止这种情况发生?

这是我的代码的类似示例:

from kivymd.uix.dialog import MDDialog
from kivy.lang import Builder
from kivymd.app import MDApp
from kivy.uix.screenmanager import Screen, ScreenManager
from kivymd.uix.snackbar import Snackbar
from kivy.properties import ObjectProperty
from kivymd.uix.list import ILeftBodyTouch, ThreeLineListItem, ThreeLineAvatarIconListItem
from kivy.uix.recycleview import RecycleView
from kivymd.uix.selectioncontrol import MDCheckbox
from functools import partial

KV='''
WindowManager:
    #LoginWindow:
    MainWindow:
    SecondWindow:

<ListItemWithCheckbox>:

    LeftCheckbox:


<MainWindow>
    name: 'main'
    MDBoxLayout:
        orientation: 'vertical'
        MDToolbar:
            title: 'test'

        MDBoxLayout:
            orientation:'vertical'
            spacing: dp(10)
            padding: dp(20)

            RecycleView:
                id: rv
                key_viewclass: 'viewclass'
                key_size: 'height'
                bar_width: dp(10)
                RecycleBoxLayout:
                    padding: dp(10)
                    default_size: None, dp(80)
                    default_size_hint: 1, None
                    size_hint_y: None
                    height: self.minimum_height
                    orientation: 'vertical'



'''

product_dict='name 1': (1, 2),
                'name 2': (3,4),
                'name 3':(4,2),
                'name 4':(4,2),
                'name 5':(4,2),
                'name 6':(4,2),
                'name 7':(4,2),
                'name 8':(4,2),
                'name 9':(4,2) ,
                'name 10':(4,2)


class MainWindow(Screen):
    pass

class SecondWindow(Screen):
    pass

class WindowManager(ScreenManager):
    pass

class ListItemWithCheckbox(ThreeLineAvatarIconListItem):
    pass

class LeftCheckbox(ILeftBodyTouch, MDCheckbox):
    pass

class MainApp(MDApp):

    def build(self):
        self.theme_cls.theme_style="Dark"
        self.theme_cls.primary_palette="Green"
        return Builder.load_string(KV)

    def on_start(self):

        self.set_list()

    def set_list(self):

        self.root.get_screen('main').ids.rv.data = []
        for key in product_dict:
            self.root.get_screen('main').ids.rv.data.append(
                
                    "viewclass": "ListItemWithCheckbox",
                    "text": key,
                    "secondary_text": f'Item 1: product_dict[key][0]',
                    "tertiary_text": f'Item 2: product_dict[key][1]',
                    "on_press": partial(self.action, key)
                
            )

    def action(self, instance):
        print(instance)

MainApp().run()

【问题讨论】:

【参考方案1】:

因为RecycleView 回收viewclass,所以在viewclass 中设置的属性将反映在viewclass 的回收实例中,除非您明确处理这些属性。因此,如果您的ListItemWithCheckbox 中的LeftCheckboxstate 在一个实例中设置为down,则当该实例被回收以显示data 中的另一个项目时,它将保持为down

解决方法是在您的data 中处理LeftCheckboxstate。为此,您需要能够确定data 的哪个项目当前与LeftCheckbox 相关联。

一种方法是向ListItemWithCheckbox 添加属性:

class ListItemWithCheckbox(ThreeLineAvatarIconListItem):
    selected = BooleanProperty(False)  # is this checkbox down
    data_index = NumericProperty(-1)  # index into the RV data

    def state_changed(self):
        self.selected = self.ids.lcb.state == 'down'  # set the selected property

        # save the change to the data
        rv = MDApp.get_running_app().root.get_screen('main').ids.rv
        rv.data[self.data_index]['selected'] = self.selected

然后调整set_list()方法初始化新属性:

def set_list(self):

    self.root.get_screen('main').ids.rv.data = []
    index = 0
    for key in product_dict:
        self.root.get_screen('main').ids.rv.data.append(
            
                "viewclass": "ListItemWithCheckbox",
                "text": key,
                "secondary_text": f'Item 1: product_dict[key][0]',
                "tertiary_text": f'Item 2: product_dict[key][1]',
                "on_press": partial(self.action, key),
                "selected": False,
                "data_index": index
            
        )
        index += 1

并在kv修改ListItemWithCheckbox

<ListItemWithCheckbox>:
    LeftCheckbox:
        id: lcb
        on_press: root.state_changed()  # save change to the data
        state: 'down' if root.selected else 'normal' #  get state from the "selected" property

【讨论】:

以上是关于在 kivyMD 中选择 MDCheckbox 时出现应用错误的主要内容,如果未能解决你的问题,请参考以下文章

在多个 kivymd 窗口中更改背景

如何在 KivyMD 中创建选择列表

带有 MDCheckbox 的条件语句

KivyMD 带有下拉菜单的文本字段

单击 KivyMD 复选框取消选择任何其他复选框

在 Android 上的 kivymd 中使用 python 3.8 exchangelib 时出错