调整窗口大小时,对Kivy recycleview项目进行重新排序

时间:2019-03-03 11:44:24

标签: python kivy

我使用Kivy的recycleview以类似表格的方式显示数据列表。我以example from the docs作为实施基础。

在我的程序中,RecycleDataView基于BoxLayout,并且其子窗口小部件是动态生成的。

这似乎起作用,但是如果调整窗口大小,有时显示项目的顺序会颠倒,并且会不断变化。更糟糕的是,如果向下滚动,则布局将变得完全疯狂。如果我将简单的标签用作项目类,则不会发生这种情况,因此我想问题出在我动态创建的小部件逻辑上,但我不明白为什么。

以下是显示问题的最小代码。

from kivy.app import App
from kivy.lang import Builder
from kivy.uix.recycleview import RecycleView
from kivy.uix.recycleview.views import RecycleDataViewBehavior
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import ObjectProperty

Builder.load_string('''

<RV>:
    viewclass: 'RVItem'
    RecycleBoxLayout:
        default_size: None, dp(56)
        default_size_hint: 1, None
        size_hint_y: None
        height: self.minimum_height
        orientation: 'vertical'

''')

class Attribute:
    def __init__(self, name, values):
        self.name = name
        self.values = values


class RVItem(RecycleDataViewBehavior, BoxLayout):
    index = None
    attribute = ObjectProperty()

    def refresh_view_attrs(self, rv, index, data):
        ''' Catch and handle the view changes '''
        self.index = index
        self.create_widgets(data.pop('attribute', None))
        return super(RVItem, self).refresh_view_attrs(
            rv, index, data)

    def create_widgets(self, value: Attribute):
        """Dynamically create the needed Widgets"""

        if value is None:
            return
        self.add_widget(Label(text=value.name, height=self.height, size_hint=(1, None)))

        if not isinstance(value.values, dict):
            self.add_widget(Label(text=value.values, height=self.height, size_hint=(1, None)))
        else:
            for _, v in value.values.items():
                self.add_widget(Label(text=v, height=self.height, size_hint=(1, None)))

        image_button = Button(text='+')
        #image_button.source = 'wm_ui/glyphs/plus.png'
        image_button.size_hint = None, None
        image_button.size = "30sp", "30sp"
        image_button.bind(on_press=self.add_button_pressed)
        self.add_widget(image_button)

    def add_button_pressed(self, s):
        print("Would add a new item to the recycleview if implemented.")


class RV(RecycleView):
    def __init__(self, **kwargs):
        super(RV, self).__init__(**kwargs)
        self.data = [{'attribute': Attribute(str(x), "test")} for x in range(100)]


class TestApp(App):
    def build(self):
        return RV()

if __name__ == '__main__':
    TestApp().run()

为了简单起见,我将一些Widget更改为其基类(例如ImageButton和Label)

运行该应用程序时,您应该会看到,如果由于某些原因,这些项是相反的,则顺序从10开始而不是100。

the started application

用鼠标在窗口的一个角上调整窗口大小后,您应该会看到内容不断重新排序。

after resizing the window a bit

如果向下滚动,事情将会变得更加疯狂。

after scrolling down the list

不幸的是,我不知道是什么导致了这种现象。我之前曾开发过一些Kivy应用程序,但这是我第一次真正的深入研究,它不仅使用标签和一些输入内容。

1 个答案:

答案 0 :(得分:1)

这是对您的代码的修改,似乎可以正常工作:

from kivy.app import App
from kivy.lang import Builder
from kivy.uix.recycleview import RecycleView
from kivy.uix.recycleview.views import RecycleDataViewBehavior
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import ObjectProperty

Builder.load_string('''

<RV>:
    viewclass: 'RVItem'
    RecycleBoxLayout:
        default_size: None, dp(56)
        default_size_hint: 1, None
        size_hint_y: None
        height: self.minimum_height
        orientation: 'vertical'

''')

class Attribute:
    def __init__(self, name, values):
        self.name = name
        self.values = values


class RVItem(RecycleDataViewBehavior, BoxLayout):
    index = None
    attribute = ObjectProperty()

    def refresh_view_attrs(self, rv, index, data):
        ''' Catch and handle the view changes '''
        self.index = index
        self.create_widgets(data['attribute'])
        return super(RVItem, self).refresh_view_attrs(
            rv, index, data)

    def create_widgets(self, value: Attribute):
        rv = App.get_running_app().root
        rv.cache_widgets(self.children)
        self.clear_widgets()
        label = rv.get_label()
        label.text = value.name
        self.add_widget(label)
        if isinstance(value.values, dict):
            for _,v in value.values.items():
                label = rv.get_label()
                label.text = v
                self.add_widget(label)
        else:
            label = rv.get_label()
            label.text = value.values
            self.add_widget(label)
        image_button = rv.get_button()
        image_button.text = '+'
        image_button.size_hint = None, None
        image_button.size = "30sp", "30sp"
        image_button.bind(on_press=self.add_button_pressed)
        self.add_widget(image_button)


    def add_button_pressed(self, s):
        print("Would add a new item to the recycleview if implemented.")


class RV(RecycleView):
    def __init__(self, **kwargs):
        super(RV, self).__init__(**kwargs)
        self.label_cache = []
        self.button_cache = []
        self.data = [{'attribute': Attribute(str(x), "test")} for x in range(100)]
        for i in range(100):
            if i % 5 == 0:
                self.data[i]['attribute'].values = {'1': 'test1', '2': 'test2', '3': 'test3'}

    def get_button(self):
        if len(self.button_cache) > 0:
            return self.button_cache.pop()
        else:
            return Button()

    def get_label(self):
        if len(self.label_cache) > 0:
            return self.label_cache.pop()
        else:
            return Label()

    def cache_widgets(self, widgets):
        for w in widgets:
            if isinstance(w, Button):
                self.button_cache.append(w)
            else:
                self.label_cache.append(w)


class TestApp(App):
    def build(self):
        return RV()

if __name__ == '__main__':
    TestApp().run()

在此版本中,refresh_view_attrs方法始终设置RVItem的所有属性。线

self.create_widgets(data.pop('attribute', None))

被替换为

self.create_widgets(data['attribute'])

因为pop()实际上删除了数据,我认为您不想这样做。

RV类现在为Label小部件提供了一个缓存,为Button小部件提供了一个缓存,它们将被回收(类似于RecycleView的工作。{{ 1}}方法将删除create_widgets的所有子项并将其添加到缓存,然后根据需要回收或创建小部件以填充RVItem

我向RVItem字典添加了一些数据项,以帮助说明其工作原理。