Search code examples
pythongtkgladepygobject

Changing Gtk accelerator set by glade


I'm using Gtk (python3) + Glade to create an application. I've set some accelerators in glade like this :

 <child>
     <object class="GtkImageMenuItem" id="imagemenuitem5">
         <property name="label">gtk-quit</property>
         <accelerator key="q" signal="activate" modifiers="GDK_CONTROL_MASK"/>
     </object>
 </child>

But I don't see how I can change the accelerator for this event to something else while the application is running. Is it possible? Is my implementation wrong for what i plan to do ?


Solution

  • The <accelerator> element uses gtk_widget_add_accelerator() internally. From the documentation of that function:

    Accelerators added through this function are not user changeable during runtime. If you want to support accelerators that can be changed by the user, use gtk_accel_map_add_entry() and gtk_widget_set_accel_path() or gtk_menu_item_set_accel_path() instead.

    In fact there is even a more modern way to do it than that, using GAction, GApplication, and GMenuModel, without creating any menu bars at all. Here's an example of changing the accelerators of a menu item at runtime:

    from gi.repository import Gio, Gtk
    
    
    class App(Gtk.Application):
        def __init__(self, **props):
            super(App, self).__init__(application_id='org.gnome.example',
                flags=Gio.ApplicationFlags.FLAGS_NONE, **props)
            # Activating the LEP GEX VEN ZEA menu item will rotate through these
            # accelerator keys
            self.keys = ['L', 'G', 'V', 'Z']
    
        def do_activate(self):
            Gtk.Application.do_activate(self)
    
            actions = self.create_actions()
            self.add_action(actions['quit'])
    
            self.win = Gtk.ApplicationWindow()
            self.add_window(self.win)
            self.win.add_action(actions['lep'])
            self.set_accels_for_action('win.lep', ['<primary>' + self.keys[0]])
            # Ctrl-Q is automatically assigned to an app action named quit
    
            model = self.create_menus()
            self.set_menubar(model)
    
            actions['lep'].connect('activate', self.on_lep_activate)
            actions['quit'].connect('activate', lambda *args: self.win.destroy())
    
            self.win.show_all()
    
        def create_actions(self):
            return {name: Gio.SimpleAction(name=name) for name in ['lep', 'quit']}
    
        def create_menus(self):
            file_menu = Gio.Menu()
            file_menu.append('LEP GEX VEN ZEA', 'win.lep')
            file_menu.append('Quit', 'app.quit')
            menu = Gio.Menu()
            menu.append_submenu('File', file_menu)
            return menu
    
        def on_lep_activate(self, *args):
            self.keys = self.keys[1:] + self.keys[:1]
            self.set_accels_for_action('win.lep', ['<primary>' + self.keys[0]])
    
    if __name__ == '__main__':
        App().run([])
    

    You can also do some of this with Glade files and hook it up automatically by creating a menus.ui file and making it available to your app at a particular GResource path: this is described here.