将 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.clock
在build()
的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 传递给另一个类的主要内容,如果未能解决你的问题,请参考以下文章