Python and GTK+3: How to Create a Right-Click Menu

A common task that comes up when developing a GUI is the need for a menu to popup when you right-click on a widget (like the menu that comes up when you right click on this webpage). Today I will be walking you through a very simple example of how we can create a right-click menu in a Python GTK+3 app. For the purposes of demonstration I will be creating a menu that comes up when you right click on the window itself, but it's trivial to adapt this tutorial to any widget you desire.

First, the Code

Let's start things off by seeing the complete code for our example:

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

class ExampleWindow(Gtk.Window):  
    def __init__(self):
        Gtk.Window.__init__(self, title="Right Click Menu Tutorial")
        self.set_default_size(400, 600)

        # Setup a button-press-event listener
        self.connect("button-press-event", self.on_button_press_event)

        #Build the right click menu
        self.right_click_menu = Gtk.Menu()
        menu_item_example = Gtk.MenuItem("Example Menu Item")
        self.right_click_menu.append(menu_item_example)
        menu_item_example.connect("activate", self.example_clicked)
        menu_item_exit = Gtk.MenuItem("Exit")
        self.right_click_menu.append(menu_item_exit)
        menu_item_exit.connect("activate", Gtk.main_quit)
        self.right_click_menu.show_all()

        self.show_all()

    # Handles button press events for our window
    def on_button_press_event(self, widget, event):
        if event.type == Gdk.EventType.BUTTON_PRESS and event.button == 3:
            self.right_click_menu.popup(None, None, None, None, 0, Gtk.get_current_event_time())

    # Triggered when our Example Menu Item is pressed
    def example_clicked(self, *args):
        print("Example button clicked")

# Create a new instance of ExampleWindow and enter GTK Loop
window = ExampleWindow()  
Gtk.main()  

Code Breakdown

Now that we've taken a look at the complete code, let's go through it line by line to see what's going on here.

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

These 3 lines simply import Gtk 3 and Gdk, the two libraries that we will be using to make all this work.

class ExampleWindow(Gtk.Window):  
    def __init__(self):
        Gtk.Window.__init__(self, title="Right Click Menu Tutorial")
        self.set_default_size(400, 600)

Here we are creating a new class, called ExampleWindow, which extends Gtk.Window (line 1). We next set the title and size of our window, via lines 3 and 4.

# Setup a button-press-event listener
self.connect("button-press-event", self.on_button_press_event)  

Here we are simply setting up a listener for the button-press-event. All this line says is: when a button press is detected within the window, call our self.on_button_press_event() method.

#Build the right click menu
self.right_click_menu = Gtk.Menu()  
menu_item_example = Gtk.MenuItem("Example Menu Item")  
self.right_click_menu.append(menu_item_example)  
menu_item_example.connect("activate", self.example_clicked)  
menu_item_exit = Gtk.MenuItem("Exit")  
self.right_click_menu.append(menu_item_exit)  
menu_item_exit.connect("activate", Gtk.main_quit)  
self.right_click_menu.show_all()  

Here is where we build the actual right-click menu. For the purpose of this example, I've created a menu with two entries: Example Menu Item and Exit. We will now take a look at what this code is doing.

In line 2, we are simply creating a new Gtk.Menu(). We then create a new menu item, which I've called menu_item_example (line 3) and appended it to our right_click_menu (line 4). In line 5, we setup a listener on our menu_item_example to listen for an activate event. When the event is detected, it will call our self.example_clicked() method.

Lines 6-8 are functioning in the same way as our lines to setup our menu_item_example. The only difference here is that, on an activate event, we will be calling Gtk.main_quit() to exit the application.

In line 9 we simply call the show_all() method on our right_click_menu.

self.show_all()  

This final line of our constructor simply calls show_all() on our window class.

# Handles button press events for our window
def on_button_press_event(self, widget, event):  
    if event.type == Gdk.EventType.BUTTON_PRESS and event.button == 3:
        self.right_click_menu.popup(None, None, None, None, 0, Gtk.get_current_event_time())

Here we have our on_button_press_event(self, widget, event) method. If you'll recall line 11 of our sample program, we set things up in such a way that any time there is a button-press-event on our window, this method will be called. This means that this is effectively where we will be testing for our right-click.

Line 3 of this method is an if statement that is testing for 2 things:

1) We are testing that the type of our event is a Gdk.EventType.BUTTON_PRESS. In other words, is the event we are receiving a button press event.

2) Is the button pressed equal to button number 3. It's worth noting here that the right mouse button is considered button 3.

Line 4 is where we actually show our right-click menu (assuming, of course the tests in our if-statement pass). We do this by simple calling popup(parent_menu_shell, parent_menu_item, menu_pos_func, data, button, time) on our right_click_menu.

# Triggered when our Example Menu Item is pressed
def example_clicked(self, *args):  
    print("Example button clicked")

The final method in our ExampleWindow class is the example_clicked(self, *args) method. This is an extremely simple method that we set to be triggered any time we press the Example Menu Item. All the method does is print our Example button Clicked to the terminal.

# Create a new instance of ExampleWindow and enter GTK Loop
window = ExampleWindow()  
Gtk.main()  

Finally, we have the final 2 lines of code. All we are doing here is creating an instance of our ExampleWindow class and calling Gtk.main(), which is the main loop for a Gtk application.

Now, if we run this code and right click anywhere within our window, we will be presented with a right-click menu:

Right Click Menu

I hope you found this helpful. If you have any questions, comments, or perhaps suggestions for future posts, hit me up @serialphotog on Twitter.