24. Application
Gtk.Application encompasses many repetitive tasks that a modern
application needs such as handling multiple instances, D-Bus activation,
opening files, command line parsing, startup/shutdown, menu management,
window management, and more.
24.1. Actions
Gio.Action is a way to expose any single task your application or widget
does by a name. These actions can be disabled/enabled at runtime and they can
either be activated or have a state changed (if they contain state).
The reason to use actions is to separate out the logic from the UI. For example
this allows using a menubar on OSX and a gear menu on GNOME both simply
referencing the name of an action. The main implementation of this you will
be using is Gio.SimpleAction which will be demonstrated later.
Many classes such as Gio.MenuItem and Gtk.ModelButton support
properties to set an action name.
These actions can be grouped together into a Gio.ActionGroup and
when these groups are added to a widget with Gtk.Widget.insert_action_group()
they will gain a prefix. Such as "win" when added to a Gtk.ApplicationWindow.
You will use the full action name when referencing it such as "app.about" but when
you create the action it will just be "about" until added to the application.
You can also very easily make keybindings for actions by setting the accel
property in the Gio.Menu file or by using Gtk.Application.set_accels_for_action().
24.3. Command Line
When creating your application it takes a flag property of Gio.ApplicationFlags.
Using this you can let it handle everything itself or have more custom behavior.
You can use HANDLES_COMMAND_LINE to allow custom behavior in Gio.Application.do_command_line().
In combination with Gio.Application.add_main_option() to add custom options.
Using HANDLES_OPEN will do the work of simply taking file arguments for you and
let you handle it in Gio.Application.do_open().
If your application is already open these will all be sent to the existing instance unless you use NON_UNIQUE to allow multiple instances.
24.4. Example
_images/application_example.png1importsys 2 3importgi 4 5gi.require_version("Gtk", "3.0") 6fromgi.repositoryimport GLib, Gio, Gtk 7 8# This would typically be its own file 9MENU_XML = """ 10<?xml version="1.0" encoding="UTF-8"?> 11<interface> 12 <menu id="app-menu"> 13 <section> 14 <attribute name="label" translatable="yes">Change label</attribute> 15 <item> 16 <attribute name="action">win.change_label</attribute> 17 <attribute name="target">String 1</attribute> 18 <attribute name="label" translatable="yes">String 1</attribute> 19 </item> 20 <item> 21 <attribute name="action">win.change_label</attribute> 22 <attribute name="target">String 2</attribute> 23 <attribute name="label" translatable="yes">String 2</attribute> 24 </item> 25 <item> 26 <attribute name="action">win.change_label</attribute> 27 <attribute name="target">String 3</attribute> 28 <attribute name="label" translatable="yes">String 3</attribute> 29 </item> 30 </section> 31 <section> 32 <item> 33 <attribute name="action">win.maximize</attribute> 34 <attribute name="label" translatable="yes">Maximize</attribute> 35 </item> 36 </section> 37 <section> 38 <item> 39 <attribute name="action">app.about</attribute> 40 <attribute name="label" translatable="yes">_About</attribute> 41 </item> 42 <item> 43 <attribute name="action">app.quit</attribute> 44 <attribute name="label" translatable="yes">_Quit</attribute> 45 <attribute name="accel"><Primary>q</attribute> 46 </item> 47 </section> 48 </menu> 49</interface> 50""" 51 52 53classAppWindow(Gtk.ApplicationWindow): 54 def__init__(self, *args, **kwargs): 55 super().__init__(*args, **kwargs) 56 57 # This will be in the windows group and have the "win" prefix 58 max_action = Gio.SimpleAction.new_stateful( 59 "maximize", None, GLib.Variant.new_boolean(False) 60 ) 61 max_action.connect("change-state", self.on_maximize_toggle) 62 self.add_action(max_action) 63 64 # Keep it in sync with the actual state 65 self.connect( 66 "notify::is-maximized", 67 lambda obj, pspec: max_action.set_state( 68 GLib.Variant.new_boolean(obj.props.is_maximized) 69 ), 70 ) 71 72 lbl_variant = GLib.Variant.new_string("String 1") 73 lbl_action = Gio.SimpleAction.new_stateful( 74 "change_label", lbl_variant.get_type(), lbl_variant 75 ) 76 lbl_action.connect("change-state", self.on_change_label_state) 77 self.add_action(lbl_action) 78 79 self.label = Gtk.Label(label=lbl_variant.get_string(), margin=30) 80 self.add(self.label) 81 self.label.show() 82 83 defon_change_label_state(self, action, value): 84 action.set_state(value) 85 self.label.set_text(value.get_string()) 86 87 defon_maximize_toggle(self, action, value): 88 action.set_state(value) 89 if value.get_boolean(): 90 self.maximize() 91 else: 92 self.unmaximize() 93 94 95classApplication(Gtk.Application): 96 def__init__(self, *args, **kwargs): 97 super().__init__( 98 *args, 99 application_id="org.example.myapp", 100 flags=Gio.ApplicationFlags.HANDLES_COMMAND_LINE, 101 **kwargs 102 ) 103 self.window = None 104 105 self.add_main_option( 106 "test", 107 ord("t"), 108 GLib.OptionFlags.NONE, 109 GLib.OptionArg.NONE, 110 "Command line test", 111 None, 112 ) 113 114 defdo_startup(self): 115 Gtk.Application.do_startup(self) 116 117 action = Gio.SimpleAction.new("about", None) 118 action.connect("activate", self.on_about) 119 self.add_action(action) 120 121 action = Gio.SimpleAction.new("quit", None) 122 action.connect("activate", self.on_quit) 123 self.add_action(action) 124 125 builder = Gtk.Builder.new_from_string(MENU_XML, -1) 126 self.set_app_menu(builder.get_object("app-menu")) 127 128 defdo_activate(self): 129 # We only allow a single window and raise any existing ones 130 if not self.window: 131 # Windows are associated with the application 132 # when the last one is closed the application shuts down 133 self.window = AppWindow(application=self, title="Main Window") 134 135 self.window.present() 136 137 defdo_command_line(self, command_line): 138 options = command_line.get_options_dict() 139 # convert GVariantDict -> GVariant -> dict 140 options = options.end().unpack() 141 142 if "test" in options: 143 # This is printed on the main instance 144 print("Test argument recieved: %s" % options["test"]) 145 146 self.activate() 147 return 0 148 149 defon_about(self, action, param): 150 about_dialog = Gtk.AboutDialog(transient_for=self.window, modal=True) 151 about_dialog.present() 152 153 defon_quit(self, action, param): 154 self.quit() 155 156 157if __name__ == "__main__": 158 app = Application() 159 app.run(sys.argv)