2012-07-13 11:07:23 +00:00
|
|
|
from kivy.uix.gridlayout import GridLayout
|
|
|
|
from kivy.uix.label import Label
|
|
|
|
from kivy.uix.button import Button
|
|
|
|
from kivy.uix.listview import ListView, ListAdapter
|
|
|
|
from kivy.uix.mixins.selection import SelectionObserver, SelectableItem
|
2012-07-13 11:28:54 +00:00
|
|
|
from kivy.properties import ListProperty, StringProperty, ObjectProperty
|
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
|
|
|
|
# cascading from the selection of one list view to another.
|
2012-07-13 11:07:23 +00:00
|
|
|
|
2012-07-13 11:18:05 +00:00
|
|
|
# Generic list item will do fine for both list views:
|
2012-07-13 11:07:23 +00:00
|
|
|
|
|
|
|
class ListItem(SelectableItem, Button):
|
|
|
|
selected_color = ListProperty([1., 0., 0., 1])
|
2012-07-13 17:48:02 +00:00
|
|
|
deselected_color = None
|
2012-07-13 11:07:23 +00:00
|
|
|
|
|
|
|
def __init__(self, selection_callback, **kwargs):
|
|
|
|
self.selection_callback = selection_callback
|
|
|
|
super(ListItem, self).__init__(**kwargs)
|
|
|
|
|
2012-07-13 17:48:02 +00:00
|
|
|
# Set deselected_color to be default Button bg color.
|
|
|
|
self.deselected_color = self.background_color
|
|
|
|
|
2012-07-13 11:07:23 +00:00
|
|
|
self.bind(on_release=self.handle_selection)
|
|
|
|
|
|
|
|
def handle_selection(self, button):
|
|
|
|
if self.is_selected:
|
|
|
|
self.select()
|
|
|
|
else:
|
|
|
|
self.deselect()
|
|
|
|
|
|
|
|
self.selection_callback(self)
|
|
|
|
|
|
|
|
def select(self, *args):
|
2012-07-13 16:35:42 +00:00
|
|
|
print self.text, 'is now selected'
|
2012-07-13 11:07:23 +00:00
|
|
|
self.background_color = self.selected_color
|
|
|
|
|
|
|
|
def deselect(self, *args):
|
2012-07-13 16:35:42 +00:00
|
|
|
print self.text, 'is now unselected'
|
2012-07-13 11:07:23 +00:00
|
|
|
self.background_color = self.deselected_color
|
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
return self.text
|
|
|
|
|
|
|
|
|
|
|
|
class FruitsListView(SelectionObserver, ListView):
|
|
|
|
# The fruit categories list view can be a simple ListView,
|
|
|
|
# but the fruits list, showing fruits for a given category,
|
|
|
|
# will be a SelectionObserver, changing when fruit category
|
|
|
|
# changes.
|
|
|
|
def __init__(self, **kwargs):
|
|
|
|
super(FruitsListView, self).__init__(**kwargs)
|
|
|
|
|
|
|
|
def observed_selection_changed(self, observed_selection):
|
|
|
|
if len(observed_selection.selection) == 0:
|
|
|
|
return
|
|
|
|
|
2012-07-14 12:27:46 +00:00
|
|
|
self.item_view_instances = {}
|
2012-07-13 16:35:42 +00:00
|
|
|
|
|
|
|
self.selection = []
|
|
|
|
|
2012-07-13 11:07:23 +00:00
|
|
|
selected_object = observed_selection.selection[0]
|
|
|
|
|
|
|
|
if type(selected_object) is str:
|
2012-07-13 11:28:54 +00:00
|
|
|
fruit_category = selected_object
|
2012-07-13 11:07:23 +00:00
|
|
|
else:
|
2012-07-13 11:28:54 +00:00
|
|
|
fruit_category = str(selected_object)
|
|
|
|
|
2012-07-14 12:27:46 +00:00
|
|
|
# Reset item_keys for the adapter. This will trigger a call
|
2012-07-13 16:35:42 +00:00
|
|
|
# to update_selection().
|
2012-07-14 12:27:46 +00:00
|
|
|
self.adapter.item_keys = fruit_categories[fruit_category]
|
2012-07-13 11:07:23 +00:00
|
|
|
|
|
|
|
self.populate()
|
|
|
|
|
2012-07-13 16:35:42 +00:00
|
|
|
# Manually call update_selection(), because items are new objects.
|
|
|
|
self.adapter.update_selection()
|
|
|
|
|
2012-07-13 11:07:23 +00:00
|
|
|
print 'just added or updated fruit category'
|
|
|
|
|
|
|
|
|
|
|
|
class DetailView(SelectionObserver, GridLayout):
|
|
|
|
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
|
|
|
|
|
|
|
def observed_selection_changed(self, observed_selection):
|
|
|
|
if len(observed_selection.selection) == 0:
|
|
|
|
return
|
|
|
|
|
|
|
|
selected_object = observed_selection.selection[0]
|
|
|
|
|
|
|
|
if type(selected_object) is str:
|
|
|
|
self.fruit_name = selected_object
|
|
|
|
else:
|
|
|
|
self.fruit_name = str(selected_object)
|
|
|
|
print 'just added or updated detail label'
|
|
|
|
|
|
|
|
|
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
|
|
|
'''
|
|
|
|
|
|
|
|
fruit_categories_list_adapter = ObjectProperty(None)
|
|
|
|
fruit_categories_list_view = ObjectProperty(None)
|
|
|
|
|
|
|
|
fruits_list_adapter = ObjectProperty(None)
|
|
|
|
fruits_list_view = ObjectProperty(None)
|
|
|
|
|
|
|
|
detail_view = ObjectProperty(None)
|
|
|
|
|
|
|
|
def __init__(self, fruit_categories, fruits, **kwargs):
|
|
|
|
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:
|
|
|
|
#
|
|
|
|
self.fruit_categories_list_adapter = \
|
|
|
|
ListAdapter(fruit_categories,
|
2012-07-14 12:27:46 +00:00
|
|
|
item_view_args_converter=list_item_args_converter,
|
2012-07-13 11:07:23 +00:00
|
|
|
selection_mode='single',
|
|
|
|
allow_empty_selection=False,
|
2012-07-14 12:27:46 +00:00
|
|
|
item_view_cls=ListItem)
|
2012-07-13 11:07:23 +00:00
|
|
|
self.fruit_categories_list_view = \
|
|
|
|
ListView(adapter=self.fruit_categories_list_adapter,
|
|
|
|
size_hint=(.2, 1.0))
|
|
|
|
self.add_widget(self.fruit_categories_list_view)
|
|
|
|
|
|
|
|
# Fruits, for a given category, in the middle:
|
|
|
|
#
|
|
|
|
self.fruits_list_adapter = \
|
|
|
|
ListAdapter(fruits,
|
2012-07-14 12:27:46 +00:00
|
|
|
item_view_args_converter=list_item_args_converter,
|
2012-07-13 11:07:23 +00:00
|
|
|
selection_mode='single',
|
|
|
|
allow_empty_selection=False,
|
2012-07-14 12:27:46 +00:00
|
|
|
item_view_cls=ListItem)
|
2012-07-13 11:07:23 +00:00
|
|
|
self.fruits_list_view = \
|
|
|
|
FruitsListView(adapter=self.fruits_list_adapter,
|
|
|
|
size_hint=(.2, 1.0))
|
|
|
|
self.add_widget(self.fruits_list_view)
|
|
|
|
|
|
|
|
# Set the fruits_list_view as an observer of the selection of
|
|
|
|
# the fruit category.
|
|
|
|
self.fruit_categories_list_adapter.register_selection_observer(
|
|
|
|
self.fruits_list_view)
|
|
|
|
|
|
|
|
# Detail view, for a given fruit, on the right:
|
|
|
|
#
|
|
|
|
self.detail_view = DetailView(size_hint=(.6, 1.0))
|
|
|
|
self.add_widget(self.detail_view)
|
|
|
|
|
|
|
|
# Manually initialize detail view to show first object of list view,
|
|
|
|
# which will be auto-selected, but the observed_selection_changed
|
|
|
|
# call would have already fired.
|
|
|
|
#
|
|
|
|
self.fruits_list_adapter.register_selection_observer(self.detail_view)
|
|
|
|
|
|
|
|
# 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-13 11:18:05 +00:00
|
|
|
fruit_categories_detail = CascadingView(fruit_categories.keys(),
|
2012-07-13 11:07:23 +00:00
|
|
|
fruit_categories['Melons'], width=800)
|
|
|
|
|
|
|
|
runTouchApp(fruit_categories_detail)
|