Problem
I've been messing around with Kivy's RecycleViews in hopes of creating a list builder for one of my projects. I am working out of the second example on Kivy docs' RecycleView page, as it is already almost what I am trying to create. For reference, the example contains a list where multiple items can be selected or unselected.
My main issue is that I have been unable to find any way to get any sort of list containing all of the selected items in the RecycleView. I thought at the very least, I could have a separate list in the RecycleView containing all of the selected items using the apply_selection()
method in the SelectableLabel
class, however I am unable to distinguish unselecting a SelectableLabel
from moving the label outside of the RecycleView's view.
Code
listBuilder.py
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import Screen
from kivy.uix.recycleview import RecycleView
from kivy.uix.recycleview.datamodel import RecycleDataModel
from kivy.uix.recycleview.views import RecycleDataViewBehavior
from kivy.uix.label import Label
from kivy.properties import BooleanProperty
from kivy.uix.recycleboxlayout import RecycleBoxLayout
from kivy.uix.behaviors import FocusBehavior
from kivy.uix.recycleview.layout import LayoutSelectionBehavior
class TestScreen(Screen):
''' Screen for testing stuff '''
def pressed(self):
print(f'Selected: {self.ids.rv.data}')
self.ids.rv.data.append({'text': '200'})
class RV(RecycleView):
''' Recycle View '''
def __init__(self, **kwargs):
super(RV, self).__init__(**kwargs)
self.data = [{'text': str(x)} for x in range(100)]
class SelectableRecycleBoxLayout(FocusBehavior, LayoutSelectionBehavior,
RecycleBoxLayout, RecycleDataModel):
''' Adds selection and focus behaviour to the view. '''
def on_data_changed(self, **kwargs):
print('Data changed: ', kwargs)
super(SelectableRecycleBoxLayout, self).on_data_changed(**kwargs)
class SelectableLabel(RecycleDataViewBehavior, Label):
''' Add selection support to the Label '''
index = None
selected = BooleanProperty(False)
selectable = BooleanProperty(True)
def refresh_view_attrs(self, rv, index, data):
''' Catch and handle the view changes '''
self.index = index
return super(SelectableLabel, self).refresh_view_attrs(
rv, index, data)
def on_touch_down(self, touch):
''' Add selection on touch down '''
if super(SelectableLabel, self).on_touch_down(touch):
return True
if self.collide_point(*touch.pos) and self.selectable:
return self.parent.select_with_touch(self.index, touch)
def apply_selection(self, rv, index, is_selected):
''' Respond to the selection of items in the view. '''
self.selected = is_selected
if is_selected:
print("selection changed to {0}".format(rv.data[index]))
else:
print("selection removed for {0}".format(rv.data[index]))
Builder.load_file('listBuilder.kv')
class MainApp(App):
def build(self):
return TestScreen()
if __name__ == '__main__':
MainApp().run()
listBuilder.kv
#:kivy 1.11
#:import ScrollEffect kivy.effects.scroll.ScrollEffect
<SelectableLabel>:
# Draw a background to indicate selection
canvas.before:
Color:
rgba: (0.2, 0.2, 0.2, 1) if self.selected else (0.5, 0.5, 0.5, 1)
Rectangle:
pos: self.pos
size: self.size
<RV>:
viewclass: 'SelectableLabel'
# Scroll effects
effect_cls: ScrollEffect # Disable overscroll
scroll_type: ['bars', 'content']
bar_width: dp(15)
scroll_wheel_distance: dp(90)
# Content of recycle view
SelectableRecycleBoxLayout:
default_size: None, dp(30)
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
multiselect: True
touch_multiselect: True
<TestScreen>:
name: 'test'
FloatLayout:
canvas:
Color:
rgba: 1,1,1,1
Rectangle:
pos: 0,0
size: self.width, self.height
# Container for recycle view
RV:
id: rv
size_hint: 0.3, 0.5
pos_hint: {'center_x': 0.3, 'center_y': 0.5}
Button:
text: 'Print selected'
size_hint: 0.2, 0.05
pos_hint: {'center_x': 0.8, 'center_y': 0.1}
on_release:
root.pressed()
For anyone wondering why I am using screens in this example--it's because this is test code for a larger program that uses screens.
I'm using Kivy 1.11.1 and Python 3.7.8
Any help is appreciated as I do not really understand yet fully grasp the RecycleView data models.
Thanks!