在ModalView被解雇后留下的Kivy属性观察者对象

时间:2017-06-20 16:29:27

标签: kivy

我在弹出窗口(ModalView)中显示一个动态变化的值。我在我的主窗口小部件类中使用一个方法来打开/关闭弹出窗口,并在弹出窗口中将Kivy StringProperty绑定到Label。有一个问题 - 每次弹出窗口被取消时,都会留下一些东西。列出StringProperty的所有观察者显示每个循环的打开/关闭如何累积对象的数量。请参阅下面的示例代码。当我在Raspbian Jessie(Pixel)的Raspberry Pi 2上运行此操作时,128M分配给VRAM,在大约一分钟内,程序停止正常运行 - 弹出窗口开始显示黑屏。我在代码中做些蠢事吗?

from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.uix.label import Label
from kivy.uix.modalview import ModalView
from kivy.clock import Clock
from kivy.properties import StringProperty
from random import randint


Builder.load_string('''
#:kivy 1.9.2

<MainWidget>:
    BoxLayout:
        Button:
''')


class MainWidget(BoxLayout):

    value_str = StringProperty()

    def show_popup(self, even=True): 
        if even:
            popup = ModalView(size_hint=(None, None), auto_dismiss=False, size=(700,480))
            popup_label = Label(font_size = 200, text_size=self.size, halign='center', valign='center')
            self.bind(value_str=popup_label.setter('text')) # value_str must be a Kivy StringProperty
            popup.add_widget(popup_label)
            self.value_str = str(randint(0,100))
            popup.open()
        else: # find all instances of ModalView and dismiss them
            for widget in App.get_running_app().root_window.children:
                if isinstance(widget, ModalView): 
                    print "observers of value_str property:"
                    observers = self.get_property_observers('value_str')
                    for observer in observers:
                        print observer

                    widget.dismiss(force=True, animation=False)    

        Clock.schedule_once(lambda dt: self.show_popup(not even), 0.25)



class MyApp(App):
    def build(self):
        mw=MainWidget()
        Clock.schedule_once(lambda dt: mw.show_popup(),0)
        return mw


if __name__ == '__main__':

    MyApp().run()

1 个答案:

答案 0 :(得分:0)

我找到了一个解决方法,受到How to unbind a property automatically binded in Kivy language?的启发 我现在保留Label子项,方法是将它从ModalView中删除,然后在ModalView被解除之前将其添加到MainWidget,然后将其反转为下一个弹出窗口。这样,属性绑定只发生一次,因此不会创建新的观察者。通过为绑定属性分配空字符串,可以使标签不可见。

我认为这可能是一个错误--ModalView dismiss()方法不应该留下观察者,但不能用最新的Kivy版本(1.10.1.dev0)进行测试。

以下是代码:

from kivy.app import App
from kivy.lang import Builder
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.button import Button
from kivy.uix.label import Label
from kivy.uix.modalview import ModalView
from kivy.clock import Clock
from kivy.properties import StringProperty
from random import randint

Builder.load_string('''
#:kivy 1.9.2

<MyLabel>:
    font_size: 100
    text_size: self.size
    halign: 'center'
    valign: 'center'

<MainWidget>:
    Button:
        background_color: 0.5, 0.5, 1, 1
''')


class MyLabel(Label):
    pass

class MainWidget(FloatLayout):

    value_str = StringProperty()
    popup_label = MyLabel()


    def __init__(self, **kwargs):
        super(MainWidget, self).__init__(**kwargs)
        self.bind(value_str=self.popup_label.setter('text')) # value_str must be a Kivy StringProperty
        self.add_widget(self.popup_label)



    def show_popup(self, even=True):
        if even:
            popup = ModalView(size_hint=(None, None), auto_dismiss=False, size=(500,380))
            self.remove_widget(self.popup_label)
            popup.add_widget(self.popup_label)
            self.value_str = str(randint(0,100))
            popup.open()

        else: # find all instances of ModalView and dismiss them
            for widget in App.get_running_app().root_window.children:
                if isinstance(widget, ModalView): 
                    print "observers of value_str property:"
                    observers = self.get_property_observers('value_str')
                    for observer in observers:
                        print observer
                    widget.remove_widget(self.popup_label)
                    self.add_widget(self.popup_label)
                    self.value_str =''
                    widget.dismiss(force=True, animation=False)  
        Clock.schedule_once(lambda dt: self.show_popup(not even), 0.25)


class MyApp(App):
    def build(self):
        mw=MainWidget()
        Clock.schedule_once(lambda dt: mw.show_popup(),0)
        return mw


if __name__ == '__main__':

    MyApp().run()