KivyMD RecycleView 网格中的复选框操作重复
Posted
技术标签:
【中文标题】KivyMD RecycleView 网格中的复选框操作重复【英文标题】:CheckBox Action Repeats in a KivyMD RecycleView Grid 【发布时间】:2021-09-16 10:09:00 【问题描述】:当在回收视图网格中单击/取消单击项目的复选框时,单击/取消单击也会自动重复网格中的其他数据项。为什么会这样?下面的代码是一个最小的工作示例。谢谢。
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()
【问题讨论】:
【参考方案1】:这是您发布的原始代码的修改版本。此版本有效,但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()
修改的要点是将index
和cb_state
属性添加到GridTile
类和data
。在调整data
时,index
属性仅用作data
的索引。而cb_state
是MDCheckbox
的state
。由于MDCheckbox
没有出现在kv
中,因此如果cb_state
属性与MDChckbox
的实际state
没有自动绑定,因此绑定是在GridTile
类中显式创建的。此外,MDCheckbox
的绑定以更新data
更改为绑定到on_press
,而不是on_active
,因为active
属性将由RecycleView
基于data
更改并可能导致循环效果。
【讨论】:
感谢您这样做。使用您的代码,我将 MDCheckBox 移动到 kv 中,奇怪的交互仍然发生。是时候仅使用 kivy 重写代码了。我会接受你的回答,如果我遇到困难,我会写一篇新的 SO 帖子。 我只将代码更改为 kivy,但出现同样的问题。我应该在此处发布最小代码还是在新帖子中发布? 发新帖。 在***.com/questions/68277396/…完成【参考方案2】:RecycleView
通过回收viewclass
的最少实例来工作,在您的情况下为GridTile
。 RecycleView
根据data
中的条目将属性分配给GridTile
的那些实例。如果您更改GridTile
或其子项的任何属性,而这些属性未在data
中处理,则RecycleView
不会意识到这些更改,并且这些更改仍保留在GridTile
的回收实例中。因此,如果您希望正确处理MDCheckBox
状态,则必须将其作为GridTile
的另一个属性包含在您的data
中。您的MDCheckBox
不在您的kv
中,这一事实使这更难实现。这回答了why
的问题。
【讨论】:
MDCheckBox 放置在 kv 之外的原因是因为 kivymd 板上的人说要这样做 - 与 SmartTile 中的 _box_overlay id 有关。这没有意义,但我们到了。最初,MDCheckBox 在 kv 中,我只是在 kv 中再次对其进行了测试,并且复选框操作像以前一样重复。解决这个问题的方法是什么?顺便说一句,感谢您慷慨明确的回答。 kv中的MDCheckBox代码:以上是关于KivyMD RecycleView 网格中的复选框操作重复的主要内容,如果未能解决你的问题,请参考以下文章
KivyMD RecycleView 的 FPS 和延迟较低
表单中的 SwiftUI Picker 不显示来自 CoreData 的复选标记