Menuitem greyed-out

I have implemented the following. Before adding the 2nd parameter (‘link_remove’) to Gio.MenuItem.new() the menu item is not greyed-out. After adding the 2nd parameter, the menu item is greyed-out.

Seems to me the 2nd parameter is necessary as an association to SimpleAction().

‘set_enabled’ does not prevent it from being greyed-out.

I have looked at several code examples and cannot find anything that suggests why this is happening. In fact, the examples show the same kind of thing I am doing with the exception that they do not make use of Gio.SimpleAction.activate(). But, removing it or leaving it in makes no difference.

Any ideas?

item = Gio.MenuItem.new('_Remove Link', 'link_remove')
remove_action = Gio.SimpleAction.new('link_remove', None)
# Documentation says this 'set_enabled' has to occur before activation.
remove_action.set_enabled(True)     # Makes the menu item sensitive# Documentation says 'activate()' must appear before the activation.
remove_action.activate(None)
# shows that the this menu item is enabled.
logger.debug('Menu: populate_popup(): Remove Link enabled = %s', remove_action.get_enabled())
remove_action.connect('activate', lambda o: self.remove_link(iter=iter))
# This following results in an 'argument must be callable' error. Don't know why??
#action.connect('activate', self.remove_link(iter=iter))
menu_section.prepend_item(item)

Hi,

remove_action.set_enabled(True)     # Makes the menu item sensitive

That should not be needed, actions are enabled by default. Besides, it’s not enough to make the menu item sensitive, the action must also be part of an action group in the same window (see below).

# Documentation says 'activate()' must appear before the activation.

Huh? that should not be needed… where have you seen that doc?

# This following results in an 'argument must be callable' error. Don't know why??
#action.connect('activate', self.remove_link(iter=iter))

That will call self.remove_link(iter=iter) immediately and pass the return value as 2nd argument… That’s not what you need here, you shall provide a callback instead, like the lambda a few lines above.

Back to the question :slight_smile: : the menu is grey because the action is not linked to the window. You shall create an action group, add the action to it, then add the action group to a widget (typically a GtkMenuButton or GtkPopoverMenuBar):

agroup = Gio.SimpleActionGroup()
agroup.add_action(remove_action)
widget.insert_action_group('menu', agroup)

If you use a GtkApplicationWindow, they already have an action group named “win”, you can directly add the action to it:

app_window.add_action(remove_action)

And last point: make sure the action name of the menu item is the full action, including the action group name:

item = Gio.MenuItem.new('_Remove Link', 'menu.link_remove')

First off, thank you for taking the time to respond.

Before, your response I had looked at the problem further and had implemented what you suggested. However, the menu item remains greyed-out.

The things I added were as follows (to the Menu class):

if link:
    # Group all the actions|
    self.action_group = Gio.SimpleActionGroup()
    self.action_group.add_action(remove_action)

def get_actiongroup(self):
      return self.action_group

Then in the calling class this:

action_group = popup.get_actiongroup()
self.insert_action_group('app', action_group)

Where ‘self’ is a Gtk.EventBox.

I changed ‘app’ to ‘menu’ as per your suggestion. But, it did not help.

“# Documentation says ‘activate()’ must appear before the activation.”

Can’t seem to find where I found that before. In any case, lets just ignore that part.

I am not using that anyway. But, I get what you are saying.

I should have used this instead:

action.connect('activate', self.remove_link, iter)

I should add some more details here for clarity.

The Gtk.EventBox is inherited by a class called ‘ExpanderWidget’.

This class then defines an hierarchy as follows:

Gtk.EventBox > Gtk.Overlay > Gtk.Label
             > Gtk.Expander > Gtk.Frame > TextView

A right-mouse click instantiates the Menu class. This is not the way I will leave this but for now it is good enough.

	def on_button_press_event(self, widget, event):
		elif event.type == Gdk.EventType.BUTTON_PRESS and event.button == 3:
			iter, coords = self.textview._get_pointer_location()
			self.textview._set_popup_menu_mark(iter)
			popup = Menu(widget, self.preferences, self.readonly, self.notebook, self.page)
			popup.set_relative_to(widget)
			LEFT = 0
			left = Gtk.PositionType(LEFT)
			popup.set_position(left)
			popup.show_all()
			action_group = popup.get_actiongroup()
			self.insert_action_group('menu', action_group)

‘self’ is ExpanderWidget

Just on a whim, I replaced ‘self.insert_action_group’ with ‘widget.insert_action_group’. (‘widget’ equates to ‘self.textview’). I did this because the right-click actually occurs within this textview widget. The menu item still remains greyed-out.

Just to be in the safe side I checked using action_group.query_action(‘link_remove’). It returns True that it exists and True that it is enabled.

Another thing I just discovered is this:

The documentation states this:
https://athenajc.gitbooks.io/python-gtk-3-api/content/gtk-group/gtkwidget.html#insertactiongroup

" Children of widget that implement Gtk.Actionable can then be associated with actions in group bysetting their “action-name” toprefix .action-name ."

I have performed the insert_action_group() on my class ExpanderWidget which has a Gtk.TextView as a child widget. According the statement above it suggests that this widget needs to implement ‘Gtk.Actionable’ in order for the insert action(s) to be used within that widget. Am I understanding this correctly?

Or, perhaps this means that ONLY Gtk.Actionable widgets can use the inserted action. Since I use Gtk.PopoverMenu and it contains Gio.MenuItems (inside of Gio.Menu widgets) and the widget I right-click in is a Gtk.TextView, I am at a total loss of how to make this work because none of these widgets implement Gtk.Actionable.

I tried adding Gio.Actionable to my menu class as follows and the app complains about not having property ‘action-name’ and ‘action-state’ not being implemented.

class Menu(Gtk.PopoverMenu, Gtk.Actionable):

So, I added the following without cessation of this error:

    @property
    def action_name(self, name):
         name = 'menu.link_remove'
         return name

    @property
    def action_state(self, state):
        state = None
        return state

I also tried this in a TextView class (that inherits Gtk.TextView) and in my ExpanderWidget class .

Man, it sure would be nice if there was some half decent documentation. It would save many people a LOT of time!

Maybe the idea is if you use Gtk3 to use only Gtk.Menu, etc (even though it is deprecated) instead of Gio classes. And if you use Gtk4, use the Gio classes. Have no idea.

Signed, VERY Frustrated!!

Hi,

It’s hard to say what’s wrong without seeing all your code…

I made a fully working example:

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


class MyApp(Gtk.Application):

    def __init__(self):
        Gtk.Application.__init__(self)
        self.connect('activate', self.on_activate)

    def on_activate(self, app):
        self.add_window(MyAppWindow())


class MyAppWindow(Gtk.ApplicationWindow):

    def __init__(self):
        Gtk.Window.__init__(self, default_width=600, default_height=400)
        model = Gio.Menu()
        model.append("Remove link", 'menu.remove_link')
        action = Gio.SimpleAction.new('remove_link', None)
        action.connect('activate', self.on_remove_link)
        agroup = Gio.SimpleActionGroup()
        agroup.add_action(action)
        self.evbox = Gtk.EventBox()
        self.evbox.add_events(Gdk.EventMask.BUTTON_PRESS_MASK | Gdk.EventMask.BUTTON_RELEASE_MASK)
        self.evbox.connect('button-press-event', self.on_event_buttonpress)
        self.evbox.insert_action_group('menu', agroup)
        self.evbox.add(Gtk.Label.new("Hello"))
        self.add(self.evbox)
        self.menu = Gtk.Menu.new_from_model(model)
        self.menu.attach_to_widget(self.evbox)
        self.show_all()

    def on_remove_link(self, action, param):
        print(action.get_name())

    def on_event_buttonpress(self, widget, event):
        if event.triggers_context_menu():
            self.menu.popup_at_pointer(event)
            return Gdk.EVENT_STOP
        return Gdk.EVENT_PROPAGATE


if __name__ == '__main__':
    sys.exit(MyApp().run(sys.argv))

Can you please compare with your code?

Regarding Gtk.Actionable : the menu items themselves are actionable by design (when clicked), so no need to do anything specific in that regard.

When it’s actioned, gtk will look if the widget or its parents have an “action group + action” matching the menu item’s linked action.
Could it be you forgot to call menu.attach_to_widget(), so the menu couldn’t find a parent widget with the action?

There are examples just like yours on the Internet and I have tested them and they work just fine.

The problem with mine seems to be in attaching the action_group to the correct widget (or at least one that will work with actions). In your case it is Gtk.ApplicationWindow. If you look at that class you will see that it inherits from ActionGroup and ActionMap. These classes permit it to work as expected.

In my case, I do not have direct access to Gtk.ApplicationWindow. In fact, it is not even used in the Zim python app that I am trying to adapt my code to work with. Zim uses the deprecated Gtk.Menu, etc. And it could be that I will have to step back into using that. I wanted to use the newer Gio code so that my code is more up-to-date. But, it could be that I would have to use Gtk4 to get things working as needed. I am currently using Gtk3 as that is what Zim depends on. These changes that the Gnome team are implementing are resulting in some issues. Maybe what needs to happen is for Zim to be entirely updated. I am not the lead developer on Zim. All I have been trying to do is add a new feature of folding text within a Zim wiki page.

The folding text part is working as needed with the exception of the popup menu when right-clicking on various objects such as a link or an image.

I have not implemented that as I was under the impression that the insert_action_group() method was all that was needed. I will try your suggestion.

Just tried it and Gio.Menu has no such attach_to_widget() method.

That method is part of Gtk.Menu not Gio.Menu (which is what I am using).

Not at all :slight_smile:
In my example the group is attached to the eventbox, see:

self.evbox.insert_action_group('menu', agroup)

For menus, Gio.Menu works fine with both gtk3 and gtk4, as per my personal experience.

I see a little confusion here :slight_smile:
Gio.Menu is an abstract model, describing the contents of menus.
It does not display anything by itself.

For displaying a Gio.Menu model, you need a Gtk widget, typically:

  • Gtk.Menu or Gtk.MenuBar in gtk3
  • Gtk.PopoverMenu or Gtk.PopoverMenuBar in gtk4

In our case, for context menus in gtk3, Gtk.Menu is the best choice.
I used Gtk.Menu.new_from_model(model) to create the menu widget from the model, gtk3 will automatically parse the Gio.Menu model and create the popups and buttons accordingly.

This Gtk.Menu (or whatever) shall be correctly inserted in the widgets hierarchy to be able so resolve actions, that’s why I need to call self.menu.attach_to_widget(self.evbox).

I have abandoned the Gio approach and am using the same Gtk.Menu method (that Zim uses) and everything is working as expected. Seems the Gio method is not ready for prime time. It seems that there is no way to integrate Gtk3 classes with Gio (at least for certain types of applications).

For example, it should be possible to use Gio.Menu with a Gtk.TextView. But, there is, currently, no way to do this with Gtk3.

It maybe, as I suggested before, necessary to use Gtk4 completely.
* In comparing some of the Gtk4 code for Gtk.Widget with that of Gtk3 there are significant changes that may very well be necessary for Gio.Menu to function properly. I am more convinced than ever that one MUST use Gtk4 with Gio.Menu. If one uses Gtk3 then it must be Gtk.Menu.

The problem is not Gio: it’s that you cannot mix and match.

GTK4 definitely changed some things because it removed the menu item widgets, so you can only use Gio.Menu to construct menus—but you definitely can do that with GTK3, as well, as long as the whole application uses the same API consistently.

I modified your code to use Gtk.TextView as my code does and it all works as it should.

Actually the Zim code does not use Gtk.TextView directly. It uses TextView (which is a Zim defined class) which inherits Gtk.TextView. I was assuming because it inherits Gtk.TextView that it would work; but, alas, it does not.

It is looking like there is something going on within this Zim TextView that is causing the menu item to be greyed-out.

Well, I appreciate all your efforts in helping me track this problem down. Now, I need to determine why this Zim TextView is causing the problem.

Thank you again.

I still wish there was better documentation on the Gnome side. It would save people a lot of time.

Ah, I finally figured it out.

Your idea was to use the following:

    model = popup_menu.get_model()
    menu = Gtk.Menu.new_from_model(model)
    menu.attach_to_widget(widget)
    menu.popup_at_pointer(event)

While this works it is not a good solution as it uses the deprecated Gtk.Menu. My whole purpose for using Gio.Menu, in the first place, was to not use any deprecated code.

I fixed the problem by properly understanding how to use the following statement:

 `self.bind_model(self.menu, None)`

I originally had this set as follows:

 `self.bind_model(self.menu, 'contextMenu')`

The ‘self’ in ‘self.bind’ refers to Gtk.PopoverMenu.

That last parameter did not match what I was using in my Gio.Menuitem declaration (of ‘win’). It doesn’t matter what this name is. It just has to be the same.

Once that name is the same the menu items are no longer greyed-out (which now makes sense).

However, if an Gio.ActionGroup is used, this last parameter MUST be None.

Using Gtk.PopoverMenu.bind_model() permits the use of Gtk.PopoverMenu.popup() to display the menu.

And yes, I do understand that Gio.Menu is a menu model. But, in actuality it IS a menu. It is just a model of a menu of a proposed menu which gets implemented with Gtk.PopoverMenu.bind_model() and Gtk.PopoverMenu.popup().

1 Like