Premature deletion of widgets

In the following code, the reference count for label goes to zero right after it is created, presumably because it goes out of scope when on_button_add_clicked returns. Indeed, if the Label is bound to self.label as well, then it does not go out of scope at that time and it is not queued for deletion. Doesn’t the vbox in which the Label is packed keep a reference to the label which would prevent its deletion? The program seems to work fine without binding label to the class, but is it safe?

'''Test creation and deletion of Widget.'''

import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gio, GLib, GObject

class Label(Gtk.Label):
    def __init__(self):
        super().__init__()
        print('Create Label', self)

    def __del__(self):
        print('Deleting', self)

class Tester(Gtk.Window):
    def __init__(self):
        super().__init__()
        self.set_default_size(100, -1)

        button_add = Gtk.Button()
        button_add.set_label('Add')
        button_add.connect('clicked', self.on_button_add_clicked)
        button_add.show()

        button_remove = Gtk.Button()
        button_remove.set_label('Remove')
        button_remove.connect('clicked', self.on_button_remove_clicked)
        button_remove.show()

        self.vbox = vbox = Gtk.VBox()
        vbox.pack_start(button_add, False, False, 0)
        vbox.pack_start(button_remove, False, False, 0)
        self.add(vbox)

        self.connect_after('destroy', self.on_destroy)

    def on_button_add_clicked(self, button):
        print('In on_button_add_clicked')
        label = Label()
        #self.label = label = Label()
        label.set_text('Label Text')
        label.show()
        self.vbox.pack_start(label, False, False, 0)
        print('label is packed')

    def on_button_remove_clicked(self, button):
        print('In on_button_remove_clicked')
        label = self.vbox.get_children()[-1]
        if isinstance(label, Label):
            label.destroy()

    def on_destroy(self, window):
        Gtk.main_quit()

if __name__ == '__main__':
    testerwindow = Tester()
    testerwindow.show_all()
    Gtk.main()

Yes, it is safe. You don’t need an explicit reference to the widget, once it’s added to the container.

Thanks for the reply. I am glad to know that the code is safe. Can you say any more about why it is safe? What is preventing the Python interpreter from disposing the object as its reference count is zero?

Because the refcount of the GtkLabel is not zero, try this:

GTK_DEBUG=interactive python3 your_example.py

Press the “Add” button and inspect the GtkLabel, you will see the refcount is 1.

GtkBox has an internal list for its children widgets, e.g. https://gitlab.gnome.org/GNOME/gtk/blob/gtk-3-24/gtk/gtkbox.c#L123

1 Like

Ah. So the box does hold a reference. In that case, I don’t understand why my __del__ got called, since Python calls __del__ when the ref count goes to 0.

The interactive thingy is very helpful. Thanks for showing it to me and for explaining that there is still a reference.

You’re destroying the Python object wrapper around the underlying C object. The C object is kept alive by its container; if you iterated the children of the Box widget, you’d create new Python wrappers around the C objects returned.

2 Likes

Do you also know where additional member fields are stored when we subclass a widget in python? In the C widget or in the Python wrapper (proxy) object? (For the Nim bindings I store it currently in the Nim proxy object, which works fine, but I am open for improvements, even dreaming from joining both objects…) Nim is using the gtk toggle-ref formalism invented in 2005.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.