mirror of https://github.com/kivy/kivy.git
Added SimpleListAdapter to be the bare-bones, no selection, list adapter. ListAdapter now uses SimpleListAdapter if no adapter is provided. ListAdapter implements SelectionSupport, as before, but now subclasses SimpleListAdapter. Renamed SelectableListsAdapter to ListsAdapter. Updated examples and tests accordingly. Updated and fixed code examples in the docs for ListView.
This commit is contained in:
parent
b27adda021
commit
397619e58d
|
@ -1,5 +1,5 @@
|
|||
from kivy.adapters.listadapter import ListAdapter, \
|
||||
SelectableListsAdapter
|
||||
ListsAdapter
|
||||
from kivy.uix.gridlayout import GridLayout
|
||||
from kivy.uix.label import Label
|
||||
from kivy.uix.listview import ListView, ListItemButton
|
||||
|
@ -78,9 +78,9 @@ class CascadingView(GridLayout):
|
|||
# Fruits, for a given category, in the middle:
|
||||
#
|
||||
fruits_list_adapter = \
|
||||
SelectableListsAdapter(
|
||||
ListsAdapter(
|
||||
observed_list_adapter=fruit_categories_list_adapter,
|
||||
selectable_lists_dict=fruit_categories,
|
||||
lists_dict=fruit_categories,
|
||||
data=fruit_categories[categories[0]],
|
||||
args_converter=list_item_args_converter,
|
||||
selection_mode='single',
|
||||
|
|
|
@ -1,11 +1,8 @@
|
|||
from kivy.adapters.listadapter import ListAdapter
|
||||
from kivy.adapters.mixins.selection import SelectableItem
|
||||
from kivy.uix.listview import ListItemButton, ListItemLabel, \
|
||||
CompositeListItem, ListView
|
||||
|
||||
from kivy.uix.boxlayout import BoxLayout
|
||||
from kivy.uix.gridlayout import GridLayout
|
||||
from kivy.properties import ObjectProperty, ListProperty
|
||||
|
||||
|
||||
class MainView(GridLayout):
|
||||
|
@ -35,7 +32,7 @@ class MainView(GridLayout):
|
|||
'kwargs': {'text': "Right",
|
||||
'merge_text': True,
|
||||
'delimiter': '-'}}]}
|
||||
|
||||
|
||||
# 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:
|
||||
|
|
|
@ -18,23 +18,17 @@ from kivy.adapters.adapter import Adapter
|
|||
from kivy.adapters.mixins.selection import SelectionSupport
|
||||
|
||||
|
||||
class ListAdapter(SelectionSupport, Adapter):
|
||||
class SimpleListAdapter(Adapter):
|
||||
'''ListAdapter is an adapter around a simple Python list.
|
||||
|
||||
From the SelectionSupport mixin, ListAdapter has these properties:
|
||||
|
||||
- selection
|
||||
- selection_mode
|
||||
- allow_empty_selection
|
||||
|
||||
and several methods used in selection operations.
|
||||
|
||||
From Adapter, ListAdapter gets these properties:
|
||||
From Adapter, SimpleListAdapter gets these properties:
|
||||
|
||||
- cls, for the list item class to use to instantiate row view
|
||||
instances
|
||||
|
||||
- template, an optional kv template to use instead of a python class
|
||||
(to use instead of cls)
|
||||
|
||||
- args_converter, an optional function to transform data item argument
|
||||
sets, in preparation for either a cls instantiation,
|
||||
or a kv template invocation
|
||||
|
@ -49,13 +43,10 @@ class ListAdapter(SelectionSupport, Adapter):
|
|||
|
||||
def __init__(self, **kwargs):
|
||||
if 'data' not in kwargs:
|
||||
raise Exception('ListAdapter: input must include data argument')
|
||||
raise Exception('list adapter: input must include data argument')
|
||||
if type(kwargs['data']) not in (tuple, list):
|
||||
raise Exception('ListAdapter: data must be a tuple or list')
|
||||
super(ListAdapter, self).__init__(**kwargs)
|
||||
|
||||
# Reset and update selection, in SelectionSupport, if data changes.
|
||||
self.bind(data=self.initialize_selection)
|
||||
raise Exception('list adapter: data must be a tuple or list')
|
||||
super(SimpleListAdapter, self).__init__(**kwargs)
|
||||
|
||||
def get_count(self):
|
||||
return len(self.data)
|
||||
|
@ -65,11 +56,31 @@ class ListAdapter(SelectionSupport, Adapter):
|
|||
return None
|
||||
return self.data[index]
|
||||
|
||||
|
||||
class ListAdapter(SelectionSupport, SimpleListAdapter):
|
||||
'''From the SelectionSupport mixin, ListAdapter has these properties:
|
||||
|
||||
- selection
|
||||
- selection_mode
|
||||
- allow_empty_selection
|
||||
|
||||
and several methods used in selection operations.
|
||||
|
||||
If you wish to have a bare-bones list adapter, without selection, use
|
||||
SimpleListAdapter.
|
||||
'''
|
||||
def __init__(self, **kwargs):
|
||||
super(ListAdapter, self).__init__(**kwargs)
|
||||
|
||||
# Reset and update selection, in SelectionSupport, if data changes.
|
||||
self.bind(data=self.initialize_selection)
|
||||
|
||||
def get_view(self, index):
|
||||
'''This method is identical to the one in Adapter, but here we pass
|
||||
self to the list item class (cls) instantiation, so that the list
|
||||
item class, required to mix in SelectableItem, will have access to
|
||||
ListAdapter for calling to SelectionSupport methods.
|
||||
'''This method is identical to the one in Adapter and
|
||||
SimpleListAdapter, but here we pass self to the list item class (cls)
|
||||
instantiation, so that the list item class, required to mix in
|
||||
SelectableItem, will have access to ListAdapter for calling to
|
||||
SelectionSupport methods.
|
||||
'''
|
||||
item = self.get_item(index)
|
||||
if item is None:
|
||||
|
@ -91,27 +102,31 @@ class ListAdapter(SelectionSupport, Adapter):
|
|||
return Builder.template(self.template, **item_args)
|
||||
|
||||
def on_selection_change(self, *args):
|
||||
'''on_selection_change() is the default handler for the
|
||||
on_selection_change event, which is registered in SelectionSupport.
|
||||
'''
|
||||
pass
|
||||
|
||||
|
||||
class SelectableListsAdapter(ListAdapter):
|
||||
'''SelectableListsAdapter is specialized for use in chaining
|
||||
list_adapters in a "cascade," where selection of the first,
|
||||
changes the selection of the next, and so on.
|
||||
class ListsAdapter(ListAdapter):
|
||||
'''ListsAdapter is specialized for managing a dict of lists. It has wide
|
||||
application, because a list of lists is a common need. ListsAdapter may
|
||||
be used for chaining several list_adapters in a "cascade," where selection
|
||||
of the first, changes the selection of the next, and so on.
|
||||
'''
|
||||
|
||||
selectable_lists_dict = DictProperty({})
|
||||
lists_dict = DictProperty({})
|
||||
'''The selection of the observed_list_adapter, which must be single
|
||||
selection here, is the key into selectable_lists_dict, which is a dict
|
||||
selection here, is the key into lists_dict, which is a dict
|
||||
of list item lists.
|
||||
'''
|
||||
|
||||
observed_list_adapter = ObjectProperty(None)
|
||||
|
||||
selectable_lists_dict = DictProperty({})
|
||||
lists_dict = DictProperty({})
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super(SelectableListsAdapter, self).__init__(**kwargs)
|
||||
super(ListsAdapter, self).__init__(**kwargs)
|
||||
self.observed_list_adapter.bind(
|
||||
on_selection_change=self.on_selection_change)
|
||||
|
||||
|
@ -122,7 +137,7 @@ class SelectableListsAdapter(ListAdapter):
|
|||
self.data = []
|
||||
return
|
||||
|
||||
self.data = self.selectable_lists_dict[str(observed_selection[0])]
|
||||
self.data = self.lists_dict[str(observed_selection[0])]
|
||||
|
||||
|
||||
class AccumulatingListAdapter(ListAdapter):
|
||||
|
|
|
@ -10,7 +10,7 @@ from kivy.uix.button import Button
|
|||
from kivy.uix.listview import ListView, ListItemButton
|
||||
from kivy.properties import NumericProperty, ListProperty, StringProperty
|
||||
from kivy.adapters.mixins.selection import SelectableItem
|
||||
from kivy.adapters.listadapter import ListAdapter, SelectableListsAdapter, \
|
||||
from kivy.adapters.listadapter import ListAdapter, ListsAdapter, \
|
||||
AccumulatingListAdapter
|
||||
|
||||
|
||||
|
@ -192,7 +192,7 @@ class ListAdapterTestCase(unittest.TestCase):
|
|||
self.assertEqual(selection_observer.call_count, 2)
|
||||
|
||||
|
||||
class SelectableListsAdapterTestCase(unittest.TestCase):
|
||||
class ListsAdapterTestCase(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.args_converter = lambda x: {'text': x,
|
||||
|
@ -220,13 +220,13 @@ class SelectableListsAdapterTestCase(unittest.TestCase):
|
|||
# Fruits, for a given category, are shown based on the fruit category
|
||||
# selected in the first categories list above. The selected item in
|
||||
# the first list is used as the key into a dict of lists of list
|
||||
# items to reset the data in SelectableListsAdapter's
|
||||
# items to reset the data in ListsAdapter's
|
||||
# observed_selection_changed() method.
|
||||
#
|
||||
# data is initially set to the first list of list items.
|
||||
#
|
||||
fruits_l_a = \
|
||||
SelectableListsAdapter(observed_list_adapter=fruit_categories_l_a,
|
||||
ListsAdapter(observed_list_adapter=fruit_categories_l_a,
|
||||
selectable_lists_dict=fruit_categories,
|
||||
data=fruit_categories[categories[0]],
|
||||
args_converter=self.args_converter,
|
||||
|
|
|
@ -21,29 +21,57 @@ From AbstractView we have these properties and methods:
|
|||
Basic Example
|
||||
-------------
|
||||
|
||||
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.
|
||||
|
||||
Here we make a list view with 100 items.
|
||||
|
||||
from kivy.uix.listview import ListView
|
||||
from kivy.uix.gridlayout import GridLayout
|
||||
|
||||
|
||||
class MainView(GridLayout):
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
kwargs['cols'] = 2
|
||||
kwargs['size_hint'] = (1.0, 1.0)
|
||||
super(MainView, self).__init__(**kwargs)
|
||||
|
||||
list_view = ListView([str(index) for index in xrange(100)])
|
||||
|
||||
self.add_widget(list_view)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
from kivy.base import runTouchApp
|
||||
runTouchApp(MainView(width=800))
|
||||
|
||||
Using a ListAdapter
|
||||
-------------------
|
||||
|
||||
The basic example above uses :class:`SimpleListAdapter` internally, an adapter
|
||||
that does not offer selection support.
|
||||
|
||||
For most uses of a list, however, selection support is needed. It is built in
|
||||
to :class:`ListAdapter`.
|
||||
|
||||
The view used for items in a list view can be totally custom, but to have a
|
||||
simple button, we may use the :class:`ListItemButton` class.
|
||||
|
||||
from kivy.adapters.list_adapter import ListAdapter
|
||||
from kivy.uix.listview import ListItem, ListView
|
||||
|
||||
data = ["Item {0}".format(index) for index in xrange(100)]
|
||||
|
||||
# A list view needs a list adapter to provide a mediating service to the
|
||||
# data, in the case here our data 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.
|
||||
# data, which is 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.
|
||||
# When an item is selected, its background color will change to red.
|
||||
list_adapter = ListAdapter(data=data,
|
||||
selection_mode='single',
|
||||
allow_empty_selection=False,
|
||||
cls=ListItem)
|
||||
cls=ListItemButton)
|
||||
|
||||
# The list view is a simple component. Just give it the list adapter that
|
||||
# will provide list item views based on its data.
|
||||
|
@ -55,65 +83,13 @@ Composite List Item 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:
|
||||
separate properties pertaining to the label. We add default buttons and a
|
||||
label in this example.
|
||||
|
||||
from kivy.adapters.listadapter import ListAdapter
|
||||
from kivy.adapters.mixins.selection import SelectableItem
|
||||
from kivy.uix.listview import ListItemButton, ListItemLabel, ListView
|
||||
|
||||
from kivy.uix.boxlayout import BoxLayout
|
||||
from kivy.uix.listview import ListItemButton, ListItemLabel, \
|
||||
CompositeListItem, ListView
|
||||
from kivy.uix.gridlayout import GridLayout
|
||||
from kivy.properties import ObjectProperty, ListProperty
|
||||
|
||||
|
||||
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 for the list_adapter, required by the SelectableItem
|
||||
# mixing, and a selection_target argument, setting it to self (to
|
||||
# this composite list item).
|
||||
kwargs['selection_target'] = self
|
||||
|
||||
kwargs['text']="left-{0}".format(kwargs['text'])
|
||||
self.left_button = ListItemButton(**kwargs)
|
||||
|
||||
kwargs['text']="middle-{0}".format(kwargs['text'])
|
||||
self.middle_label = ListItemLabel(**kwargs)
|
||||
|
||||
kwargs['text']="right-{0}".format(kwargs['text'])
|
||||
self.right_button = ListItemButton(**kwargs)
|
||||
|
||||
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):
|
||||
if self.middle_label is not None:
|
||||
return self.middle_label.text
|
||||
else:
|
||||
return 'empty'
|
||||
|
||||
|
||||
class MainView(GridLayout):
|
||||
|
@ -123,18 +99,50 @@ choice of layout, BoxLayout:
|
|||
kwargs['size_hint'] = (1.0, 1.0)
|
||||
super(MainView, self).__init__(**kwargs)
|
||||
|
||||
args_converter = \
|
||||
lambda x: \
|
||||
{'text': x,
|
||||
'size_hint_y': None,
|
||||
'height': 25,
|
||||
'cls_dicts': [{'cls': ListItemButton,
|
||||
'kwargs': {'text': "Left",
|
||||
'merge_text': True,
|
||||
'delimiter': '-'}},
|
||||
{'cls': ListItemLabel,
|
||||
'kwargs': {'text': "Middle",
|
||||
'merge_text': True,
|
||||
'delimiter': '-',
|
||||
'is_representing_cls': True}},
|
||||
{'cls': ListItemButton,
|
||||
'kwargs': {'text': "Right",
|
||||
'merge_text': True,
|
||||
'delimiter': '-'}}]}
|
||||
|
||||
# Here we create a list adapter with some item strings, passing
|
||||
# our CompositeListItem as the list item view class, and then we
|
||||
# create list view using this adapter:
|
||||
data = ["{0}".format(index) for index in xrange(100)]
|
||||
list_adapter = ListAdapter(data=data,
|
||||
# create a list view using this adapter:
|
||||
item_strings = ["{0}".format(index) for index in xrange(100)]
|
||||
|
||||
# And now the list adapter, constructed with the item_strings as
|
||||
# the data, and our args_converter() that will operate one each
|
||||
# item in the data to produce list item view instances from the
|
||||
# :class:`CompositeListItem` class.
|
||||
list_adapter = ListAdapter(data=item_strings,
|
||||
args_converter=args_converter,
|
||||
selection_mode='single',
|
||||
allow_empty_selection=False,
|
||||
cls=CompositeListItem)
|
||||
|
||||
# Pass the adapter to ListView.
|
||||
list_view = ListView(adapter=list_adapter)
|
||||
|
||||
self.add_widget(list_view)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
from kivy.base import runTouchApp
|
||||
runTouchApp(MainView(width=800))
|
||||
|
||||
Using With kv
|
||||
-------------
|
||||
|
||||
|
@ -333,10 +341,10 @@ class ListView(AbstractView):
|
|||
if 'adapter' not in kwargs:
|
||||
if item_strings is None:
|
||||
raise Exception('ListView: input needed, or an adapter')
|
||||
list_adapter = ListAdapter(data=item_strings,
|
||||
selection_mode='single',
|
||||
allow_empty_selection=False,
|
||||
cls=ListItemButton)
|
||||
list_adapter = SimpleListAdapter(data=item_strings,
|
||||
selection_mode='single',
|
||||
allow_empty_selection=False,
|
||||
cls=ListItemButton)
|
||||
kwargs['adapter'] = list_adapter
|
||||
|
||||
super(ListView, self).__init__(**kwargs)
|
||||
|
|
Loading…
Reference in New Issue