Recap

In the previous article we setup the environment using pyenv, pipenv, and brew. Then we built a really simple GUI using Gtk.Window(), but as the title mentions, we are going to be using Gtk.Application and Gtk.Menu. So with out further ado, let’s get started.

Gtk.Application

Before you start using Gtk.Application over Gtk.Window it’s important to understand why in the first place. In short it is intended to help your code be an actual Application, not a script with a GUI. It takes responsibility for handling things like:

  • Session Management
  • Application Uniqueness
  • Startup/Shutdown
  • Etc. Go here if you are interested in reading the full writeup on it, I won’t dive too deeply into the details about that here.

Show Me the Code Already!

Okay, okay, so first things first, we keep our imports from last time.

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

In the first article we used Gtk.Window() by declaring it as a variable, which is really not recommended or scalable. Let’s make a class that inherits Gtk.Application, Superclasses Gtk.Application, and sets a valid application id.

class Application(Gtk.Application):
    def __init__(self):
        super().__init__(
          # Set Valid Application Id
          application_id="com.app.example"
          )

app = Application()
app.run()

Run python <filename>.py:

Your application does not implement g_application_activate() and has no handlers connected to the ‘activate’ signal. It should do one of these.

I guess we still have some work to do..

For things to start shaping up we need to add another class inheriting Gtk.ApplicationWindow, and a method do_activate() that will run when our Application class is instantiated. From the docs: “Gtk.ApplicationWindow is a Gtk.Window subclass that offers some extra functionality for better integration with Gtk.Application features. Notably, it can handle both the application menu as well as the menubar.

class Window(Gtk.ApplicationWindow):
    def __init__(self, application):
        # Superclass window, setting a title and initializing "Application" in our window.
        super().__init__(
          title="MyApplication",
          application=application
          )


class Application(Gtk.Application):
    def __init__(self):
        super().__init__(
          application_id="com.app.example"
          )

    def do_activate(self):
        win = Window(self)
        win.show_all()


app = Application()
app.run()

We should end up with this:

MyApp

Great! This gives us a beautifully blank window… Didn’t we have that last time? In short, yes; but this time it’s a full fledged application!

Gtk.Menu

When it comes to creating a menu in there are a few options, in this example we will be using menubar (a menu within the application window) vs. app-menu (a menu within the OS). In the __init__ function of the Window Class we created, we will add a menubar, submenu, menu items, and connect it to an action.

class Window(Gtk.ApplicationWindow):
    def __init__(self, application):
        # Superclass window, setting a title and initializing "Application" in our window.
        super().__init__(title="MyApplication", application=application)
        self.app = application
        # Create a grid because it's the easiest way (IMO).
        self.grid = Gtk.Grid()
        # Create the MeanuBar to go at the top of the application window
        menubar = Gtk.MenuBar()
        # Create a Top level Menu Item
        file_menubar_item = Gtk.MenuItem.new_with_label("File")
        # Create a sub menu for the File Menu Item
        file_menu = Gtk.Menu()
        # Create a Submenu Item
        exit_item = Gtk.MenuItem.new_with_label("Exit")
        # Connect it to an action
        exit_item.connect("activate", self.quit)
        # Add the exit item to the file menu
        file_menu.append(exit_item)
        # Set the Submenu for the File Menu Item
        file_menubar_item.set_submenu(file_menu)
        # Finally, add the menu to the initially created menubar
        menubar.add(file_menubar_item)
        # Attach the menubar to the grid
        self.grid.attach(menubar, 0, 0, 1, 1)
        # Add the grid to the Application
        self.add(self.grid)

    def quit(self, *args):
        self.app.quit()

It doesn’t take much to get the menubar created and added to the application window, it’s not the prettiest code I’ve ever seen, although you have near infinite options available to you with a bit of creativity to create a generator functions of sorts that can create menu items with much DRYer code.

With that, we can run our application and we do get an application with a working menubar. A side-effect however, it eliminated the empty space in our window and we get this:

FileMenu

This is an easy fix. Gtk.ApplicationWindow supports the keyword arguments default_width and default_height which take an integer representing the pixel height and width that we will set.

class Window(Gtk.ApplicationWindow):
    def __init__(self, application):
        # Superclass window, setting a title and initializing "Application" in our window.
        super().__init__(
            title="MyApplication",
            application=application,
            default_height=200,
            default_width=200,
        )

Now we get a decent looking window with our working menu!

FinalApp

Wrapping up, I mentioned in the last post that this is for a budget application that I have been using for a while, which you can follow that project on GitLab. I will be updating the repo until I feel it’s completed and use what I learn there to continue this series. The primary focus for this series is the GTK relevant pieces, but functionality is still important. So in the next one we will be working on adding text box inputs as well as going more in depth with Gtk.Grid.