Search code examples
pythonkivy

How do I programmatically add an AccordionItem to an Accordion?


UPDATED WITH MORE EXAMPLE CODE

I have a test application which contains an Accordion. I want to be able to add items to the Accordion from within my python code.

Here is the kv code:

#:kivy 1.0.9

<LayoutTest>:
    BoxLayout:
        id: mainBox
        orientation: 'horizontal'
        padding: 4
        spacing: 4
        canvas:
            Color:
                rgba: 0, 0.2, 0.4, 1  # Blue-ish color
            Rectangle:
                pos: self.pos
                size: self.size

        # workBox
        BoxLayout:
            orientation: 'vertical'
            canvas:
                Color:
                    rgba: 0.16, 0.11, 0, 1  # brownish color
                Rectangle:
                    pos: self.pos
                    size: self.size

            ListView:
                id: r_list
                orientation: 'vertical'


[AccordionItemTitle@Label]:
    text: '  ' + ctx.title
    halign: 'left'
    valign: 'center'
    text_size: self.size
    normal_background: ctx.item.background_normal if ctx.item.collapse else ctx.item.background_selected
    disabled_background: ctx.item.background_disabled_normal if ctx.item.collapse else ctx.item.background_disabled_selected
    canvas.before:
        Color:
            rgba: self.disabled_color if self.disabled else self.color
        BorderImage:
            source: self.disabled_background if self.disabled else self.normal_background
            pos: self.pos
            size: self.size
        PushMatrix
        Translate:
            xy: self.center_x, self.center_y
        Rotate:
            angle: 90 if ctx.item.orientation == 'horizontal' else 0
            axis: 0, 0, 1
        Translate:
            xy: -self.center_x, -self.center_y
    canvas.after:
        PopMatrix

<ListItem@AccordionItem>:
    title: '2024/12/20  22:00    <reference>'
    BoxLayout:
        orientation: 'vertical'
        Label:
            text: 'Type 1'
            text_size: self.width, None
        Label:
            text: 'Content 3'
            text_size: self.width, None

<ListView@BoxLayout>:
    Accordion:
        id: accordyN
        orientation: 'vertical'

        AccordionItem:
            title: '2024/12/20  00:00    <reference>'
            BoxLayout:
                orientation: 'vertical'
                size_hint_y: None
                # height: self.minimum_height
                # height: type_label.height + content_label.height
                valign: 'top'
                Label:
                    id: type_label
                    text: 'Type 1'
                    text_size: self.size
                    halign: 'left'
                    valign: 'top'
                Label:
                    id: content_label
                    text: 'Content 1'
                    text_size: self.size
                    halign: 'left'
                    valign: 'top'

        AccordionItem:
            title: '2024/12/20  09:00    <reference>'
            BoxLayout:
                orientation: 'vertical'
                Label:
                    text: 'Type 1'
                    text_size: self.width, self.height
                Label:
                    text: 'Content 2'
                    text_size: self.width, self.height

Using this layout, my application can display the Accordion, as well as the two sample AccordionItems shown above.

In my Python code, I somehow need to access the Accordion in order to add widgets to it. Here's my code which doesn't work:

import kivy
from kivy.app import App

# below this kivy version you cannot use the app
kivy.require('1.9.0')

from kivy.uix.boxlayout import BoxLayout
from kivy.uix.accordion import AccordionItem


class LayoutTest(BoxLayout):
    temp = 0


class ListItem(AccordionItem):
    temp = 0


class LayoutTestApp(App):
    mainLayout = None

    def __init__(self, **kwargs):
        super().__init__()

    def build(self):
        self.mainLayout = LayoutTest()
        entry = ListItem(title='2024/12/20  22:00    <reference>')
        self.mainLayout.ids['accordyN'].add_widget(entry)
        return self.mainLayout


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

When I run the Python code above, I get an error:

self.mainLayout.ids['accordyN'].add_widget(entry) '~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^ KeyError: 'accordyN'

How do I add more kv widgets to the layout from my Python code?


Solution

  • The problem is that you are incorrectly trying to access the accordyN id. That id is defined in the <ListView@BoxLayout> rule, so it only appears in the ids of the ListView instance. But you also have have id of r_list that appears in the <LayoutTest> rule, so that id will appear in the ids of the LayoutTest instance, which is your root of the widget tree. So you can access the accordyN id by using the r_list. Try replacing your line:

    self.mainLayout.ids['accordyN'].add_widget(entry)
    

    with:

    self.mainLayout.ids.r_list.ids.accordyN.add_widget(entry)
    

    This code uses the r_list id in the LayoutTest instance to access the ListView instance, and then uses its id of accordyN to access the Accordion instance.