Элементы Kivy recycleview переупорядочиваются при изменении размеров окна - PullRequest
0 голосов
/ 03 марта 2019

Я использую Kivy's recycleview, чтобы отобразить список данных в виде таблицы.Я использовал пример из документации в качестве основы для моей реализации.

В моей программе 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()

Для простоты я изменил некоторые виджеты на их базовые классы (например, ImageButton и Label)

Когда вы запустите приложение, вы должны увидеть, что порядок, в котором элементы обращены, и запускс 10 вместо 100 по какой-то причине.

the started application

После изменения размера окна с помощью мыши в одном из углов окна вы должны увидеть, как содержимое непрерывно переупорядочивается..

after resizing the window a bit

А если прокрутить вниз, все становится еще более сумасшедшим.

after scrolling down the list

К сожалению, я понятия не имею, что вызывает поведение.Я уже разрабатывал некоторые приложения Kivy, но это мое первое по-настоящему глубокое погружение, в котором используются не только ярлыки и несколько входов.

1 Ответ

0 голосов
/ 04 марта 2019

Вот модификация вашего кода, которая, кажется, работает правильно:

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. Метод create_widgets удаляет все дочерние элементы RVItem идобавляет их в кэш, а затем перезапускает или создает виджеты по мере необходимости, чтобы заполнить RVItem.

. Я добавил дополнительные элементы в диктовку values для некоторых данных, чтобы проиллюстрировать, какэто работает.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...