24. Application

Gtk.Application täcker flera upprepande uppgifter som ett modernt program behöver så som att hantera flera instanser, D-Bus-aktivering, öppna filer, kommandoradstolkning, uppstart/nedstängning, menyhantering, fönsterhantering med mera.

24.1. Åtgärder

Gio.Action är ett sätt att exponera varje enskild uppgift ditt program eller din komponent gör med ett namn. Dessa åtgärder kan inaktiveras/aktiveras vid körtid och de kan antingen aktiveras eller få ett tillstånd ändrat (om de innehåller tillstånd).

Orsaken för att använda åtgärder är att separera logiken från användargränssnittet. Till exempel så tillåter detta användning av en menyrad i OSX och en kugghjulsmeny i GNOME genom att helt enkelt referera namnet på en åtgärd. Den huvudsakliga implementationen av detta som du kommer att använda är Gio.SimpleAction som kommer demonstreras senare.

Många klasser så som Gio.MenuItem och Gtk.ModelButton stöder egenskaper för att ställa in ett åtgärdsnamn.

Dessa åtgärder kan grupperas tillsammans i en Gio.ActionGroup och då dessa grupper läggs till i en komponent med Gtk.Widget.insert_action_group() kommer de få ett prefix. Exempelvis ”win” då de läggs till ett Gtk.ApplicationWindow. Du kommer använda det fullständiga åtgärdsnamnet då du refererar till det, som ”app.about”, men då du skapar åtgärden kommer den bara vara ”about” tills den läggs till i programmet.

Du kan också väldigt lätt göra tangentbindningar för åtgärder genom att ställa in egenskapen accel i Gio.Menu-filen eller genom att använda Gtk.Application.set_accels_for_action().

24.3. Kommandorad

Då du skapar ditt program tar det en flaggegenskap från Gio.ApplicationFlags. Med denna kan du låta det hantera allting själv eller ha mer anpassat beteende.

Du kan använda HANDLES_COMMAND_LINE för att tillåta anpassat beteende i Gio.Application.do_command_line(). Kombinera med Gio.Application.add_main_option() för att lägga till anpassade alternativ.

Att använda HANDLES_OPEN kommer göra jobbet med att helt enkelt ta filargument åt dig och låta dig hantera det i Gio.Application.do_open().

Om ditt program redan är öppet kommer alla dessa sändas till den befintliga instansen om du inte använder NON_UNIQUE för att tillåta flera instanser.

24.4. Exempel

_images/application_example.png
  1import sys
  2
  3import gi
  4
  5gi.require_version("Gtk", "3.0")
  6from gi.repository import 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">&lt;Primary&gt;q</attribute>
 46    </item>
 47    </section>
 48  </menu>
 49</interface>
 50"""
 51
 52
 53class AppWindow(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    def on_change_label_state(self, action, value):
 84        action.set_state(value)
 85        self.label.set_text(value.get_string())
 86
 87    def on_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
 95class Application(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    def do_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    def do_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    def do_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    def on_about(self, action, param):
150        about_dialog = Gtk.AboutDialog(transient_for=self.window, modal=True)
151        about_dialog.present()
152
153    def on_quit(self, action, param):
154        self.quit()
155
156
157if __name__ == "__main__":
158    app = Application()
159    app.run(sys.argv)

24.5. Se även