将Kivy ObjectProperty绑定到子窗口小部件似乎不能在根窗口小部件之外工作

时间:2018-02-24 14:17:58

标签: python kivy kivy-language

尝试遵循本指南: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没有被创建一样。

3 个答案:

答案 0 :(得分:0)

根据docs

  

@字符用于将类名与要子类的类分开。 [...]

从结论来看,它是一种在.kv中类似于python的继承的等效方法,所以你应该只选择其中一种方式。这导致.py的PlotBox永远不会被调用。

另一个错误,根据docs,我不知道它是不是你的错误,但.kv必须是gui.kv,小写。

在执行父构造函数后不会直接加载子元素,因此在构造函数中添加它可能会产生问题,建议和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()

输出:

enter image description here

答案 1 :(得分:0)

我认为在onPlaybackEvent(PlayerEvent playerEvent)期间尚未设置02-24 13:19:15.500 5766-5766/<YOUR_PACKAGE> D/MainActivity: Playing long song 02-24 13:19:15.516 5766-5766/<YOUR_PACKAGE> D/MainActivity: Playback event received: kSpPlaybackNotifyPlay 02-24 13:19:15.574 5766-5766/<YOUR_PACKAGE> D/MainActivity: Playback event received: kSpPlaybackNotifyMetadataChanged 02-24 13:19:15.692 5766-5766/<YOUR_PACKAGE> D/MainActivity: Playback event received: kSpPlaybackNotifyMetadataChanged 02-24 13:19:15.692 5766-5766/<YOUR_PACKAGE> D/MainActivity: Playback event received: kSpPlaybackNotifyMetadataChanged 02-24 13:19:15.692 5766-5766/<YOUR_PACKAGE> D/MainActivity: Playback event received: kSpPlaybackNotifyContextChanged 02-24 13:19:15.791 5766-5766/<YOUR_PACKAGE> D/MainActivity: Playback event received: kSpPlaybackNotifyTrackChanged 02-24 13:19:15.791 5766-5766/<YOUR_PACKAGE> D/MainActivity: Playback event received: kSpPlaybackEventAudioFlush - 02-24 13:20:04.286 5766-5766/<YOUR_PACKAGE> D/MainActivity: Playing short song 02-24 13:20:04.307 5766-5766/<YOUR_PACKAGE> D/MainActivity: Playback event received: kSpPlaybackNotifyPlay 02-24 13:20:04.357 5766-5766/<YOUR_PACKAGE> D/MainActivity: Playback event received: kSpPlaybackNotifyMetadataChanged 02-24 13:20:04.463 5766-5766/<YOUR_PACKAGE> D/MainActivity: Playback event received: kSpPlaybackNotifyMetadataChanged 02-24 13:20:04.463 5766-5766/<YOUR_PACKAGE> D/MainActivity: Playback event received: kSpPlaybackNotifyMetadataChanged 02-24 13:20:04.463 5766-5766/<YOUR_PACKAGE> D/MainActivity: Playback event received: kSpPlaybackNotifyContextChanged 必须在添加任何子项之前返回。

您可以通过执行02-24 13:20:06.020 5766-5766/<YOUR_PACKAGE> D/MainActivity: Playing short song 02-24 13:20:06.035 5766-5766/<YOUR_PACKAGE> D/MainActivity: Playback event received: kSpPlaybackNotifyPlay 02-24 13:20:06.088 5766-5766/<YOUR_PACKAGE> D/MainActivity: Playback event received: kSpPlaybackNotifyMetadataChanged 02-24 13:20:06.179 5766-5766/<YOUR_PACKAGE> D/MainActivity: Playback event received: kSpPlaybackNotifyMetadataChanged 02-24 13:20:06.179 5766-5766/<YOUR_PACKAGE> D/MainActivity: Playback event received: kSpPlaybackNotifyMetadataChanged 之类的操作来解决此问题。

答案 2 :(得分:0)

我的工作假设您希望在创建应用程序时运行此代码,不会迟到。

千伏。

<PlotBox>:
    BoxLayout:
        id:graph2_id

<RootWidget>:
    BoxLayout:
        id:graph_id
    PlotBox:
        id: plot

吡啶

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或子项来完成:

self.ids.IDGOESHERE

OR

self.children[INDEXOFIDGOESHERE]

关于这一行:

    root.ids.plot.ids.graph2_id.add_widget(Button(text='This works!'))

Root有一个带有id'plot'的plotbox类的实例。 Plot类(以及绘图类的所有实例)都有一个BoxLayout实例,其中包含我们可以访问的id图。

所以我们正在做的是:

Root - &gt;情节 - &gt; Graph2

如果我们要添加另一个id为'big_plot'的情节框,那么我们可以做我们之前做的一个Graph2或者我们可以得到一个不同的graph2,因为它属于一个不同的情节框实例。

我们之前做过什么

Root - &gt;情节 - &gt; Graph2

一个不同的id,因此是一个不同的小部件。

Root - &gt; big_plot - &gt; Graph2

除非你调用super,否则你很少需要在Kivy Widget类中使用init方法(不管怎样我的经验)。

编辑:

如果要重复访问超长地址,可以将它们包装在函数中以获取它们。

示例:

不太好:

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