Skip to content

GObjects don't seem to provide an API for disconnecting signals, nor do so automatically on reassignment #333

Open
@oezingle

Description

@oezingle

Hi All,

I'm not sure to which degree these behaviors are expected, but I've found some difficulty in connecting, and then disconnecting a signal with a Gtk.Widget. I have a feeling this extends to every GObject that implements signals

  1. Using the signal assignment (widget.on_<signal> = fn) syntax does not disconnect any previous assignments. This runs contrary to any assignment logic anywhere else in lua. In my demo, this is button_1 (top button)

  2. No signal disconnection methods seem to exist. I couldn't find anyhting of the sort in the guide. I checked "disconnect", "disconnect_by_func", "handler_disconnect", and "handlers_disconnect_by_func". I assume widget.on_<signal>:connect(fn) uses GObject.signal_connect(object, signal, fn) under the hood, but it's unfortunate that the memory management functions associated with signals are not exposed, as far as I can tell. In my demo I use GObject.signal_handler_disconnect(handler_id) to accomplish this, where handler_id is the return value of widget.on_<signal>:connect(fn). In my demo, this is button_2 (bottom button)

Here's the code for my demo:

local lgi = require("lgi")
local Gtk = lgi.require("Gtk", "3.0")
local GObject = lgi.GObject

-- This button demonstrates that re-assigning a signal handler doesn't disconnect the previous assignment:
local button_1 = Gtk.Button.new_with_label("I can be clicked multiple times, but I have multiple handlers")

---@diagnostic disable-next-line:duplicate-set-field
button_1.on_clicked = function ()
    print("Button 2 Click handler 1!")
end
---@diagnostic disable-next-line:duplicate-set-field
button_1.on_clicked = function ()
    print("Button 2 Click handler 2!")
end

-- This button demonstrates the difficulty in disconnecting a signal:
local button_2 = Gtk.Button.new_with_label("I can be clicked once!")

local handler_id
-- using widget.<signal>:connect from https://github.com/lgi-devs/lgi/blob/master/docs/guide.md#341-connecting-signals
handler_id = button_2.on_clicked:connect(function(widget)
    print("Button 1 Clicked!")
    widget:set_label("I can no longer be clicked!")

    do
        -- Checking for any signal disconnection methods attached to our widget:
        local widget_truncated = tostring(widget):match("^lgi.obj 0x[%w]+:([^%(]+)")
        for _, fn_name in ipairs({
            "disconnect",
            "disconnect_by_func",
            "handler_disconnect",
            "handlers_disconnect_by_func"
        }) do
            local ok, ret = pcall(function ()
                return widget.on_clicked[fn_name]
            end)

            if not ok or not ret then
                print(string.format("\t%s does not provide %q", widget_truncated, fn_name))
            end
        end
    end

    -- Ideally, I'd use
    --widget:disconnect(handler_id)
    -- as that uses the same object-oriented syntax lgi has lead me to expect
    print("Disconnecting signal using GObject")
    GObject.signal_handler_disconnect(widget, handler_id)
end)

local window = Gtk.Window {
    title = "GitHub issue demo",
    default_width = 400,
    default_height = 300,
    on_destroy = Gtk.main_quit,
    child = Gtk.VBox {
        button_1,
        button_2
    },
}

window:present()
window:show_all()

Gtk.main()

Edit: I was using widget[fn_name] which would always fail - oops! now using widget.on_clicked[fn_name]

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions