如何取消绑定在 Kivy 语言中自动绑定的属性?

2022-01-15 00:00:00 python kivy

问题描述

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

The Kivy Language automatically creates internal binds in properties. For example, if we assign the position of the parent to the position of the child, then the position of the child is going to be updated automatically:

Widget:
    Label:
        pos: self.parent.pos

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

In this case, if we move the parent Widget, then the child is also going to move. How do I unbind the property pos from the child? I know how to unbind (properties)[http://kivy.org/docs/api-kivy.uix.widget.html#using-properties] that I bind myself but how do I unbind them if I don't know the name of the method it is bound.

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

Here is a small example to show what I mean. The Button Up moves the GridLayout to the top and Down to the Bottom. The Button Center center itself in the middle of the screen. My problem is that when I click Up or Down my Centered button is not anymore.

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


Why do I want to do that in any case? I am using an animations on the GridLayout that constantly updates the position. The normal position of the buttons should be inside the gridlayout but once in a while one of the buttons flies over the screen and come back to the same position. The problem is that I cannot make them fly while my gridlayout is also moving because the property is bound and as soon as the button try to fly it goes back to the grid. That also means that the binding is sometimes desirable. What I want is have control of the bind and unbind.


解决方案

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

Comments don't seem to be working right now so I'll post this as a answer.

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

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

Here's your code with these fixes::

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 绑定的属性,您可以使用自省来获得 属性的观察者列表.所以对于你的情况,它会是这样的::

If for whatever reason you still need to unbind the properties bound by kvlang you can do so using introspection to get a list of observers for the property. so for your case it would be something like this::

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 语言的优势......只有在您真正了解自己在做什么时才使用它.

You would need to use the latest master for this. I should fore-warn you to be extremely careful with this though you'd need to reset the bindings set in kvlanguage, but then you loose the advantage of kv language... Only use this If you really understand what you are doing.

相关文章