0

I display in a popup (ModalView) a dynamically changing value. I use a method in my main widget class to open/dismiss the popup, and bind a Kivy StringProperty to a Label in the popup. There is a problem - each time the popup is dismissed, something is left behind. Listing all observers of the StringProperty shows how with each cycle of open/dismiss the number of objects accumulates. See the example code below. When I run this on Raspberry Pi 2 under Raspbian Jessie (Pixel) with 128M allocated for VRAM, within about a minute the progam stops functioning correctly - popup starts to show a black screen. Am I doing something silly in my code?

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()
Eugene B
  • 47
  • 7
  • You are constantly creating new instances of ModalView. The number of objects grow until it ends up filling the RAM. – FJSevilla Jun 20 '17 at 18:11
  • Yes indeed I am, but every time I create an instance of ModalView, I dismiss it also. This does not seem to destroy it fully, should I do something more explicit to remove all references to it, so that it gets garbage collected? The problem goes away if I do not bind a Kivy property to the text field of the Label child of the ModalView instance. – Eugene B Jun 21 '17 at 08:30
  • I think it may be helpful to re-state the question like this. When I continuously open and dismiss new instances of ModalView with static content, I do not run into a problem with RAM filling up. When I do the same but this time I bind to a Kivy property inside a child widget of the ModalView instance, VRAM? gets filled up. How to prevent this? – Eugene B Jun 21 '17 at 11:47

1 Answers1

0

I found a workaround, inspired by this How to unbind a property automatically binded in Kivy language? I now preserve the Label child by removing it from the ModalView and adding it to the MainWidget before ModalView is dismissed, then reversing this for the next popup. This way the property binding takes place only once so no new observers are created. The label can be made invisible by assigning an empty string to the bound property.

I think this may be a bug - ModalView dismiss() method should not leave behind observers, but cannot test with latest Kivy version (1.10.1.dev0).

Here's the code:

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()
Eugene B
  • 47
  • 7