将 Kivy ObjectProperty 绑定到子小部件似乎在根小部件之外不起作用
Binding Kivy ObjectProperty to a child widget doesn't seem to work outside of root widget
正在尝试遵循本指南:https://kivy.org/docs/guide/lang.html#accessing-widgets-defined-inside-kv-lang-in-your-python-code
我正在尝试使用 id 定义访问小部件。这在根小部件内运行良好,但在它之外似乎不起作用。
作为示例,这是代表我的问题的最低限度代码:
GUI.kv 文件:
<PlotBox@BoxLayout>:
graph2:graph2_id
BoxLayout:
id:graph2_id
<RootWidget@BoxLayout>:
graph:graph_id
BoxLayout:
id:graph_id
PlotBox:
python 文件:
#kivy imports
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.properties import ObjectProperty
class PlotBox(BoxLayout):
graph2 = ObjectProperty(None)
def __init__(self,**kwargs):
super(PlotBox,self).__init__(**kwargs)
self.graph2.add_widget(Button(text="This doesn't work"))
class RootWidget(BoxLayout):
graph = ObjectProperty(None)
def __init__(self,**kwargs):
super(RootWidget,self).__init__(**kwargs)
self.graph.add_widget(Button(text='This works'))
class GUIApp(App):
def build(self):
self.root = RootWidget()
return self.root
if __name__ == "__main__":
GUIApp().run()
我收到错误:
AttributeError: 'NoneType' object has no attribute 'add_widget'
在 RootWidget 上,即使我不使用 graph = ObjectProperty(None) 它也能正常工作。
在我的其他小部件上,好像没有创建 id。
根据docs:
The @ character is used to separate your class name from the classes you want to subclass. [...]
根据得出的结论,它是在 .kv 中进行继承的等效方法,类似于 python,因此您应该只 select 这些方法中的一种。这会导致 .py 中的 PlotBox 永远不会被调用。
另一个错误,根据docs,我不知道这是不是你的错误,但是.kv必须是gui.kv,小写。
children 在执行parent 的构造函数后不会直接加载,所以在constructor 中添加它会产生问题,在kivy 中推荐和一个非常普遍的做法是使用Clock
.
以上所有我已经在以下代码中实现:
gui.kv
<PlotBox>:
graph2:graph2_id
BoxLayout:
id:graph2_id
<RootWidget>:
graph:graph_id
BoxLayout:
id:graph_id
PlotBox:
main.py
#kivy imports
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.properties import ObjectProperty
from kivy.clock import Clock
class PlotBox(BoxLayout):
graph2 = ObjectProperty(None)
def __init__(self,**kwargs):
super(PlotBox,self).__init__(**kwargs)
Clock.schedule_once(lambda dt: self.graph2.add_widget(Button(text="This now works")))
class RootWidget(BoxLayout):
graph = ObjectProperty(None)
def __init__(self,**kwargs):
super(RootWidget,self).__init__(**kwargs)
self.graph.add_widget(Button(text='This works'))
class GUIApp(App):
def build(self):
root = RootWidget()
return root
if __name__ == "__main__":
GUIApp().run()
输出:
我认为 self.graph2
在 __init__
期间尚未设置 - __init__
必须 return 才能添加任何子项。
您可以通过执行 Clock.schedule_once(function_that_adds_the_button, 0)
.
之类的操作来解决此问题
我假设您希望此代码在创建应用程序时 运行,而不是稍后。
kv.
<PlotBox>:
BoxLayout:
id:graph2_id
<RootWidget>:
BoxLayout:
id:graph_id
PlotBox:
id: plot
py
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
class PlotBox(BoxLayout):
pass
class RootWidget(BoxLayout):
pass
class GUIApp(App):
def build(self):
root = RootWidget()
# We can add things to the Root during build before we return it
# This means we add this information before the user sees anything
root.ids.graph_id.add_widget(Button(text='This works'))
# See end of answer for more about this
root.ids.plot.ids.graph2_id.add_widget(Button(text='This works!'))
return root
if __name__ == "__main__":
GUIApp().run()
首先,您不需要对象属性来访问 id,您可以通过 id 或 children 来实现:
self.ids.IDGOESHERE
或
self.children[INDEXOFIDGOESHERE]
关于这一行:
root.ids.plot.ids.graph2_id.add_widget(Button(text='This works!'))
Root 有一个 plotbox class 的 实例 ,id 为 'plot'。 Plot class(因此所有的 plot class 实例)都有一个 BoxLayout 实例,其中包含我们可以访问的 id 图。
所以我们正在做的是:
根 -> 图 -> Graph2
如果我们要添加另一个 ID 为 'big_plot' 的绘图框,那么我们可以按照之前的操作来获得一个 Graph2,或者我们可以获得一个不同的 graph2,因为它属于绘图盒。
我们之前做了什么
根 -> 图 -> 图 2
不同的 ID,因此不同的小部件。
根 -> big_plot -> Graph2
除非你调用 super,否则你很少需要在 Kivy Widget 中使用 init 方法Class(至少根据我的经验)。
编辑:
如果你要重复访问超长地址,你可以将它们包装在一个函数中来获取它们。
示例:
不太好:
def func_one(self):
newtext = 'new'
self.ids.IDONE.ids.IDTWO.ids.IDTHREE.ids.IDFOUR.text = newtext
def func_two(self):
newtext = 'newtwo'
self.ids.IDONE.ids.IDTWO.ids.IDTHREE.ids.IDFOUR.text = newtext
def func_three(self):
newtext = 'newthree'
self.ids.IDSONE.Ids.IDTWO.ids.IDTHREE.ids.IDFOUR.text = newtext
更好:
def long_address(self):
return self.ids.IDSONE.ids.IDSTWO.ids.IDTHREE.ids.IDFOUR
def func_one(self):
newtext = 'new'
self.long_address().text = newtext
def func_two(self):
newtext = 'newtwo'
self.long_address().text = newtext
def func_three(self):
newtext = 'newthree'
self.long_address().text = newtext
正在尝试遵循本指南:https://kivy.org/docs/guide/lang.html#accessing-widgets-defined-inside-kv-lang-in-your-python-code
我正在尝试使用 id 定义访问小部件。这在根小部件内运行良好,但在它之外似乎不起作用。 作为示例,这是代表我的问题的最低限度代码:
GUI.kv 文件:
<PlotBox@BoxLayout>:
graph2:graph2_id
BoxLayout:
id:graph2_id
<RootWidget@BoxLayout>:
graph:graph_id
BoxLayout:
id:graph_id
PlotBox:
python 文件:
#kivy imports
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.properties import ObjectProperty
class PlotBox(BoxLayout):
graph2 = ObjectProperty(None)
def __init__(self,**kwargs):
super(PlotBox,self).__init__(**kwargs)
self.graph2.add_widget(Button(text="This doesn't work"))
class RootWidget(BoxLayout):
graph = ObjectProperty(None)
def __init__(self,**kwargs):
super(RootWidget,self).__init__(**kwargs)
self.graph.add_widget(Button(text='This works'))
class GUIApp(App):
def build(self):
self.root = RootWidget()
return self.root
if __name__ == "__main__":
GUIApp().run()
我收到错误:
AttributeError: 'NoneType' object has no attribute 'add_widget'
在 RootWidget 上,即使我不使用 graph = ObjectProperty(None) 它也能正常工作。 在我的其他小部件上,好像没有创建 id。
根据docs:
The @ character is used to separate your class name from the classes you want to subclass. [...]
根据得出的结论,它是在 .kv 中进行继承的等效方法,类似于 python,因此您应该只 select 这些方法中的一种。这会导致 .py 中的 PlotBox 永远不会被调用。
另一个错误,根据docs,我不知道这是不是你的错误,但是.kv必须是gui.kv,小写。
children 在执行parent 的构造函数后不会直接加载,所以在constructor 中添加它会产生问题,在kivy 中推荐和一个非常普遍的做法是使用Clock
.
以上所有我已经在以下代码中实现:
gui.kv
<PlotBox>:
graph2:graph2_id
BoxLayout:
id:graph2_id
<RootWidget>:
graph:graph_id
BoxLayout:
id:graph_id
PlotBox:
main.py
#kivy imports
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.properties import ObjectProperty
from kivy.clock import Clock
class PlotBox(BoxLayout):
graph2 = ObjectProperty(None)
def __init__(self,**kwargs):
super(PlotBox,self).__init__(**kwargs)
Clock.schedule_once(lambda dt: self.graph2.add_widget(Button(text="This now works")))
class RootWidget(BoxLayout):
graph = ObjectProperty(None)
def __init__(self,**kwargs):
super(RootWidget,self).__init__(**kwargs)
self.graph.add_widget(Button(text='This works'))
class GUIApp(App):
def build(self):
root = RootWidget()
return root
if __name__ == "__main__":
GUIApp().run()
输出:
我认为 self.graph2
在 __init__
期间尚未设置 - __init__
必须 return 才能添加任何子项。
您可以通过执行 Clock.schedule_once(function_that_adds_the_button, 0)
.
我假设您希望此代码在创建应用程序时 运行,而不是稍后。
kv.
<PlotBox>:
BoxLayout:
id:graph2_id
<RootWidget>:
BoxLayout:
id:graph_id
PlotBox:
id: plot
py
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
class PlotBox(BoxLayout):
pass
class RootWidget(BoxLayout):
pass
class GUIApp(App):
def build(self):
root = RootWidget()
# We can add things to the Root during build before we return it
# This means we add this information before the user sees anything
root.ids.graph_id.add_widget(Button(text='This works'))
# See end of answer for more about this
root.ids.plot.ids.graph2_id.add_widget(Button(text='This works!'))
return root
if __name__ == "__main__":
GUIApp().run()
首先,您不需要对象属性来访问 id,您可以通过 id 或 children 来实现:
self.ids.IDGOESHERE
或
self.children[INDEXOFIDGOESHERE]
关于这一行:
root.ids.plot.ids.graph2_id.add_widget(Button(text='This works!'))
Root 有一个 plotbox class 的 实例 ,id 为 'plot'。 Plot class(因此所有的 plot class 实例)都有一个 BoxLayout 实例,其中包含我们可以访问的 id 图。
所以我们正在做的是:
根 -> 图 -> Graph2
如果我们要添加另一个 ID 为 'big_plot' 的绘图框,那么我们可以按照之前的操作来获得一个 Graph2,或者我们可以获得一个不同的 graph2,因为它属于绘图盒。
我们之前做了什么
根 -> 图 -> 图 2
不同的 ID,因此不同的小部件。
根 -> big_plot -> Graph2
除非你调用 super,否则你很少需要在 Kivy Widget 中使用 init 方法Class(至少根据我的经验)。
编辑:
如果你要重复访问超长地址,你可以将它们包装在一个函数中来获取它们。
示例:
不太好:
def func_one(self):
newtext = 'new'
self.ids.IDONE.ids.IDTWO.ids.IDTHREE.ids.IDFOUR.text = newtext
def func_two(self):
newtext = 'newtwo'
self.ids.IDONE.ids.IDTWO.ids.IDTHREE.ids.IDFOUR.text = newtext
def func_three(self):
newtext = 'newthree'
self.ids.IDSONE.Ids.IDTWO.ids.IDTHREE.ids.IDFOUR.text = newtext
更好:
def long_address(self):
return self.ids.IDSONE.ids.IDSTWO.ids.IDTHREE.ids.IDFOUR
def func_one(self):
newtext = 'new'
self.long_address().text = newtext
def func_two(self):
newtext = 'newtwo'
self.long_address().text = newtext
def func_three(self):
newtext = 'newthree'
self.long_address().text = newtext