How to create menus for apps using Python?

Im in Debian, Idk when Gtk 4 will be available for me… When will Gtk 4 be released?

So namespaces will be used instead of pure object references?

When it’s ready.

That’s not what I said.

It seems you don’t really understand how actions and menus work; have you read the wiki page on GAction and GMenu?

Oops sorry. I will read it.

The application menu at

https://python-gtk-3-tutorial.readthedocs.io/en/latest/application.html

works well. I tried it on Ubuntu18.04. Give it a try on Debian.

Eric

1 Like

Actually, as its part of Gtk3, it worry fine in Debian.

I read the docs. Thanks. And why is the coding mixed up? Like somewhere C and somewhere Python (that too Python 2 !!).

Maybe because some of the other supported languages like Crystal, Ada, Fortran, PHP, … are not used by that many people?

1 Like

But, having multiple languages on same site doesn’t look good

Can you update this answer to be forward compatible? Using GMenu and GIo.Action

First of all: no, we should have examples in multiple languages in the GNOME documentation, because people use multiple languages to write GNOME applications.

Second: what website had different languages? I only linked two—both on the GNOME wiki. The other link was to the Python/GTK tutorial, which is hosted on a separate infrastructure.

Im not against having docs for all langs. Im kinda not supporting, a single wiki page having multiple langs (C and Python).

I will give it a try. Not sure how to get around the xml in python. I see there is a GMenu.Tree in the lazka documentation but I don’t know how to create a GMenu without the xml. If I have some things wrong, tell me. Trying to learn these things myself and I am not the greatest at python.

Eric


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

MENU_XML="""
<?xml version="1.0" encoding="UTF-8"?>
<interface>
  <menu id="app-menu">
    <submenu>
    <attribute name='label' translatable='yes'>File</attribute>
    <section>
      <item>
        <attribute name="action">app.open_file</attribute>
        <attribute name="label" translatable="yes">Open File</attribute>
      </item>
      <item>
        <attribute name="action">app.save_file</attribute>
        <attribute name="label" translatable="yes">Save File</attribute>
      </item>
    </section>
    </submenu>
  </menu>
</interface>
"""
 
class Application(Gtk.Application):
    def __init__(self):
        Gtk.ApplicationWindow.__init__(self, application_id="test.menu", flags=Gio.ApplicationFlags.FLAGS_NONE)
        self.window=None
        self.connect("startup", self.startup_menu)
        self.connect("activate", self.activate_window)
                     
    def startup_menu(self, *args):
        self.action=Gio.SimpleAction.new("open_file", None)
        self.action.connect("activate", self.open_file)
        self.add_action(self.action)

        self.action=Gio.SimpleAction.new("save_file", None)
        self.action.connect("activate", self.save_file)
        self.add_action(self.action)

        builder=Gtk.Builder.new_from_string(MENU_XML, -1)
        self.set_menubar(builder.get_object("app-menu"))

    def activate_window(self, *args):
        self.window=Gtk.ApplicationWindow(application=self, title="Menu")
        self.window.set_default_size(200, 100)

        label=Gtk.Label("Menu Test")
        self.window.add(label)

        self.window.show_all()

    def open_file(self, *args):
        print("Open File")

    def save_file(self, *args):
        print("Save File")

if __name__ == "__main__":
    app=Application()
    app.run(None)
1 Like

I created a GMenu example with GAction without XML some months ago, because I was not able to find one. Was working, but I got not yet confirmation from people like ebassi or baedert that it really is correct – and well ebassi generally recomments using gtk-builder with XML files, so we should better try using that XML.

Is there a way in this forum for adding a plain link without these ugly preview pictures?

1 Like

The GMenu.Tree is part of gnome-desktop, and it’s meant to be used to create menus of installed applications; it has nothing to do with application menus. Sadly, there’s a bit of a namespace collision.

You want Gio.Menu.

A programmatic menu description from the XML you pasted would look like this:

from gi.repository import Gio

# The main menu
menu = Gio.Menu()

# The "open file" menu item
open_item = Gio.MenuItem()
open_item.set_label("Open File")
open_item.set_detailed_action("app.open_file")

# The "save file" menu item
save_item = Gio.MenuItem()
save_item.set_label("Save File")
save_item.set_detailed_action("app.save_file")

# The open and save menu items are part of a "section"; sections are used
# to tell the component that will render the menu to visually/semantically
# separate the items
section_menu = Gio.Menu()
section_menu.append_item(open_item)
section_menu.append_item(save_item)

# Put the section in a sub-menu, without a label
submenu = Gio.Menu()
submenu.append_section(None, section_menu)

# Put the sub-menu in the main menu, with a label
menu.append_submenu("File", submenu)
2 Likes

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.