How to create menus for apps using Python?

Hi. I’d like to know if GtkApplication is necessary for creating any kind of Menu?? Because my trivial program , doesn’t work( menu buttons are insensitive). Please give me a good example in Python that uses no GtkApplication.

Additionally, I find use of linking GioAction’s name and Menus, a bit strange. Like instead of providing name to connect to the menu item with conventions like ‘app’ or ‘win’, we can simply use the objects. Something like this :
menuitem = Gio.MenuItem(name=“File”)
action = Gio.Action()
menuitem.add_action(action)

You can set up a menu easy enough without a GtkApplication.

Eric


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

class MainWindow(Gtk.Window):
    def __init__(self):
        Gtk.Window.__init__(self, title="Menu")
        self.set_default_size(200, 100)

        self.menubar1=Gtk.MenuBar()  
        self.menu1=Gtk.Menu()
        self.menuitem1=Gtk.MenuItem("File") 
        self.menuitem1.set_submenu(self.menu1)
        self.menuitem2=Gtk.MenuItem("Open File")
        self.menuitem2.connect("activate", self.open_file)      
        self.menuitem3=Gtk.MenuItem("Save File")
        self.menuitem3.connect("activate", self.save_file)   
        self.menu1.append(self.menuitem2)
        self.menu1.append(self.menuitem3) 
        self.menubar1.append(self.menuitem1)
       
        self.label1=Gtk.Label("Menu Test")
        self.label1.set_hexpand(True)
        self.label1.set_vexpand(True)

        self.grid=Gtk.Grid()
        self.grid.attach(self.menubar1, 0, 0, 1, 1)
        self.grid.attach(self.label1, 0, 1, 1, 1)
        self.add(self.grid)
        
    def open_file(self, menuitem2):
        print("Open File")

    def save_file(self, menuitem3):
        print("Save File")

win=MainWindow()
win.connect("delete-event", Gtk.main_quit) 
win.show_all()
Gtk.main()

Weird enough, I used Gio.Action and it didn’t work :joy: Thanks.

A side note: GtkMenu and related widgets have been removed from GTK4. Application developers should always use GMenu and GAction to describe their menus, especially if they wish to remain forward compatible.

The namespacing is meant to be used when exposing actions over an IPC mechanism, like DBus. This allows calling actions remotely from other components, like the Shell; or to have the menus moved over to the top panel, on macOS. The app vs win namespace is less important, now that GNOME has deprecated the app menu; but in the future, we’re going to expose more namespaces in GTK—like shortcut actions, for instance; or accessibility-related ones; or actions in context menus.

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

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?

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)

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?

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)