Zum Inhalt springen

Python unter Linux: GTK

Aus Wikibooks

In diesem Kapitel geht es um die Programmierung von grafischen Benutzeroberflächen mit GTK+. GTK+ wird in Anwendungen wie GIMP benutzt und bildet die Basis für die Desktop-Umgebung GNOME. Falls Sie mehr Informationen zur Programmierung in GTK+ in anderen Sprachen suchen, werden Sie im Wikibuch GTK mit Builder fündig. In diesem Kapitel geben wir ihnen einen Einblick in die Programmierung mit dem Modul python-gtk2, welches Sie sich mit ihrem Paketmanager leicht installieren können.

Das erste Fenster

[Bearbeiten]

Das folgende Programm öffnet ein Fenster und stellt Text in einem Label dar. Anschließend wartet das Programm darauf, beendet zu werden:

#!/usr/bin/python

import gtk

class HalloWelt(object):

    def __init__(self):
        """ Initialisiert das Fenster """
        self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
        self.window.set_title("Mein erstes Fenster")
        self.window.set_default_size(300, 100)
        self.window.connect("delete_event", self.event_delete)
        self.window.connect("destroy", self.destroy)
        label = gtk.Label("Hallo, Welt!")
        self.window.add(label)
        label.show()
        self.window.show()

    def event_delete(self, widget, event, data=None):
        """ reagiert auf 'delete_event' """
        return False

    def destroy(self, data=None):
        """ reagiert auf 'destroy' """
        gtk.main_quit()

    def main(self):
        """ Nachrichtenschleife """
        gtk.main()


if __name__ == "__main__":
    hallo = HalloWelt()
    hallo.main()

Die Klasse HalloWelt enthält vier Methoden.

Innerhalb von __init__() wird mit gtk.Window() ein neues so genanntes Top-Level-Fenster erzeugt. Es gibt verschiedene Arten von Fenstern, auf die wir nicht eingehen. Dieses Top-Level-Fenster ist genau das, was man sich unter einem Programm-Fenster vorstellt. set_title() legt einen Fenstertitel fest und set_default_size() eine Anfangsgröße. Die Größe des Fensters ist zur Laufzeit änderbar.

Die nächsten beiden Funktionen verknüpfen Ereignisse mit so genannten Callback-Methoden. Diese werden aufgerufen, wenn das Ereignis "delete_event" oder "destroy" auftritt. Das delete_event wird immer dann aufgerufen, wenn der Anwender versucht, das Fenster zu schließen. destroy erfolgt dann, wenn das Fenster echt geschlossen wird. Dieses Signal kann man im Gegensatz zum delete_event nicht abweisen.

Nun fehlt uns noch ein Label, das wir mit gtk.Label() erzeugen und mit window.add() dem Fenster hinzufügen. Das Label wie auch das Fenster werden sichtbar gemacht. Hierzu dient die Funktion show().

Die Methode event_delete() ist verknüpft mit dem delete_event. An dieser Stelle könnte das Programm fragen, ob der Anwender das Programm wirklich beenden möchte. Gibt die Methode False zurück, wird destroy als neues Signal gesendet. Gibt man True zurück, wird das Ereignis abgewiesen.

Die Methode destroy() hat nun die Aufgabe, das Fenster zu zerstören, in diesem Fall soll die gesamte Anwendung beendet werden.

Jede Anwendung, die grafische Nutzeroberflächen benutzt, verfügt über eine Hauptschleife, in der Nachrichten an ihre Empfänger weitergeleitet werden. Solche Nachrichten können Anwenderwünsche wie das Beenden der Anwendung sein, Mausbewegungen oder das Drücken von Knöpfen. Diese Hauptschleife wird mit gtk.main() bereitgestellt.

Layout

[Bearbeiten]

Benutzt man mehrere grafische Elemente, wie zum Beispiel mehrere Labels, kann man mit Hilfe von Layout-Funktionen bestimmen, wie diese Elemente angeordnet werden. So lässt sich ein Hauptfenster mit einem Tabellenlayout versehen oder in waagerechte und senkrechte Boxen unterteilen.

Tabellenlayout

[Bearbeiten]

Das einfachste Layout ist die Anordnung von Elementen in Form einer Tabelle. Folgendes Beispiel verdeutlicht das:

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

import gtk

class Tabelle1(object):

    def __init__(self):
        self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
        self.window.set_title("Mein erstes Fenster")
        self.window.set_default_size(300, 100)
        self.window.connect("delete_event", self.event_delete)
        self.window.connect("destroy", self.destroy)
        table = gtk.Table(3, 2, False)
        self.window.add(table)
        label = gtk.Label("Zelle 1-1")
        table.attach(label, 0, 1, 0, 1)
        label.show()
        label = gtk.Label("Zelle 1-2")
        table.attach(label, 1, 2, 0, 1)
        label.show()
        label = gtk.Label("Eine etwas größere Zelle")
        table.attach(label, 0, 1, 1, 2)
        label.show()
        label = gtk.Label("Zelle 2-2")
        table.attach(label, 1, 2, 1, 2)
        label.show()
        label = gtk.Label("<u>die letzte Zeile geht über die gesamte Tabellenbreite</u>")
        label.set_use_markup(True)
        table.attach(label, 0, 2, 2, 3)
        label.show()
        table.show()
        self.window.show()


    def event_delete(self, widget, event, data=None):
        return False


    def destroy(self, data=None):
        gtk.main_quit()


    def main(self):
        gtk.main()


if __name__ == "__main__":
    tab = Tabelle1()
    tab.main()

Innerhalb von __init__() wird eine Tabelle erzeugt. In die Tabellenzellen werden jeweils einige Labels untergebracht. Eines der Labels benutzt HTML-Markup, um seinen Text darzustellen und geht über die gesamte Tabellenbreite.

Eine Tabelle wird mit gtk.Table(Zeilen, Spalten, homogen). Zeilen und Spalten sind selbst erklärend. Ist der Wert von homogen True, dann haben alle Tabellenelemente die gleiche Größe. Obwohl man die Tabelle selbst nicht sieht, muss man sie mit window.add(table) dem Fenster hinzufügen und mit show() sichtbar machen.

Ein Element wird der Tabelle mit der Methode attach(Element, links, rechts, oben, unten) hinzugefügt. Dabei beziehen sich die Koordinaten auf die Tabellenränder, nicht auf die Zeilen/Spalten-Nummerierung. Der linke Rand einer 3X2-Tabelle ist 0, der rechte Rand ist 2. Oben ist 0, unten ist 3. Es werden also die (gedachten) Gitterlinien durchnummeriert. Auf diese Weise kann man auch Elemente einfügen, die sich über mehrere Zeilen und Spalten erstrecken, wie im letzten Label-Beispiel.

Der Text in einem Label kann mit HTML-Markup versehen werden, in diesem Fall wird er unterstrichen dargestellt. Um Markup zu aktivieren, benutzt man die Methode set_use_markup().

Boxenlayout

[Bearbeiten]

Es gibt horizontale und vertikale Boxen. Horizontale Boxen legen Widgets nebeneinander, vertikale übereinander. Im folgenden Beispiel benutzen wir statt Labels gtk.Button()-Objekte (Schaltknöpfe), diese haben den Vorteil, dass man ihre Ausdehnung sieht. Das Hauptlayout besteht aus einer horizontalen Box, in die eine vertikale Box und ein Schaltknopf gelegt werden:

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

import gtk

class Box1(object):

    def __init__(self):  
        self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
        self.window.set_title("Mein erstes Fenster")
        self.window.set_default_size(300, 100)
        self.window.connect("delete_event", self.event_delete)
        self.window.connect("destroy", self.destroy)
        # horizontale Box
        hbox = gtk.HBox(True, 30)
        # Vertikale Box
        vbox = gtk.VBox(True, 1)
        button = gtk.Button("vbox 0")
        button.show()
        vbox.pack_start(button)
        button = gtk.Button("vbox 1")
        button.show()
        vbox.pack_start(button)
        button = gtk.Button("vbox 2")
        button.show()
        vbox.pack_start(button)
        button = gtk.Button("vbox 3 in der letzten Reihe")
        button.show()
        vbox.pack_start(button)
        # Ende vertikale box 
        hbox.pack_start(vbox)
        button = gtk.Button("Noch ein Knopf")
        button.show()
        hbox.pack_start(button)
        # Alles zusammenfügen
        self.window.add(hbox)
        # Anzeigen 
        vbox.show()
        hbox.show()
        self.window.show()

    def event_delete(self, widget, event, data=None):
        return False

    def destroy(self, data=None):
        gtk.main_quit()

    def main(self):
        gtk.main() 

if __name__ == "__main__":
    box = Box1()
    box.main()

Boxen werden erzeugt mit gtk.HBox(homogen, abstand) und gtk.VBox(homogen, abstand). Der Parameter homogen bestimmt, dass alle enthaltenen Widgets dieselbe Breite (HBox) oder dieselbe Höhe (VBox) haben. abstand ist ein Wert in Pixeln, der den Abstand zweier benachbarter Widgets vorgibt.

Widgets werden mit pack_start(Widget) von links nach rechts und von oben nach unten hinzugefügt.Um in umgekehrter Reihenfolge hinzuzufügen, kann man die Funktion pack_end(Widget) benutzen. Beide Funktionen haben mehr Parameter, als hier dargestellt, nämlich pack_start(Widget, expandieren, füllen, platz). Mit ihnen kann man bestimmen, ob ein Widget sich über den gesamten Platz ausdehnen darf (expandieren=True) und wenn ja, ob der zusätzliche Platz dem Widget gehört (füllen=True). Mit platz wird zusätzlicher Rand um das hinzugefügte Widget herum belegt.

Boxen müssen ebenso wie andere Widgets sichtbar gemacht werden.

Wir ermutigen Sie an dieser Stelle, die verschiedenen Möglichkeiten der Parameter auszuprobieren, um ein Gefühl für ihre Bedeutung zu bekommen.

Grafische Elemente

[Bearbeiten]

Button

[Bearbeiten]

Schaltknöpfe dienen dazu, beim Drücken ein Signal zu erzeugen. Dieses Signal kann mit einer so genannten Callback-Funktion aufgefangen und bearbeitet werden, wie folgendes Beispiel zeigt. Auf Knopfdruck wird der Text eines Labels verändert:

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

class Button1(object):
   
    def __init__(self):
        self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
        self.window.set_title("Von Buttons und Labeln")
        self.window.set_default_size(300, 100)
        self.window.connect("delete_event", self.event_delete)
        self.window.connect("destroy", self.destroy)
        # vertikale Box
        vbox = gtk.VBox(True, 30)
        button = gtk.Button("Drück mich")
        button.connect("clicked", self.button_clicked)
        button.show()
        vbox.pack_start(button)
        self.label = gtk.Label("")
        self.label.show()
        vbox.pack_start(self.label)
        self.window.add(vbox)
        vbox.show()
        self.window.show()

    def button_clicked(self, data=None):
        self.label.set_text("Danke! Sie haben ein einfaches\nLabel sehr glücklich gemacht.")

    def event_delete(self, widget, event, data=None):
        return False

    def destroy(self, data=None):
        gtk.main_quit()

    def main(self):
        gtk.main() 

if __name__ == "__main__":
    b = Button1()
    b.main()

Callback-Funktionen wie button_clicked(data), oder in diesem Fall Methoden, haben in GTK+ immer eine ähnliche Gestalt. Mit dem optionalen Parameter data können Daten übergeben werden, die von connect(signal, callback, data) bereitgestellt werden.

Dialog, Textfeld, Rahmen

[Bearbeiten]

Das folgende Beispiel enthält zwei Textzeilen, in die der Anwender seinen Namen und seine E-Mail eingeben kann. Diese Daten werden in einem Anzeigedialog aufgeführt. Die Widgets sind innerhalb eines Rahmens mit einem Titelfeld untergebracht.

#!/usr/bin/python
# -*- coding: utf-8 -*-
 
import gtk
  
class Entry1(object):
   
    def __init__(self):
        self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
        self.window.set_title("Entry und Frame")
        self.window.set_default_size(300, 100)
        self.window.connect("delete_event", self.event_delete)
        self.window.connect("destroy", self.destroy)
        
        frame = gtk.Frame("Möchten Sie SPAM bekommen?")
        self.window.add(frame)
        # vertikale Box
        vbox = gtk.VBox(True, 10)
        frame.add(vbox)
        # obere hbox
        hbox1 = gtk.HBox(True, 0)
        vbox.pack_start(hbox1)
        label = gtk.Label("Name")
        label.show()
        hbox1.pack_start(label)
        self.entry1 = gtk.Entry()
        self.entry1.show()
        hbox1.pack_start(self.entry1)
        hbox1.show()
        # untere hbox
        hbox2 = gtk.HBox(True, 0)
        vbox.pack_start(hbox2)
        label = gtk.Label("E-Mail")
        label.show()
        hbox2.pack_start(label)
        self.entry2 = gtk.Entry()
        self.entry2.show()
        hbox2.pack_start(self.entry2)
        hbox2.show()
        # Knopf
        button = gtk.Button("Im SPAM-Verteiler eintragen")
        button.connect("clicked", self.button_clicked)
        button.show()
        vbox.pack_start(button)
        # fertig vertikale Box
        vbox.show()
        frame.show()
        self.window.show()

    def button_clicked(self, data=None):
        name = self.entry1.get_text()
        email = self.entry2.get_text()
        text = "%s, wir schicken ihnen ab sofort regelmäßig SPAM an die Adresse %s!" % (name, email)
        dlg = gtk.MessageDialog(flags=gtk.DIALOG_MODAL, buttons=gtk.BUTTONS_OK, message_format=text)
        dlg.set_title("Danke für ihre Adresse")
        dlg.run()
        dlg.destroy()

    def event_delete(self, widget, event, data=None):
        return False
    
    def destroy(self, data=None):
        gtk.main_quit()
    
    def main(self):
        gtk.main()

if __name__ == "__main__":
    e = Entry1()
    e.main()

Rahmen werden mit einem optionalen Titelfeld durch die Funktion gtk.Frame(titel) erzeugt. Dieser Rahmen kann selbst ein Layout aufnehmen, in diesem Fall eine vertikale Box. Innerhalb dieser Box werden Label und Texteingaben eingefügt. Texteingaben sind einzeilige Textfelder, die mit gtk.Entry(länge) erzeugt werden. länge ist optional und meint die maximale Anzahl an Zeichen, die das Feld aufnehmen kann. Zum Schluss wird ein Knopf eingefügt, der mit der Callback-Methode button_clicked() verknüpft wird.

Innerhalb von button_clicked() werden die Textzeilen mit get_text() ausgelesen und in einem Text zusammengefügt. Anschließend wird ein Dialogobjekt erstellt. Dieser Dialog ist modal, lässt bis zum Ende keine weiteren Eingaben zu. Er enthält lediglich den Knopf "OK" und stellt den Text wie auch einen Titel dar.

Der Dialog wird mit run() gestartet. Wenn der Anwender auf "OK" klickt, wird die run()-Methode beendet und der Dialog mit destroy() geschlossen.

Menü, Zeichnen mit Cairo

[Bearbeiten]
Vektorgrafik mit Cairo

Im folgenden Beispiel erzeugen wir ein Menü und eine Malfläche, in die wir mit Hilfe der Vektorgrafik-Bibliothek Cairo einige Grafikprimitive wie Kreise, Rechtecke oder Text zeichnen. Hierzu benötigen wir zwei Klassen, nämlich Malfeld, die Klasse, die den Zeichenbereich bereitstellt und Menu1, die das Menü erzeugt und das Malfeld innerhalb eines Frames aufnimmt. Aktiviert der Nutzer das Menü, wird entweder etwas Text gezeichnet oder das Programm verlassen.

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

class Malfeld(gtk.DrawingArea):

    def __init__(self):
        gtk.DrawingArea.__init__(self)
        self.connect("expose_event", self.event_expose)
        self.drawText = False

    def event_expose(self, widget, event):
        grafik = widget.window.cairo_create()
        width, height = widget.window.get_size()
        width = width / 5
        height = height / 3
        self.draw(grafik, width, height)
        if self.drawText:
            self.draw_text(grafik, *self.drawText)
            self.drawText = False
        return False

    def draw(self, context, w, h):
        context.set_source_rgb(0.5, 0.0, 0.0)
        context.rectangle(w, h, w, h)
        context.stroke()
        context.set_source_rgb(0.0, 1.0, 0.0)
        context.arc(3.0 * w, h, min(w, h), 0, 2.0 * math.pi)
        context.fill() 

    def draw_text(self, context, x, y, text):
        context.set_source_rgb(0.0, 0.0, 1.0)
        context.set_font_size(30)
        context.move_to(x, y + 30)
        context.show_text(text)

    def set_draw_text(self, x, y, text):
        self.drawText = (x, y, text)
        self.queue_draw()


class Menu1(object):
   
    def __init__(self):
        self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
        self.window.set_title("Menü")
        self.window.set_default_size(300, 100)
        self.window.connect("delete_event", self.event_delete)
        self.window.connect("destroy", self.destroy)
        # Menu
        menu = gtk.Menu()
        # erstes Item
        menuItem = gtk.MenuItem("Hallo")
        menuItem.show()
        menuItem.connect("activate", self.menu_clicked)
        menu.append(menuItem)
        # Quit-Item
        menuItem = gtk.MenuItem("Quit")
        menuItem.show()
        menuItem.connect("activate", lambda w: gtk.main_quit())
        menu.append(menuItem)
        # Hauptmenu wird in der Menuzeile angezeigt
        mainMenu = gtk.MenuItem("Hauptmenu")
        mainMenu.set_submenu(menu)
        mainMenu.show()
        # Menuzeile
        menuBar = gtk.MenuBar()
        menuBar.append(mainMenu)
        menuBar.show()
        # Malfeld
        frame = gtk.Frame("Malfeld")
        self.malen = Malfeld()
        self.malen.show()
        frame.add(self.malen)
        frame.show()
        # VBox
        vbox = gtk.VBox()
        vbox.pack_start(menuBar, False, False, 0)
        vbox.pack_start(frame, True, True, 0)
        vbox.show()
        self.window.add(vbox)
        self.window.show()

    def menu_clicked(self, widget, data=None):
        self.malen.set_draw_text(10, 10, "Menü wurde geklickt")

    def event_delete(self, widget, event, data=None):
        return False
    
    def destroy(self, data=None):
        gtk.main_quit()
    
    def main(self):
        gtk.main()


if __name__ == "__main__":
    m = Menu1()
    m.main()

Die Klasse Malfeld wird von der GTK+-Klasse DrawingArea abgeleitet, einer Klasse, die selbst eine Zeichenfläche implementiert, jedoch nur wenig Unterstützung für Grafikprimitive mitbringt. Diese Klasse stellt Ereignisse bereit, die darauf hinweisen, wann neu gezeichnet werden soll. "expose_event" ist ein solches Ereignis. Es tritt dann ein, wenn das Fenster verdeckt oder seine Größe verändert wurde. Dieses Ereignis verknüpfen wir mit der Methode event_expose().

event_expose(widget, event) übergibt das neu zu zeichnende Widget und das betreffende Ereignis. Innerhalb der Funktion wird mit cairo_create() die Unterstützung für Vektorgrafiken aktiviert. Der Rückgabewert ist ein Grafikkontext, also eine Klasse, die alles Nötige zum Zeichnen mitbringt. Es kann übrigens nur innerhalb von event_expose() gezeichnet werden. Der Grafikkontext kann nicht für spätere Zwecke gespeichert werden. Mit get_size() holen wir uns die Größe des Zeichenbereiches und berechnen Orte vor, wo wir Dinge zeichnen wollen. Anschließend werden einige Grafiken gemalt und bei Bedarf Text gezeichnet. Der Bedarf ergibt sich aus dem Aufruf der Methode set_draw_text(), die beim Aktivieren eines Menüs aufgerufen wird.

Innerhalb von draw() wird mit set_source_rgb(rot, grün, blau) eine Farbe vorgegeben. Die Farbwerte dürfen zwischen 0.0 und 1.0 liegen. Ein Rechteck wird mit rectangle(x, y, Breite, Höhe) vorbereitet, mit stroke() wird das Rechteck in seinen Umrissen gezeichnet. Füllen kann man das zuletzt angegebene Objekt mit fill(). Einen Kreis zeichnet man mit der Methode arc(x, y, Radius, Winkel_Von, Winkel_Bis).

draw_text() zeichnet Text an einer angegebenen Position. Hierzu wird die Schriftgröße festgelegt, anschließend wird ein Grafikcursor an eine Stelle im Fenster gesetzt (move_to(x, y)). show_text(Text) zeichnet dann den eigentlichen Text in der zuletzt festgelegten Farbe.

Die Klasse Menu1 erzeugt das Hauptfenster. Es werden einige Menüitems mit MenuItem(Text) erzeugt und mit entsprechenden Callback-Funktionen verknüpft. Diese Items werden zu einem Menüeintrag zusammengefasst und dem Menü, das mit Menu() erzeugt wird, hinzugefügt. Das angezeigte Menü ist wiederum ein Menuitem und hat den Namen mainMenu. Dieses wird der MenuBar() hinzugefügt. Dies ist schon das, was im oberen Bereich des Fensters angezeigt werden soll.

Es werden also zuerst Items erzeugt, dann werden diese Items in einem Menü zusammengefasst und wiederum in einem Item eingetragen. Diese Items werden einer Menüzeile hinzugefügt. Auf diese Weise erzeugt man Menüs und Untermenüs. Das Verfahren gleicht jenem der Layouts. Dabei werden ebenfalls Elemente in Layoutboxen gepackt, diese werden wiederum in Boxen verpackt und irgendwann dem Fenster oder dem Frame hinzugefügt.

Benutzeroberflächen - schnell und einfach

[Bearbeiten]
Glade hilft beim Erstellen von grafischen Benutzeroberfächen

Benutzeroberflächen können unter GTK+ mit grafischen Werkzeugen erstellt werden. Ein bekanntes Werkzeug ist Glade[1], dessen Bedienung wir kurz ansprechen wollen. Sie benötigen für das Beispiel das Modul python-glade2 sowie - selbstverständlich - Glade Version 2[2].

Eine Benutzeroberfläche erstellt man in Glade fast vollständig visuell. Es wird eine XML-Datei gespeichert, die im Hauptprogramm geöffnet wird. Dort werden auch die in Glade erstellten Callback-Funktionen registriert, anschließend kann die Benutzeroberfläche genutzt werden.

Benutzeroberfläche erstellen

[Bearbeiten]
Benutzeroberfläche in Glade erstellen

Für das folgende Beispiel starten Sie Glade und erzeugen ein Hauptfenster und einen Dialog. Wenn Sie das Beispiel praktisch nachvollziehen wollen, müssen Sie darauf achten, dass die Namen der GUI-Elemente (ID) mit denen im Quellcode übereinstimmen.

Hauptfenster

[Bearbeiten]
Das Hauptfenster

In das Hauptfenster kommt zuerst eine vertikale Box mit drei Abschnitten. Anschließend wird ein Menü, ein Knopf und ein Label (als Statuszeile) eingefügt. Im Eigenschaften-Dialog wird im Reiter "Signale" ein Signal hinzugefügt. Wir verbinden dazu das Signal destroy (nicht zu verwechseln mit destroy_signal) mit der Callbackfunktion on_fenster1_destroy. Ebenfalls wird das "clicked"-Signal vom Knopf mit der Callback-Funktion on_button1_clicked verknüpft.

Das Menü wird etwas aufgeräumt, übrig bleiben Datei->Quit und Hilfe->Info. Die "activate"-Signale werden mit on_quit1_activate beziehungsweise on_info1_activate verknüpft.

Damit das folgende Beispiel funktioniert, muss das Statuszeilen-Label die ID "Label2" tragen und das Fenster die ID "fenster1" haben.

Dialog

[Bearbeiten]
Der Dialog

Es wird ein Dialog mit der ID "dialog1" erstellt. Der Dialog soll über eine Standard-Knopfanordnung mit einem Schließen-Knopf verfügen. Das clicked-Signal dieses Knopfes wird mit on_closebutton1_clicked verknüpft. Wir fügen ein Label mit etwas Beispieltext hinzu.

Abschluss

[Bearbeiten]

Dann sind wir fertig und können das Projekt speichern, wobei wir nur die entstehende projekt1.glade-Datei benötigen. Im Speichern-Dialog können wir darauf verzichten, dass alle sonstigen Programmdateien erstellt werden. Diese sind für Python nur überflüssiger Ballast.

Quellcode

[Bearbeiten]

Das folgende Programm lädt die in Glade erstellte Benutzeroberfläche und stellt sie dar. In der Statuszeile wird verfolgt, wie oft der zentrale Knopf gedrückt wird. Mit den Menüs kann man das Programm verlassen oder den erstellten Dialog aufrufen.

#!/usr/bin/python

import gtk
import gtk.glade

class HelloGlade(object):
    def __init__(self):
        self.gladeDatei = "projekt1.glade"
        self.widgets = gtk.glade.XML(self.gladeDatei, "fenster1")
        events = {"on_fenster1_destroy"    : gtk.main_quit,
            "on_quit1_activate"  : gtk.main_quit,
            "on_info1_activate"  : self.info_dlg,
            "on_button1_clicked" : self.statusbar_info}
        self.widgets.signal_autoconnect(events)
        self.numClicked = 0

    def info_dlg(self, widget):
        self.dlg = gtk.glade.XML(self.gladeDatei, "dialog1")
        events = {"on_closebutton1_clicked" :  self.dlg_close}
        self.dlg.signal_autoconnect(events)

    def dlg_close(self, widget):
        d = self.dlg.get_widget("dialog1")
        d.destroy()

    def statusbar_info(self, widget):
        label = self.widgets.get_widget("label2")
        self.numClicked += 1
        text = "Es wurde %d mal geklickt" % self.numClicked
        label.set_text(text)


if __name__ == "__main__":
    h = HelloGlade()
    gtk.main()

Die GUI-Datei wird mit gtk.glade.XML(datei, id) geladen. id ist hierbei optional, kann also entfallen. Sonst bezeichnet dieser Parameter den Namen des GUI-elementes, das geladen werden soll.

In einem Dictionary legen wir alle das Fenster betreffenden Callback-Funktionen mit ihren zugehörigen Aktionen an. Wird beispielsweise on_info1_activate aufgerufen, führt das in unserem Beispiel dazu, dass info_dlg() ebenfalls aufgerufen wird. Mit signal_autoconnect(events) verknüpfen wir die Ereignisse mit unseren entsprechenden Klassenmethoden.

Wenn das Hilfe-Info-Menü betätigt wird, dann wird in info_dlg() der betreffende Dialog gestartet. Auch hierfür holen wir uns die Beschreibung des Dialogs aus der Glade-Datei und verknüpfen die passenden Ereignisse. Soll mit einem Klick auf den Schließen-Knopf der Dialog beendet werden, wird dlg_close() aufgerufen. Das eigentliche Widget dieses Dialogs wird mit get_widget(id) ermittelt, anschließend wird ihm das destroy-Signal mit destroy() geschickt.

Drückt der Anwender auf den zentralen Knopf in der Mitte vom Hauptfenster, wird die Methode statusbar_info() aufgerufen. Es wird auch hier das Label-Widget mit get_widget(id) geholt, anschließend wird der Text mit set_text() verändert.

Zusammenfassung

[Bearbeiten]

Anwendungen mit grafischen Benutzeroberflächen lassen sich mit PyGTK leicht schreiben. In diesem Kapitel haben wir die Grundlagen des Layouts kennen gelernt, einige wenige Widgets vorgestellt und gezeigt, wie sich GUIs mit Glade erstellen lassen.

Anmerkungen

[Bearbeiten]
  1. Weitere Designer-Tools finden Sie unter http://wiki.python.org/moin/GuiProgramming aufgelistet.
  2. Zur neuen Version 3 liegen uns noch keine Python-Erfahrungen vor.