I'm trying to sort a list of dictionaries in a listview. I'm going about it by changing the ListAdapter.data.
In the kv file a spinner button above the listview selects the sort value and on_release makes a call to root.beer_sort which changes the ListAdapter (named beer_la) data. Printing out the beer_la.data to the console, it looks like it changes, but the App's display order doesn't update.
class CellarDoor(BoxLayout):
def __init__(self, **kwargs):
self.beer_archive = []
self.wine_archive = []
with open(join(FILE_ROOT, 'beer_archive.csv'), 'rb', 1) as beer_csv:
self.beer_archive = list(csv.DictReader(beer_csv))
self.beer_la = ListAdapter(data=self.beer_archive,
args_converter=self.beer_formatter,
cls=CompositeListItem,
selection_mode='single',
allow_empty_selection=True)
super(CellarDoor, self).__init__(**kwargs)
def beer_formatter(self, row_index, beer_data):
return {'text': beer_data['Beer'],
'size_hint_y': None,
'height': 50,
'cls_dicts': [{'cls': ListItemImage,
'kwargs': {'size_hint_x': .2,
'size': self.parent.size,
'cellar': 'Beer',
'style': beer_data['Style']}},
{'cls': ListItemLabel,
'kwargs': {'text': beer_data['Beer']}},
{'cls': ListItemButton,
'kwargs': {'text': str(beer_data['Stock'])}}]}
def beer_sort(self, sort, reverse):
self.beer_la.data = sorted(self.beer_archive,
key=lambda k: k[sort],
reverse=reverse)
print "new sort = %s" % sort
print self.beer_la.data
The relevant portion of the kv file
<CellarDoor>
...
Spinner:
id: beer_sort_spinner
text: 'Brewery'
values: ['Brewery', 'Beer', 'Year', 'Style', 'Stock', 'Rating', 'Price']
on_release: root.beer_sort(self.text, (True if future_search_button.state=='down' else False))
ToggleButton:
id: future_search_button
text: 'Reverse'
Button:
text: 'Add Beer'
ListView:
adapter: root.beer_la
I hope I can help a little bit with your problem. I found out that if I add the list view after calling the constructor of the CellarDoor
(instead of using Kivy Language) then the sort
method start working. My example is a simplified version of yours but the behaviour that you describe was still present.
If you uncomment:
# ListView:
# adapter: root.beer_la
and comment:
self.add_widget(ListView(adapter=self.beer_la))
then, the sorting stop working just as you describe.
Curiously, if I have a big list (let's say 100), the ListView
is updated after performing and action on it (scrolling or selecting) and item. Somehow this means that the property of the adapter is not bound to update the screen. This could be an issue that might deserve to be reported to the developers in Github. I cannot explain you better but I hope at least it solves your problem. You might also want to try create and add the adapter directly in Kivy Language.
from kivy.adapters.listadapter import ListAdapter
from kivy.uix.listview import ListView, ListItemButton
from kivy.uix.boxlayout import BoxLayout
from kivy.lang import Builder
from kivy.app import App
Builder.load_string("""
<CellarDoor>:
ToggleButton:
text: 'Reverse'
on_state: root.beer_sort((True if self.state=='down' else False))
# ListView:
# adapter: root.beer_la
""")
class CellarDoor(BoxLayout):
def __init__(self, **kwargs):
self.beer_archive = ["Item #{0}".format(i) for i in range(10)]
self.beer_la = ListAdapter(data=self.beer_archive,
cls=ListItemButton,
selection_mode='single',
allow_empty_selection=True)
super(CellarDoor, self).__init__(**kwargs)
self.add_widget(ListView(adapter=self.beer_la))
def beer_sort(self, reverse):
self.beer_la.data = sorted(self.beer_archive,
reverse=reverse)
print self.beer_la.data
class TestApp(App):
def build(self):
return CellarDoor()
if __name__ == '__main__':
TestApp().run()