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()
2 Likes

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.

1 Like

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

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