Desktopintegrationsbemühungen

English   |   Source

Desktopintegration: Icon, Headerbar, Kommandozeilenoptionen

(Fortsetzung zum Artikel GtkApplication)

/images/15_application.thumbnail.png

Glade

Icon

Einem Fenster lässt sich direkt in Glade unter "Allgemein > Darstellung > Symboldatei" ein Icon auswählen. Das Problem dabei ist, dass Glade Bilddateien nur anzeigt, wenn sie sich im selben Verzeichnis wie die Gladedatei selbst befinden, auch wenn man ein anderes Verzeichnis auswählt.

Am einfachsten behebt man dies, indem man die Gladedatei in einem Texteditor bearbeitet und den (relativen) Pfad zum Icon angibt. Diese Einstellung bleibt auch erhalten, wenn die Datei später mit Glade bearbeitet und gespeichert wird:

<object class="GtkApplicationWindow" id="window">
  ...
  <!-- <property name="icon">duckyou.svg</property> -->
  <property name="icon">../files/duckyou.svg</property>
  ...

Headerbar

Die Headerbar wurde mit GNOME 3.10 eingeführt und vereint Titelleiste und Toolbar in einem Widget, d.h neben Titel und Untertitel können rechts und/oder links verschiedene Widgets (Menüs, Buttons) angelegt sowie clientseitige Fensterkontrollknöpfe angezeigt werden.

Die Headerbar ist optional. Möchte man sie nutzen, muss in den Fenstereinstellungen "Allgemein > Darstellung > Klienseitige Fensterdekoration" ausgewählt werden. Daraufhin erscheint im oberen Bereich des Fensters ein reservierter Bereich, in dem die Headerbar platziert wird. Wird die Headerbar außerhalb davon platziert, wird weiterhin zusätzlich die normale Titelleiste angezeigt.

/images/15_headerbarglade.png

Kommandozeilenoptionen

GtkApplication stellt die erforderlichen Mittel für anwendungseigene Kommandozeilenoptionen zur Verfügung (Handling command line options in GApplication).

Optionen anlegen

Verfügbare Optionen werden mit der Funktion add_main_option_entries(entrylist) hinzugefügt. Diese Einträge haben das Format GLib.OptionEntry, welches allerlei Parameter besitzt.

def __init__(self):
    self.app = Gtk.Application.new("org.application.test", Gio.ApplicationFlags(0))
    self.app.add_main_option_entries([
        self.create_option_entry("--version", description="Show version numbers and exit"),
        self.create_option_entry("--setlabel", description="Set label widget",arg=GLib.OptionArg.STRING,),
        self.create_option_entry("--bollocks", description="Additional test option - exit"),
    ])

def create_option_entry(self,long_name, short_name=None, flags=0, arg=GLib.OptionArg.NONE,arg_data=None, description=None, arg_description=None):
    option = GLib.OptionEntry()
    option.long_name = long_name.lstrip('-')
    option.short_name = 0 if not short_name else short_name.lstrip('-')
    option.flags = flags
    option.arg = arg
    option.arg_data = arg_data
    option.description = description
    option.arg_description = arg_description
    return option

Signal verbinden

Der GtkApplication-eigene "handle-local-options"-Handler verarbeitet die Optionen. Sobald Optionen angelegt sind, wird dieses Signal noch vor dem "startup"-Signal ausgelöst

self.app.connect("handle-local-options", self.on_local_option)

Optionen verarbeiten

Die an die Handler-Funktion übergebene option ist ein Element der Klasse GLib.VariantDict. Mit contains("option") lässt sich nach der übergebenen Option suchen.

def on_local_option(self, app, option):
    if option.contains("option1"):
        #do something and exit normally
        return 0
    elif option.contains("option2"):
        #do something different and exit
        return 0
    elif option.contains("option3"):
        #do more and continue
    return -1

Ein übergebener String kann extrahiert werden, indem GLib.VariantDict mit end() in GLib.Variant konvertiert wird, das sich wiederum mit keys() auslesen lässt:

var = GLib.VariantDict.end(option)
option_string = var[var.keys()[0]]
Ein Return-Wert ist zwingend erforderlich, er entspricht dabei dem Exit-Status:
  • -1: Anwendung wird weiter ausgeführt
  • 0: erfolgreiche Ausführung, Anwendung wird beendet, "startup/activate" werden nicht ausgeführt
  • 1 bzw. positiver Wert: nicht erfolgreiche Ausführung, Anwendung wird beendet

Optionen übergeben

Die Option, die immer verfügbar ist, ist --help. Hier werden unter "Anwendungsoptionen" die angelegten Optionen samt Beschreibung aufgeführt. Die Optionen können wie definiert angegeben werden:

$ python script.py --version
Python: 3.6.0
GTK+:   3.22.6

oder mit --setlabel einen String an Gtk.Label übergeben:

$ python script.py --setlabel "I can haz options!"

Listings

Glade

15_application.glade (Source)

<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.20.0 -->
<interface>
  <requires lib="gtk+" version="3.20"/>
  <object class="GtkMenu" id="menu">
    <property name="visible">True</property>
    <property name="can_focus">False</property>
    <child>
      <object class="GtkMenuItem">
        <property name="visible">True</property>
        <property name="can_focus">False</property>
        <property name="label" translatable="yes">MenuItem 1</property>
        <property name="use_underline">True</property>
      </object>
    </child>
    <child>
      <object class="GtkMenuItem">
        <property name="visible">True</property>
        <property name="can_focus">False</property>
        <property name="label" translatable="yes">MenuItem 2</property>
        <property name="use_underline">True</property>
      </object>
    </child>
    <child>
      <object class="GtkMenuItem">
        <property name="visible">True</property>
        <property name="can_focus">False</property>
        <property name="label" translatable="yes">MenuItem 3</property>
        <property name="use_underline">True</property>
      </object>
    </child>
  </object>
  <object class="GtkApplicationWindow" id="window">
    <property name="width_request">400</property>
    <property name="height_request">300</property>
    <property name="can_focus">False</property>
    <property name="title" translatable="yes">Titel</property>
    <property name="icon">../files/duckyou.svg</property>
    <property name="show_menubar">False</property>
    <child>
      <object class="GtkBox">
        <property name="visible">True</property>
        <property name="can_focus">False</property>
        <property name="orientation">vertical</property>
        <child>
          <object class="GtkImage">
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <property name="pixbuf">../files/duckyou.svg</property>
          </object>
          <packing>
            <property name="expand">True</property>
            <property name="fill">True</property>
            <property name="position">0</property>
          </packing>
        </child>
        <child>
          <object class="GtkLabel" id="label">
            <property name="visible">True</property>
            <property name="can_focus">False</property>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">True</property>
            <property name="pack_type">end</property>
            <property name="position">1</property>
          </packing>
        </child>
        <child>
          <object class="GtkSeparator">
            <property name="visible">True</property>
            <property name="can_focus">False</property>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">True</property>
            <property name="position">2</property>
          </packing>
        </child>
      </object>
    </child>
    <child type="titlebar">
      <object class="GtkHeaderBar">
        <property name="visible">True</property>
        <property name="can_focus">False</property>
        <property name="title">Titel</property>
        <property name="subtitle">Untertitel</property>
        <property name="show_close_button">True</property>
        <child>
          <object class="GtkMenuButton">
            <property name="visible">True</property>
            <property name="can_focus">True</property>
            <property name="receives_default">True</property>
            <property name="popup">menu</property>
            <child>
              <placeholder/>
            </child>
          </object>
        </child>
        <child>
          <object class="GtkButton">
            <property name="label">gtk-no</property>
            <property name="visible">True</property>
            <property name="can_focus">True</property>
            <property name="receives_default">True</property>
            <property name="use_stock">True</property>
            <property name="always_show_image">True</property>
          </object>
          <packing>
            <property name="pack_type">end</property>
            <property name="position">2</property>
          </packing>
        </child>
        <child>
          <object class="GtkButton">
            <property name="label">gtk-yes</property>
            <property name="visible">True</property>
            <property name="can_focus">True</property>
            <property name="receives_default">True</property>
            <property name="use_stock">True</property>
            <property name="always_show_image">True</property>
          </object>
          <packing>
            <property name="pack_type">end</property>
            <property name="position">2</property>
          </packing>
        </child>
      </object>
    </child>
  </object>
</interface>

Python

15_application.py (Source)

#!/usr/bin/python
# -*- coding: utf-8 -*-

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

class ExampleApp:

    def __init__(self):

        self.app = Gtk.Application.new("org.application.test", Gio.ApplicationFlags(0))

        self.app.add_main_option_entries([
            self.create_option_entry("--version", description="Show version numbers and exit"),
            self.create_option_entry("--setlabel", description="Set label widget",arg=GLib.OptionArg.STRING),
            self.create_option_entry("--bollocks", description="Additional test option - exit"),
        ])

        self.app.connect("handle-local-options", self.on_local_option)
        self.app.connect("activate", self.on_app_activate)

    def on_local_option(self, app, option):
        self.option_string = ""
        if option.contains("version"):
            var = GLib.VariantDict.end(option)
            print("Python: {}".format(sys.version[:5]))
            print("GTK+:   {}.{}.{}".format(Gtk.MAJOR_VERSION, Gtk.MINOR_VERSION, Gtk.MICRO_VERSION))
            return 0
        elif option.contains("bollocks"):
            return 1
        elif option.contains("setlabel"):
            var = GLib.VariantDict.end(option)
            self.option_string = var[var.keys()[0]]
        return -1

    def create_option_entry(self,long_name, short_name=None, flags=0, arg=GLib.OptionArg.NONE,arg_data=None, description=None, arg_description=None):
        option = GLib.OptionEntry()
        option.long_name = long_name.lstrip('-')
        option.short_name = 0 if not short_name else short_name.lstrip('-')
        option.flags = flags
        option.arg = arg
        option.arg_data = arg_data
        option.description = description
        option.arg_description = arg_description
        return option

    def on_app_activate(self, app):
        builder = Gtk.Builder()
        builder.add_from_file("15_application.glade")

        self.obj = builder.get_object
        self.obj("window").set_application(app)
        self.obj("label").set_text(self.option_string)

        #display application name in upper panel of the GNOME Shell
        self.obj("window").set_wmclass("Application test","Application test")
        self.obj("window").show_all()

    def run(self, argv):
        self.app.run(argv)

app = ExampleApp()
app.run(sys.argv)
Comments powered by Disqus