Search code examples
pythongtk3pygobject

How to dispose GtkWidget's in Python, GTK3 and PyGObject?


I am creating a plug-in to a GTK3 program. This plug-in can be enabled or disabled at runtime. When enabled, it should populate its GUI in a given area (a GtkBin) in the host program. When disabled, it should remove itself from that area.

This simple program depicts the usage:

#!/usr/bin/python2

from gi.repository import Gtk

window = Gtk.Window()

class Plugin(object):
    def __init__(self, host):
        assert(isinstance(host, Gtk.Bin))
        self.host = host
        self.guest = None

    def enable(self):
        box = Gtk.Box(orientation = Gtk.Orientation.VERTICAL)
        for x in range(10):
            box.add(Gtk.Button("Button {}".format(x)))

        self.guest = box
        self.host.add(self.guest)

    def disable(self):
        self.host.remove(self.guest)
        # self.guest.destroy() # is this better?
        self.guest = None

plugin = Plugin(window)

plugin.enable()
#plugin.disable()

window.connect("destroy", Gtk.main_quit)
window.show_all()

Gtk.main()

I wish that when the plug-in is disabled, all widgets it added to the host should be properly disposed.

I have found this question quite similar: Free object/widget in GTK? It proposed gtk_container_remove and gtk_widget_destroy. But I am worrying:

  1. Consider gtk_container_remove. It removes the direct child of the host container. In my case, the child is also a composition of many other widgets and they may be referencing each other. Will removing the direct child enough for all the widgets to be disposed?

  2. Consider gtk_widget_destroy. It is recursive and appears to be what I need, but also seems too brutal. Is this really necessary to manually destroy a widget? Would it be better to leave that job to the reference-counter?

I am willing to hear the "best practice" for this case.


Solution

  • Best practice is to never rely on a garbage collector to collect an object that controls a limited resource in a timely fashion. It could delay collecting any particular garbage indefinitely. You wouldn't want to leave a file open for the garbage collector to clean up, because there's a limit to the number of file handles you can have open at once.

    It happens that Python's garbage collector has a reference counter and will free objects with no references immediately, but that is an implementation detail. If you use another implementation, such as PyPy or IronPython, this does not apply. I've had a program break when I moved it to another implementation, because I had inadvertently relied on Python's reference counting to clean up resources. Also, you can end up with bugs that happen because you accidentally create a cycle somewhere.

    I don't know of any best practices for widgets specifically. I hadn't considered the possibility that I should be cleaning those up. If a widget has a window associated with it, that is an OS handle that you should theoretically clean up. Usually, only a GtkWindow will have a real window, but it's possible for your plugin to create a widget with a window. So, I would say that in that one specific unlikely case, you should theoretically destroy the widget. Otherwise, it's fine to destroy them manually if you don't need them, but I would say don't go out of your way to do so.