2012-07-30 02:32:03 +00:00
|
|
|
from kivy.adapters.listadapter import ListAdapter, \
|
2012-07-31 22:44:37 +00:00
|
|
|
ListsAdapter
|
2012-07-13 11:07:23 +00:00
|
|
|
from kivy.uix.gridlayout import GridLayout
|
|
|
|
from kivy.uix.label import Label
|
2012-07-31 03:39:15 +00:00
|
|
|
from kivy.uix.listview import ListView, ListItemButton
|
2012-07-31 19:08:00 +00:00
|
|
|
from kivy.properties import StringProperty
|
2012-07-13 11:07:23 +00:00
|
|
|
|
2012-07-13 11:18:05 +00:00
|
|
|
# This is an expansion on the "master-detail" example to illustrate
|
2012-07-30 03:47:32 +00:00
|
|
|
# cascading from the selection of one list view to another. In this
|
|
|
|
# example the lists are restricted to single selection. The list on the
|
|
|
|
# left is a simple list. The list in the middle is specialized for
|
|
|
|
# observing the selection in the first, and using that item as the key
|
|
|
|
# into a dict providing its own list items. The view on the right is
|
|
|
|
# the sames as the DetailView in the master-detail example.
|
2012-07-13 11:07:23 +00:00
|
|
|
|
|
|
|
|
2012-07-31 19:08:00 +00:00
|
|
|
class DetailView(GridLayout):
|
2012-07-13 11:07:23 +00:00
|
|
|
fruit_name = StringProperty('')
|
|
|
|
|
|
|
|
def __init__(self, **kwargs):
|
|
|
|
kwargs['cols'] = 2
|
|
|
|
super(DetailView, self).__init__(**kwargs)
|
|
|
|
self.bind(fruit_name=self.redraw)
|
|
|
|
|
|
|
|
def redraw(self, *args):
|
|
|
|
self.clear_widgets()
|
|
|
|
self.add_widget(Label(text="Name:", halign='right'))
|
|
|
|
self.add_widget(Label(text=self.fruit_name))
|
|
|
|
for category in descriptors:
|
|
|
|
self.add_widget(Label(text="{0}:".format(category),
|
|
|
|
halign='right'))
|
2012-07-13 11:12:00 +00:00
|
|
|
self.add_widget(
|
|
|
|
Label(text=str(fruit_data[self.fruit_name][category])))
|
2012-07-13 11:07:23 +00:00
|
|
|
|
2012-07-31 19:08:00 +00:00
|
|
|
def on_selection_change(self, list_adapter, *args):
|
2012-07-15 13:07:20 +00:00
|
|
|
if len(list_adapter.selection) == 0:
|
2012-07-13 11:07:23 +00:00
|
|
|
return
|
|
|
|
|
2012-07-15 13:07:20 +00:00
|
|
|
selected_object = list_adapter.selection[0]
|
2012-07-13 11:07:23 +00:00
|
|
|
|
|
|
|
if type(selected_object) is str:
|
|
|
|
self.fruit_name = selected_object
|
|
|
|
else:
|
|
|
|
self.fruit_name = str(selected_object)
|
2012-07-31 19:08:00 +00:00
|
|
|
|
|
|
|
self.redraw()
|
2012-07-13 11:07:23 +00:00
|
|
|
|
|
|
|
|
2012-07-13 11:18:05 +00:00
|
|
|
class CascadingView(GridLayout):
|
|
|
|
'''Implementation of a master-detail style view, with a scrollable list
|
|
|
|
of fruit categories on the left (source list), a list of fruits for the
|
|
|
|
selected category in the middle, and a detail view on the right.
|
2012-07-13 11:07:23 +00:00
|
|
|
'''
|
|
|
|
|
2012-07-15 15:55:04 +00:00
|
|
|
def __init__(self, **kwargs):
|
2012-07-13 11:07:23 +00:00
|
|
|
kwargs['cols'] = 3
|
|
|
|
kwargs['size_hint'] = (1.0, 1.0)
|
2012-07-13 11:18:05 +00:00
|
|
|
super(CascadingView, self).__init__(**kwargs)
|
2012-07-13 11:07:23 +00:00
|
|
|
|
2012-07-14 12:27:46 +00:00
|
|
|
list_item_args_converter = lambda x: {'text': x,
|
|
|
|
'size_hint_y': None,
|
|
|
|
'height': 25}
|
2012-07-13 11:07:23 +00:00
|
|
|
|
|
|
|
# Fruit categories list on the left:
|
|
|
|
#
|
2012-07-15 15:55:04 +00:00
|
|
|
categories = sorted(fruit_categories.keys())
|
2012-07-30 03:47:32 +00:00
|
|
|
fruit_categories_list_adapter = \
|
2012-07-31 21:13:17 +00:00
|
|
|
ListAdapter(data=categories,
|
2012-07-14 15:55:14 +00:00
|
|
|
args_converter=list_item_args_converter,
|
2012-07-13 11:07:23 +00:00
|
|
|
selection_mode='single',
|
|
|
|
allow_empty_selection=False,
|
2012-07-31 03:39:15 +00:00
|
|
|
cls=ListItemButton)
|
2012-07-30 03:47:32 +00:00
|
|
|
fruit_categories_list_view = \
|
|
|
|
ListView(adapter=fruit_categories_list_adapter,
|
Made a new adapter called ChainedListAdapter to contain code that had been done at a higher level in a custom list view. Now ListView is used directly, and the selection chaining, or cascading, from list to list is pushed down to the adapters. SelectionObserver was modified to help in this reorganization. ChainedListAdapter uses the modified SelectionObserver, and implements the observed_selection_changed() method to update its selection. ChainedListAdapter contains a selectable_lists_dict, containing the lists of list items that change when selection of the observed list adapter changes, so the observed list selection is the key into this dict. Updated the listview widget examples, which became considerably simpler. Added a _trigger_hard_populate() method in ListView that fires when self.adapter.data changes, where the "hard" reference is to a reset of the item_view_instances dict to an empty before calling populate(). This is an effort to get the list to stay in sync with its list_adapter, but initial selection, despite all of these changes, still is not shown in the list view display.
2012-07-29 18:01:56 +00:00
|
|
|
size_hint=(.2, 1.0))
|
2012-07-30 03:47:32 +00:00
|
|
|
self.add_widget(fruit_categories_list_view)
|
2012-07-13 11:07:23 +00:00
|
|
|
|
|
|
|
# Fruits, for a given category, in the middle:
|
|
|
|
#
|
2012-07-30 03:47:32 +00:00
|
|
|
fruits_list_adapter = \
|
2012-07-31 22:44:37 +00:00
|
|
|
ListsAdapter(
|
2012-07-30 03:47:32 +00:00
|
|
|
observed_list_adapter=fruit_categories_list_adapter,
|
2012-07-31 22:44:37 +00:00
|
|
|
lists_dict=fruit_categories,
|
2012-07-29 18:59:15 +00:00
|
|
|
data=fruit_categories[categories[0]],
|
|
|
|
args_converter=list_item_args_converter,
|
|
|
|
selection_mode='single',
|
|
|
|
allow_empty_selection=False,
|
2012-07-31 03:39:15 +00:00
|
|
|
cls=ListItemButton)
|
2012-07-30 03:47:32 +00:00
|
|
|
fruits_list_view = \
|
|
|
|
ListView(adapter=fruits_list_adapter,
|
2012-07-29 22:49:52 +00:00
|
|
|
size_hint=(.2, 1.0))
|
2012-07-31 19:08:00 +00:00
|
|
|
fruit_categories_list_adapter.bind(
|
|
|
|
on_selection_change=fruits_list_adapter.on_selection_change)
|
2012-07-30 03:47:32 +00:00
|
|
|
self.add_widget(fruits_list_view)
|
2012-07-13 11:07:23 +00:00
|
|
|
|
|
|
|
# Detail view, for a given fruit, on the right:
|
|
|
|
#
|
2012-07-30 03:47:32 +00:00
|
|
|
detail_view = DetailView(
|
|
|
|
observed_list_adapter=fruits_list_adapter,
|
Made a new adapter called ChainedListAdapter to contain code that had been done at a higher level in a custom list view. Now ListView is used directly, and the selection chaining, or cascading, from list to list is pushed down to the adapters. SelectionObserver was modified to help in this reorganization. ChainedListAdapter uses the modified SelectionObserver, and implements the observed_selection_changed() method to update its selection. ChainedListAdapter contains a selectable_lists_dict, containing the lists of list items that change when selection of the observed list adapter changes, so the observed list selection is the key into this dict. Updated the listview widget examples, which became considerably simpler. Added a _trigger_hard_populate() method in ListView that fires when self.adapter.data changes, where the "hard" reference is to a reset of the item_view_instances dict to an empty before calling populate(). This is an effort to get the list to stay in sync with its list_adapter, but initial selection, despite all of these changes, still is not shown in the list view display.
2012-07-29 18:01:56 +00:00
|
|
|
size_hint=(.6, 1.0))
|
2012-07-31 19:08:00 +00:00
|
|
|
fruits_list_adapter.bind(
|
|
|
|
on_selection_change=detail_view.on_selection_change)
|
2012-07-30 03:47:32 +00:00
|
|
|
self.add_widget(detail_view)
|
2012-07-13 11:07:23 +00:00
|
|
|
|
2012-07-31 19:08:00 +00:00
|
|
|
|
2012-07-13 11:07:23 +00:00
|
|
|
# Data from http://www.fda.gov/Food/LabelingNutrition/\
|
|
|
|
# FoodLabelingGuidanceRegulatoryInformation/\
|
|
|
|
# InformationforRestaurantsRetailEstablishments/\
|
|
|
|
# ucm063482.htm
|
|
|
|
fruit_categories = \
|
|
|
|
{'Melons': ['Cantaloupe', 'Honeydew Melon', 'Watermelon'],
|
2012-07-13 17:51:15 +00:00
|
|
|
'Tree Fruits': ['Apple', 'Avocado, California', 'Banana', 'Nectarine',
|
|
|
|
'Peach', 'Pear', 'Pineapple', 'Plums',
|
|
|
|
'Sweet Cherries'],
|
2012-07-13 11:07:23 +00:00
|
|
|
'Citrus Fruits': ['Grapefruit', 'Lemon', 'Lime', 'Orange',
|
|
|
|
'Tangerine'],
|
|
|
|
'Miscellaneous Fruits': ['Grapes', 'Kiwifruit', 'Strawberries']}
|
|
|
|
descriptors = """(gram weight/ ounce weight) Calories Calories from Fa
|
|
|
|
t Total Fat Sodium Potassium Total Carbo-hydrate Dietary Fiber Suga
|
|
|
|
rs Protein Vitamin A Vitamin C Calcium Iron""".replace('\n', '')
|
|
|
|
descriptors = [item.strip() for item in descriptors.split('\t')]
|
|
|
|
units = """(g) (%DV) (mg) (%DV) (mg) (%DV) (g) (%DV) (g)
|
|
|
|
(%DV) (g) (g) (%DV) (%DV) (%DV) (%DV)""".replace('\n', '')
|
|
|
|
units = [item.strip() for item in units.split('\t')]
|
|
|
|
raw_fruit_data = [
|
|
|
|
{'name':'Apple',
|
|
|
|
'Serving Size': '1 large (242 g/8 oz)',
|
|
|
|
'data': [130, 0, 0, 0, 0, 0, 260, 7, 34, 11, 5, 20, 25, 1, 2, 8, 2, 2]},
|
|
|
|
{'name':'Avocado, California',
|
|
|
|
'Serving Size': '1/5 medium (30 g/1.1 oz)',
|
|
|
|
'data': [50, 35, 4.5, 7, 0, 0, 140, 4, 3, 1, 1, 4, 0, 1, 0, 4, 0, 2]},
|
|
|
|
{'name':'Banana',
|
|
|
|
'Serving Size': '1 medium (126 g/4.5 oz)',
|
|
|
|
'data': [110, 0, 0, 0, 0, 0, 450, 13, 30, 10, 3, 12, 19, 1, 2, 15, 0, 2]},
|
|
|
|
{'name':'Cantaloupe',
|
|
|
|
'Serving Size': '1/4 medium (134 g/4.8 oz)',
|
|
|
|
'data': [50, 0, 0, 0, 20, 1, 240, 7, 12, 4, 1, 4, 11, 1, 120, 80, 2, 2]},
|
|
|
|
{'name':'Grapefruit',
|
|
|
|
'Serving Size': '1/2 medium (154 g/5.5 oz)',
|
|
|
|
'data': [60, 0, 0, 0, 0, 0, 160, 5, 15, 5, 2, 8, 11, 1, 35, 100, 4, 0]},
|
|
|
|
{'name':'Grapes',
|
|
|
|
'Serving Size': '3/4 cup (126 g/4.5 oz)',
|
|
|
|
'data': [90, 0, 0, 0, 15, 1, 240, 7, 23, 8, 1, 4, 20, 0, 0, 2, 2, 0]},
|
|
|
|
{'name':'Honeydew Melon',
|
|
|
|
'Serving Size': '1/10 medium melon (134 g/4.8 oz)',
|
|
|
|
'data': [50, 0, 0, 0, 30, 1, 210, 6, 12, 4, 1, 4, 11, 1, 2, 45, 2, 2]},
|
|
|
|
{'name':'Kiwifruit',
|
|
|
|
'Serving Size': '2 medium (148 g/5.3 oz)',
|
|
|
|
'data': [90, 10, 1, 2, 0, 0, 450, 13, 20, 7, 4, 16, 13, 1, 2, 240, 4, 2]},
|
|
|
|
{'name':'Lemon',
|
|
|
|
'Serving Size': '1 medium (58 g/2.1 oz)',
|
|
|
|
'data': [15, 0, 0, 0, 0, 0, 75, 2, 5, 2, 2, 8, 2, 0, 0, 40, 2, 0]},
|
|
|
|
{'name':'Lime',
|
|
|
|
'Serving Size': '1 medium (67 g/2.4 oz)',
|
|
|
|
'data': [20, 0, 0, 0, 0, 0, 75, 2, 7, 2, 2, 8, 0, 0, 0, 35, 0, 0]},
|
|
|
|
{'name':'Nectarine',
|
|
|
|
'Serving Size': '1 medium (140 g/5.0 oz)',
|
|
|
|
'data': [60, 5, 0.5, 1, 0, 0, 250, 7, 15, 5, 2, 8, 11, 1, 8, 15, 0, 2]},
|
|
|
|
{'name':'Orange',
|
|
|
|
'Serving Size': '1 medium (154 g/5.5 oz)',
|
|
|
|
'data': [80, 0, 0, 0, 0, 0, 250, 7, 19, 6, 3, 12, 14, 1, 2, 130, 6, 0]},
|
|
|
|
{'name':'Peach',
|
|
|
|
'Serving Size': '1 medium (147 g/5.3 oz)',
|
|
|
|
'data': [60, 0, 0.5, 1, 0, 0, 230, 7, 15, 5, 2, 8, 13, 1, 6, 15, 0, 2]},
|
|
|
|
{'name':'Pear',
|
|
|
|
'Serving Size': '1 medium (166 g/5.9 oz)',
|
|
|
|
'data': [100, 0, 0, 0, 0, 0, 190, 5, 26, 9, 6, 24, 16, 1, 0, 10, 2, 0]},
|
|
|
|
{'name':'Pineapple',
|
|
|
|
'Serving Size': '2 slices, 3" diameter, 3/4" thick (112 g/4 oz)',
|
|
|
|
'data': [50, 0, 0, 0, 10, 0, 120, 3, 13, 4, 1, 4, 10, 1, 2, 50, 2, 2]},
|
|
|
|
{'name':'Plums',
|
|
|
|
'Serving Size': '2 medium (151 g/5.4 oz)',
|
|
|
|
'data': [70, 0, 0, 0, 0, 0, 230, 7, 19, 6, 2, 8, 16, 1, 8, 10, 0, 2]},
|
|
|
|
{'name':'Strawberries',
|
|
|
|
'Serving Size': '8 medium (147 g/5.3 oz)',
|
|
|
|
'data': [50, 0, 0, 0, 0, 0, 170, 5, 11, 4, 2, 8, 8, 1, 0, 160, 2, 2]},
|
|
|
|
{'name':'Sweet Cherries',
|
|
|
|
'Serving Size': '21 cherries; 1 cup (140 g/5.0 oz)',
|
|
|
|
'data': [100, 0, 0, 0, 0, 0, 350, 10, 26, 9, 1, 4, 16, 1, 2, 15, 2, 2]},
|
|
|
|
{'name':'Tangerine',
|
|
|
|
'Serving Size': '1 medium (109 g/3.9 oz)',
|
|
|
|
'data': [50, 0, 0, 0, 0, 0, 160, 5, 13, 4, 2, 8, 9, 1, 6, 45, 4, 0]},
|
|
|
|
{'name':'Watermelon',
|
|
|
|
'Serving Size': '1/18 medium melon; 2 cups diced pieces (280 g/10.0 oz)',
|
|
|
|
'data': [80, 0, 0, 0, 0, 0, 270, 8, 21, 7, 1, 4, 20, 1, 30, 25, 2, 4]}]
|
|
|
|
|
|
|
|
fruit_data = {}
|
|
|
|
descriptors_and_units = dict(zip(descriptors, units))
|
|
|
|
for row in raw_fruit_data:
|
|
|
|
fruit_data[row['name']] = {}
|
|
|
|
fruit_data[row['name']] = dict({'Serving Size': row['Serving Size']},
|
|
|
|
**dict(zip(descriptors_and_units.keys(), row['data'])))
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
|
|
|
|
from kivy.base import runTouchApp
|
|
|
|
|
|
|
|
# All fruit categories will be shown in the left left (first argument),
|
|
|
|
# and the first category will be auto-selected -- Melons. So, set the
|
|
|
|
# second list to show the melon fruits (second argument).
|
2012-07-15 15:55:04 +00:00
|
|
|
runTouchApp(CascadingView(width=800))
|