将 Kivy Widget 传递给另一个类

Posted

技术标签:

【中文标题】将 Kivy Widget 传递给另一个类【英文标题】:Passing Kivy Widget to another class 【发布时间】:2020-04-21 22:42:29 【问题描述】:

我正在使用 Python 和 Kivy 构建动态用户界面。因为我需要动态添加和删除小部件,所以我想使用一个单独的类来处理从 GridLayout 中添加和删除小部件。我把这个类叫做 LayoutManager。

GridLayout 在我的 kv 文件中定义(id:“config_box_layout”)。在我的 python 代码的根小部件类中,我通过 id 引用 GridLayout。这工作正常。此引用在构造函数中传递给 LayoutManager。我尝试通过 ObjectProperty 或 GridLayout 传递它。

问题是,如果我尝试从布局中删除小部件,我总是会遇到这种错误:

'kivy.properties.ObjectProperty' object has no attribute 'remove_widget'

如果我尝试使用 config_box_layout.remove_widget(一些新创建的标签)在我的 Tab 类中的保存方法中删除一个小部件,它工作正常。

我认为问题在于 Kivy 和所有 kv-widget 都是弱引用的,并且将这些引用处理到其他类似乎不是预期的用例。

我尝试将类分开来避免在一个大的主要布局类中完成所有编码工作。

期待任何帮助! :)

main.py

import kivy
from util.layoutManager import LayoutManager

from kivy.app import App

kivy.require('1.11.1')

from kivy.uix.label import Label
from kivy.uix.tabbedpanel import TabbedPanel
from kivy.properties import ObjectProperty
from kivy.uix.popup import Popup
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.dropdown import DropDown
from kivy.uix.button import Button
from functools import partial

class ChooseDirectoryDialog(FloatLayout):
    add = ObjectProperty(None)
    cancel = ObjectProperty(None)
    save = ObjectProperty(None)

class Tab(TabbedPanel):

    config_add_src = ObjectProperty()
    ingest_layout = ObjectProperty()
    configManager = ConfigManager()
    config_box_layout = ObjectProperty()
    layoutManager = LayoutManager(config_box_layout)

    def add_src(self):
        content = ChooseDirectoryDialog(cancel=self.cancel, save=self.save)
        self._popup = Popup(title="Add Source", content=content,
        size_hint=(0.9, 0.9))
        self._popup.open()

    def cancel(self):
        self._popup.dismiss()

    def delete_source(self, description, widget):
        self.configManager.delete_source(description)
        self.remove_source_from_ui(description)

    def remove_source_from_ui(self, description):
        self.layoutManager.remove_configuration(description)

    def save(self, srcpath, destpath, description):
        desc_label = Label(text=description)
        desc_label.size_hint_y = None
        desc_label.height = 30
        self.config_box_layout.add_widget(desc_label)

        srcpath_label = Label(text=srcpath)
        srcpath_label.size_hint_y = None
        srcpath_label.height = 30
        self.config_box_layout.add_widget(srcpath_label)

        destpath_label = Label(text=destpath)
        destpath_label.size_hint_y = None
        destpath_label.height = 30
        self.config_box_layout.add_widget(destpath_label)

        deleteButton = Button(text="Quelle löschen")
        deleteButton.size_hint_y = None
        deleteButton.height = 30
        deleteButton.bind(on_press=partial(self.delete_source, description))
        self.config_box_layout.add_widget(deleteButton)

        self.layoutManager.add_configuration(description, 
        desc_label, srcpath_label, destpath_label, deleteButton)

        self.configManager.add_source(description, srcpath, destpath)

        self._popup.dismiss()

    def copyToDestination(self, srcpath, destpath):
        pass


class AutoIngest(App):
    def build(self):
        return Tab()

if __name__ == '__main__':
    #Builder.load_file('autoingest.kv')
    AutoIngest().run()

autoingest.kv

#:kivy 1.11.1

<Tab>:
    do_default_tab: False
    config_add_button: add_button
    config_box_layout: config_box

    TabbedPanelItem:
        text: 'Konfiguration'
        TabbedPanel:
            tab_width: 200
            do_default_tab: False                
            TabbedPanelItem:
                text: 'Quellen verwalten'
                StackLayout:
                    orientation: "lr-tb"
                    padding: 10
                    Button:
                        size_hint: .2, .1
                        id: add_button
                        text: 'Quelle hinzufügen'
                        on_press: root.add_src()
                    GridLayout:
                        id: config_box
                        cols: 4
                        Label:
                            size_hint_y: None
                            height: 30
                            text: "Bezeichnung"
                        Label:
                            size_hint_y: None
                            height: 30
                            text: "Quell-Pfad"
                        Label:
                            size_hint_y: None
                            height: 30
                            text: "Ziel-Pfad"
                        Label:
                            size_hint_y: None
                            height: 30
                            text: "Aktionen"

<ChooseDirectoryDialog>:
    text_input: text_input
    BoxLayout:
        size: root.size
        pos: root.pos
        orientation: "vertical"
        Label:
            size_hint_y: None
            height: 30
            text: "Bezeichnung"
        TextInput:
            id: text_input
            size_hint_y: None
            height: 30
            multiline: False
        Label:
            size_hint_y: None
            height: 30
            text: "Quellverzeichnis auswählen"
        FileChooserListView:
            id: source_chooser
        Label:
            size_hint_y: None
            height: 30
            text: "Zielverzeichnis auswählen"
        FileChooserListView:
            id: destination_chooser

        BoxLayout:
            size_hint_y: None
            height: 30
            Button:
                text: "Cancel"
                on_release: root.cancel()

            Button:
                text: "Add"
                on_release: root.save(source_chooser.path, destination_chooser.path, text_input.text)

layoutManager.py

from kivy.uix.gridlayout import GridLayout
from kivy.properties import ObjectProperty

class LayoutManager:

    #I alrey tried to pass the GridLayout itself. This didn't work either.
    def __init__(self, configlayout: ObjectProperty):
        self.configurations = 
        self.configlayout = configlayout

    def remove_configuration(self, description):
        widgets = self.configurations.get(description)
        for x in widgets:
            self.configlayout.remove_widget(x)

    def add_configuration(self, description, *widgets):
        self.configurations[description] = 'widgets': widgets

【问题讨论】:

【参考方案1】:

我认为问题在于您的线路时Tab 实例ids 尚未设置:

config_box_layout = ObjectProperty()
layoutManager = LayoutManager(config_box_layout)

被执行,所以config_box_layout 尚未设置。

您可以强制延迟创建LayoutManager,直到使用kivy.clockbuild()build() 方法中创建ids

class AutoIngest(App):
    def build(self):
        Clock.schedule_once(self.setup_layoutManager)
        return Tab()

    def setup_layoutManager(self, dt):
        self.root.layoutManager = LayoutManager(self.root.config_box_layout)

还有一行:

layoutManager = LayoutManager(config_box_layout)

可以从Tab 类中删除。

一旦它起作用了,你会发现你的 remove_configuration() 方法中的问题 LayoutManager

【讨论】:

我决定改变我的整体应用架构,因此我不再需要一个 LayoutManager 类。所有布局现在都有自己的类,并且在这些类中拥有自己的方法。

以上是关于将 Kivy Widget 传递给另一个类的主要内容,如果未能解决你的问题,请参考以下文章

将二维数组传递给另一个类 C++

如何将变量从一个类传递给另一个?

将结果集值传递给另一个类

将 ArrayList 的 ArrayList 传递给另一个类

将模板类实例传递给另一个类的构造函数

如何将 NSMutableArray 传递给另一个 ViewController 类