Home > Gtk3, PyGObject, python > PyGObject: Gtk.Application

PyGObject: Gtk.Application

13 Aprile 2020

torna all’indice degli appunti

Application

Gtk.Application comprende molti compiti ripetitivi che una applicazione deve gestire, come ad
esempio la gestione di istanze multiple, l’attivazione di D-Bus, l’apertura dei files, il parsing
della linea di comando, lo startup e lo shutdown, la gestione dei menu, la gestione della window, ecc.
Eredita dalla classe Gio.Application, ovvero la classe cardine di
una applicazione.
I compiti che deve svolgere un’applicazione (tasks), sono rappresentati dalla classe Gio.Action.
E’ consigliato utilizzare la classe Gtk.Application in concomitanza con la window class
Gtk.ApplicationWindow.

command line

Come accennato in precedenza, Gtk.Application si occupa di alcuni compiti, come ad esempio il
parsing della linea di comando.
A questo proposito, Gtk.Application eredita da Gio.Application il metodo do_command_line
che prende in ingresso un oggetto Gio.ApplicationCommandLine,
che rappresenta l’invocazione dell’applicazione da riga di comando, parametri compresi.

Metodi command line

I metodi più utili dell’oggetto Gio.ApplicationCommandLine sono:

get_arguments()

Ritorna la lista di argomenti passati sulla linea di comando.

get_cwd()

Ritorna la working directory corrente al momento dell’invocazione da linea di comando

get_options_dict()

Ritorna le opzioni passate in linea di comando. Se non viene passata nessuna opzione,
verrà ritornato un dizionario vuoto.
Il dizionario è un oggetto GLib.VariantDict
che puè essere riconvertito in un oggetto GLib.Variant,
invocandone il metodo end().
e a sua volta convertito in un oggetto Python nativo, con il metodo unpack().

Application Flags

Quando creiamo una applicazione questa utilizza una flag property di Gio.ApplicationFlags.
L’utilizzo di tali flags permette di personalizzare il comportamento della applicazione stessa.
Possiamo ad esempio utilizzare HANDLES_COMMAND_LINE per personalizzare il metodo
Gio.Application.do_command_line() gestendo gli argomenti passati in riga di comando.
Il tutto in collaborazione con Gio.Application.add_main_option()

Ecco un esempio:

import sys

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


class AppWindow(Gtk.ApplicationWindow):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.label = Gtk.Label(label="Application example",
                               margin=30)
        self.add(self.label)
        self.show_all()


class Application(Gtk.Application):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, application_id="bancaldo.myapp",
                         flags=Gio.ApplicationFlags.HANDLES_COMMAND_LINE,
                         **kwargs)
        self.window = None

        self.add_main_option(long_name="test", short_name=ord("t"),
                             flags=GLib.OptionFlags.NONE,
                             arg=GLib.OptionArg.NONE,
                             description="Command line test",
                             arg_description=None)

    def do_activate(self):
        if not self.window:
            print("INFO: activating window...")
            self.window = AppWindow(application=self, title="Main Window")
        self.window.present()

    def do_command_line(self, command_line):
        options = command_line.get_options_dict()
        # converto GVariantDict -> GVariant -> dict
        options = options.end().unpack()
        if "test" in options:
            print("INFO: command line argument recieved: %s" % options["test"])
        self.activate()
        return False


if __name__ == "__main__":
    app = Application()
    app.run(sys.argv)

Nota:
il metodo do_activate(), come tutti i virtual methods con prefisso “do_”, è un
metodo virtuale che può essere ridefinito nella classe (Gtk.Application) che deriva dalla classe
genitore (Gio.Application).
Tali metodi equivalgono all’emissione dei segnali corrispondenti.
In tal senso do_activate equivale all’emissione del segnale “activate”.
Nell’esempio richiamiamo il virtual method do_activate(), dall’interno di
do_command_line, emettendo il segnale “activate” (con il metodo activate()).
Questo segnale attiva di fatto l’applicazione.

Actions

Gio.Action è una classe che permette di rappresentare ogni singolo compito
che generalmente svolge una app, con un nome.
Tali “azioni” possono essere abilitate e disabilitate a runtime, possono essere attivate o
cambiare stato (se ne possiedono uno).
L’utilizzo delle actions è utile per tenere separata la logica dell’applicazione, dalla UI.
La principale e più semplice implementazione delle actions, si ottiene utilizzando la classe
Gio.SimpleAction.
Con il metodo Gio.SimpleAction.new_stateful(name, parameter_type, state) si crea, ad esempio,
un oggetto SimpleAction, passandogli come argomenti:
Parametri:
name: il nome (str) della action;
parameter_type: il tipo GLib.VariantType che sarà passato
all’handler del segnale “activate” di Gio.SimpleAction, o None per nessun parametro;
state: l’oggetto GLib.Variant per lo stato iniziale
della action;

Anche molte classi come Gio.MenuItem e Gtk.ModelButton permettono
di settare una action.
Le actions possono anche essere raggruppate insieme in un oggetto Gio.ActionGroup
e quando questi gruppi verranno aggiunti ad un widget con il metodo Gtk.Widget.insert_action_group()
avranno in dote un prefisso corrispondente al tipo di widget (ad esempio prefisso “win”,
per Gtk.ApplicationWindow).
Nel momento della creazione, daremo un nome parziale, ad esempio “about”, ma quando lo
aggiungeremo ad esempio al widget Gtk.Application, ci riferiremo ad esso con il prefisso: “app.about”.
Con il metodo Gtk.Application.add_accelerator() potremo facilmente fare il
keybinding della azione desiderata.

Menu

I Menu vengono definiti in XML usando Gio.Menu e
devono referenziare le actions viste precedentemente.
L’oggetto Gtk.Application permette di settare un menu, tramite i metodi
Gtk.Application.set_app_menu() o Gtk.Application.set_menubar().
Per questo motivo è bene accennare l’oggetto Gtk.Builder,
che è un oggetto che legge una descrizione testuale riguardante la UI (il nostro XML) e crea gli
oggetti descritti in essa grazie ai metodi Gtk.Builder.new_from_file(),
Gtk.Builder.new_from_resource() o Gtk.Builder.new_from_string().

Ecco un codice di esempio che comprende anche actions e menu:

import sys

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


MENU_XML = """
<?xml version="1.0" encoding="UTF-8"?>
<interface>
  <menu id="app-menu">
    <section>
      <item>
        <attribute name="action">win.maximize</attribute>
        <attribute name="label" translatable="yes">Maximize</attribute>
      </item>
    </section>
    <section>
      <item>
        <attribute name="action">app.about</attribute>
        <attribute name="label" translatable="yes">_About</attribute>
      </item>
    </section>
  </menu>
</interface>
"""


class AppWindow(Gtk.ApplicationWindow):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.set_default_size(250, 150)

        self.max_action = Gio.SimpleAction.new_stateful(
            name="maximize",
            parameter_type=None,  # nessun parametro da passare all'handler
            state=GLib.Variant.new_boolean(False))  # state iniziale
        # bindings
        self.max_action.connect("change-state", self.on_maximize_toggle)
        self.connect("notify::is-maximized", self.on_change_state)

        self.add_action(self.max_action)
        # layout
        box = Gtk.Box(spacing=10)
        label = Gtk.Label(label="Click on top-left menu")
        box.pack_start(label, True, True, 0)
        box.set_homogeneous(True)
        self.add(box)
        self.show_all()

    def on_maximize_toggle(self, action, value):
        action.set_state(value)
        if value.get_boolean():
            self.maximize()
        else:
            self.unmaximize()

    def on_change_state(self, obj, pspec):
        state = obj.props.is_maximized
        self.max_action.set_state(GLib.Variant.new_boolean(state))


class Application(Gtk.Application):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, application_id="bancaldo.myapp",
                         flags=Gio.ApplicationFlags.HANDLES_COMMAND_LINE,
                         **kwargs)
        self.window = None

        self.add_main_option(long_name="test", short_name=ord("t"),
                             flags=GLib.OptionFlags.NONE,
                             arg=GLib.OptionArg.NONE,
                             description="Command line test",
                             arg_description=None)

    def do_startup(self):
        Gtk.Application.do_startup(self)
        action = Gio.SimpleAction.new("about", None)
        action.connect("activate", self.on_about)
        self.add_action(action)
        builder = Gtk.Builder.new_from_string(MENU_XML, -1)
        self.set_app_menu(builder.get_object("app-menu"))

    def do_activate(self):
        if not self.window:
            self.window = AppWindow(application=self, title="Main Window")

        self.window.present()

    def do_command_line(self, command_line):
        options = command_line.get_options_dict()
        # converto GVariantDict -> GVariant -> dict
        options = options.end().unpack()
        if "test" in options:
            print("INFO: command line argument recieved: %s" % options["test"])
        self.activate()
        return False

    def on_about(self, action, param):
        about_dialog = Gtk.AboutDialog(transient_for=self.window, modal=True)
        about_dialog.connect("response", self.on_about_close)
        about_dialog.present()

    @staticmethod
    def on_about_close(dialog, param):
        dialog.destroy()


if __name__ == "__main__":
    app = Application()
    app.run(sys.argv)

link di riferimento:

torna all’indice degli appunti
Gtk3 Application

Categorie:Gtk3, PyGObject, python Tag: , ,
I commenti sono chiusi.