mirror of https://github.com/kivy/kivy.git
Started writing formal examples for ListView in its doc header, which prompted several clean-ups. ListItemButton and ListItemLabel were added to listview.py, so that various custom ListItem classes made for the examples and tests could be removed. The todo list for this uix-listview branch was moved to a gist. A new adapters/util.py file was added, containing only list_item_args_adapter(). ListAdapter was modified to check for an args_converter argument, and if not present, to use the default one in adapters/util.py.
This commit is contained in:
parent
f292049d4c
commit
b530e42850
|
@ -3,7 +3,7 @@ from kivy.adapters.listadapter import ListAdapter, \
|
|||
from kivy.uix.gridlayout import GridLayout
|
||||
from kivy.uix.label import Label
|
||||
from kivy.uix.button import Button
|
||||
from kivy.uix.listview import ListView
|
||||
from kivy.uix.listview import ListView, ListItemButton
|
||||
from kivy.adapters.mixins.selection import SingleSelectionObserver, \
|
||||
SelectableItem
|
||||
from kivy.properties import ListProperty, StringProperty, ObjectProperty
|
||||
|
@ -18,27 +18,6 @@ from kivy.properties import ListProperty, StringProperty, ObjectProperty
|
|||
|
||||
# Generic list item will do fine for both list views:
|
||||
|
||||
class ListItem(SelectableItem, Button):
|
||||
selected_color = ListProperty([1., 0., 0., 1])
|
||||
deselected_color = None
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super(ListItem, self).__init__(**kwargs)
|
||||
|
||||
# Set deselected_color to be default Button bg color.
|
||||
self.deselected_color = self.background_color
|
||||
|
||||
def select(self, *args):
|
||||
print self.text, 'is now selected'
|
||||
self.background_color = self.selected_color
|
||||
|
||||
def deselect(self, *args):
|
||||
print self.text, 'is now unselected'
|
||||
self.background_color = self.deselected_color
|
||||
|
||||
def __repr__(self):
|
||||
return self.text
|
||||
|
||||
class DetailView(SingleSelectionObserver, GridLayout):
|
||||
fruit_name = StringProperty('')
|
||||
|
||||
|
@ -97,7 +76,7 @@ class CascadingView(GridLayout):
|
|||
args_converter=list_item_args_converter,
|
||||
selection_mode='single',
|
||||
allow_empty_selection=False,
|
||||
cls=ListItem)
|
||||
cls=ListItemButton)
|
||||
fruit_categories_list_view = \
|
||||
ListView(adapter=fruit_categories_list_adapter,
|
||||
size_hint=(.2, 1.0))
|
||||
|
@ -113,7 +92,7 @@ class CascadingView(GridLayout):
|
|||
args_converter=list_item_args_converter,
|
||||
selection_mode='single',
|
||||
allow_empty_selection=False,
|
||||
cls=ListItem)
|
||||
cls=ListItemButton)
|
||||
fruits_list_view = \
|
||||
ListView(adapter=fruits_list_adapter,
|
||||
size_hint=(.2, 1.0))
|
||||
|
|
|
@ -5,7 +5,7 @@ from kivy.uix.button import Button
|
|||
from kivy.properties import ObjectProperty, \
|
||||
NumericProperty, ListProperty, \
|
||||
StringProperty
|
||||
from kivy.uix.listview import ListView
|
||||
from kivy.uix.listview import ListView, ListItemButton
|
||||
from kivy.adapters.mixins.selection import SelectionObserver, SelectableItem
|
||||
from kivy.adapters.listadapter import ListAdapter
|
||||
|
||||
|
@ -33,60 +33,17 @@ from kivy.adapters.listadapter import ListAdapter
|
|||
# For the master list, we need to create a custom "list item" type that
|
||||
# subclasses SelectableItem.
|
||||
|
||||
# "Sub" to indicate that this class is for "sub" list items -- a list item
|
||||
# could consist of a button on the left, several labels in the middle, and
|
||||
# another button on the right. Not sure of the merit of allowing, perhaps,
|
||||
# some "sub" list items to react to touches and others not, if that were to
|
||||
# be enabled.
|
||||
|
||||
class ListItemSubButton(SelectableItem, Button):
|
||||
selected_color = ListProperty([1., 0., 0., 1])
|
||||
deselected_color = None
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super(ListItemSubButton, self).__init__(**kwargs)
|
||||
|
||||
# Set deselected_color to be default Button bg color.
|
||||
self.deselected_color = self.background_color
|
||||
|
||||
# [TODO] At least there is some action on this set, but
|
||||
# the color gets somehow composited.
|
||||
def select(self, *args):
|
||||
self.background_color = self.selected_color
|
||||
|
||||
# [TODO] No effect seen, but it is grey, so might be happening.
|
||||
def deselect(self, *args):
|
||||
self.background_color = self.deselected_color
|
||||
|
||||
def __repr__(self):
|
||||
return self.text
|
||||
# A composite list item could consist of a button on the left, several labels
|
||||
# in the middle, and another button on the right. Not sure of the merit of
|
||||
# allowing, perhaps, some "sub" list items to react to touches and others not,
|
||||
# if that were to be enabled.
|
||||
|
||||
|
||||
class ListItemSubLabel(SelectableItem, Label):
|
||||
# Same idea as "sub" for button above.
|
||||
selected_color = ListProperty([1., 0., 0., 1])
|
||||
deselected_color = ListProperty([.33, .33, .33, 1])
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super(ListItemSubLabel, self).__init__(**kwargs)
|
||||
|
||||
# [TODO] Should Label have background_color, like Button, etc.?
|
||||
# [TODO] Not tested yet.
|
||||
def select(self, *args):
|
||||
self.bold = True
|
||||
|
||||
def deselect(self, *args):
|
||||
self.bold = False
|
||||
|
||||
def __repr__(self):
|
||||
return self.text
|
||||
|
||||
|
||||
class ListItem(SelectableItem, BoxLayout):
|
||||
# ListItem (BoxLayout) by default uses orientation='horizontal',
|
||||
class CompositeListItem(SelectableItem, BoxLayout):
|
||||
# CompositeListItem (BoxLayout) by default uses orientation='horizontal',
|
||||
# but could be used also for a side-to-side display of items.
|
||||
#
|
||||
# ListItemSubButton sublasses Button, which has background_color.
|
||||
# ListItemButton sublasses Button, which has background_color.
|
||||
# Here we must add this property.
|
||||
background_color = ListProperty([1, 1, 1, 1])
|
||||
|
||||
|
@ -97,11 +54,11 @@ class ListItem(SelectableItem, BoxLayout):
|
|||
content_button = ObjectProperty(None)
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super(ListItem, self).__init__(**kwargs)
|
||||
super(CompositeListItem, self).__init__(**kwargs)
|
||||
|
||||
# Now this button just has text '>', but it would be neat to make the
|
||||
# left button hold icons -- the list would be heterogeneous, containing
|
||||
# different ListItem types that could be filtered perhaps (an option
|
||||
# different list item types that could be filtered perhaps (an option
|
||||
# for selecting all of a given type, for example).
|
||||
|
||||
# For sub list items, set selection_target to self (this is a kind of
|
||||
|
@ -115,10 +72,10 @@ class ListItem(SelectableItem, BoxLayout):
|
|||
icon_kwargs = kwargs.copy()
|
||||
icon_kwargs['text'] = '>'
|
||||
icon_kwargs['size_hint_x'] = .05
|
||||
self.icon_button = ListItemSubButton(**icon_kwargs)
|
||||
self.icon_button = ListItemButton(**icon_kwargs)
|
||||
|
||||
# Use the passed in kwargs for the "content" button.
|
||||
self.content_button = ListItemSubButton(**kwargs)
|
||||
self.content_button = ListItemButton(**kwargs)
|
||||
|
||||
self.add_widget(self.icon_button)
|
||||
self.add_widget(self.content_button)
|
||||
|
@ -197,7 +154,7 @@ class MasterDetailView(GridLayout):
|
|||
args_converter=args_converter,
|
||||
selection_mode='single',
|
||||
allow_empty_selection=False,
|
||||
cls=ListItem)
|
||||
cls=CompositeListItem)
|
||||
self.master_list_view = ListView(adapter=self.list_adapter,
|
||||
size_hint=(.3, 1.0))
|
||||
self.add_widget(self.master_list_view)
|
||||
|
|
|
@ -4,7 +4,7 @@ from kivy.uix.button import Button
|
|||
from kivy.properties import ObjectProperty, \
|
||||
NumericProperty, ListProperty, \
|
||||
StringProperty
|
||||
from kivy.uix.listview import ListView
|
||||
from kivy.uix.listview import ListView, ListItemButton
|
||||
from kivy.adapters.mixins.selection import SingleSelectionObserver, \
|
||||
SelectableItem
|
||||
from kivy.adapters.listadapter import ListAdapter
|
||||
|
@ -13,29 +13,6 @@ from kivy.adapters.listadapter import ListAdapter
|
|||
# (the master) and another view (detail view) that gets updated upon
|
||||
# selection.
|
||||
|
||||
# For the master list, we need to create a custom "list item" type that
|
||||
# subclasses SelectableItem.
|
||||
|
||||
class ListItem(SelectableItem, Button):
|
||||
selected_color = ListProperty([1., 0., 0., 1])
|
||||
deselected_color = None
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super(ListItem, self).__init__(**kwargs)
|
||||
|
||||
# Set deselected_color to be default Button bg color.
|
||||
self.deselected_color = self.background_color
|
||||
|
||||
def select(self, *args):
|
||||
self.background_color = self.selected_color
|
||||
|
||||
def deselect(self, *args):
|
||||
self.background_color = self.deselected_color
|
||||
|
||||
def __repr__(self):
|
||||
return self.text
|
||||
|
||||
|
||||
# DetailView is an observer of the selection of the associated list view,
|
||||
# so SelectionObserver is mixed in, requiring an override of the
|
||||
# observed_selection_changed() method.
|
||||
|
@ -96,7 +73,7 @@ class MasterDetailView(GridLayout):
|
|||
args_converter=list_item_args_converter,
|
||||
selection_mode='single',
|
||||
allow_empty_selection=False,
|
||||
cls=ListItem)
|
||||
cls=ListItemButton)
|
||||
master_list_view = ListView(adapter=list_adapter,
|
||||
size_hint=(.3, 1.0))
|
||||
self.add_widget(master_list_view)
|
||||
|
|
|
@ -3,7 +3,7 @@ from kivy.adapters.listadapter import ListAdapter, \
|
|||
from kivy.uix.gridlayout import GridLayout
|
||||
from kivy.uix.label import Label
|
||||
from kivy.uix.button import Button
|
||||
from kivy.uix.listview import ListView
|
||||
from kivy.uix.listview import ListView, ListItemButton
|
||||
from kivy.adapters.mixins.selection import SingleSelectionObserver, \
|
||||
SelectableItem
|
||||
from kivy.properties import ListProperty, StringProperty, ObjectProperty
|
||||
|
@ -13,30 +13,6 @@ from kivy.properties import ListProperty, StringProperty, ObjectProperty
|
|||
# to have one list allow multiple selection and the other to show the
|
||||
# multiple items selected in the first.
|
||||
|
||||
# Generic list item will do fine for both list views:
|
||||
|
||||
class ListItem(SelectableItem, Button):
|
||||
selected_color = ListProperty([1., 0., 0., 1])
|
||||
deselected_color = None
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super(ListItem, self).__init__(**kwargs)
|
||||
|
||||
# Set deselected_color to be default Button bg color.
|
||||
self.deselected_color = self.background_color
|
||||
|
||||
def select(self, *args):
|
||||
print self.text, 'is now selected'
|
||||
self.background_color = self.selected_color
|
||||
|
||||
def deselect(self, *args):
|
||||
print self.text, 'is now unselected'
|
||||
self.background_color = self.deselected_color
|
||||
|
||||
def __repr__(self):
|
||||
return self.text
|
||||
|
||||
|
||||
class MultipleCascadingView(GridLayout):
|
||||
'''Implementation of a master-detail style view, with a scrollable list
|
||||
of fruits on the left and the selection in that list on the right in
|
||||
|
@ -57,7 +33,7 @@ class MultipleCascadingView(GridLayout):
|
|||
args_converter=list_item_args_converter,
|
||||
selection_mode='multiple',
|
||||
allow_empty_selection=False,
|
||||
cls=ListItem)
|
||||
cls=ListItemButton)
|
||||
fruits_list_view = \
|
||||
ListView(adapter=fruits_list_adapter,
|
||||
size_hint=(.2, 1.0))
|
||||
|
@ -72,7 +48,7 @@ class MultipleCascadingView(GridLayout):
|
|||
args_converter=list_item_args_converter,
|
||||
selection_mode='single',
|
||||
allow_empty_selection=True,
|
||||
cls=ListItem)
|
||||
cls=ListItemButton)
|
||||
selected_fruits_list_view = \
|
||||
ListView(adapter=selected_fruits_list_adapter,
|
||||
size_hint=(.2, 1.0))
|
||||
|
|
|
@ -25,6 +25,7 @@ Notes:
|
|||
from kivy.event import EventDispatcher
|
||||
from kivy.properties import ObjectProperty
|
||||
from kivy.lang import Builder
|
||||
from kivy.adapters.util import list_item_args_converter
|
||||
|
||||
|
||||
class Adapter(EventDispatcher):
|
||||
|
@ -37,6 +38,10 @@ class Adapter(EventDispatcher):
|
|||
args_converter = ObjectProperty(None)
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
if hasattr(kwargs, 'args_converter'):
|
||||
self.args_converter = kwargs['args_converter']
|
||||
else:
|
||||
self.args_converter = list_item_args_converter
|
||||
super(Adapter, self).__init__(**kwargs)
|
||||
if self.cls is None and self.template is None:
|
||||
raise Exception('A cls or template must be defined')
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
'''
|
||||
The default list item args converter for list adapters is this simple function
|
||||
that takes a string and returns the string as the text argument in a dict,
|
||||
along with two properties suited for simple text items with height of 25.
|
||||
|
||||
[TODO] Might there be other useful converters to put here, with descriptive
|
||||
names?
|
||||
'''
|
||||
list_item_args_converter = lambda x: {'text': x,
|
||||
'size_hint_y': None,
|
||||
'height': 25}
|
|
@ -7,6 +7,7 @@ import unittest
|
|||
|
||||
from kivy.uix.widget import Widget
|
||||
from kivy.uix.button import Button
|
||||
from kivy.uix.listview import ListView, ListItemButton
|
||||
from kivy.properties import NumericProperty, ListProperty
|
||||
from kivy.adapters.mixins.selection import SelectionObserver, SelectableItem
|
||||
from kivy.adapters.listadapter import ListAdapter
|
||||
|
@ -101,26 +102,6 @@ for row in raw_fruit_data:
|
|||
**dict(zip(descriptors_and_units.keys(), row['data'])))
|
||||
|
||||
|
||||
class FruitListItem(SelectableItem, Button):
|
||||
selected_color = ListProperty([1., 0., 0., 1])
|
||||
deselected_color = None
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super(FruitListItem, self).__init__(**kwargs)
|
||||
|
||||
# Set deselected_color to be default Button bg color.
|
||||
self.deselected_color = self.background_color
|
||||
|
||||
def select(self, *args):
|
||||
self.background_color = self.selected_color
|
||||
|
||||
def deselect(self, *args):
|
||||
self.background_color = self.deselected_color
|
||||
|
||||
def __repr__(self):
|
||||
return self.text
|
||||
|
||||
|
||||
class AdaptersTestCase(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
|
@ -134,9 +115,9 @@ class AdaptersTestCase(unittest.TestCase):
|
|||
args_converter=self.args_converter,
|
||||
selection_mode='none',
|
||||
allow_empty_selection=True,
|
||||
cls=FruitListItem)
|
||||
cls=ListItemButton)
|
||||
|
||||
self.assertEqual(list_adapter.cls, FruitListItem)
|
||||
self.assertEqual(list_adapter.cls, ListItemButton)
|
||||
self.assertEqual(list_adapter.args_converter, self.args_converter)
|
||||
self.assertEqual(list_adapter.template, None)
|
||||
|
||||
|
@ -144,4 +125,4 @@ class AdaptersTestCase(unittest.TestCase):
|
|||
self.assertTrue(isinstance(apple_data_item, str))
|
||||
|
||||
apple_view = list_adapter.get_view(0)
|
||||
self.assertTrue(isinstance(apple_view, FruitListItem))
|
||||
self.assertTrue(isinstance(apple_view, ListItemButton))
|
||||
|
|
|
@ -7,7 +7,7 @@ import unittest
|
|||
|
||||
from kivy.uix.widget import Widget
|
||||
from kivy.uix.button import Button
|
||||
from kivy.uix.listview import ListView
|
||||
from kivy.uix.listview import ListView, ListItemButton
|
||||
from kivy.properties import NumericProperty, ListProperty, StringProperty
|
||||
from kivy.adapters.mixins.selection import SelectionObserver, SelectableItem
|
||||
from kivy.adapters.listadapter import ListAdapter, SelectableListsAdapter, \
|
||||
|
@ -103,26 +103,6 @@ for row in raw_fruit_data:
|
|||
**dict(zip(descriptors_and_units.keys(), row['data'])))
|
||||
|
||||
|
||||
class FruitListItem(SelectableItem, Button):
|
||||
selected_color = ListProperty([1., 0., 0., 1])
|
||||
deselected_color = None
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super(FruitListItem, self).__init__(**kwargs)
|
||||
|
||||
# Set deselected_color to be default Button bg color.
|
||||
self.deselected_color = self.background_color
|
||||
|
||||
def select(self, *args):
|
||||
self.background_color = self.selected_color
|
||||
|
||||
def deselect(self, *args):
|
||||
self.background_color = self.deselected_color
|
||||
|
||||
def __repr__(self):
|
||||
return self.text
|
||||
|
||||
|
||||
class FruitSelectionObserver(SelectionObserver, Widget):
|
||||
fruit_name = StringProperty('')
|
||||
call_count = NumericProperty(0)
|
||||
|
@ -147,7 +127,7 @@ class ListAdapterTestCase(unittest.TestCase):
|
|||
args_converter=self.args_converter,
|
||||
selection_mode='none',
|
||||
allow_empty_selection=True,
|
||||
cls=FruitListItem)
|
||||
cls=ListItemButton)
|
||||
|
||||
self.assertEqual(len(list_adapter.selection), 0)
|
||||
list_adapter.check_for_empty_selection()
|
||||
|
@ -158,7 +138,7 @@ class ListAdapterTestCase(unittest.TestCase):
|
|||
args_converter=self.args_converter,
|
||||
selection_mode='single',
|
||||
allow_empty_selection=True,
|
||||
cls=FruitListItem)
|
||||
cls=ListItemButton)
|
||||
|
||||
self.assertEqual(len(list_adapter.selection), 0)
|
||||
list_adapter.check_for_empty_selection()
|
||||
|
@ -173,7 +153,7 @@ class ListAdapterTestCase(unittest.TestCase):
|
|||
args_converter=self.args_converter,
|
||||
selection_mode='single',
|
||||
allow_empty_selection=False,
|
||||
cls=FruitListItem)
|
||||
cls=ListItemButton)
|
||||
|
||||
self.assertEqual(len(list_adapter.selection), 1)
|
||||
list_adapter.check_for_empty_selection()
|
||||
|
@ -184,7 +164,7 @@ class ListAdapterTestCase(unittest.TestCase):
|
|||
args_converter=self.args_converter,
|
||||
selection_mode='multiple',
|
||||
allow_empty_selection=False,
|
||||
cls=FruitListItem)
|
||||
cls=ListItemButton)
|
||||
|
||||
self.assertEqual(len(list_adapter.selection), 1)
|
||||
list_adapter.handle_selection(list_adapter.get_view(1))
|
||||
|
@ -197,7 +177,7 @@ class ListAdapterTestCase(unittest.TestCase):
|
|||
args_converter=self.args_converter,
|
||||
selection_mode='single',
|
||||
allow_empty_selection=False,
|
||||
cls=FruitListItem)
|
||||
cls=ListItemButton)
|
||||
selection_observer = FruitSelectionObserver(
|
||||
observed_list_adapter=list_adapter)
|
||||
|
||||
|
@ -230,7 +210,7 @@ class SelectableListsAdapterTestCase(unittest.TestCase):
|
|||
args_converter=self.args_converter,
|
||||
selection_mode='single',
|
||||
allow_empty_selection=False,
|
||||
cls=FruitListItem)
|
||||
cls=ListItemButton)
|
||||
|
||||
fruit_categories_list_view = \
|
||||
ListView(adapter=fruit_categories_l_a,
|
||||
|
@ -251,7 +231,7 @@ class SelectableListsAdapterTestCase(unittest.TestCase):
|
|||
args_converter=self.args_converter,
|
||||
selection_mode='single',
|
||||
allow_empty_selection=False,
|
||||
cls=FruitListItem)
|
||||
cls=ListItemButton)
|
||||
fruits_list_view = ListView(adapter=fruits_l_a,
|
||||
size_hint=(.2, 1.0))
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ import unittest
|
|||
|
||||
from kivy.uix.button import Button
|
||||
from kivy.properties import ListProperty, StringProperty
|
||||
from kivy.uix.listview import ListView
|
||||
from kivy.uix.listview import ListView, ListItemButton
|
||||
from kivy.adapters.mixins.selection import SelectionObserver, SelectableItem
|
||||
from kivy.adapters.listadapter import ListAdapter
|
||||
|
||||
|
@ -104,32 +104,6 @@ for row in raw_fruit_data:
|
|||
**dict(zip(descriptors_and_units.keys(), row['data'])))
|
||||
|
||||
|
||||
class FruitListItem(SelectableItem, Button):
|
||||
selected_color = ListProperty([1., 0., 0., 1])
|
||||
deselected_color = None
|
||||
|
||||
def __init__(self, list_adapter, **kwargs):
|
||||
self.list_adapter = list_adapter
|
||||
super(FruitListItem, self).__init__(**kwargs)
|
||||
|
||||
# Set deselected_color to be default Button bg color.
|
||||
self.deselected_color = self.background_color
|
||||
|
||||
self.bind(on_release=self.handle_selection)
|
||||
|
||||
def handle_selection(self, button):
|
||||
self.list_adapter.handle_selection(self)
|
||||
|
||||
def select(self, *args):
|
||||
self.background_color = self.selected_color
|
||||
|
||||
def deselect(self, *args):
|
||||
self.background_color = self.deselected_color
|
||||
|
||||
def __repr__(self):
|
||||
return self.text
|
||||
|
||||
|
||||
class AdaptersTestCase(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
|
@ -143,7 +117,7 @@ class AdaptersTestCase(unittest.TestCase):
|
|||
args_converter=self.args_converter,
|
||||
selection_mode='none',
|
||||
allow_empty_selection=True,
|
||||
cls=FruitListItem)
|
||||
cls=ListItemButton)
|
||||
|
||||
self.assertEqual(len(list_adapter.selection), 0)
|
||||
list_adapter.check_for_empty_selection()
|
||||
|
@ -161,7 +135,7 @@ class AdaptersTestCase(unittest.TestCase):
|
|||
args_converter=self.args_converter,
|
||||
selection_mode='multiple',
|
||||
allow_empty_selection=True,
|
||||
cls=FruitListItem)
|
||||
cls=ListItemButton)
|
||||
|
||||
self.assertEqual(len(list_adapter.selection), 0)
|
||||
list_adapter.check_for_empty_selection()
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
'''
|
||||
|
||||
'''
|
||||
|
||||
'''
|
||||
List View
|
||||
===========
|
||||
|
@ -6,68 +10,173 @@ List View
|
|||
|
||||
The :class:`ListView` widget provides a scrollable/pannable viewport that is
|
||||
clipped at the scrollview's bounding box, which contains a list of
|
||||
item_view_instances.
|
||||
list item view instances.
|
||||
|
||||
[TODO]:
|
||||
:class:`ListView` implements AbstractView as a vertical scrollable list.
|
||||
From AbstractView we have these properties and methods:
|
||||
|
||||
- Initial selection is apparently working in the associated ListAdapter,
|
||||
but the list view display does not show the initial selection (red, in
|
||||
example code). After the list view has been clicked for the first manual
|
||||
selection, the updating of selected items (in red) works.
|
||||
- Explain why multiple levels of abstraction are needed. (Adapter,
|
||||
ListAdapter, AbstractView, ListView) -- Tie discussion to inspiration
|
||||
for Adapter and related classes:
|
||||
- adapter (an instance of ListAdapter or one of its subclasses here)
|
||||
|
||||
http://developer.android.com/reference/android/\
|
||||
widget/Adapter.html#getView(int,%20android/\
|
||||
.view.View,%20android.view.ViewGroup)
|
||||
- item_view_instances, a dict with indices as keys to the list item view
|
||||
instances created and held in the ListAdapter
|
||||
|
||||
- Divider isn't used (yet).
|
||||
- Consider adding an associated SortableItem mixin, to be used by list
|
||||
item classes in a manner similar to the SelectableItem mixin.
|
||||
- Consider a sort_by property. Review the use of the items property.
|
||||
(Presently items is a list of strings -- are these just the
|
||||
strings representing the item_view_instances, which are instances of
|
||||
the provided cls input argument?). If so, formalize and document.
|
||||
- Work on item_view_instances marked [TODO] in the code.
|
||||
- set_item_view() and get_item_view() methods to list item view instances
|
||||
|
||||
Examples (in examples/widgets):
|
||||
Basic Example
|
||||
-------------
|
||||
|
||||
- Improve examples:
|
||||
- Add fruit images.
|
||||
- Add an example where selection doesn't just change background color
|
||||
or font, but animates.
|
||||
The list items you put in a list view can be totally custom, but to have a
|
||||
simple button, we may use the :class:`ListItem` class.
|
||||
|
||||
Other Possibilities:
|
||||
Here we make a list view with 100 items.
|
||||
|
||||
- Consider a horizontally scrolling variant.
|
||||
- Is it possible to have dynamic item_view height, for use in a
|
||||
master-detail list view in this manner?
|
||||
from kivy.adapters.list_adapter import ListAdapter
|
||||
from kivy.uix.listview import ListItem, ListView
|
||||
|
||||
http://www.zkoss.org/zkdemo/grid/master_detail
|
||||
item_strings = ["Item {0}".format(index) for index in xrange(100)]
|
||||
|
||||
(Would this be a new widget called MasterDetailListView, or would the
|
||||
listview widget having a facility for use in this way?)
|
||||
# A list view needs a list adapter to provide a mediating service to the
|
||||
# data, in the case here our item_strings list, passed as the first
|
||||
# argument. We choose single selection mode. We may also set this for
|
||||
# allowing multiple selection. We set allow_empty_selection to False so
|
||||
# that there is always an item selected if at least one is in the list.
|
||||
# Setting this to true will make the list useful for display only.
|
||||
# Finally, we pass ListItem as the class (cls) to be instantiated by the
|
||||
# list adapter for each list item. ListItem is based on Button. When an
|
||||
# item is selected, its background color will change to red.
|
||||
list_adapter = ListAdapter(data=item_strings,
|
||||
selection_mode='single',
|
||||
allow_empty_selection=False,
|
||||
cls=ListItem)
|
||||
|
||||
(See the list_disclosure.py file as a start.)
|
||||
# The list view is a simple component. Just give it the list adapter that
|
||||
# will provide list item views based on its data.
|
||||
list_view = ListView(adapter=list_adapter)
|
||||
|
||||
Custom Composite Example
|
||||
------------------------
|
||||
|
||||
Let's say you would like to make a list view with composite list item views
|
||||
consisting of a button on the left, a label in the middle, and a second button
|
||||
on the right. Perhaps the buttons could be made as toggle buttons for two
|
||||
separate properties pertaining to the label. First, we need to make a custom
|
||||
list item, which we will base on the required SelectableItem mixin and our
|
||||
choice of layout, BoxLayout:
|
||||
|
||||
from kivy.adapters.list_adapter import ListAdapter
|
||||
from kivy.adapters.mixins.selection import SelectableItem
|
||||
from kivy.uix.listview import ListItemButton, ListItemLabel, ListView
|
||||
|
||||
|
||||
class CompositeListItem(SelectableItem, BoxLayout):
|
||||
# ListItem sublasses Button, which has background_color.
|
||||
# For a composite list item, we must add this property.
|
||||
background_color = ListProperty([1, 1, 1, 1])
|
||||
|
||||
selected_color = ListProperty([1., 0., 0., 1])
|
||||
deselected_color = ListProperty([.33, .33, .33, 1])
|
||||
|
||||
left_button = ObjectProperty(None)
|
||||
middle_label = ObjectProperty(None)
|
||||
right_button = ObjectProperty(None)
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super(CompositeListItem, self).__init__(**kwargs)
|
||||
|
||||
# For the component list items in our composite list item, we want
|
||||
# their individual selection to select the composite. Here we pass
|
||||
# an argument called selection_target, setting it to self (this
|
||||
# composite list item).
|
||||
self.left_button = ListItemButton(selection_target=self)
|
||||
self.middle_label = ListItemLabel(selection_target=self)
|
||||
self.right_button = ListItemButton(selection_target=self)
|
||||
|
||||
self.add_widget(self.left_button)
|
||||
self.add_widget(self.middle_label)
|
||||
self.add_widget(self.right_button)
|
||||
|
||||
def select(self, *args):
|
||||
self.background_color = self.selected_color
|
||||
|
||||
def deselect(self, *args):
|
||||
self.background_color = self.deselected_color
|
||||
|
||||
def __repr__(self):
|
||||
return self.middle_label.text
|
||||
|
||||
# Here we create a list adapter with some item strings, passing our
|
||||
# CompositeListItem as the list item view class, and then we create a
|
||||
# list view using this adapter:
|
||||
item_strings = ["Item {0}".format(index) for index in xrange(100)]
|
||||
list_adapter = ListAdapter(data=item_strings,
|
||||
selection_mode='single',
|
||||
allow_empty_selection=False,
|
||||
cls=CompositeListItem)
|
||||
list_view = ListView(adapter=list_adapter)
|
||||
|
||||
|
||||
Using With kv
|
||||
-------------
|
||||
|
||||
[TODO]
|
||||
|
||||
Cascading Selection Between Lists
|
||||
---------------------------------
|
||||
|
||||
[TODO]
|
||||
|
||||
- Make a separate master-detail example that works like an iphone-style
|
||||
animated "source list" that has "disclosure" buttons per item_view, on
|
||||
the right, that when clicked will expand to fill the entire list view
|
||||
area (useful on mobile devices especially). Similar question as above --
|
||||
would listview be given expanded functionality or would this become
|
||||
another kind of "master-detail" widget?)
|
||||
'''
|
||||
|
||||
__all__ = ('ListView', )
|
||||
|
||||
from kivy.clock import Clock
|
||||
from kivy.uix.widget import Widget
|
||||
from kivy.uix.button import Button
|
||||
from kivy.uix.label import Label
|
||||
from kivy.adapters.mixins.selection import SelectableItem
|
||||
from kivy.adapters.util import list_item_args_converter
|
||||
from kivy.uix.abstractview import AbstractView
|
||||
from kivy.properties import ObjectProperty, DictProperty, NumericProperty
|
||||
from kivy.properties import ObjectProperty, DictProperty, \
|
||||
NumericProperty, ListProperty
|
||||
from kivy.lang import Builder
|
||||
from math import ceil, floor
|
||||
|
||||
|
||||
class ListItemButton(SelectableItem, Button):
|
||||
selected_color = ListProperty([1., 0., 0., 1])
|
||||
deselected_color = None
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super(ListItemButton, self).__init__(**kwargs)
|
||||
|
||||
# Set deselected_color to be default Button bg color.
|
||||
self.deselected_color = self.background_color
|
||||
|
||||
def select(self, *args):
|
||||
self.background_color = self.selected_color
|
||||
|
||||
def deselect(self, *args):
|
||||
self.background_color = self.deselected_color
|
||||
|
||||
def __repr__(self):
|
||||
return self.text
|
||||
|
||||
|
||||
class ListItemLabel(SelectableItem, Label):
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super(ListItemLabel, self).__init__(**kwargs)
|
||||
|
||||
def select(self, *args):
|
||||
self.bold = True
|
||||
|
||||
def deselect(self, *args):
|
||||
self.bold = False
|
||||
|
||||
def __repr__(self):
|
||||
return self.text
|
||||
|
||||
|
||||
Builder.load_string('''
|
||||
<ListView>:
|
||||
container: container
|
||||
|
@ -83,16 +192,6 @@ Builder.load_string('''
|
|||
|
||||
|
||||
class ListView(AbstractView):
|
||||
'''Implementation of an Abstract View as a vertical scrollable list.
|
||||
|
||||
From AbstractView we have these properties and methods:
|
||||
|
||||
- adapter (in usage here, a ListAdapter)
|
||||
- item_view_instances, a dictionary with data item indices as keys
|
||||
to the item view instances created and held in the ListAdapter
|
||||
- set_item_view() and get_item_view() methods to item view instances
|
||||
|
||||
'''
|
||||
|
||||
divider = ObjectProperty(None)
|
||||
'''[TODO] Not used.
|
||||
|
@ -105,10 +204,10 @@ class ListView(AbstractView):
|
|||
container = ObjectProperty(None)
|
||||
'''The container is a GridLayout widget held within a ScrollView widget.
|
||||
(See the associated kv block in the Builder.load_string() setup). Item
|
||||
view instances managed in the ListAdapter are added to this container. The
|
||||
container is cleared with a call to contain.clear_widgets() when the list
|
||||
is rebuilt in the populate() method. A padding Widget instance is also
|
||||
added as needed, depending on the row height calculations.
|
||||
view instances managed and provided by the ListAdapter are added to this
|
||||
container. The container is cleared with a call to clear_widgets() when
|
||||
the list is rebuilt by the populate() method. A padding Widget instance
|
||||
is also added as needed, depending on the row height calculations.
|
||||
'''
|
||||
|
||||
row_height = NumericProperty(None)
|
||||
|
@ -198,7 +297,6 @@ class ListView(AbstractView):
|
|||
continue
|
||||
sizes[index] = item_view.height
|
||||
container.add_widget(item_view)
|
||||
|
||||
else:
|
||||
available_height = self.height
|
||||
real_height = 0
|
||||
|
|
Loading…
Reference in New Issue