Das eine Panel bringt pfiffige Applets mit, das andere hat eine spektakuläre Optik – aber alles Gute ist selten beisammen. Als Abhilfe bietet sich Perlpanel an. Plattformunabhängig ist es ganz nach dem eigenen Geschmack mit selbst geschriebenen Perl-Applets erweiterbar. Als Beispiel dient ein Börsenticker.
Ob Gnome oder KDE, jeder Desktop bietet so genannte Panels an. Sie schmiegen sich oben oder unten an den Bildschirmrand, beherbergen Menüs und Icons zum Starten von Programmen, zeigen laufende Applikationen mit Task-Bars an oder schalten zwischen virtuellen Desktops hin und her. Ziel des Projekts Perlpanel ist es, ein solches Panel plattformunabhängig bereitzustellen. Außerdem soll der Anwender es mit ein paar schnell zusammengeklopften Skripten um eigene Applets erweitern können.
Unter Ubuntu installiert der User dieses Panel mit »apt-get install perlpanel« und startet es probeweise mit dem Aufruf »/usr/bin/perlpanel«. Abbildung 1 zeigt am unteren Rand des Desktops das Panel-GUI. Belegt den Platz an der Bildschirmunterkante bereits ein anderes Panel, dann verlegt es der Anwender entweder an den rechten oder an den linken Rand oder er löscht es gleich ganz – falls er tatsächlich die Brücken hinter sich abbrechen möchte.
Abbildung 1: Perlpanel bietet die üblichen Funktionen wie virtuelle Desktops und Task-Bars, erlaubt aber auch selbst geschriebene Applets.
Aktien stets im Blick
Wer die Kurse seiner Aktien oder geplanter Erwerbungen laufend im Blick behalten möchte, ist womöglich mit einer einschlägigen Applikation nicht optimal bedient, weil andere Anwendungen deren Fenster im Zweifel verdecken. Stattdessen könnte ein Panel-Applet ideal für den Spekulanten sein, das es erlaubt, die aktuellen Kurse nie aus den Augen zu verlieren. Dafür könnte es etwa alle 5 Minuten die fraglichen Aktienwerte von Yahoo Finance abholen und anzeigen. (Ihre Namen entnimmt es einer lokalen Konfigurationsdatei »~/.ticker-rc« im Homeverzeichnis). Wie in Abbildung 2 demonstriert, ignoriert das Applet Kommentar- und Leerzeilen im Konfigurationsfile und erwartet pro Zeile ein Aktienkürzel. Abbildung 1 zeigt, wie es die vier ausgewählten Kurse im Panel auf den Desktop bringt.

Abbildung 2: Die Kurse der in »~/.ticker-rc« eingetragenen Aktien holt das Applet in regelmäßigen Abständen vom Yahoo-Server.
Das Einholen der Kurse vom Yahoo-Server erledigt das Skript »getquote« in Listing 1, das auf der Kommandozeile eine Reihe von Aktienkürzeln entgegennimmt und deren letzte Kurswerte zeilenweise ausgibt. Der eigentliche Applet-Code »Ticker.pm« ist in Listing 2 zu sehen. Das Applet liest einfach die Konfigurationsdatei ein, ruft im 5-Minuten-Rhythmus das Skript »getquote« auf und frischt mit den zurückgegebenen Werten seine Anzeige im Perlpanel auf. Falls der User mit der Maus auf das Applet klickt, ist er offensichtlich ungeduldig und möchte die neuesten Kurse, also fragt das Applet den Yahoo-Server dann sofort wieder an und frischt die Anzeige im Panel auf.
|
Listing 1: |
|---|
01 #!/usr/local/bin/perl -w
02 use strict;
03 use Finance::YahooQuote;
04
05 $Finance::YahooQuote::TIMEOUT = 60;
06
07 exit 0 unless @ARGV;
08
09 my @quotes = getcustomquote(
10 [@ARGV],
11 ["Symbol", "Last Trade (Price Only)"]);
12
13 if(! exists $quotes[0][1]) {
14 die "Fetching quote failedn";
15 }
16
17 for my $quote (@quotes) {
18 print "@$quoten";
19 }
|
|
Listing 2: |
|---|
001 ###########################################
002 package PerlPanel::Applet::Ticker;
003 use strict;
004 use Log::Log4perl qw(:easy);
005
006 my $REFRESH = 5 * 30_000;
007 my($CFG_FILE)= glob "~/.ticker-rc";
008 my $GETQUOTE = "/usr/bin/getquote";
009
010 Log::Log4perl->easy_init({
011 level => $DEBUG,
012 file => ">>/tmp/ticker.log",
013 });
014
015 ###########################################
016 sub new {
017 ###########################################
018 my($package) = @_;
019
020 my $self = {};
021 bless($self, $package);
022 return $self;
023 }
024
025 ###########################################
026 sub configure {
027 ###########################################
028 my($self) = @_;
029
030 $self->{label} =
031 Gtk2::Label->new("Ticker");
032
033 $self->{widget} = Gtk2::Button->new();
034 $self->{widget}->signal_connect(
035 'clicked', sub {
036 $self->stocks_update() });
037
038 $self->{widget}->set_relief('none');
039 $self->{widget}->add($self->{label});
040
041 $self->{widget}->show_all;
042
043 $self->stocks_update();
044
045 PerlPanel::add_timeout(5 * 60_000,
046 sub { $self->stocks_update() ;
047 return 1 });
048
049 return 1;
050 }
051
052 ###########################################
053 sub symbols {
054 ###########################################
055 my @symbols = ();
056
057 if(! open(FILE, "<$CFG_FILE")) {
058 ERROR "Cannot open $CFG_FILE";
059 return ();
060 }
061
062 while(<FILE>) {
063 s/#.*//g;
064 s/[^w.]//g;
065 next if /^s*$/;
066 chomp;
067 push @symbols, $_;
068 }
069
070 return @symbols;
071 }
072
073 ###########################################
074 sub stocks_update {
075 ###########################################
076 my ($self) = @_;
077
078 my($tag, $buffer);
079 my $symbols = join " ", symbols();
080
081 DEBUG "Updating '$symbols'";
082
083 if($symbols eq "") {
084 $self->{label}->set_markup(
085 "No symbols defined");
086 return undef;
087 }
088
089 if (!open(COMMAND,
090 "$GETQUOTE $symbols |")) {
091 $self->{label}->set_markup(
092 "Fetch failed ($!)");
093 ERROR "Fetch failed ($!)";
094 return undef;
095 }
096
097 $tag = Gtk2::Helper->add_watch(
098 fileno(COMMAND), 'in',
099 sub {
100 if (eof(COMMAND)) {
101 DEBUG "Received data: $buffer";
102 close(COMMAND);
103 Gtk2::Helper->remove_watch($tag);
104 $buffer =~ s/n/ /g;
105 $self->{label}->set_markup(
106 $buffer);
107 } else {
108 $buffer .= <COMMAND>;
109 }
110 });
111
112 return 1;
113 }
114
115 ###########################################
116 sub expand { return 0; }
117 sub fill { return 0; }
118 sub widget { return $_[0]->{widget} }
119 sub get_default_config { return undef; }
120
121 1;
|
Schnapp die Kurse
Die an »getquote« auf der Kommandozeile übergebenen Aktienkürzel liegen wie in Perl üblich im Array »@ARGV«. Das Modul Finance::YahooQuote holt mit der Funktion »getcustomquote()« die Kurse vom Yahoo-Server und gibt an, dass es nur an den Spalten »Symbol« (dem vorher bei der Anfrage angegebene Aktienkürzel) und »Last Trade (Price Only)« (dem letzten Börsenkurs) interessiert ist. Yahoo liefert sonst mit der Funktion »getquote()« einen ganzen Rattenschwanz an Daten, die das Applet aber nicht braucht und daher von vornherein ablehnt.
Passiert ein Fehler bei der Übertragung, kommt ein einzelner Wert mit einer Fehlermeldung zurück, während Yahoo im Erfolgsfall ein Array zurückliefert, dessen Einträge wiederum Referenzen auf Arrays mit den Einträgen zu Aktienkürzel und Kurs sind. Zeile 13 prüft daher, ob tatsächlich ein zweispaltiger Eintrag zurückkam, und bricht andernfalls sofort ab. Zeile 5 setzt noch einen Time-out von 60 Sekunden, der bei Netzwerkstörungen dafür sorgt, dass das Skript nicht ewig wartet. Über die Leitung eingetrudelte Kurswerte gibt die »for«-Schleife ab Zeile 17 im Format »Kürzel Kurs« zeilenweise auf der Standardausgabe aus.
Streng nach Vorschrift
Listing 2 enthält den Applet-Code und muss den Richtlinien von Perlpanel genügen. Dazu gehören ein Modul im Namensraum Perlpanel::Applet::Name, ein Konstruktor »new()« sowie eine vom Panel anfänglich angesprungene Initial-Funktion »configure()«. Die Funktionen »expand()« und »fill()« geben an, wie sich das Widget verändert, wenn sich der verfügbare Platz im Panel vergrößert. »widget()« muss eine Referenz auf das oberste GTK-Widget des Applets zurückgeben, »get_default_config()« liefert normalerweise eine Datenstruktur, die das Applet konfiguriert. Da aber im vorliegenden Fall eine externe Datei die Konfiguration speichert, weil Änderungen des Benutzers keinen Neustart des Applets erfordern sollen, gibt die Funktion hier nur »undef« zurück.
Damit ist dem API von Perlpanel Genüge getan, es fehlt nur noch der eigentliche Applikationscode. In »configure()« baut das Applet sein GUI auf, das aus einem Label und den Kursangaben besteht und einem damit verbundenen Button, der Klicks des Benutzers verarbeitet. Zeile 34 definiert dies mit der Methode »signal_connect()« des Widget, das dem Event »clicked« eine anonyme Subroutine zuordnet, die die Applet-Methode »stocks_update()« aufruft. Sind alle Widgets definiert, zeichnet »show_all()« sie in das Panel ein. Zeile 43 ruft das erste Mal »stocks_update()« auf, bevor das Programm mit »add_timeout()« ein alle 5 Minuten (5 * 60 * 1000 Millisekunden) wiederkehrendes Ereignis definiert, das ebenfalls »stocks_update()« aufruft.
Die Funktion startet ihrerseits jeweils »symbols()«, die die Datei »~/.ticker-rc« ausliest und damit kurzfristige Kürzelupdates des Users sofort erkennt. Kommentarzeilen, Leerzeilen und alles, was nicht nach Aktienkürzel aussieht, verwirft die Funktion, denn später landen diese Kürzel bei »getquotes()« und dann sollte es keine bösen Überraschungen mehr geben.
Beim Aufruf von »open()« legt »stocks_update()« eine Pipe an, damit »getquote()« im Hintergrund startet. Aber aufpassen: Wenn sich der Code gleich in die Ausgabe des extern aufgerufenen Prozesses verbeißt, müsste er eventuell ein paar Sekunden warten, bis die Ergebnisse über das Internet eintreffen, und das ist bei GUIs, die stets flink auf User-Eingaben reagieren müssen, keine gute Idee.
Ohne Pause
Stattdessen nutzt »stocks_update()« die Gtk2::Helper-Funktion »add_watch()«, die einen Filedeskriptor entgegennimmt (»fileno()« generiert aus einem Perl-Filehandle einen Deskriptor), und springt bei eintreffenden Daten eine Callback-Funktion an. Dadurch läuft das Programm in der Zwischenzeit weiter, beendet »stocks_update()« und springt in die Haupteventschleife des Applets, wo es Ereignisse ohne Verzug abarbeitet.
Schickt »get_quote« endlich Daten, hat aber noch mehr auf Lager, ist »eof(COMMAND)« falsch und der Else-Zweig ab Zeile 107 schnappt sich die bisher vorliegenden Ergebnisse. Ist »get_quote« hingegen fertig, kommt der If-Zweig an die Reihe, der das Filehandle mit »close()« abräumt und den Watch auf den zugehörigen Deskriptor mit »remove_watch()« beendet. Zeile 104 des Listings zieht das Spaltenformat dann einfach zu einer Zeile im Format »Kürzel Kurs Kürzel Kurs …« zusammen und schickt es mit der Methode »set_markup()« an das Label-Widget, das den Text auf dem Panel anzeigt.
Damit der Entwickler weiß, was das Applet treibt, initialisiert »Ticker.pm« anfangs Log4perl, um die in den Code eingebetteten Log-Statements in die Datei »/tmp/ticker.log« umzuleiten. In Abbildung 3 sind einige Zeilen daraus zu sehen. Wer auf die zusätzlichen Daten verzichten kann, kommentiert einfach den Aufruf von »easy_init()« im Code aus.

Abbildung 3: In der Datei »/tmp/ticker.log« protokolliert das Perl-Applet mit, woran es gerade arbeitet.
Installation
Unter Ubuntu installiert der Befehl »sudo apt-get install perlpanel« Perlpanel einschließlich aller abhängigen Perl-Module. Die stammen aus dem Ubuntu-Package-Repository, weshalb es keine CPAN-Shell braucht. Das Skript »getquote« landet in »/usr/bin« und benötigt das Modul Finance::YahooQuote, das ebenfalls per Ubuntu-Paket (»libfinance-yahooquote-perl«) erhältlich ist.
Damit Perlpanel von dem neuen Ticker-Applet weiß, sind zwei Schritte erforderlich: Der erste kopiert die Datei »Ticker.pm« ins Verzeichnis »/usr/share/perlpanel/PerlPanel«, wo auch noch weitere Applets liegen, die zusammen mit Perlpanel auf die Platte gelangen. Der zweite Schritt erweitert die Applet-Registry in der Datei »/usr/share/perlpanel/applet.registry« um diese Zeile:
Ticker:Stock Ticker:Utilities
Sie sagt dem Panel, dass es das Widget »Ticker« im Appletverzeichnis unter dem Namen »Ticker.pm« findet, dass es sich um einen Stock Ticker handelt und schließlich, dass der Anwender das kleine Börsendaten-Display im Bereich »Utilities« finden kann.
Nun weiß Perlpanel nach einem Neustart von der Existenz des Applets, stellt es aber noch nicht dar.
Applet-Installation
Dafür muss der User es erst dem Panel hinzufügen und das geht, indem er auf den »Actions«-Button (Abbildung 1) des Panels klickt und den Menüpunkt »Configure« auswählt. In der folgenden Dialogbox (Abbildung 4) betätigt er anschließend den Knopf »+Add«. Zum Vorschein kommt die Auswahlbox (Abbildung 5), die eine Vielzahl von bereits fertigen Applets anbietet. Unter ihnen befindet sich auch Ticker, das erst auszuwählen und dann mit »+Install Applet« zu installieren ist. Anschließend erscheint es wieder in der Box (Abbildung 4), wo der User es nach oben oder unten verschieben kann, um seine horizontale Lage auf dem Panel zu verändern.

Abbildung 4: Ein Klick auf den »Action«-Knopf des Panels und die anschließende Wahl des Menüpunkts »Configure« bringen diesen Dialog zum Vorschein, mit dem der User weitere Applets zum Panel hinzufügt.

Abbildung 5: Das neu geschriebene Perl-Applet Ticker steht zur Auswahl bereit und lässt sich per Mausklick zum Panel hinzufügen.
Damit das Panel automatisch startet, wird das Programm »/usr/bin/perlpanel« in den Session-Startdialog eingefügt, unter Gnome geht dies – wie in Abbildung 6 gezeigt – mit der Dialogbox, sie erscheint, wenn der User den Eintrag »System | Preferences | Sessions« im Hauptmenü anklickt.

Abbildung 6: Mit dieser Konfiguration startet der Gnome Session Manager Perlpanel jeweils beim Einloggen.
Weitere Informationen zum Bauen eigener Perl-Applets können experimentierfreudige Panel-Nutzer der Datei »perlpanel-applet-howto.pod« entnehmen, die zwar nicht dem Ubuntu-Paket, aber dem in CVS eingecheckten Sourcecode des Perlpanel-Projekts auf [2] beiliegt. Alle Funktionen des Panels, inklusive der virtuellen Desktop-Schalttafel, der Taskbar und der Dialogfenster zum Hinzufügen neuer Applets und deren Konfiguration sind in Perl geschrieben und geben einen Vorgeschmack darauf, was mit Perlpanel alles möglich ist. (jcb)
|
Infos |
|---|
|
[1] Listings zu diesem Artikel: [ftp://www.linux-magazin.de/pub/listings/magazin/2009/03/Perl] [2] Perlpanel-Projekt: [http://savannah.nongnu.org/projects/perlpanel] |






