Aus Linux-Magazin 02/2001

GNOME und Perl

Dass Skriptsprachen inzwischen vollwertige Programmiersprachen sind, hat sich inzwischen herumgesprochen. Aber nur wenige Programmierer bauen komplette Applikationen mit grafischen Oberflächen daraus. Entsprechend wenige wissen deshalb auch, dass das wesentlich schneller geht, als es mit den etablierten Sprachen möglich wäre.

Eine der größten Stärken von Perl ist das CPAN [2], das Comprehensive Perl Archive Network. Dieses weltweite Netzwerk hat sich etabliert, um Perl-Programmierern die schier unermessliche Anzahl von Modulen für ihre Sprache zugänglich zu machen. Neben kleinen Bibliotheken und nützlichen Kleinigkeiten finden sich auch Wrapper für ausgewachsene Werkzeuge zur Programmierung grafischer Oberflächen. Dabei dürfen Gtk+ und GNOME natürlich nicht fehlen und daher gibt es Gtk-Perl von Kenneth Albanowski. Seine Finger auf das benötigte Modul legen kann man mit der einfachen Installationsprozedur, wie man sie von Perl über das CPAN gewohnt ist, wie folgt:

# perl -MCPAN -e shell

cpan shell  CPAN exploration and modules installation (v1.59)
ReadLine support enabled

cpan> install Gtk-Perl

Gtk-Perl liegt zurzeit in der Version 0.7004 vor und enthält die Unterstützung für GNOME. Die Version von CPAN ist unter Umständen nicht die aktuellste; es lohnt sich daher, einen Blick auf [3] zu werfen und zu schauen, ob es etwas Neues gibt.

Listing1: hallofrosch.pl

1: #!/usr/bin/perl -w
 2: 
 3: use strict;
 4: use Gnome;
 5: 
 6: my $APPNAME = 'Hallo Froschi!';
 7: 
 8: init Gnome $APPNAME;
 9: 
10: my $app = new Gnome::App $APPNAME, $APPNAME;
11: 
12: my $button = new Gtk::Button "Hallo Froschi!";
13: $app -> set_contents ($button);
14: 
15: show_all $app;
16: 
17: main Gtk;

Erste Schritte

Normalerweise bevorzuge ich Python, um mir kleine Gtk+- oder GNOME-Programme zusammenzuhacken, Perl ist auf meinen Systemen für administrative Aufgaben abgestellt. Es liegt also nahe, mit einem kleinen Hallo-Frosch-Programm anzufangen, um erst mal das Vertrauen der Sprache zu gewinnen.

Listing 1 zeigt ein Beispiel für ein derartiges Programm, und der zugehörige Screenshot ist in Abbildung 1 zu sehen. So weit gibt es sowohl für Perl- als auch für GNOME-Programmierer keine besonderen Überraschungen. Das Modul wird in Zeile 4 eingebunden und die Applikation dann in Zeile 8 initialisiert.

Die Applikation ist in GNOME ein eigenes Widget, das im Gegensatz diverse Zusatzaufgaben übernimmt. So lassen sich beispielsweise leicht mehrere Instanzen ein und derselben Applikation erstellen. Das schlägt sich in Zeile 10 in der Erstellung dieses Widgets nieder. Auf die gleiche Weise wird ein Button erzeugt, in das Fenster eingefügt und dann die ganze Applikation angezeigt. In Zeile 17 erhält die Programmschleife gtk_main die Kontrolle. Bis hierher sieht das einem Hello World in C schon ziemlich ähnlich, mit dem Unterschied, dass Perl wieder seine Magie ausübt, dem Programmierer wesentlich weniger Tastenanschläge abzuverlangen.

Wenn sich ein aufmerksamer und schon in der Materie bewanderter Leser die Frage stellt, warum in der Titelzeile der Screenshots der Programmname nicht erscheint, obwohl er durch Zeile 10 gesetzt werden müsste: Das liegt an meinem missgebildeten Sawfish-Thema.

Abb. 1: Hallo Froschi! Hallo HalloFroschi!

Abb. 1: Hallo Froschi! Hallo HalloFroschi!

Signale und Ereignisse

Gtk+ ist ein ereignisbasiertes Widget-Set, aber Ereignisse kommen hier wesentlich zu kurz. Tritt ein Ereignis auf, wird ein dazu korrespondierendes Signal ausgesandt, auf das eine durch den Programmierer festgelegte Funktion reagieren kann. Eine solche Funktion nennt man Callback. Soll das Programm in Listing 1 beendet werden, ist zum Beispiel das Einfügen des folgenden Codes zwischen den Zeilen 12 und 13 Standard:

signal_connect $button 'clicked', sub { Gtk -> main_quit;
     print "Tschüs Froschi!n";
     return 0; };

Wird durch den Button – die Schaltfläche, ach du meine Güte – das Signal clicked ausgesandt, soll die besagte anonyme Funktion ausgeführt werden, die zur Beendung des Programms führt. Zu beachten ist, dass das Folgende nicht funktionieren kann:

signal_connect $button 'clicked', Gtk -> main_quit;

Wer nicht auf anonyme Funktionen zurückgreifen möchte, baut sich zum Beispiel eine Routine beenden. Schließlich gehört es zum guten Ton, für jedes Signal eigene Callbacks zu schreiben, zumal für solche, die das Programm beenden, denn so bleiben Möglichkeiten zum Aufräumen. Man referenziert die Routine folgendermaßen:

sub beenden {
   print "Tschüs Froschi!n";
   Gtk -> main_quit;
   return 0;
}

# ...

signal_connect $button 'clicked', 

Listing 2: bookman.pl

1: #!/usr/bin/perl -w
 2: 
 3: use strict;
 4: use Gnome;
 5: 
 6: my $APPNAME = 'Book manager';
 7: my $APPVERSION = '0.1.0';
 8: init Gnome $APPNAME;
 9: 
10: sub beenden {
11:   Gtk -> main_quit;
12:   print "Bye bye.!n";
13:   return 0;
14: }
15: 
16: sub infobox {
17:   my $about = new Gnome::About $APPNAME, $APPVERSION,
18:   '(c) 2000 Thorsten Fischer', ['Thorsten Fischer@mapmedia.de>'],
19:     'Gtk-Perl Beispielcode für das Linux-Magazin.';
20:   show $about;
21: }
22: 
23: sub selektieren {
24:   my ($clist, $row, $column, $event, @data) = @_;
25:   my $text = $clist -> get_text ($row, $column);
26:   print "Die Auswahl wurde in Zeile $row, Spalte $column getroffen.n";
27:   print "Inhalt: $textn";
28: }
29: 
30: my $app = new Gnome::App $APPNAME, $APPNAME;
31: signal_connect $app 'delete_event', &beenden
32: 
33: $app -> create_toolbar (
34:   {
35:     type => 'item',
36:     label => 'Open',
37:     pixmap_type => 'stock',
38:     pixmap_info => 'Open',
39:     hint => 'Open book list',
40:   },
41:   {
42:    type => 'item',
43:    label => 'Exit',
44:    pixmap_type => 'stock',
45:    pixmap_info => 'Quit',
46:    hint => "Quit $APPNAME",
47:    callback => &beenden
48:   }
49: );
50:  
51: $app->create_menus (
52:  {
53:   type => 'subtree',
54:   label => '_File',
55:   subtree => [
56:    {
57:     type => 'item',
58:     label => '_New',
59:     pixmap_type => 'stock',
60:     pixmap_info => 'Menu_New'
61:    },
62:    {
63:     type => 'item',
64:     label => '_Quit',
65:     pixmap_type => 'stock',
66:     pixmap_info => 'Menu_Quit',
67:     callback => &beenden
68:    }
69:   ]
70:  },
71:  {
72:   type   => 'subtree',
73:   label  => '_Help',
74:   subtree => [
75:    {
76:     type => 'item', 
77:     label => '_About...',
78:     pixmap_type => 'stock',
79:     pixmap_info => 'Menu_About',
80:     callback => &infobox
81:    }
82:   ]
83:  }
84: );
85: 
86: my $appbar = new Gnome::AppBar 1, 1, 'user';
87: $appbar -> set_status ('Willkommen!');
88: $app -> set_statusbar ($appbar);
89: 
90: my $sw = new Gtk::ScrolledWindow undef, undef;
91: $sw -> set_policy ('automatic', 'always');
92: 
93: my @listentitel = ('Autor', 'Titel', 'ISBN');
94: my $liste = new_with_titles Gtk::CList (@listentitel);
95: $liste -> signal_connect ('select_row', &selektieren);
96: 
97: my @buch1 = ('Larry Wall, et al', 'Programming Perl', 1565921496);
98: my @buch2 = ('Helmut Herold', 'Linux Unix Systemprogrammierung', 3827315123);
99: my @buch3 = ('Wiglaf Droste, Gerhard Henschel', 'Der Mullah von Bullerbü', 3894013524);
100: $liste -> append (@buch1);
101: $liste -> append (@buch2);
102: $liste -> append (@buch3);
103: 
104: for (my $i = 0; $i < $liste -> n_columns; $i++) {
105:   $liste -> set_column_width ($i, $liste -> optimal_column_width ($i) + 5);
106: }
107: 
108: $sw -> add ($liste);
109: 
110: $app -> set_contents ($sw);
111: $app -> set_default_size (640, 480);
112: show_all $app;
113: 
114: main Gtk;

Ausstattung

In eine GNOME-Applikation gehören drei Dinge als Standardausstattung: ein Statusbar am unteren Ende des Programms, ein Menü am oberen Rand und darunter ein Toolbar, der schnellen Zugriff auf die am häufigsten benutzten Funktionen bietet. Mit Perl lassen sich diese Dinge schnell und flott hinzufügen, wie man an Listing 2 sehen kann, das nur kurze Zeit zur Entwicklung benötigt hat; zugegebenermaßen mit ein wenig Cut & Paste, aber dafür ist GPL-Code schließlich da.

Dieser Code ist schon wesentlich umfangreicher, aber er leistet auch eine ganze Menge mehr. Zuerst natürlich die Initialisierung, und diesmal setze ich auch eine Versionsnummer. In den Zeilen 10, 16 und 23 sind die Callbacks definiert, auf die ich später eingehe. In Zeile 33 und den folgenden wird der Toolbar gesetzt. An die Funktion muss eine Liste von Listen übergeben werden; da es so etwas wie zweidimensionale Arrays in Perl aber nicht wirklich gibt, muss man sich mit der entsprechenden Notation behelfen.

Der geneigte Leser beachte wieder den Aufruf in Zeile 47, in der der Callback festgelegt wird für den Fall, dass der betreffende Knopf auf dem Toolbar gedrückt wird. Ich verwende im Beispiel nur so genannte Stock Pixmaps, die in GNOME voreingestellt sind. Nach dem gleichen Muster werden die Menüs erstellt. Zeile 80 verweist auf den ersten interessanten Callback: Die About-Box, die auch in Abbildung 3 zu sehen ist. Der Callback-Aufruf bringt den Informationsdialog auf den Bildschirm – eigentlich ist es kein Dialog, es gibt ja nur einen OK-Button, aber immerhin.

Zeile 86 zeigt ein Element, das in jeder GNOME-Anwendung enthalten sein sollte: ein Statusbar, der unter GNOME aus einem eigentlichen Statusbar für Nachrichten und einem Progressbar besteht, der den Fortschritt von Aktionen anzeigen kann. Da im Beispiel beide vorhanden sein sollen, sind die erforderlichen Flags auf 1 statt auf 0 gesetzt. Der zweite Parameter ist der Level, auf dem die Interaktion erfolgt. Er steht hier auf benutzerdefinierter Einstellung; diese Einstellungen lassen sich im GNOME-Kontrollzentrum tätigen.

Abbildung 2: Der beispielhafte Buchverwalter in Aktion.

Abbildung 2: Der beispielhafte Buchverwalter in Aktion.

Abbildung 3: Informationen über den Buchverwalter.

Abbildung 3: Informationen über den Buchverwalter.

Callbacks fast wie zu Hause in C

In die Applikation wird nun ein GtkScrolledWindow gestopft, in das eine GtkCList illustren Inhaltes wandert: Ab Zeile 97 wird die Liste mit Daten gefüllt, in diesem Fall mit einigen sehr schönen Büchern. Die Herren Droste und Henschel werden sich sicherlich freuen, mit Wall und Herold in eine Liste schöner Bücher aufgenommen zu werden.

In Zeile 104 läuft eine kleine Schleife, die die beste Größe für die Darstellung der Feldinhalte berechnet. Danach wird der ganze Wust tatsächlich angezeigt und das Programm startet. Das Callback in Zeile 23 fürs Selektieren in der Liste schreibt die übergebenen Daten in eigene lokale Variablen. Der Aufbau der Callbacks für einzelne Signale folgt denen in C, so dass man auch in Perl gezwungen ist, diese genau zu kennen um die Werte korrekt verteilen zu können. Die einschlägige Literatur [4] liefert allerdings Referenzen. ;-)

Abbildung 2 zeigt nun das fertige Programm. Book manager ist vielleicht ein wenig hochtrabend für eine simple GtkCList, die gerade mehr oder weniger zufällig eine Liste von Büchern enthält, aber es soll ja nur ein Beispiel sein.

Daten zur Liste

Nur selten möchte man seine Daten aufbereiten, damit sie in die Liste passen, nur um sie dann für Handlungen auf einer Selektion wieder mühsam zusammenzimmern zu müssen.

Ein Beispiel: Habe ich in Perl ein Objekt, das die Eigenschaften eines Buches hält, dann möchte ich dieses Objekt mit dem Eintrag in der Zeile assoziiert wissen, ohne mühevoll das Objekt pur aus den Einträgen zu rekonstruieren. Um dies zu bewerkstelligen, kann man Zeilen mit Daten in Verbindung bringen. Das geschieht zum Beispiel wie folgt:

$liste -> set_row_data (0, $objekt);

Hier wird mit der ersten Zeile das Objekt $objekt zugewiesen. Diese Zuweisung bleibt erhalten, auch wenn sich durch ein Sortieren die Position der Zeile ändert. Durch eine Variante dieser Funktion lässt sich bei der Zerstörung der Zeile – vielleicht wird sie ja einmal gelöscht – eine Callback-Funktion aufrufen, die die Daten übergeben bekommt und damit noch einmal nach Belieben verfahren kann:

$liste -> set_row_data_full (0, $objekt, &funktion);

Fazit

Das Schöne an einem GNOME-Programm in Perl ist die Tatsache, dass im Gegensatz zu C kein kompletter Codebaum geliefert werden muss. Denn im Grunde reicht eine einzige Datei, die das Skript beziehungsweise das Programm enthält. Die Codebäume, wie sie für die C-Programme gebraucht werden, dienen ja hauptsächlich dazu, den Quellcode an die jeweilige Plattform angepasst zu konfigurieren.

Für den hier vorgestellten Code ist das bereits bei der Installation von Perl und Gtk-Perl geleistet worden, so dass dieser Arbeitsschritt entfällt und die Datenmengen überschaubar bleiben. Außerdem lehnt sich die Syntax stark an das normale Gtk+ und GNOME unter C an, die Methoden auf Objekten haben allesamt übereinstimmende Bezeichnungen.

Die Unterstützung für Perl durch Gtk-Perl ist allerdings nicht auf die simplen GUI-Funktionen beschränkt, sondern erstreckt sich auch auf Bildbearbeitung per Imlib und GdkPixBuf, auf das Verarbeiten von Glade-Daten und auch auf exotischere Widgets wie GtkHTML und GtkGLArea. Den Code für die Beispiele gibt es unter [5]. Diesmal wirklich. Fröhliches Gnomen! ( uwo)

Infos

[1] Gnome-Website: http://www.gnome.org

[2] CPAN: http://www.cpan.org

[3] Gtk-Perl-Homepage: http://projects.prosa.it/gtkperl

[4] Thorsten Fischer: GUI-Programmierung mit Gtk+; SuSE-Press 2000

[5] Code-Beispiele zum Artikel: http://www.derfrosch.de/weichewaren/linux-magazin.html

Der Autor

Thorsten Fischer studiert Informatik und/oder Medienberatung an der Technischen Universität Berlin, sein Buch “GUI-Programmierung mit GTK+” wurde im Oktober pünktlich zur Buchmesse bei SuSE-Press veröffentlicht. Er arbeitet außerdem als Entwickler bei Mapmedia in Berlin.
LINUX-MAGAZIN KAUFEN
EINZELNE AUSGABE Print-Ausgaben Digitale Ausgaben
ABONNEMENTS Print-Abos Digitales Abo
TABLET & SMARTPHONE APPS Readly Logo
E-Mail Benachrichtigung
Benachrichtige mich zu:
0 Kommentare
Älteste
Neuste Beste Bewertung
Inline Feedbacks
Alle Kommentare anzeigen
Nach oben