【问题标题】:How to unbind a property automatically binded in Kivy language?如何取消绑定在 Kivy 语言中自动绑定的属性?
【发布时间】:2013-07-06 08:24:26
【问题描述】:

Kivy 语言会自动在属性中创建内部绑定。例如,如果我们将 parent 的位置分配给 child 的位置,那么 child 的位置将自动更新:

Widget:
    Label:
        pos: self.parent.pos

在这种情况下,如果我们移动父Widget,那么子也将移动。如何解除属性pos与孩子的绑定?我知道如何解除绑定(属性)[http://kivy.org/docs/api-kivy.uix.widget.html#using-properties] 我绑定自己,但如果我不知道如何解除绑定它所绑定的方法的名称。

这是一个小例子来说明我的意思。 Button 向上GridLayout 移动到顶部,将Down 移动到底部Button Center 位于屏幕中间。我的问题是,当我单击 UpDown 时,我的居中按钮不再存在。

from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
from kivy.lang import Builder

Builder.load_string("""
<Example>:
    GridLayout:
        id: _box
        cols: 3
        size_hint: .7, .3
        pos_hint: {'center_x': .5}
        x: 0
        Widget:
            Button:
                pos: self.parent.pos
                size: self.parent.size
                on_press: _box.y = 0
                text: "Down"
        Widget:
            Button:
                pos: self.parent.pos
                size: self.parent.size
                on_press: self.center_y = root.height/2
                text: "Out of the Grid"
        Widget:
            Button:
                pos: self.parent.pos
                size: self.parent.size
                on_press: _box.top = root.height
                text: "Up"
""")

class Example(FloatLayout):
    pass


class ExampleApp(App):
    def build(self):
        return Example()

if __name__ == "__main__":
    ExampleApp().run()

我为什么要这样做?我在GridLayout 上使用了一个不断更新位置的动画。按钮的正常位置应该在网格布局内,但有时其中一个按钮会飞过屏幕并返回到相同的位置。问题是,当我的网格布局也在移动时,我无法让它们飞行,因为属性已绑定,并且一旦按钮尝试飞行,它就会返回网格。这也意味着有时需要绑定。我想要的是控制绑定和解除绑定。

【问题讨论】:

  • 为什么不只是从父级中删除小部件,将其添加到浮动布局中,然后在动画完成时使用 parent.add_widget(widget, index) 将其重新添加回原来的位置。 Gridlayout 和类似布局的重点是控制它的孩子的位置。如果您不希望这样,只需从布局中删除小部件即可。
  • 谢谢。最初,我正在做类似的事情。我的问题是 GridLayout 重新组织了小部件。所以,我添加了另一个级别,在我的示例中,按钮级别没有附加到任何Layout(只是一个普通的Widget)。它不起作用,因为pos: self.parent.pos 仍在绑定ButtonWidget pos。我用 Python 结束了绑定和解除绑定,所以我现在有了控制权。
  • 等等,你是不是建议通过将Button 添加到FloatLayout,然后它会将事物重新绑定到新的父级?我会尝试,但我知道我不这么认为。 @Tshirman 之前向我解释过,此绑定是由构建器完成的,因为我有 reverse issue with a canvas
  • @qua-non,尽管我不相信,但这确实有效。嗯,差不多。这很奇怪,但在我按下UpDown 按钮之前,按钮不会进入垂直中间。在执行其他操作之前,它不会立即更新。然而,当我把它放回网格时它工作得很好。换句话说,当我从盒子中取出时它不会立即起作用,但当我把它放回去时它会起作用。我会粘贴代码作为答案。

标签: python kivy


【解决方案1】:

评论现在似乎不起作用,所以我会发布这个作为答案。

  1. 您已经有一个 FloatLayout(您的根小部件)。用那个代替 创建一个新的 FloatLayout。
  2. 从网格中删除小部件之前。
    • 存储它的大小,
    • 将 size_hint 设置为无、无
    • 设置 pos_hint 以将小部件定位在中心。
  3. 将小部件添加到网格时,执行相反的操作。

这是您的代码,其中包含这些修复::

from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
from kivy.lang import Builder

Builder.load_string("""
<Example>:
    center_button: _center_button
    center_widget: _center_widget
    grid:_grid
    GridLayout:
        id: _grid
        cols: 3
        size_hint: .7, .3
        pos_hint: {'center_x': .5}
        x: 0
        Widget:
            Button:
                pos: self.parent.pos
                size: self.parent.size
                on_press: _grid.y = 0
                text: "Down"
        Widget:
            id: _center_widget
            Button:
                id: _center_button
                pos: self.parent.pos
                size: self.parent.size
                on_press: root.centerize(*args)
                text: "Out of the Grid"
        Widget:
            Button:
                pos: self.parent.pos
                size: self.parent.size
                on_press: _grid.top = root.height
                text: "Up"
""")

class Example(FloatLayout):
    def centerize(self, instance):
        if self.center_button.parent == self.center_widget:
            _size = self.center_button.size
            self.center_widget.remove_widget(self.center_button)
            self.center_button.size_hint = (None, None)
            self.add_widget(self.center_button)
            self.center_button.pos_hint = {'center_x': .5, 'center_y':.5}
        else:
            self.remove_widget(self.center_button)
            self.center_button.size_hint = (1, 1)
            self.center_widget.add_widget(self.center_button)
            self.center_button.size = self.center_widget.size
            self.center_button.pos = self.center_widget.pos

class ExampleApp(App):
    def build(self):
        return Example()

if __name__ == "__main__":
    ExampleApp().run()

更新 1:

如果出于某种原因您仍然需要解除绑定由 kvlang 绑定的属性,您可以使用自省来为该属性获取 list of observers。所以对于你的情况,它会是这样的::

observers = self.center_widget.get_property_observers('pos')
print('list of observers before unbinding: {}'.format(observers))
for observer in observers:
    self.center_widget.unbind(pos=observer)
print('list of observers after unbinding: {}'.format(self.center_widget.get_property_observers('pos')))

您需要为此使用最新的主服务器。我应该预先警告您要非常小心,尽管您需要重置在 kvlanguage 中设置的绑定,但是这样您就失去了 kv 语言的优势...只有在您真正了解自己在做什么时才使用它。

【讨论】:

  • 谢谢。在标记为答案之前,我是否应该假设不可能简单地解除绑定在 Kivy 语言中的内容?我正在使用当前的手动绑定解决方案发布另一个答案。
  • 更新了答案以显示如何取消绑定未绑定的属性。
【解决方案2】:

根据@qua-non 的建议,我暂时将孩子转移给另一位家长。它实际上解除了它的绑定,或者可能将它重新绑定到新的父级。由于某种原因,这是一个部分解决方案,当我将其从 GridLayout 中取出(即当我按 Enter 键时)并将其放入新的父级时,它不会自动更新位置。我需要在“开箱即用”按钮之后按“向上”(或“向下”)。

但是,它确实会立即返回。当您第二次再次单击“开箱即用”按钮时,它会回到原来的位置。这部分工作完美。并且它继续服从它的父指令。

换句话说,当我从网格中取出时它不会立即起作用,但是当我把它放回去时它会起作用。

from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
from kivy.lang import Builder

Builder.load_string("""
<Example>:
    center_button: _center_button
    center_widget: _center_widget
    float: _float
    grid:_grid
    GridLayout:
        id: _grid
        cols: 3
        size_hint: .7, .3
        pos_hint: {'center_x': .5}
        x: 0
        Widget:
            Button:
                pos: self.parent.pos
                size: self.parent.size
                on_press: _grid.y = 0
                text: "Down"
        Widget:
            id: _center_widget
            Button:
                id: _center_button
                pos: self.parent.pos
                size: self.parent.size
                on_press: root.centerize(*args)
                text: "Out of the Grid"
        Widget:
            Button:
                pos: self.parent.pos
                size: self.parent.size
                on_press: _grid.top = root.height
                text: "Up"
    FloatLayout:
        id: _float
        size_hint: None,None
""")

class Example(FloatLayout):
    def centerize(self, instance):
        if self.center_button.parent == self.center_widget:
            self.center_widget.remove_widget(self.center_button)
            self.float.add_widget(self.center_button)
            self.float.size = self.center_button.size
            self.float.x = self.center_button.x
            self.float.center_y = self.center_y
        else:
            self.float.remove_widget(self.center_button)
            self.center_widget.add_widget(self.center_button)
            self.center_button.size = self.center_widget.size
            self.center_button.pos = self.center_widget.pos

class ExampleApp(App):
    def build(self):
        return Example()

if __name__ == "__main__":
    ExampleApp().run()

【讨论】:

    【解决方案3】:

    这与我尝试的非常相似。不同之处在于我结束了手动绑定属性,因此我可以取消绑定它们。基本上,如果我取消注释“开箱即用”按钮的#pos: self.parent.pos 行,那么除非我按照@qua-non 的建议将Button 分配给另一个父级,否则我无法取消绑定它们。

    from kivy.app import App
    from kivy.uix.floatlayout import FloatLayout
    from kivy.lang import Builder
    
    Builder.load_string("""
    <Example>:
        center_button: _center_button
        GridLayout:
            cols: 3
            size_hint: .7, .3
            pos_hint: {'center_x': .5}
            Button:
                on_press: self.parent.y = 0
                text: "Down"
            Widget:
                Button:
                    id: _center_button
                    size: self.parent.size
                    #pos: self.parent.pos
                    on_press: root.centerize(*args)
                    text: "Out of the Grid"
            Button:
                on_press: self.parent.top = root.height
                text: "Up"
    """)
    
    class Example(FloatLayout):
        def __init__(self, **kwargs):
            super(Example, self).__init__(**kwargs)
            self.center_button.parent.bind(pos=self.binder)
            self.centered = False
    
        def binder(self, instance, value):
            self.center_button.pos = instance.pos
    
        def centerize(self, instance):
            if self.centered:
                self.center_button.parent.bind(pos=self.binder)
                self.center_button.y = self.center_button.parent.y
                self.centered = False
            else:
                self.center_button.parent.unbind(pos=self.binder)
                self.center_button.center_y = self.height/2
                self.centered = True
    
    class ExampleApp(App):
        def build(self):
            return Example()
    
    if __name__ == "__main__":
        ExampleApp().run()
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-03-26
      • 1970-01-01
      • 1970-01-01
      • 2012-01-11
      • 1970-01-01
      • 2018-04-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多