How to create menus for apps using Python?

Thanks Emmanuele. That works great. I wasn’t able to figure that out the other day. Thanks again.

Eric

import gi
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk, Gio

win = Gtk.Window()

menu = Gio.Menu()
menu.append("A", "action")
a1 = Gio.SimpleAction.new("action", None)
a1.connect("activate", print)

Menu = Gtk.MenuBar.new_from_model(menu)

win.add(Menu)
win.connect("destroy", Gtk.main_quit)
win.show_all()
Gtk.main()

Can you please tell me whats wrong with this code? The menu item is not clickable.

You need to add the actions to an action group associated with the widget, otherwise GTK will not be able to know about the actions, and will automatically disable the menu items.

A proper way to create a menu bar for a generic window is:

import gi
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk, Gio 

win = Gtk.Window()

# The 'File' menu items
file_menu = Gio.Menu()

# The actions must be detailed with a group; we use 'win' here,
# as they match window-related actions
file_menu.append('Open', 'win.open_file')
file_menu.append('Save', 'win.save_file')
file_menu.append('Close', 'win.close')

# The main menu bar
menu = Gio.Menu()
menu.append_item(Gio.MenuItem.new_submenu('File', file_menu))

# Do not use the `new` method, unless it's more complicated than just
# passing properties; you should prefer the Python normal form for
# constructors. The actions name is not prefixed because the
# (prefix, name) tuple used to match the menu item is resolved via the
# action group
open_action = Gio.SimpleAction(name="open_file", parameter_type=None, enabled=True)
open_action.connect("activate", lambda x, y: print('win.open_file activated'))

save_action = Gio.SimpleAction(name="save_file", parameter_type=None, enabled=True)
save_action.connect("activate", lambda x, y: print('win.save_file activated'))

close_action = Gio.SimpleAction(name="close", parameter_type=None, enabled=True)
close_action.connect("activate", lambda x, y: print('win.close activated'))

# Create an action group, to keep all the actions you need
action_group = Gio.SimpleActionGroup()
action_group.add_action(open_action)
action_group.add_action(save_action)
action_group.add_action(close_action)

# Add the action group to the window, using the 'win' prefix.
# GTK traverses the hierarchy to find the appropriate action
# group; since these are window-related actions, they can
# live on the top level window, but you can have per-widget
# action groups
win.insert_action_group('win', action_group)

Menu = Gtk.MenuBar.new_from_model(menu)

win.add(Menu)
win.connect("destroy", Gtk.main_quit)
win.show_all()
Gtk.main()

At this point, though, you’re literally re-implementing Gtk.Application and Gtk.ApplicationWindow, but worse; so I strongly recommend you just move to those classes instead of using the base Gtk.Window class.

For instance, GtkApplication will expose your application’s menus on the DBus user session, which means you can call actions from other processes. Additionally, GtkApplication can automatically read the menu descriptions embedded in GResource files and generate the menu bar for you.

Unless you’re writing something incredibly custom, you should really, really use GtkApplication; it’s not like we added that entire API because we like adding code at random.

2 Likes

THANKS A LOT!!! I was literally confused how to make my window know the actions and really thanks a lot. Yep Im using GtkApplication, but for simple test-cases (like this is the first time, Im using Menu for my apps), I prefer the least-typed solution :). I think by mistake (because I was all confused), I used .new method, I too hate em. I try to pass all the properties in the initiator itself without writing again and again. Still I thank you and others for helping me.

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