GtkWidget.get_action_group does not find action group
The documentation of
GtkWidget.get_action_group(widget, prefix) states that the function returns a
GActionGroup which may have been registered to the
widget or to any
GtkWidget in its ancestry." My understanding of this sentence is that the
function is not only meant for accessing the action groups registered on
widget but also for searching the widget tree towards its root for a matching
When I used the function to that effect in my program it turned out, though, that the search through the widget ancestry is unreliable and fails after -- seemingly -- unrelated changes.
The following example (in Python for brevity) demonstrates the problem. It
creates a simple widget hierarchy with a
GtkWindow containing a
which has a
GtkButton in each pane. Additionally, an action group named
"mygroup" is registered on the window:
import gi gi.require_version("Gtk", "3.0") from gi.repository import Gtk, Gio class MyWindow(Gtk.Window): def __init__(self): super().__init__() paned = Gtk.Paned() button1 = Gtk.Button(label="Button 1") button2 = Gtk.Button(label="Button 2") paned.pack1(button1) paned.pack2(button2) self.add(paned) self.insert_action_group("mygroup", Gio.SimpleActionGroup()) ##### ## Uncomment any of the following lines to see the problem: ##### #button2.set_action_name("win.triggerbug") #paned.insert_action_group("triggerbug", Gio.SimpleActionGroup()) #button1.insert_action_group("triggerbug", Gio.SimpleActionGroup()) #button2.insert_action_group("triggerbug", Gio.SimpleActionGroup()) action_group = button1.get_action_group("mygroup") if action_group is not None: print("Action group 'mygroup' was found") else: print("Action group 'mygroup'mygroup was not found") win = MyWindow() win.connect("destroy", Gtk.main_quit) win.show_all() Gtk.main()
This code works as expected and finds "mygroup" when calling
However, if an action name is set on
button2 (see first commented line. It does not matter
that this action does not exist) then
button1.get_action_group does no longer return the
action group. The error can also be triggered by adding another action group to
paned or any of the two buttons (see comment in the example).
When investigating the problem, I found out that
get_action_group does not
really search the widget tree for an action group with
prefix but it simply
walks the tree upwards until it finds the first widget that has an instance of
GTK's private class
GtkActionMuxer. It then calls
this action muxer. This method only looks up action groups directly registered
on the action muxer.
This means that
get_action_group works only as long as the first action muxer
encountered happens to be the one with the action group being searched for. In
the example above this is only the case if none the commented code lines is
executed. As soon as one of the commands from the comments is added to the
code it causes all action muxers in the ancestry of
button2 to be created.
button1.get_action_group is then called, the action muxer attached to
paned is the first one found. However, this is not the one to which "mygroup"
was registered to. This results in
get_action_group returning NULL instead of
the action group as expected.
Suprisingly, actions triggered by GTK's own widgets that implemented
GtkActionable are not affected by this problem. A close look at the sources
showed that GTK uses different mechanism to locate and trigger actions. This
mechanism relies on the private helper class
has direct access to the action muxers of the widgets. This allows it to call
GtkActionMuxer.activate_action which searches the widget hierarchy correctly.
This mechanismis not available to users of GTK, though. This point seem
to have also been addressed in issue #639 (closed). However, there was no solution
get_action_group should be restricted to return only action groups registered
to the current widget. This allows for a reliable behaviour. The current
behaviour is very confusing and likely to break programs.
As suggested in issue #639 (closed) GtkActionHelper (or a similar functionality) should
be available to users of GTK, too. This saves GTk users from having to write
code traversing the ancestry and locating an action when they want to use an
action outside of an