recycleview with MDExpansion pannel implementing problem

79 views
Skip to first unread message

Degenerate Tech

unread,
May 18, 2024, 8:20:53 AMMay 18
to Kivy users support
KivyMD is master version i am using
Where i am wrong and what is the right way to implementing this ?
hiddensettingsapp.kv
main.py

ELLIOT GARBUS

unread,
May 18, 2024, 9:28:31 AMMay 18
to kivy-...@googlegroups.com
You don't need to use key_viewclass, you only have one widget class as a viewclass.
key_size is an attribute of RecyleLayout, not RecycleView, move it under the RecycleBoxLayout, and add the selected key to the data list.

When you open or close the expansion panel you will need to change the corresponding element in the data list, and set the size.

let's assume when the expansion panel is closed its size is (600, 100), and when it opens the size is (600, 400), you need to go to the entry in the data list associated with the selected widget and update the size.  You will the also likely need to call refresh_from_data() on the Recycleview.

The example below is not exactly the same  but it shows using the key_size, and manually updating the data list.

from kivy.app import App
from kivy.lang import Builder
from kivy.uix.recycleview import RecycleView
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import StringProperty, ListProperty
from kivy.uix.label import Label

import random

"""
This example extends a simple example of a RecycleView to add the use of the key_size
and dynamically changing the height of the widget based on the texture_size.
"""

kv = '''
<TwoButtons>:
# This class is used as the viewclass in the RecycleView
# The means this widget will be instanced to view one element of data from the data list.
# The RecycleView data list is a list of dictionaries.  The keys in the dictionary specify the
# attributes of the widget.
   size_hint_y: None
   # size of the layout based on the key_size
   Button:
       text: root.left_text
       text_size: self.width, None
       valign: 'center'
       halign: 'center'
       on_release: print(f'Button {self.text} pressed')
   Button:
       text: root.right_text
       on_release: print(f'Button {self.text} pressed')

BoxLayout:
   orientation: 'vertical'
   Button:
       size_hint_y: None
       height: 48
       text: 'Add widget to RV list'
       on_release: rv.add()

   RV:                          # A Reycleview
       id: rv
       viewclass: 'TwoButtons'  # The view class is TwoButtons, defined above.
       data: self.rv_data_list  # the data is a list of dicts defined below in the RV class.
       scroll_type: ['bars', 'content']
       bar_width: 10
       RecycleBoxLayout:        
           # This layout is used to hold the Recycle widgets
           orientation: 'vertical'
           default_size: None, dp(48)   # This sets the height of the BoxLayout that holds a TwoButtons instance.
           default_size_hint: 1, None
           size_hint_y: None
           height: self.minimum_height   # To scroll you need to set the layout height.
           key_size: 'vc_size'           # setting the key size
           
'''


class TwoButtons(BoxLayout):  # The viewclass definitions, and property definitions.
   left_text = StringProperty()
   right_text = StringProperty()


class RV(RecycleView):
   rv_data_list = ListProperty()  # A list property is used to hold the data for the recycleview, see the kv code

   def __init__(self, **kwargs):
       super().__init__(**kwargs)
       self.rv_data_list = [{'left_text': f'Left {i}', 'right_text': f'Right {i}',
                             'vc_size': (48, 48)} for i in range(2)]
       # This list comprehension is used to create the data list for this simple example.
       # The data created looks like:
       # [{'left_text': 'Left 0', 'right_text': 'Right 0'}, {'left_text': 'Left 1', 'right_text': 'Right 1'},
       # {'left_text': 'Left 2', 'right_text': 'Right 2'}, {'left_text': 'Left 3'},...]
       # notice the keys in the dictionary correspond to the kivy properties in the TwoButtons class.
       # The data needs to be in this kind of list of dictionary formats.  The RecycleView instances the
       # widgets, and populates them with data from this list.  Note the vc_size has been added to this list of
       # dictionaries.

   def add(self):
       """
       Add a button to the rv_data_list, with random sized text string for the left button.  Use the
       texture_size of the left button to set the height of the viewclass widget
       """
       
i = len(self.rv_data_list)
       r = random.randint(1, 50)
       left_text = f'Added Left {i} ' * r
       # calculate the texture_size of the text.  Use a Label.
       size = self.update_texture_size(left_text)
       self.rv_data_list.extend(
           [{'left_text': left_text, 'right_text': f'Added Right {i}',
             'vc_size': size}])

   def update_texture_size(self, text):
       """
       Updates the texture size given for the width of the RV
       :param text: The text to render
       :return: The texture size
       """
       
width = self.width / 2  # the left label is 1/2 with width of the RV
       # The created Label matches the dimensions of the button
       temp_label = Label(text=text, size_hint_x=None, width=width,
                          text_size=(width, None), padding=10)
       temp_label.texture_update()  # update the texture_size
       return temp_label.texture_size

   def on_width(self, obj, value):
       # when the width of the RV updates, recalculate the texture_sizes
       for i, item in enumerate(self.rv_data_list):
           size = self.update_texture_size(item['left_text'])
           self.rv_data_list[i]['vc_size'] = size
       self.refresh_from_data()  # force an update


class RVTwoApp(App):

   def build(self):
       return Builder.load_string(kv)


RVTwoApp().run()




From: kivy-...@googlegroups.com <kivy-...@googlegroups.com> on behalf of Degenerate Tech <sksah...@gmail.com>
Sent: Saturday, May 18, 2024 5:20 AM
To: Kivy users support <kivy-...@googlegroups.com>
Subject: [kivy-users] recycleview with MDExpansion pannel implementing problem
 
KivyMD is master version i am using
Where i am wrong and what is the right way to implementing this ?

--
You received this message because you are subscribed to the Google Groups "Kivy users support" group.
To unsubscribe from this group and stop receiving emails from it, send an email to kivy-users+...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/kivy-users/f11e80f6-0883-44b9-a6ae-e926828f7bcan%40googlegroups.com.

Degenerate Tech

unread,
May 18, 2024, 11:47:55 AMMay 18
to Kivy users support
could you please tell me where i am wrong ?
hiddensettingsapp.kv
main.py

ELLIOT GARBUS

unread,
May 18, 2024, 8:07:29 PMMay 18
to Kivy users support
I may not have time to put together an example based on your code today.  Here are the elements that need to be addressed to use the ExpansionPanel with RecycleVIew.
  • Use key_size and adjust the size of the widget in the data list when the panel is open or closed.
  • You need to add a property to track the index of the widget relative to the data list you can do this as below.  RecycleDataBehavior needs to be added to the class.
  • You need to create an external list that tracks the state of the ExpansionPanel, open or closed.  Create a new property that opens or closes the Panel on state_change.  This is similar to the way you would need to use a checkbox in a RecycleView.

class ExpansionPanelItem(RecycleDataViewBehavior, MDExpansionPanel):
   _dp=StringProperty('')
   _name=StringProperty('')
   _pkg_name=StringProperty('')
   _no_activity=StringProperty('1 Activity')
   _callback=ObjectProperty(None)
   index = NumericProperty()  # holds the index into the RV data list

   def refresh_view_attrs(self, rv, index, data):
       self.index = index
       return super().refresh_view_attrs(rv, index, data)


Sent: Saturday, May 18, 2024 8:47 AM

To: Kivy users support <kivy-...@googlegroups.com>
Subject: Re: [kivy-users] recycleview with MDExpansion pannel implementing problem
 
Message has been deleted

Degenerate Tech

unread,
May 19, 2024, 4:24:23 AMMay 19
to Kivy users support
now i was exprementing with scrollview . please see the code why widgets are overlapping in my code ?
why box layout not updating  widgets positions ?
hiddensettingsapp.kv
main.py

Degenerate Tech

unread,
May 19, 2024, 5:14:02 AMMay 19
to Kivy users support
Screencast from 19-05-24 02:43:14 PM IST.webm

ElliotG

unread,
May 19, 2024, 11:51:58 AMMay 19
to Kivy users support
TLDR: The ExpansionPanel does not work with RecycleView, use a ScrollView.

After playing around with RecycleView and looking at the source for ExpansionPanel, the Panel is not designed to work properly in a RecycleView.   The widget maintains state - this will not work in a recycleview.  In RecycleView you need to be able to maintain the state outside of the widget.  I had incorrectly thought the only state was open or closed. This is not correct.  The size, the state of widgets, the children...are all part of the ExpansionPanel state.

ElliotG

unread,
May 19, 2024, 11:54:56 AMMay 19
to Kivy users support
I'll take a look at your ScrollView later today.

ElliotG

unread,
May 19, 2024, 5:43:27 PMMay 19
to Kivy users support
The structure of the ExpansionPanel requires the structure below from the docs.  I did not see this in your code.
MDExpansionPanel:

    MDExpansionPanelHeader:
        # Content of header.
        [...]

    MDExpansionPanelContent:
        # Content of panel.
        [...]

Here is a minimal example:

from kivymd.app import MDApp
from kivy.lang import Builder
from kivymd.uix.expansionpanel import MDExpansionPanel

KV = '''
MDBoxLayout:
    orientation: 'vertical'
    MDLabel:
        text: 'ExpansionPanel is a ScrollView'
        halign: 'center'
        size_hint_y: None
        height: dp(30)
    MDScrollView:
        MDBoxLayout:
            id: scroll_box
            orientation: 'vertical'
            size_hint_y: None
            height: self.minimum_height
           
<PanelItem>:  # MDExpansionPanel
    orientation: 'vertical'
    size_hint_y: None
    height: dp(40)
    id: panel_item
    MDExpansionPanelHeader:  
        orientation: 'vertical'
        size_hint_y: None
        height: dp(40)
        Button:
            on_release: panel_item.open()
            text: 'Press to Open'
    MDExpansionPanelContent:
        orientation: "vertical"
        size_hint_y: None
        height: dp(200)
        Button:
            text: "Press to close"
            on_release: panel_item.close()
'''


class PanelItem(MDExpansionPanel):
    pass


class Example(MDApp):
    def build(self):
        return Builder.load_string(KV)

    def on_start(self):
        for _ in range(20):
            self.root.ids.scroll_box.add_widget(PanelItem())

Example().run()

Reply all
Reply to author
Forward
0 new messages