from kivy.adapters.dictadapter import DictAdapter from kivy.properties import NumericProperty, ListProperty, \ BooleanProperty, AliasProperty, ObjectProperty from kivy.uix.boxlayout import BoxLayout from kivy.uix.gridlayout import GridLayout from kivy.uix.listview import ListView, ListItemButton from kivy.uix.label import Label from kivy.uix.button import Button from kivy.uix.widget import Widget class OpsDictAdapter(DictAdapter): listview_id = NumericProperty(0) owning_view = ObjectProperty(None) def __init__(self, **kwargs): self.listview_id = kwargs['listview_id'] super(OpsDictAdapter, self).__init__(**kwargs) def on_selection_change(self, *args): for i in range(len(self.selection)): listview_selection_buttons[self.listview_id][i].text = \ self.selection[i].text if self.listview_id is 0: # Scroll to the most recently selected item. if len(self.selection) > 0: print('selection', self.selection) self.owning_view.scroll_to( index=self.sorted_keys.index(self.selection[-1].text)) elif self.listview_id is 1: # Scroll to the selected item that is the minimum of a sort. if len(self.selection) > 0: self.owning_view.scroll_to( index=self.sorted_keys.index( sorted([sel.text for sel in self.selection])[0])) elif self.listview_id is 2: # Scroll to the selected item that is the maximum of a sort. if len(self.selection) > 0: self.owning_view.scroll_to( index=self.sorted_keys.index( sorted([sel.text for sel in self.selection])[-1])) class SelectionMonitor(Widget): def get_count_string(self): return "Total sel: " + str(self.sel_count_0 + self.sel_count_1 + self.sel_count_2 + self.sel_count_3 + self.sel_count_4 + self.sel_count_5 + self.sel_count_6) def set_count_string(self, value): self.count_string = value sel_count_0 = NumericProperty(0) sel_count_1 = NumericProperty(0) sel_count_2 = NumericProperty(0) sel_count_3 = NumericProperty(0) sel_count_4 = NumericProperty(0) sel_count_5 = NumericProperty(0) sel_count_6 = NumericProperty(0) count_string = AliasProperty(get_count_string, set_count_string, bind=('sel_count_0', 'sel_count_1', 'sel_count_2', 'sel_count_3', 'sel_count_4', 'sel_count_5', 'sel_count_6')) def __init__(self, **kwargs): super(SelectionMonitor, self).__init__(**kwargs) def update_sel_count_0(self, adapter, *args): self.sel_count_0 = len(adapter.selection) def update_sel_count_1(self, adapter, *args): self.sel_count_1 = len(adapter.selection) def update_sel_count_2(self, adapter, *args): self.sel_count_2 = len(adapter.selection) def update_sel_count_3(self, adapter, *args): self.sel_count_3 = len(adapter.selection) def update_sel_count_4(self, adapter, *args): self.sel_count_4 = len(adapter.selection) def update_sel_count_5(self, adapter, *args): self.sel_count_5 = len(adapter.selection) def update_sel_count_6(self, adapter, *args): self.sel_count_6 = len(adapter.selection) letters_dict = \ {l: {'text': l, 'is_selected': False} for l in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'} listview_selection_buttons = {} class OpsView(BoxLayout): '''Seven list views are shown at the bottom, each focusing on one of the available operations for collection adapters: scroll_to, trim_to_sel, trim_left_of_sel, etc. At the top is a display that shows individual items selected across the seven lists, along with a total of all selected items for the lists. ''' def __init__(self, **kwargs): kwargs['orientation'] = 'vertical' super(OpsView, self).__init__(**kwargs) # UPPER PANEL # # Create an upper panel with labels for items selected in the # listviews shown in the lower panel. # upper_panel = BoxLayout() grid_layout = GridLayout(cols=1, row_force_default=True, row_default_height=40) # On the left side of the upper panel, show the selected items. There # is a total possible of 5 for each listview, so 5 buttons are made. # for listview_id in range(7): box_layout = BoxLayout() listview_selection_buttons[listview_id] = [] box_layout.add_widget( Label(text="Listview #{0} selection".format(listview_id))) for i in range(5): button = Button(size_hint_x=None, width=50, size_hint_y=None, height=35, background_color=[.25, .25, .6, 1.0]) listview_selection_buttons[listview_id].append(button) box_layout.add_widget(button) grid_layout.add_widget(box_layout) upper_panel.add_widget(grid_layout) # On the right side of the upper panel, show the total selected count. total_selection_button = Button(text="Total: 0", size_hint=(.5, 1.0), background_color=[.25, .25, .6, 1.0]) selection_monitor = SelectionMonitor() selection_monitor.bind( count_string=total_selection_button.setter('text')) upper_panel.add_widget(total_selection_button) self.add_widget(upper_panel) # LOWER PANEL # # Show 6 listviews with either a header label or a header button. # grid_layout = GridLayout(cols=7) list_item_args_converter = \ lambda row_index, rec: {'text': rec['text'], 'size_hint_y': None, 'height': 25} letters = [l for l in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'] adapters = [] # Create 7 listviews, limiting selection to 5 items for the first 3, # and allowing unlimited selection for the others, by setting the # selection limit to 1000. # # Use OpsDictAdapter, from above, which will post selections to # the display in the top panel. # listview_header_widgets = [Label(text="scroll_to rec", size_hint_y=None, height=25), Label(text="scroll_to min", size_hint_y=None, height=25), Label(text="scroll_to max", size_hint_y=None, height=25), Button(text="trim_left_of_sel", size_hint_y=None, height=25), Button(text="trim_right_of_sel", size_hint_y=None, height=25), Button(text="trim_to_sel", size_hint_y=None, height=25), Button(text="cut_to_sel", size_hint_y=None, height=25)] for listview_id in range(7): box_layout = BoxLayout(orientation='vertical') letters_dict_adapter = \ OpsDictAdapter( listview_id=listview_id, sorted_keys=letters[:], data=letters_dict, args_converter=list_item_args_converter, selection_mode='multiple', selection_limit=5 if listview_id < 3 else 1000, allow_empty_selection=True, cls=ListItemButton) adapters.append(letters_dict_adapter) letters_list_view = ListView(adapter=letters_dict_adapter) letters_dict_adapter.owning_view = letters_list_view box_layout.add_widget(listview_header_widgets[listview_id]) box_layout.add_widget(letters_list_view) grid_layout.add_widget(box_layout) # Bind selection of each list to the selection monitor. adapters[0].bind(selection=selection_monitor.update_sel_count_0) adapters[1].bind(selection=selection_monitor.update_sel_count_1) adapters[2].bind(selection=selection_monitor.update_sel_count_2) adapters[3].bind(selection=selection_monitor.update_sel_count_3) adapters[4].bind(selection=selection_monitor.update_sel_count_4) adapters[5].bind(selection=selection_monitor.update_sel_count_5) adapters[6].bind(selection=selection_monitor.update_sel_count_6) # For the last three listviews, bind the header buttons to the trim # op method in the associated dict adapter instance. button_3 = listview_header_widgets[3] button_4 = listview_header_widgets[4] button_5 = listview_header_widgets[5] button_6 = listview_header_widgets[6] button_3.bind(on_release=adapters[3].trim_left_of_sel) button_4.bind(on_release=adapters[4].trim_right_of_sel) button_5.bind(on_release=adapters[5].trim_to_sel) button_6.bind(on_release=adapters[6].cut_to_sel) self.add_widget(grid_layout) if __name__ == '__main__': from kivy.base import runTouchApp runTouchApp(OpsView(width=800))