Kivy:通过点击第一个内容中的按钮更改第一个手风琴项内容

1 投票
3 回答
1288 浏览
提问于 2025-04-18 03:22

我刚开始学习Kivy,正在做一个手风琴式的界面,里面的项目数量是动态变化的。每个项目可以有两种不同的内容。你可以通过点击第一个内容里的一个按钮来切换到另一个内容。下面是一个简单的例子:

from kivy.lang import Builder
from kivy.base import runTouchApp

from kivy.uix.gridlayout import GridLayout
from kivy.uix.button import Button
from kivy.uix.accordion import Accordion, AccordionItem

Builder.load_string('''
<MyAccordion>:
    orientation: 'vertical'

<MyAccordionItem>:
    FirstContent:

<FirstContent@GridLayout>:
    cols: 1
    BoxLayout:
        ClickMeButton:
            text: 'Click me'
            on_press: self.change_content()

<SecondContent>:
    cols: 1
    GridLayout:
        rows: 1
        Label:
            text: 'You clicked!'
''')

class MyAccordionItem(AccordionItem):
    pass

class MyAccordion(Accordion):
    def __init__(self, **kwargs):
        super(MyAccordion, self).__init__(**kwargs)

        for i in xrange(5):
            accordionitem = MyAccordionItem(title=str(i))
            self.add_widget(accordionitem)

class SecondContent(GridLayout):
    pass

class ClickMeButton(Button):
    def change_content(self):

        # how to reference the accordion item
        # this button belongs to?

        '''
        accordionitem = None
        accordionitem.clear_widgets()
        accordionitem.add_widget(SecondContent())
        '''

runTouchApp(MyAccordion())

我遇到的问题是,我不知道怎么找到按钮所在的手风琴项目。我试着通过父级链来找到,但最终只到达了FirstContent对象,而它的父级似乎是一个盒子布局,这让我很困惑(为什么会这样呢?)。而且,依赖父级链并不是个好主意,因为如果内容结构发生变化,这种方法就容易出错。

我注意到Kivy 1.8.1会有一个walk()方法,但我猜这个方法也是通过父级向上遍历树形结构,所以可能也不太好用?

有没有什么好的想法呢?

3 个回答

0

只需要给你的手风琴项(AccordionItem)一个ID。如果你想让除了当前显示的那个(active_accordion_item)之外,所有的手风琴项都收起,可以在你的按钮里这样写:

on_release: active_accordion.collapse = False

  • 这样,kivy会把其他所有的手风琴项都设置为展开状态(True)。
0

这是我为 ClickMeButton 类想出的解决方案:

class ClickMeButton(Button):

    def change_content(self):

        accordionitem = self.find_anchestor(self.parent, 'MyAccordionItem')
        accordionitem.remove_widget(accordionitem.ids['_first_content'])
        accordionitem.add_widget(SecondContent())

    def find_anchestor(self, widget, classname):

        if widget:
            if widget.__class__.__name__ == classname:
                return widget

            else:
                return self.find_anchestor(widget.parent, classname)

在 kivy 语言字符串中,我还需要在 FirstContent: 下面添加 id: _first_content,这样我就能给这个实例一个可以引用的 ID。

我创建了一个递归函数 find_anchestor 来寻找正确的父级。Paarth batra 告诉我,我可以通过父级链找到正确的祖先。但是当布局发生变化时,我不想手动添加或删除父级,所以这个递归函数会一直往上找,直到找到正确的父级。你只需要给它一个起始的组件和你想找的组件类的名字,它就能工作。

一旦找到正确的父组件,我需要移除第一个内容并添加第二个内容。因为第一个内容不是 accordionitem 组件的直接子级,所以我需要给它一个 ID,并通过 ids 属性来引用它。

最后,我只需添加第二个内容。

希望这能帮助将来有类似问题的人。感谢 Paarth batra 的想法!

1

把change_button的代码改成下面这样。这是一种你想要的引用方式。可能还有其他方法可以做到这一点,但希望这能对你有所帮助 :)

我们怎么做呢?

第一个父级是按钮在FirstContent类中的盒子布局... 你在上面的kv代码中指定了BoxLayout,位于ClickMeButton的上方。

第二个父级,也就是上面的父级,是FirstContent。

依此类推...

class ClickMeButton(Button):
    def change_content(self):
        print "I am clicked . I am button "
        print self.parent.parent.parent.parent.parent.parent.title

        #1kivy.uix.boxlayout.BoxLayout object at 0x06ED9228 -- boxlayout in ClickMeButton
        #2<kivy.factory.FirstContent object at 0x06FCA1F0>   --Parent above
        #3<kivy.uix.boxlayout.BoxLayout object at 0x06F591B8> -- Parent of firstcontent class as a boxlayout
        #4<kivy.uix.stencilview.StencilView object at 0x06F3B0A0>
        #5<kivy.uix.boxlayout.BoxLayout object at 0x06FB7068>
        #6<__main__.MyAccordionItem object at 0x06ED8030>

撰写回答