【问题标题】:Kivy: Having trouble getting ListView to update when sorting ListAdapter dataKivy:在对 ListAdapter 数据进行排序时无法更新 ListView
【发布时间】:2013-07-16 17:19:04
【问题描述】:

我正在尝试对列表视图中的字典列表进行排序。我将通过更改 ListAdapter.data 来解决这个问题。

在 kv 文件中,listview 上方的微调按钮选择排序值,on_release 调用 root.beer_sort 来更改 ListAdapter(名为 beer_la)数据。将beer_la.data打印到控制台,看起来好像变了,但是App的显示顺序没有更新。

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

kv文件的相关部分

<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

【问题讨论】:

  • 这是当前版本的一个已知问题,正在处理中。作为现在的解决方法,您可以执行以下操作: : on_adapter: self.adapter.bind_triggers_to_view(self._trigger_reset_populate) 或在实例化时将适配器作为参数传递给 ListView。

标签: python listview sorting listadapter kivy


【解决方案1】:

我希望我能帮助您解决问题。我发现如果我在调用 CellarDoor 的构造函数(而不是使用 Kivy 语言)之后添加列表视图,那么 sort 方法就会开始工作。我的示例是您的简化版本,但您描述的行为仍然存在。

如果您取消注释:

#    ListView:
#        adapter: root.beer_la

和评论:

        self.add_widget(ListView(adapter=self.beer_la))

那么,排序就像你描述的那样停止工作。

奇怪的是,如果我有一个很大的列表(比如 100 个),ListView 在对其执行和操作(滚动或选择)和项目后会更新。不知何故,这意味着适配器的属性未绑定到更新屏幕。这可能是一个值得在Github 中向开发人员报告的问题。我无法更好地解释你,但我希望它至少能解决你的问题。您可能还想试试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()

【讨论】:

  • 非常感谢 toto_tico。这似乎奏效了。
【解决方案2】:

也许这可以帮助遇到同样问题的人...

当向 ListView 添加新元素不会更新列表时,我遇到了类似的问题。 更新时再次设置数据属性(如上所述)实际上并没有为我解决(Kivy 1.8.0)。

在 Kivy Google Groups 的某个地方,展示了使用 ListViews .populate() 方法的解决方案。

def update_data(self):
  self.list_adapter.data = mydata
  self.list_view.populate()

这种方法的一个问题是,.populate() 属性似乎在创建 ListView 后并不直接存在。所以你必须在调用它之前检查属性是否存在:

def update_data(self):
  self.list_adapter.data = mydata
  if(hasattr(self.list_view, 'populate')):
    self.list_view.populate()

这确实成功了,但不幸的是列表底部的新项目是可见的,但您无法向下滚动到这些项目。

在挖掘了 ListView 的源代码后,我遇到了 ._reset_spopulate() 方法,它似乎完全符合我的要求:

def update_data(self):
  self.list_adapter.data = mydata
  if(hasattr(self.list_view, '_reset_spopulate')):
    self.list_view._reset_spopulate()

底部的新项目已正确添加,滚动也有效。 不要问我为什么这确实有效,也不要期望它在 Kivy 的未来版本中有效。

【讨论】:

    【解决方案3】:

    在 O'Reilly 的“Creating apps in Kivy”一书中,它建议使用 _trigger_reset_populate() 函数:

        def update_data(self):
            self.list_adapter.data = mydata
            self.list_view._trigger_reset_populate()
    

    【讨论】:

      猜你喜欢
      • 2014-11-09
      • 1970-01-01
      • 1970-01-01
      • 2015-02-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多