Kivy:通过唯一 属性 获取小部件 ID 和访问小部件
Kivy: Get widgets ids and accessing widgets by unique property
我是 Kivy 的新手,我有这个演示我的问题的小演示片段:
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.lang import Builder
class KivyGuiApp(App):
def build(self):
return root_widget
class MyBox(BoxLayout):
def print_ids(self, *args):
print("\nids:")
for widget in self.walk():
print("{} -> {}".format(widget, widget.id))
def print_names(self, *args):
print("\nnames:")
for widget in self.walk():
print("{} -> {}".format(widget, widget.name))
root_widget = Builder.load_string("""
MyBox:
id: screen_manager
name: 'screen_manager'
SimpleLayout:
id: simple_layout
name: 'simple_layout'
<SimpleLayout@BoxLayout>:
id: simple_layout_rule
name: 'simple_layout_rule'
Button:
id: button_ids
name: 'button_ids'
text: 'print ids to console'
on_release: app.root.print_ids()
Button:
id: button_names
name: 'button_names'
text: 'print names to console'
on_release: app.root.print_names()
""")
if __name__ == '__main__':
KivyGuiApp().run()
所以当你运行代码时会有两个按钮:
- 首先遍历所有小部件并打印它们的名称(按预期工作 - 每个小部件 returns 'name'),
- 第二个按钮也可以遍历所有小部件,但打印的不是名称而是它们的 ID(这不起作用 - returns None 每个 ID)。
我的问题是:
- 'id' 不是 属性 就像 'name' 吗?
- 如何从 python 端访问每个小部件的 ID?
加分题:
- 我可以通过它的 ID 访问小部件 "globally"(假设所有 ID 都是唯一的)吗?通过 "globally" 我的意思是例如访问(在上面的代码中)来自 'MyBox:' 小部件的 ids 而不是引用父子,而只是通过 ids(或者可能任何其他 属性 对每个都是唯一的部件)。我正在考虑创建一个包含所有小部件的字典 { id : widget object } 以便于访问,除非有另一种我不知道的方法?
需要强调的是——我试图避免以父子方式进行引用(当您想稍后更改小部件树时,这会相当混乱)并且我想以 .kv 语言生成小部件。
那么最好的方法是什么?
编辑:
所以这是我能想到的通过唯一 "global" id 引用小部件的最简单方法。
首先我创建了一个 class 它将被我的应用继承 class:
class KivyWidgetInterface():
''' interface for global widget access '''
global_widgets = {}
def register_widget(self, widget_object):
''' registers widget only if it has unique gid '''
if widget_object.gid not in self.global_widgets:
self.global_widgets[widget_object.gid] = widget_object
def get_widget(self, widget_gid):
''' returns widget if it is registered '''
if widget_gid in self.global_widgets:
return self.global_widgets[widget_gid]
else:
return None
因此,只有当小部件具有 gid - 一个小部件 class 变量 - 并且它是唯一的时,才会被注册。这样我就可以在这个字典中只存储重要的小部件。此外,它可以从 .kv 和 python 端轻松访问。
现在我创建 gid 变量并将它们注册到 .kv 中的字典:
<PickDirectory>:
gid: 'pick_directory'
on_pos: app.register_widget(self)
on_selection: app.get_widget('filelist').some_func()
<FileListView>:
gid: 'filelist'
on_pos: app.register_widget(self)
Button:
name: 'not important'
Button:
gid: 'tab_browse_button1'
on_pos: app.register_widget(self)
真正困扰我的是我在 "global" 词典中用 'on_pos' 事件注册了我的小部件...我真的不喜欢,但我找不到任何在小部件初始化后调用注册方法的可靠方法(on_pos 在初始化阶段之后立即调用,当小部件被定位时,以后很少,所以......似乎是我的最不麻烦的方法了解 kivy api,订单小部件使用 .kv 语言等进行初始化;因此,如果有更好的方法,我将非常感激任何指点)。
无论如何,这样我就可以轻松地将任何事件绑定到任何 class 中的任何方法,直接来自 .kv
要记住的一件事是 gid(全局 ID)需要在全球范围内是唯一的,但我发现没有比在本地保持 ID 唯一更令人不安的了(这对我来说可能同样甚至更令人困惑) .正如我所说 - 我想以不同的方式注册小部件,但我找不到任何其他可靠的方法来做到这一点(而且我发现 Clock 在此类事情上不可靠)。
Isn't 'id' a property just like 'name'?
不,ids 是一种特殊语法,仅存在于 kv 语言中,或者可通过 python (self.ids.idname
) 中规则的根小部件访问。 是一个id 属性,但我不确定它是否真的在任何地方使用,它似乎主要是出于遗留原因而存在。我想我们正在考虑删除它。
How can I access id for each widget from python side?
通过根小部件的 ids
属性。例如,在 MyBox 方法中,您可以编写 self.ids.simple_layout
来获取 SimpleLayout.
I was thinking of creating a dictionary { id : widget object } of all widgets for easy access
这通常是不可能的,因为多个小部件可以具有相同的 ID。这就是为什么它们是规则本地的并通过规则的根小部件访问的原因。
如果您愿意,您可以自己构建这样一个列表,要么考虑重复项,要么知道您不会创建它们。
实际上,没有。 name
在你的小部件中是一个变量,id
只是一个小部件参考,weakref
根据文档。也许 python docs 会帮助你理解它是如何工作的。您所做的是打印 id
,而不是小部件内的变量 "id"。
在kivy docs中解释说在kv
被解析后,ids被收集到一个ObservableDict中。 id 的工作方式类似于 python 字典键 id:Widget
但仅当通过字典访问时 (ids
)。我认为 kv
解析器只是将所有 id 放入 dict 中,并且只与它创建的 dict 一起工作。
Button:
id: test
text: 'self.id'
#or
Button:
id: 'test'
text: 'self.id'
即使写成字符串,也没有任何变化。所以我希望解析器的行为是这样的:抓取 id:
之后的任何整个单词,变成一个字符串,附加到 ids
字典 <id_string>:Widget_weakref
,忘记你的 id
.kv
或者如果它再次与 .kv
一起工作则忽略它。因此,当 id 被直接调用时(不是类似字典的 d[key]),它的行为就像一个空的/None
变量。希望我是对的。
回答第二个和第三个:
如果您的意思是直接在 MyBox
中通过 id
访问小部件,例如 SimpleLayout
,那么可以。
python class:
self.ids.simple_layout
kv MyBox 规则:
MyBox:
id: screen_manager
name: 'screen_manager'
BoxLayout:
Label:
id: my_label
text: 'test'
Button:
text: 'button'
on_release: self.text = root.ids.my_label.text
但是,要像 python 全局工作那样通过 ID 访问所有小部件,这是不可能的。您需要先访问 class/widget,然后再访问其 ids
字典
最近我一直在问自己一个类似的问题:如何访问另一个小部件中的属性。我自己找到了答案,我发帖在这里,因为我发现它不是很直观。
这段代码的想法很简单:当点击一个按钮时,它会改变另一个 class 的子部件中的 属性。
通常在同一个小部件树中调用时,这很容易,可以通过简单的 self.ids.simple_layout
(或 app
、self
或 root
的任何变体来调用).
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label
from kivy.uix.button import Button
kv = """
<Test>:
Button:
text: "Access label"
on_press: root.access_label()
MyWidget:
id: my_widget
<MyWidget>
Label:
id: my_label
text: "not yet accessed"
"""
Builder.load_string(kv)
class MyWidget(BoxLayout):
pass
class Test(BoxLayout):
def access_label(self):
self.ids.my_widget.ids.my_label.text = 'Accessed!'
class TestApp(App):
def build(self):
return Test()
if __name__ == '__main__':
TestApp().run()
棘手的部分是 self.ids.my_widget.ids.my_label.text
以及 id my_widget
必须在根小部件中而不是在 <My_widget>
定义中的事实。
我不确定我是否完全理解自己,但据我了解,当用另一个 class 定义另一个小部件时,它会创建另一棵树,这意味着:
- 必须在主树中分配 id
- 这两棵树没有链接,在访问 属性 或函数时必须使用两次
ids
。
如有错误请指正
我是 Kivy 的新手,我有这个演示我的问题的小演示片段:
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.lang import Builder
class KivyGuiApp(App):
def build(self):
return root_widget
class MyBox(BoxLayout):
def print_ids(self, *args):
print("\nids:")
for widget in self.walk():
print("{} -> {}".format(widget, widget.id))
def print_names(self, *args):
print("\nnames:")
for widget in self.walk():
print("{} -> {}".format(widget, widget.name))
root_widget = Builder.load_string("""
MyBox:
id: screen_manager
name: 'screen_manager'
SimpleLayout:
id: simple_layout
name: 'simple_layout'
<SimpleLayout@BoxLayout>:
id: simple_layout_rule
name: 'simple_layout_rule'
Button:
id: button_ids
name: 'button_ids'
text: 'print ids to console'
on_release: app.root.print_ids()
Button:
id: button_names
name: 'button_names'
text: 'print names to console'
on_release: app.root.print_names()
""")
if __name__ == '__main__':
KivyGuiApp().run()
所以当你运行代码时会有两个按钮:
- 首先遍历所有小部件并打印它们的名称(按预期工作 - 每个小部件 returns 'name'),
- 第二个按钮也可以遍历所有小部件,但打印的不是名称而是它们的 ID(这不起作用 - returns None 每个 ID)。
我的问题是:
- 'id' 不是 属性 就像 'name' 吗?
- 如何从 python 端访问每个小部件的 ID?
加分题:
- 我可以通过它的 ID 访问小部件 "globally"(假设所有 ID 都是唯一的)吗?通过 "globally" 我的意思是例如访问(在上面的代码中)来自 'MyBox:' 小部件的 ids 而不是引用父子,而只是通过 ids(或者可能任何其他 属性 对每个都是唯一的部件)。我正在考虑创建一个包含所有小部件的字典 { id : widget object } 以便于访问,除非有另一种我不知道的方法? 需要强调的是——我试图避免以父子方式进行引用(当您想稍后更改小部件树时,这会相当混乱)并且我想以 .kv 语言生成小部件。 那么最好的方法是什么?
编辑:
所以这是我能想到的通过唯一 "global" id 引用小部件的最简单方法。
首先我创建了一个 class 它将被我的应用继承 class:
class KivyWidgetInterface():
''' interface for global widget access '''
global_widgets = {}
def register_widget(self, widget_object):
''' registers widget only if it has unique gid '''
if widget_object.gid not in self.global_widgets:
self.global_widgets[widget_object.gid] = widget_object
def get_widget(self, widget_gid):
''' returns widget if it is registered '''
if widget_gid in self.global_widgets:
return self.global_widgets[widget_gid]
else:
return None
因此,只有当小部件具有 gid - 一个小部件 class 变量 - 并且它是唯一的时,才会被注册。这样我就可以在这个字典中只存储重要的小部件。此外,它可以从 .kv 和 python 端轻松访问。
现在我创建 gid 变量并将它们注册到 .kv 中的字典:
<PickDirectory>:
gid: 'pick_directory'
on_pos: app.register_widget(self)
on_selection: app.get_widget('filelist').some_func()
<FileListView>:
gid: 'filelist'
on_pos: app.register_widget(self)
Button:
name: 'not important'
Button:
gid: 'tab_browse_button1'
on_pos: app.register_widget(self)
真正困扰我的是我在 "global" 词典中用 'on_pos' 事件注册了我的小部件...我真的不喜欢,但我找不到任何在小部件初始化后调用注册方法的可靠方法(on_pos 在初始化阶段之后立即调用,当小部件被定位时,以后很少,所以......似乎是我的最不麻烦的方法了解 kivy api,订单小部件使用 .kv 语言等进行初始化;因此,如果有更好的方法,我将非常感激任何指点)。
无论如何,这样我就可以轻松地将任何事件绑定到任何 class 中的任何方法,直接来自 .kv
要记住的一件事是 gid(全局 ID)需要在全球范围内是唯一的,但我发现没有比在本地保持 ID 唯一更令人不安的了(这对我来说可能同样甚至更令人困惑) .正如我所说 - 我想以不同的方式注册小部件,但我找不到任何其他可靠的方法来做到这一点(而且我发现 Clock 在此类事情上不可靠)。
Isn't 'id' a property just like 'name'?
不,ids 是一种特殊语法,仅存在于 kv 语言中,或者可通过 python (self.ids.idname
) 中规则的根小部件访问。 是一个id 属性,但我不确定它是否真的在任何地方使用,它似乎主要是出于遗留原因而存在。我想我们正在考虑删除它。
How can I access id for each widget from python side?
通过根小部件的 ids
属性。例如,在 MyBox 方法中,您可以编写 self.ids.simple_layout
来获取 SimpleLayout.
I was thinking of creating a dictionary { id : widget object } of all widgets for easy access
这通常是不可能的,因为多个小部件可以具有相同的 ID。这就是为什么它们是规则本地的并通过规则的根小部件访问的原因。
如果您愿意,您可以自己构建这样一个列表,要么考虑重复项,要么知道您不会创建它们。
实际上,没有。 name
在你的小部件中是一个变量,id
只是一个小部件参考,weakref
根据文档。也许 python docs 会帮助你理解它是如何工作的。您所做的是打印 id
,而不是小部件内的变量 "id"。
在kivy docs中解释说在kv
被解析后,ids被收集到一个ObservableDict中。 id 的工作方式类似于 python 字典键 id:Widget
但仅当通过字典访问时 (ids
)。我认为 kv
解析器只是将所有 id 放入 dict 中,并且只与它创建的 dict 一起工作。
Button:
id: test
text: 'self.id'
#or
Button:
id: 'test'
text: 'self.id'
即使写成字符串,也没有任何变化。所以我希望解析器的行为是这样的:抓取 id:
之后的任何整个单词,变成一个字符串,附加到 ids
字典 <id_string>:Widget_weakref
,忘记你的 id
.kv
或者如果它再次与 .kv
一起工作则忽略它。因此,当 id 被直接调用时(不是类似字典的 d[key]),它的行为就像一个空的/None
变量。希望我是对的。
回答第二个和第三个:
如果您的意思是直接在 MyBox
中通过 id
访问小部件,例如 SimpleLayout
,那么可以。
python class:
self.ids.simple_layout
kv MyBox 规则:
MyBox:
id: screen_manager
name: 'screen_manager'
BoxLayout:
Label:
id: my_label
text: 'test'
Button:
text: 'button'
on_release: self.text = root.ids.my_label.text
但是,要像 python 全局工作那样通过 ID 访问所有小部件,这是不可能的。您需要先访问 class/widget,然后再访问其 ids
字典
最近我一直在问自己一个类似的问题:如何访问另一个小部件中的属性。我自己找到了答案,我发帖在这里,因为我发现它不是很直观。
这段代码的想法很简单:当点击一个按钮时,它会改变另一个 class 的子部件中的 属性。
通常在同一个小部件树中调用时,这很容易,可以通过简单的 self.ids.simple_layout
(或 app
、self
或 root
的任何变体来调用).
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label
from kivy.uix.button import Button
kv = """
<Test>:
Button:
text: "Access label"
on_press: root.access_label()
MyWidget:
id: my_widget
<MyWidget>
Label:
id: my_label
text: "not yet accessed"
"""
Builder.load_string(kv)
class MyWidget(BoxLayout):
pass
class Test(BoxLayout):
def access_label(self):
self.ids.my_widget.ids.my_label.text = 'Accessed!'
class TestApp(App):
def build(self):
return Test()
if __name__ == '__main__':
TestApp().run()
棘手的部分是 self.ids.my_widget.ids.my_label.text
以及 id my_widget
必须在根小部件中而不是在 <My_widget>
定义中的事实。
我不确定我是否完全理解自己,但据我了解,当用另一个 class 定义另一个小部件时,它会创建另一棵树,这意味着:
- 必须在主树中分配 id
- 这两棵树没有链接,在访问 属性 或函数时必须使用两次
ids
。
如有错误请指正