使用 RecycleView 的 Kivy 的 CheckBox 问题
CheckBox problems with Kivy using RecycleView
最少的代码用于使用 Kivy、RecycleView 和 RecycleGridLayout 的图像网格,每个图像都有一个复选框。问题包括: i) 复选框的 selection/deselection 不显示; 2) 它正在重置复选框,因此 deselect 看起来是 select - 请参阅 on_checkbox_active 和 on_checkbox_press 中 print() 语句的输出(两者的代码都是包括)。
from kivy.app import App
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
Builder.load_string("""
<GridTile>:
AsyncImage:
source: root.tile
size_hint_y: None
height: '150dp'
CheckBox:
id: ck
root_ref: root.index # create reference to containing GridTile
# on_active: app.on_checkbox_active(self, self.active, self.state, self.root_ref)
on_press: app.on_checkbox_press(self, self.active, self.state, self.root_ref)
<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')
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 Rolling Stones', "index": i, "cb_state": 'normal'} for i in range(41)]
class ThisApp(App):
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, active, state, root_ref):
if active:
print(active, state, root_ref)
else:
print(active, state, root_ref)
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[root_ref]['cb_state'] = new_state
rv.refresh_from_data() # set the state from data
def on_checkbox_press(self, checkbox, active, state, root_ref):
if active:
print(active, state, root_ref)
else:
print(active, state, root_ref)
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[root_ref]['cb_state'] = new_state
rv.refresh_from_data() # set the state from data
if __name__ == "__main__":
ThisApp().run()
当 GridTile
的 cb_state
属性 被 [=15= 更改时,您只是缺少设置 CheckBox
state
的代码]:
class GridTile(Screen):
# properties to be set in the rv.data
tile = StringProperty('')
index = NumericProperty(-1)
cb_state = StringProperty('normal')
# code to set CheckBox state based on cb_state property
# this could also be accomplished with a `bind`
def on_cb_state(self, grid_tile, new_state):
self.ids.ck.state = new_state
在 GridTile 中设置了“on_active: app.on_checkbox_active(self, ...)”的最小代码。 The RecycleView enters an infinite loop when any checkbox is selected.连续输出中的信息为:
[CRITICAL] [Clock] Warning, too much iteration done before the next
frame. Check your code, or increase the Clock.max_iteration attribute
某个地方需要 Clock() 函数吗?
from kivy.app import App
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
Builder.load_string("""
<GridTile>:
AsyncImage:
source: root.tile
size_hint_y: None
height: '150dp'
CheckBox:
id: ck
root_ref: root.index # create reference to containing GridTile
on_active: app.on_checkbox_active(self, self.active, self.state, self.root_ref)
# on_press: app.on_checkbox_press(self, self.active, self.state, self.root_ref)
<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 CheckBox
def set_cb_state(self, gridtile, cb_state_value):
self.ids.ck.state = cb_state_value # actually set the state of the CheckBox
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 ThisApp(App):
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, active, state, root_ref):
if active:
print(active, state, root_ref)
else:
print(active, state, root_ref)
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[root_ref]['cb_state'] = new_state
rv.refresh_from_data() # set the state from data
def on_checkbox_press(self, checkbox, active, state, root_ref):
if active:
print(active, state, root_ref)
else:
print(active, state, root_ref)
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[root_ref]['cb_state'] = new_state
rv.refresh_from_data() # set the state from data
if __name__ == "__main__":
ThisApp().run()
最少的代码用于使用 Kivy、RecycleView 和 RecycleGridLayout 的图像网格,每个图像都有一个复选框。问题包括: i) 复选框的 selection/deselection 不显示; 2) 它正在重置复选框,因此 deselect 看起来是 select - 请参阅 on_checkbox_active 和 on_checkbox_press 中 print() 语句的输出(两者的代码都是包括)。
from kivy.app import App
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
Builder.load_string("""
<GridTile>:
AsyncImage:
source: root.tile
size_hint_y: None
height: '150dp'
CheckBox:
id: ck
root_ref: root.index # create reference to containing GridTile
# on_active: app.on_checkbox_active(self, self.active, self.state, self.root_ref)
on_press: app.on_checkbox_press(self, self.active, self.state, self.root_ref)
<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')
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 Rolling Stones', "index": i, "cb_state": 'normal'} for i in range(41)]
class ThisApp(App):
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, active, state, root_ref):
if active:
print(active, state, root_ref)
else:
print(active, state, root_ref)
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[root_ref]['cb_state'] = new_state
rv.refresh_from_data() # set the state from data
def on_checkbox_press(self, checkbox, active, state, root_ref):
if active:
print(active, state, root_ref)
else:
print(active, state, root_ref)
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[root_ref]['cb_state'] = new_state
rv.refresh_from_data() # set the state from data
if __name__ == "__main__":
ThisApp().run()
当 GridTile
的 cb_state
属性 被 [=15= 更改时,您只是缺少设置 CheckBox
state
的代码]:
class GridTile(Screen):
# properties to be set in the rv.data
tile = StringProperty('')
index = NumericProperty(-1)
cb_state = StringProperty('normal')
# code to set CheckBox state based on cb_state property
# this could also be accomplished with a `bind`
def on_cb_state(self, grid_tile, new_state):
self.ids.ck.state = new_state
在 GridTile 中设置了“on_active: app.on_checkbox_active(self, ...)”的最小代码。 The RecycleView enters an infinite loop when any checkbox is selected.连续输出中的信息为:
[CRITICAL] [Clock] Warning, too much iteration done before the next frame. Check your code, or increase the Clock.max_iteration attribute
某个地方需要 Clock() 函数吗?
from kivy.app import App
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
Builder.load_string("""
<GridTile>:
AsyncImage:
source: root.tile
size_hint_y: None
height: '150dp'
CheckBox:
id: ck
root_ref: root.index # create reference to containing GridTile
on_active: app.on_checkbox_active(self, self.active, self.state, self.root_ref)
# on_press: app.on_checkbox_press(self, self.active, self.state, self.root_ref)
<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 CheckBox
def set_cb_state(self, gridtile, cb_state_value):
self.ids.ck.state = cb_state_value # actually set the state of the CheckBox
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 ThisApp(App):
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, active, state, root_ref):
if active:
print(active, state, root_ref)
else:
print(active, state, root_ref)
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[root_ref]['cb_state'] = new_state
rv.refresh_from_data() # set the state from data
def on_checkbox_press(self, checkbox, active, state, root_ref):
if active:
print(active, state, root_ref)
else:
print(active, state, root_ref)
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[root_ref]['cb_state'] = new_state
rv.refresh_from_data() # set the state from data
if __name__ == "__main__":
ThisApp().run()