0

当在回收视图网格中单击/取消单击项目的复选框时,单击/取消单击也会自动重复网格中的其他数据项。为什么会这样?下面的代码是一个最小的工作示例。谢谢。

from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.recycleview import RecycleView
from kivy.properties import StringProperty, ListProperty
from kivy.clock import Clock

from kivymd.app import MDApp
from kivymd.uix.imagelist import SmartTile
from kivymd.uix.selectioncontrol import MDCheckbox

Builder.load_string("""
<Check>:

<GridTile>:
    SmartTile:
        source: root.tile
        size_hint_y: None
        height: '150dp'
        Check:

<GridScreen>:
    name: 'grid_screen'
    RV:
        id: rv
        viewclass: 'GridTile'
        RecycleGridLayout:
            cols: 2
            size_hint_y: None
            default_size: 1, dp(150)
            default_size_hint: 1, None
            height: self.minimum_height
""")

class GridTile(Screen):
    tile = StringProperty('')

class GridScreen(Screen):
    pass

class RV(RecycleView):
    data = ListProperty('[]')

    def __init__(self, **kwargs):
        super(RV, self).__init__(**kwargs)
        self.cell_data()

    def cell_data(self):
        self.data = [{"tile": 'The Beatles'} for i in range(41)]

class Check(SmartTile):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        Clock.schedule_once(self.add_checkbox)

    def add_checkbox(self, interval):
        app = MDApp.get_running_app()
        self.check = MDCheckbox(size_hint=(None, None), size=(48, 48))
        self.check.bind(active=app.on_checkbox_active)
        self._box_overlay.add_widget(self.check)

class ThisApp(MDApp):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)

    def build(self):
        self.sm = ScreenManager()
        self.sm.add_widget(GridScreen(name='grid_screen'))
        return self.sm

    def on_checkbox_active(self, checkbox, value):
        if value:
            print('The checkbox', checkbox, 'is active', 'and', checkbox.state, 'state')
        else:
            print('The checkbox', checkbox, 'is inactive', 'and', checkbox.state, 'state')

if __name__ == "__main__":
    ThisApp().run()
4

2 回答 2

0

这是您原始发布代码的修改版本。此版本有效,但实例之间存在一些交互GridTile(当您单击一个复选框时,另一个复选框GridTile会自行刷新)。我只见过这种与 KivyMd 的互动。在没有 KivyMD 的情况下编写类似的应用程序不会显示那种奇怪的交互。

from functools import partial

from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.recycleview import RecycleView
from kivy.properties import StringProperty, ListProperty, NumericProperty, ObjectProperty
from kivy.clock import Clock

from kivymd.app import MDApp
from kivymd.uix.imagelist import SmartTile
from kivymd.uix.selectioncontrol import MDCheckbox

Builder.load_string("""
<GridTile>:
    SmartTile:
        source: root.tile
        size_hint_y: None
        height: '150dp'
        Check:
            id: ck
            root_ref: root  # creat reference to containing GridTile

<GridScreen>:
    name: 'grid_screen'
    RV:
        id: rv
        viewclass: 'GridTile'
        RecycleGridLayout:
            cols: 2
            size_hint_y: None
            default_size: 1, dp(150)
            default_size_hint: 1, None
            height: self.minimum_height
""")

class GridTile(Screen):
    # properties to be set in the rv.data
    tile = StringProperty('')
    index = NumericProperty(-1)
    cb_state = StringProperty('normal')

    def __init__(self, **kwargs):
        super(GridTile, self).__init__(**kwargs)
        self.bind(cb_state=self.set_cb_state)  # bind the cb_state property to set the state of the MDCheckBox

    def set_cb_state(self, gridtile, cb_state_value):
        self.ids.ck.check.state = cb_state_value  # actually set the state of the MDCheckBox

class GridScreen(Screen):
    pass

class RV(RecycleView):
    data = ListProperty('[]')

    def __init__(self, **kwargs):
        super(RV, self).__init__(**kwargs)
        self.cell_data()

    def cell_data(self):
        self.data = [{"tile": 'The Beatles', "index": i, "cb_state": 'normal'} for i in range(41)]

class Check(SmartTile):
    root_ref = ObjectProperty(None)  # reference to the containing GridTile (set by kv)

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        Clock.schedule_once(self.add_checkbox)

    def add_checkbox(self, interval):
        app = MDApp.get_running_app()
        self.check = MDCheckbox(size_hint=(None, None), size=(48, 48))
        self.check.bind(on_press=partial(app.on_checkbox_press, self))  # bind to on_press to avoid possible looping when active is changed
        self._box_overlay.add_widget(self.check)

class ThisApp(MDApp):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)

    def build(self):
        self.sm = ScreenManager()
        self.sm.add_widget(GridScreen(name='grid_screen'))
        return self.sm

    def on_checkbox_press(self, check, checkbox):
        new_state = checkbox.state

        # set checkbox state back to the default
        checkbox.state = 'normal'  # avoids setting checkbox state without data

        rv = self.root.get_screen('grid_screen').ids.rv
        rv.data[check.root_ref.index]['cb_state'] = new_state
        rv.refresh_from_data()  # set the state from data

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

修改的要点是将indexcb_state属性添加到GridTile类和data. 该index属性仅用作data调整 时的索引datacb_statestateMDCheckbox。_ 由于MDCheckbox没有出现在 中kv,如果cb_state属性与 的实际state没有自动绑定MDChckbox,因此绑定是在GridTile类中显式创建的。此外,MDCheckbox更新 的data绑定更改为绑定到on_press,而不是on_active,因为active属性将RecycleView基于更改data并且可能导致循环效果。

于 2021-07-06T01:47:19.483 回答
0

通过RecycleView回收最少数量的 实例来工作viewclassGridTile在您的情况下。将属性分配给基于 中的条目的RecycleView那些实例。如果您更改 的任何属性或其子级,而. 因此,如果您希望正确处理状态,则必须将其作为. your不在 your 中的事实使这更难完成。这回答了这个问题。GridTiledataGridTiledataRecycleViewGridTileMDCheckBoxdataGridTileMDCheckBoxkvwhy

于 2021-07-05T20:03:00.667 回答