Using GTK with Python: Part 2 Gtk.Application & Gtk.Menu
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:
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:
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!
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
.