Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
oezingle opened this issue Feb 12, 2025 · 1 comment

Comments

@oezingle
Copy link

oezingle commented Feb 12, 2025

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]

@oezingle
Copy link
Author

I created a pull request resolving my issues, though the code definitely needs to be cleaned up before it is merged: #334

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant