Как мне добиться поведения, подобного аккордеону, в RecycleView?Python

Программы на Python
Ответить
Anonymous
 Как мне добиться поведения, подобного аккордеону, в RecycleView?

Сообщение Anonymous »

Я хотел использовать виджет Kivy Accordion в своем приложении Python, но не смог заставить его работать правильно — элементы аккордеона расширялись или сжимались, чтобы точно заполнить пространство в окне. Именно тогда я понял, что у меня есть более серьезная проблема: количество элементов аккордеона может увеличиваться до бесконечности, но у моего аккордеона не было полосы прокрутки. Итак, после некоторых поисков я нашел материал RecycleView в Kivy. Посмотрев онлайн-документацию, а также демонстрационный код в \Python311\share\kivy-examples\widgets\recycleview, я решил взять файл Basic_data.py и внести некоторые изменения.
Вот мой код:

Код: Выделить всё

from random import sample, randint
from string import ascii_lowercase
from datetime import date

from kivy.app import App
from kivy.lang import Builder
from kivy.properties import ObjectProperty, StringProperty
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label
from kivy.uix.recycleview.views import RecycleKVIDsDataViewBehavior

kv = """
:
heading: ''
BoxLayout:
orientation: 'horizontal'
Button:
background_normal: ''
background_color: 0.3, 0.4, 0.3, 1
text: root.heading
on_press: root.expand()

:
canvas:
Color:
rgba: 0.3, 0.3, 0.3, 1
Rectangle:
size: self.size
pos: self.pos
rv: rv
orientation: 'vertical'
GridLayout:
cols: 3
rows: 2
size_hint_y: None
height: dp(108)
padding: dp(8)
spacing: dp(16)
Button:
text: 'Populate list'
on_press: root.populate()
Button:
text: 'Sort list'
on_press: root.sort()
Button:
text: 'Clear list'
on_press: root.clear()
BoxLayout:
spacing: dp(8)
Button:
text: 'Insert new item'
on_press: root.insert(new_item_input.text)
TextInput:
id: new_item_input
size_hint_x: 0.6
hint_text: 'heading'
padding: dp(10), dp(10), 0, 0
BoxLayout:
spacing: dp(8)
Button:
text: 'Update first item'
on_press: root.update(update_item_input.text)
TextInput:
id: update_item_input
size_hint_x: 0.6
hint_text: 'new heading'
padding: dp(10), dp(10), 0, 0
Button:
text: 'Remove first item'
on_press: root.remove()

RecycleView:
id: rv
scroll_type: ['bars', 'content']
scroll_wheel_distance: dp(114)
bar_width: dp(10)
viewclass: 'Row'
RecycleBoxLayout:
default_size: None, dp(56)
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
spacing: dp(2)
"""

Builder.load_string(kv)

class Row(RecycleKVIDsDataViewBehavior, BoxLayout):
is_expanded = ObjectProperty(None)
heading = StringProperty(None)
label = ObjectProperty(None)
orientation = 'vertical'

def expand(self):
print(f"Row with heading '{self.heading}' has been expanded!")
if self.is_expanded:
self.is_expanded = False
self.remove_widget(self.label)
self.height -= self.label.height
else:
self.is_expanded = True
self.label = Label(text='Expanded data ...', markup=True)
self.add_widget(self.label)
self.height += self.label.height

class Test(BoxLayout):

def populate(self):
self.rv.data = [
{'heading': date.today().__str__() + '   ' + str(randint(0, 2000))}
for x in range(50)]

def sort(self):
self.rv.data = sorted(self.rv.data, key=lambda x: x['heading'])

def clear(self):
self.rv.data = []

def insert(self, heading):
self.rv.data.insert(0, {
'name.text': heading or 'default heading', 'heading': 'unknown'})

def update(self, heading):
if self.rv.data:
self.rv.data[0]['name.text'] = heading or 'default new heading'
self.rv.refresh_from_data()

def remove(self):
if self.rv.data:
self.rv.data.pop(0)

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

#   def expand(self, row):
#        row.expand()

if __name__ == '__main__':
TestApp().run()
Когда я запустил код, мне удалось заполнить RecycleView. Я изменил определение строки, включив в нее кнопку для расширения и сжатия отдельного элемента аккордеона. При раскрытии виджет «Метка» добавляется в строку. При сжатии Этикетка удаляется. Пока все хорошо...
Но при расширении элемент строки не увеличивался в высоте - по крайней мере, поначалу. Таким образом, кнопка и метка просто стали более компактными:
Изображение

Я хотел, чтобы строка расширялась, чтобы метка отображалась под кнопкой нормального размера.
Продолжая экспериментировать с ней, я понял, что это на самом деле противоречиво. в своем поведении:
Изображение

Один раз метка вообще не отображалась , но кнопка будет в два раза выше. В другой раз все окажется так, как я надеялся! Я понятия не имею, как гарантировать последовательное и ожидаемое поведение.
Я попробовал внести изменения в код...
Я сделал небольшой изменить на уровне приложения, чтобы сохранить основной макет, чтобы позже получить доступ к RecycleView:

Код: Выделить всё

class TestApp(App):
testLayout = ObjectProperty(None)

def build(self):
testLayout = Test()
return testLayout
Затем я изменил код расширения строки:

Код: Выделить всё

def expand(self):
if self.is_expanded:
self.is_expanded = False
self.remove_widget(self.label)
self.height -= self.label.height
print(f"Row with heading '{self.heading}' has been contracted!")
else:
self.is_expanded = True
self.label = Label(text='Expanded data ...', markup=True, size_hint_y=None, height=dp(50))
self.add_widget(self.label)
self.height += self.label.height
print(f"Row with heading '{self.heading}' has been expanded!")
print(f"Label height '{self.label.height}' Row height '{self.height}' ")
# Use Clock to schedule refresh after the layout update
Clock.schedule_once(self.refresh_view, 0)

def refresh_view(self, *args):
rv = self.get_recycleview_parent()
if rv:
rv.refresh_from_data()

def get_recycleview_parent(self):
parent = self.parent
while parent and not isinstance(parent, RecycleView):
parent = parent.parent
return parent
Я попробовал добавить высоту метки к высоте строки при расширении, но вычесть высоту метки из высоты строки при сжатии. Затем я получил RecycleView из основного макета и вызвал update_from_data(), чтобы приспособить все к новой реальности.
Это не сработало. Мне не удалось получить RecycleView таким способом.
Именно тогда я добавил вызов get_recycleview_parent() для получения RecycleView. Теперь нажатие на строку приводит к непоследовательному расширению нескольких случайных строк, и программа зависает.
Может ли кто-нибудь предложить варианты, как добиться желаемого поведения (в отличие от неэффективные догадки второго пилота)?

Подробнее здесь: https://stackoverflow.com/questions/792 ... ecycleview
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «Python»