Zurck zur Homepage

Verwendung FOX Toolkit

Free Objects for X(FOX) ist ein cross-platform GUI Toolkit, der vom Jeroen van der Zijp etwickelt wurde. In Vergleich zu Tk und GTK+ ist FOX ein ziemlich junger Werkzeug, aber mit der Zeit gewinnt er unter Softwareentwicklern mehr an Beliebtheit. FOX wurde ursprünglich für UNIX und Linux geschrieben und später auf andrere Betriebsysteme(Windows und MacOS) übertragen. FXRuby ist ein Ruby Erweiterungsmodul, das eine Schnittstelle zwischen FOX und einem Ruby Programm anbietet. Das Modul wurde vom Lyle Johnson entwickelt und kann von Homeseite http:://fxruby.sourceforge.net heruntergeladen werden.

Installation

Die Voraussetzung für die Programmierung mit FXRuby ist eine arbeitsfähige FOX-Umgebung. Wenn Sie Standard-Rubyinstllation fürs Windows verwenden(von der Pragmatic Programmer's Homeseite), ist die Wahrscheinlichkeit, dass das Modul schon mit Rubyinstallation mitinstalliet wurde, groß. Sie können aber eine kompatible schon vorbereitete Binary-Distribution des FXRuby Moduls von der FXRuby Homeseite herunterladen. Der Vorteil dieses Pakets ist, dass die "shared" Bibliothek in dieser Distribution schon ein FOX Bibliothek enthält, so dass man nach dem Downloaden und dem Installieren der Distribution in der Lage ist, mit dem Programmieren anzufangen und besoderes in der erster Stelle sich das Ergebnis anzuzeigen. Man braucht also keine Extrainstallation von FOX.

Wenn Sie eine andere Ruby-Version benutzen(nicht für Windows), werden Sie sich höchstwahrscheinlich etwas mehr anstrengen. Im Gegensatz zu Tk und GTK+ beinhaltet keine Linux-Distributionen ein Standard-Installation Paket von FOX, was sich allerdings mit der Zeit ändern wird. Also Sie müssen sich FOX downloaden, kompilieren und anschließend installieren. Den Sourcecode von FOX kann man sich von der FOX Homeseite herunterladen(www.fox-toolkit.org/). Eine Installationsanweisung sollte dabei sein. Für Linux und Unix-Derivaten wird der Prozess der Instllation bekannt vorkommen: configure, make und make install. Sobald man eine funktonsfähige FOX-Installation hat, kann man mit dem FXRuby-Erweiterungsmodul beginnen. Der Installtionsprozess für FXRuby beginnt mit der Konfiguration:

ruby setup.rb config

Dann ruft man die Installtion selbst:

ruby setup.rb setup

Erst wenn der Buildprozess beendet ist, kann man FXRuby komplett installieren:

ruby setup.rb install

Um mehr die Information über den Installationsprozess zu bekommen, schlagen Sie in der FXRuby Dokumenation nach.

FXRuby Basis

FXRuby's API(Application Programming Interface)-Schnittstelle folgt genau der FOX's C++ API und meistens werden Sie die Standard FOX-Klasse Dokumentation verwenden, um FXRuby Klassehierarchie und Schinttstelle zu lernen. Alle FXRuby Klassen , Methoden un Konstanten sind in dem Single-Ruby Modul, benannt als Fox untergebracht und das meistens von FXRuby wurde im C++ Erweiterungsmodul implementiert, dessen Name fox ist. Ein kleines FXRuby Programm würde so aussehen:

require 'fox'
 
 include Fox
 
 application = FXApp.new("Hello", "FoxTest")
 application.init(ARGV)
 main = FXMainWindow.new(application, "Hello", nil, nil, DECOR_ALL)
 FXButton.new(main, "&Hello World!", nil, application, FXApp::ID_QUIT)
 application.create()
 main.show(PLACEMENT_SCREEN)
 application.run()

Das Programm lädt das Fox Modul bei erforderlicher fox Feature. Trotz der Tatsache, dass alle FXRuby Klasse in dem entsprechenden Namensraum vom Fox Modul zu finden sind, beginnt der Klassenname mit FX Prefix, um sozusagen eine Diskrepanz mit andrenen Klasssennamen zu vermeiden. Und das ist aus dem Grund, dass viele FXRuby Programms können sicher den Inhalt des Fox Modules direkt in globalen Namensraum verwenden(include Fox)

Bevor wir uns mit der Analyse dieses kleines Programms beginnen, füge ein paar Worte dazu, die im Originalartikel nicht zu finden sind. Also dieses kleines Programm kann man statt normalen Dateinamenerweiterung .rb die Erweiterung .rbw verwenden. Das ist die übliche Erweiterung für FXRuby-Applikationen Welche Vorteile bringt das uns? Unter Windows kann man einfach auf die Datei doppelklicken, dabei wird keine Eingabeaufforderung geöffnet, die da für Ein- und Ausgabe bereitsteht, sondern wird das Programm wie üblich gestartet. Der "Nachteil" dieses Vorgehens ist es, dass man dabei alle Fehlermeldungen verloren gehen.

Die Applikation beginnt mit der Erzeugung eines FXApp Objektes. Bei seiner Erzeugung mit new werden zwei Zeichenketten(stings) als Argumente übergeben: ein interner Name für FOX-Anwendung(hier einfach "Hello") und ein so gennanter vendor key, welcher die Art oder den Hersteller der Anwendung(hier "FoxTest") beschreibt. Von allen Toolkits, die wir unter der Lupe nehmen, ist FOX der Einzigste, der eine explizite Erzeugung und Verweisung auf application Objekt braucht, welches als eine Art der Quelle für globale Programmenresoursen(wie Grundapplikation's Farben, Schriften und etc.) Das ist auch im Wesentlichen für die Verwaltung des Ereignisschleife(event loop) verantwortlich, wie wir am Ende sehen werden.

Der nächste Schritt ist das Initialisieren des Programmes. Wir rufen die init Methode auf, die zu unserem Anwendung's Objekt gehört und übergeben in der Kommandozeile ein Argument und zwar Ruby's ARGV Array, welches für FOX sinnvolle Optionen übergibt. Zum Beispiel, um FOX's tracing(spur) Output zu aktivieren, kann man in der Kommandozeile die tracelevel Option verwenden:

ruby hello.rb -tracelevel 301

Um mehr Information darüber zu bekommen, schlagen Sie bitte in der FOX Dokumentaion.

An dieser Stelle sind wir endlich dazu gekommen, unseren ersten Widget zu erzeugen. Das Hauptfenster(bennant main) ist eine Instanz von der Fox::FXMainWindow Klasse, die new Methode erwartet eine Referenz zu Applikation's Objekt, das ist zuvor erzeugte FOX-Anwendung, den Fenstertitel und auch drei Optionen, die das Aussehen des Fensters beeinflussen.

Der nächste Widget ist ein Button(also eine Instanz von Fox::FXButton), und wurde als ein Kind des Hauptfensters erzeugt. Dieser Button zeigt uns eine Zeichenkette "Hello World!" und die erste Buchstabe("H") wird untergestrichen, weil wir ein "Ampersand"-Zeichen("&") vor der Buchstabe plaziert haben. Das ist ein spezieller Signal, das zum FXButton Widget gesendet wird, um den sogenannter "Schortkey" in diesem Fall Strg+H zu verwenden, damit kann man den gleichen Effekt erreichen, wenn man einfach direkt auf den Button klickt. Der vierte und fünfte Argumente von der new Methode sind wichtig, und zwar in Bezug, wie FOX die Ereignisse verarbeitet. Also der vierte Argument ist ein Zielobjekt(eine Instanz von FXApp oder eine von seiner Subklasse), das eine Nachricht bekommt, und schließlich der fünfte Argument, der als "Nachrichtenbezeichner" (message identifier) da ist.In unserem Fall ist "application"(das Programm selbst) ein "Nachrichtenziel" für den Button, und wenn man darauf klickt, werden, bzw. wird, Ereignisse generiert und als ein Ergebnis an das Programm geschickt. Der Nachichtenbezeichner dient zur Unterscheidung zwischen ähnlichen aber verschiedenen Nachrichten, welche das Zielobjekt empfangen könnte; irgendein Objekt kann als "Nachrichtentziel" für mehrere Widgets sein.

Die serverseitige(gemeint wird hier,dass FOX ursprünglich für X-Server-Client Applikationen geschafft worden war) Ressourcen, wie Fenster, Schriftarten und Cursors, werden während des Aufrufes der create Methode erzeugt. Dass der FOX zwischen der Darstellung von klientseitgen Objekten(so wie seine Instanzvariablen) und serverseitigen Ressourcen, die mit ihren Objekten verbunden sind, unterscheidet, macht FOX einzigartig. Eins wenn Fenster's und andere Ressourcen erzeugt worden sind, wird das Hauptfenster zentriert und danach starten wir Hauptereignisschleife (main event loop) mit der Hilfer der run Methode.

Zielobjekte und Nachrichten

FOX's Ereignis Model basiert auf der Idee der Sendung messages von einem Applikationsobjekt zum anderen, das als sein Ziel da ist, when etwas geschieht. Also das ist Ihr Job als Programmier, sich zu entscheiden, wie das Zielobjekt auf eine Nahricht(message) antwortet. Da dieses "Ziel-Nachricht" System "innewohnend"(inherently) und "bidirectional" ist, bringt ein Teil des Antwortes mit sich häufig eine back Nachricht zum Originalsender mit.

Jeder Widget in ihrem Programm hat eine Fähigkeit zum Senden einer Nachricht, aber manche Nachrichten sind mehr "bedeutunsvoller" als andere; zum Beispiel Sie möchten sich wahrscheinlich ein Programm, das sich in irgendeiner Art und Weise reagiert, when der Anwender einen neuen Text in einem Textfeld eintippt, oder sich einen Eintrag in einer baumstrukturigen Liste auswählt. Sie würden ein Zielobjekt, das Nachrichten entgegenimmt, für diese Widgets festlegen, an denen Sie möchten die wichtige Nachrichten senden. Fast jeder von FOX's Widgets erlaubt uns in seiner Konstruktion, seine Zielobjekte festzulegen. Zum Beispiel um eine neue Instanz von der FXList Klasse und ihres anObject als Ziel festzulegen, würden so schreiben:

myList = FXList.new(parent, numItems, anObject, ...)

Sie können auch, wenn man so braucht, das Ziel, nachdem der Widget schon erzeugt wurde, ändern, dabei wird eine setTarget Methode aufgerufen:

myList.setTarget(anotherObject)

Jede Nachricht hat ein sogenannter Typ type, was auf seine Wichtigkeit hinweist. Manche Nachrichten stellen low-level Ereignisse dar, die vom Windows System generiert wurden.; zum Beispiel die SEL_LEFTBUTTONPRESS Nachricht wird gesendet, wenn die linke Maustaste gedrückt wird, während die SEL_LEFTBUTTONRELEASE Nachricht gesendet wird, wenn der glecher Button released wird. Andere Nachrichten werden von FOX generiert und weisen mehr auf die Ereignisse,die von Benutezern ausgelöst sind, hin, wie zum Beispiel das Löschen eines Textes aus dem Textwidget oder das Auswählen einer Zelle in einer Tabelle. Die Nachrichten Typs sind einfach ganze Zahlen(integer)(in Gegensatz zur Zeinchenketten(Strings),di in Ruby/Tk Ereignissen oder Ruby/GTK Signalen verwendet werden), aber Sie müssen immer eine sybolische Konstante mit dem Namen in der Form wie SEL_name verwenden.

Ein Nachrichtentyp, den Sie häufig in FOX's Programmen treffen würden ist die SEL_COMMAND Nachricht. In Allgemeinen wird damit gemeint, dass der Widget nur sein Hauptaktion erledigt, es klingt ein wenig komplizierter, aber lassen wir uns das an einem Beispiel erläutern; ein FXButton Widget wird eine SEL_COMMAND zu seinem Zeilobjekt aussenden, nachdem der User auf den Button klickt, während ein FXTextField Widget wird eine SEL_COMMAND aussenden, wenn der User die Enter Taste im Textfeld betätigt, oder einfach außer des Textfeldes klickt.

Es is schon eine allgemeine Gewohnheit in FOX Applikationen, ein Objekt als ein Ziel mehreren Widget's Nachrichten zu machen. Zum Beispiel, ihres Programm könnte mehrere Menübuttons enthalten, für solche Operationen wie "Öffnen Datei", "Speichern Datei", oder "Drücken Datei", und alle diese Button senden ihre SEL_COMMAND Nachrichten zu einem Zielobjekt. Aber Sie werden sich fragen, wie dieser Zielobjekt in Zustande ist, zwischen ähnlichen Nachrichten von verschiedenen Widgets zu unterscheiden. Sie denken, wenn das Zielobjekt eine SEL_COMMAND Nachricht empfängt, wie das Zielobjekt herausgefunden, welcher Button eine Nachricht geschickt hat? Die Antwort ist einfach, weil jede Nachricht ein Nachrichten-ID(indentifier) enthält(zusätzlich zu seinem Typ), damit wird zusätziliche Information über der Quelle und der Wichtigkeit der Nachricht erreicht.

Das Nachrichten-ID ist einfach eine ganze Zahl(integer), gewöhnlich als eine symbolische Konstante dargestellt, die beim "Empfänger" der Nachricht definiert wurde. Eine Klasse definiert verschiedene Nachrichten-ID und, da FOX ein objekt-orientierender Toolkit ist, ein Objekt versteht auch alle Nachrichten-ID, die bei der Vorfahrklasse festgelegt wurden. Zum Beispiel, da FXButton eine Subklasse von FXLabel ist, erbt er die Nachrichten-ID, die bei FXLabel und FXLabel's Basisklassen definiert wurden.

Um für ein Zielobjekt eine Nachricht zu empfangen und danach zu antworten, muss man Nahrichtenbehandlungsfunktionen für verschiedene Nachrichtentypen und Nachrichten-ID registrieren. Diese Zuordnung erfolgt in der initialize Methode unserer definierten Klasse durch die Verwendung FXMAPFUNC Methode erledigt, damit werden der Nachrichtentyp und die Nachrichten-ID mit dem Namen der Instanzmethode für diese Klasse verbunden. Zum Beispiel wenn wir sozusagen SEL_COMMAND Nachrichtentyp mit ID_OPEN_FILE Nachrichten-ID verwenden wollen, und benutzen onOpenFile Methode um diese Nachricht zuhandhaben, würde wir so in der initialize Methode schreiben:

FXMAPFUNC(SEL_COMMAND, ID_OPEN_FILE, "onOpenFile")

Die Funktionen, die die Nachrichten abwickeln, wie unsere onOpenFile Methode, haben immer drei Argumente:

def onOpenFile(sender, sel, ptr)
 ...irgendwelcher Code...
 end

Das erste Argument(sender) ist das FOX-Objekt, das die Nachricht versendet(der Sender), Eis ist oft praktisch zu wissen, wer eine Nachricht versendet hat, vor allem, wenn ein Teil der Antwort eine Nachricht back mit sich bringt,um zum ursprünglichen Sender etwas mitzuteilen. Das zweite Argument ist der sogennte Selektor und ist eingentlich eine ganze Zahl, welcher(Selektor) die beide Attribute Nachrichtentyp und Nachrichten-ID verschlüsselt. When es erforderlich ist, können Sie den Nachrichtentyp und die Nachrichten-ID aus dem Selektor entnehmen, dabei muss man die SELTYPE und SELID Funktionen verwenden:

def onOpenFile(sender, sel, ptr)
hfkhfmessageType = SELTYPE(sel)
hfkhfmessageId = SELID(sel)
hfkhf...irgendwelcher Coder...
  end

Das letzte Argument, das zur Nachrichtbehandlung weitergeleitet wird, ist ptr; es enthält zusätzliche Daten; der Typ der Daten hängt von Sender- und Nachrichttypen ab. Zum Beispiel, when ein FXTextField Widget eine SEL_COMMAND Nachricht zu einem Ziel aussendet, die Daten, die mit der Nachricht mitgesendet werden, beinhalten eine Zeichenkette(string),die nicht alles Anderes ist, als ein Inhalt des Texfeldes. When ein FXColorWell Widget zum seinem Ziel SEL_COMMAND Nachricht aussendet, wie auch immer, die Nachrichtendaten sind ein ganze Zahl, die auf die Farbe der aktuellen Farbenquelle hinweist. Unseres Beispielprogramm hat einige Arten der Nachrichten, die innerhab des Programm verwendet werden, aber um komplete Information darüber zu erlangen, schlagen Sie auf der FOX Homeseite nach.

Arbeitsweise mit FOX's Layout Manager

Die Auswahl von Fox's Layout Manager ist ähnlich zu der, die wir für Tk und GTK+ hatten, es gibt dabei ein wenig Unterschiede. Wir werden uns lediglich auf vier Arten des Layout Manager's konzentrieren und zwar: FXPacker, FXHorizontalFrame, FXVerticalFrame und FXMatrix. Wie ihre Rivalen in Tk und GTK+ sind diese Layout Manager, die am häufsten beim GUI Programmieren verwendet werden. Für eine Information darüber, wie andere spezifische Layout Manager(wie FXSwitcher, FXSplitter und FX4Splitter) benutzt werden, müssen in der FOX Dokumentation nachschlagen.

Wie in GTK+ sind FOX Layout Manager selbs einfach unsichtbare Behälter für andere Widgets. Sie sind aber nicht so genau unsichtbar, weil Sie eine gewisse sozusagen Kontrole darüber haben, wie zum Beispiel die Außenränder des Behälters gezogen werden(Frame Style), aber wir werden uns meistens mit der Zuordnung von Kinder-Widgets auf der Oberfläche beschäftigen. Wie in Tk werden FOX Widgets immer so erzeugt, dass sie dabei als erstes Argument ihre Vater-Widget bekommen. Sie können später einem Kind-Widget einen neuen Elternteil zuweisen (das ist, wenn Sie ihn von einem Vater-Widget entfernen und dem anderen hinzufügen), aber in Gegensatz zu Ruby/GTK kann Kind-Widget ohne den Vaterteil nicht existieren. Auch so ungleich verhalten sich die Fox Kind-Widgets bei der Festlegung ihrer Layout's Preferenzen(oder layout hints gennant) und zwar als ein Teil ihrer Konstruktion. Sie können ihre Lyout's Einstellungen ändern, auch wenn sie schon existieren. Man ruft dabei eine setLayout Instanzmethode, aber das ist trotzdem ein anderes Modell, als das in Tk und GTK+ verwendet wird. Wie der FOX Layout Manager funktioniert, ist es einzigartige Layout's Strategie. Er braucht die Layout's Einstellungen von jedem seiner Kind-Widgets und danach verwendet sie, um die Größe und Position festzusetzen.

Wir werden uns jetzt FXPacker anschauen, weil dieser meistens von allen Layout Manager verwendet wird und der dient als Basisklasse für die anderen drei: FXHorizontalFrame, FXVerticalFrame und FXMatrix). FXPacker verwendet ungefähr die gleiche Layout' Strategie als Tk's Packer und Namen der Layout's Prefärenzen spiegeln sozusagen ihre "Erbschaft" wieder. Die new Methode für FXPacker könnte es so aussehen:

aPacker = FXPacker.new(parent, opts=0,
hfkhfkahkahjhkjhkjx=0, y=0, h=0,
hfkhfkahkahjhkjhkjpl=DEFAULT_SPACING, pr=DEFAULT_SPACING,
hfkhfkahkahjhkjhkjpt=DEFAULT_SPACING, pb=DEFAULT_SPACING
hfkhfkahkahjhkjhkjhs=DEFAULT_SPACING, vs=DEFAULT_SPACING)

Es mag sein, dass Sie beim Ansehen dieses Codes den Eindruck gewinnen können, dass der eine sehr lange Argumentenliste hat. Bei näherer Prüfung sollten Sie erleichtert sein, weil alles außer den ersten Argument optional ist, das heisst, sie haben eingentlich "default" Werte. Genauso wenn Sie sich die new Methode für andere Widgets ansehen, werden Sie diesen Mustern wieder begegnen: eine lange Argumentenliste mit Grundwerten für die meisten Argumenten. Und eigentlich, meistens oder alle von diesen Argumenten können umgetauscht werden, nachdem der Widget schon erzeugt wurde, und dabei werden seine zusätzliche Methode verwendet, so dass man folgende Code schreiben kann:

aPacker = FXPacker.new(parent, LAYOUT_EXPLICIT, 0, 0, 150, 80)

und das gleichbedeutend mit diesen vier Zeilen des Codes:

aPacker = FXPacker.new(parent)hfkhfkajhkjh# annehmen der Grundwerten
  aPacker.width = 150JHHGjklhjhjhjkkjljljkhjhjh# festsetzen der Breite(in pixel)
  aPacker.height = 80hfkhfkahjhkhkkjlkahjhkjh# festsetzen der Höhe(in pixel)
  aPacker.layoutHints = LAYOUT_EXPLICIThfkd# kontrolliert die Aktualität der Breite und Höhe
  enforced!

Nun müssen wir mehr über die Bedeutung dieser Argumente sagen. Lassen wir uns kurz das zweite Argument(opts) stehen und betrachten wir restlichen Argumente. Die x, y, w und h sind ganze Zahlen, die auf die Position, wobei wird das "vaterliche" Koordinatesystem verwendet, und die Größe für den Widget hindeuten. Diese Argumente kann man ignorieren, wenn wir nicht auch die entsprechende Layoutshinweise festsetzen (LAYOUT_FIX_X, LAYOUT_FIX_Y, LAYOUT_FIX_WIDTH oder LAYOUT_FIX_HEIGHT). Diese Argumente zeigen sich in beinahe jeder new Methode eines Widgets, und sie sind übliche Argumente für FXPacker.new Widgets. Im Codebeispiel, den wir gerade besprochen haben, haben wir die "shortcut option" LAYOUT_EXPLICIT verwendet, die einfach die oben erwähnte Argumente vereinigt; das hat Sinn, weil man ein Layout mit festgelegten Positionen und Größe verwendet wird, alles, dass wir brauchen, diese Option zu setzen. Die nächsten vier Argumenten(pl=DEFAULT...) für FXPacker.new sind die innere Abstände links, rechts, oben und unten vom Rand in pixel. Wie schon erwähnt wurde, bezieht sich das auf den Extra-Raum, der um die innere Rände des Behälters plaziert wird, und wenn der Layout in ihrem Programm besonders aussehen sollte, kann man extra einen Raum an den Ränden verschaffen, um zu versuchen, die Werte vom Grundwert DEFAULT_SPACING(ein Konstantwert gleich 4 pixel) abweichen zu lassen. Die letzte zwei Argumente für den Widget deuten auf einen horizontalen und vertikallen Abstand in pixel hin, welcher zwischen den Kind-Widgets ist. Wie die oben erwähnte interne Abstände(padding), haben diese zwei Argumente Grundwerte jewels vier pixel. Nun lassen wir uns zum zweiten Argument zurückkehren und zwar der Option opts Die meisten FOX's new Methoden verwenden diesen Wert. Damit werden verschiedene "bitartige-flags" ein-, oder ausgeschaltet. Diese "flags" beschreiben das Erscheinen und das Verhalten von Widgets. Wir haben schon bereits gesehen, dass manche von diesen "flags" Layouthinweise beinhalten, die Hinweise vom Kind-Widget zum seinen Vater-Widget, und auch darüber , wie es während der Layoutprozedur behandelt werden sollte. Zusätzlich zum LAYOUT_FIX Hinweis gibt's auch:
LAYOUT_SIDE_LEFT, LAYOUT_SIDE_RIGHT und LAYOUT_SIDE_TOP, LAYOUT_SIDE_BOTTOM die deuten darauf hin, welcher(n) Seite(n) gegenüber die Kind-Widgets gepackt werden können;
LAYOUT_FILL_X, LAYOUT_CENTER_X und LAYOUT_FILL_Y, LAYOUT_CENTER_Y weisen darauf hin, wie der Kind-Widget sich verhalten würde, sich ausdehnen und damit den ganzen Raum füllen oder sich lediglich im Zentrum platzieren.

Es gibt noch zwei spezifischen Optionen oder sozusagen "packing styles": PACK_UNIFORM_WIDTH und PACK_UNIFORM_HEIGHT. Ähnlich wie "homogeneous" Eigenschaften des Ruby/GTK Layout Managers, erzwingen sozusagen diese zwei Optionen den Layout Manager für die Kind-Widgets die gleiche Breite oder, bzw. und Höhe. Diese zwei Optionen sind auf alle Kind-Widgets anwendbar und dabei überschreiben sie alle andere Präferenzen (einschließlich LAYOUT_FIX_WIDTH und LAYOUT_FIX_HEIGHT). Diese Optionen sind mehr für die anderene Layout Manager geeignet, die von FXPacker abgeleitet sind, aber man kann sie trotzdem generell verwenden, wenn man weiss, was man tut.

Nun betrachten wir die nächste Layout Manager und zwar FXHorizontalFrame und FXVerticalFrame zusammen, weil sie ähnlich sind. Wie Sie schon vermuten, ordnen diese beide iher Kind-Widgets in der horizontaler, bzw. vertikaler Richtung an. Die new Methode für FXHorizontalFrame sieht so aus:

aHorizFrame = FXHorizontalFrame.new(parent, opts=0,
enjoyyourselfenjoyyourselfenjoyyourselfex=0, y=0, w=0, h=0,
enjoyyourselfenjoyyourselfenjoyyourselfepl=DEFAULT_SPACING,
enjoyyourselfenjoyyourselfenjoyyourselfepr=DEFAULT_SPACING,
enjoyyourselfenjoyyourselfenjoyyourselfept=DEFAULT_SPACING,
enjoyyourselfenjoyyourselfenjoyyourselfepb=DEFAULT_SPACING,
enjoyyourselfenjoyyourselfenjoyyourselfehs=DEFAULT_SPACING,
enjoyyourselfenjoyyourselfenjoyyourselfevs=DEFAULT_SPACING)

Die Grundeinstellung für diesen Layout Manager sieht es vor, die Kind-Widgets horizontal von links nach rechts anzuordnen, in dieser Reihenfolge werden sie hinzugefügt. Wenn es erforderlich ist, dass ein bestimmter Kind-Widget an der rechten Seite des Raumes angeordnet werden soll, gibt man einfach den LAYOUT_RIGHT Layoutshinweis über. Vertikale Frames ordnen ihre Kind-Widgets von oben nach unten und zwar "defaultmäßig, mit dem LAYOUT_BOTTOM Hinweis kann man dieses Vorgehen verändern. Der letzte Layout Manager, den wir besprechen, ist FXMatrix, welcher seine Kind-Widgets in Zeilen und Spalten anordnet. Die new Methode für FXMatrix ist:

aMatrix = FXMatrix.new(parent, size=1, opts=0,
enjoyyourselfenjoyyourselfx=0, y=0, w=0, h=0,
enjoyyourselfenjoyyourselfpl=DEFAULT_SPACING, pr=DEFAULT_SPACING,
enjoyyourselfenjoyyourselfpt=DEFAULT_SPACING, pb=DEFAULT_SPACING,
enjoyyourselfenjoyyourselfhs=DEFAULT_SPACING, vs=DEFAULT_SPACING)

Der Widget FXMatrix hat zwei wichtige Optionen MATRIX_BY_ROWS und MATRIX_BY_COLUMNS Sie weisen darauf hin, wie das size Argument für den FXMatrix.new interpretiert werden kann. Für die MATRIX_BY_ROWS, das ist die Grudneinstellung, zeigt die size die Anzahl der Zeilen; die Anzahl der Spalten ist schließlich die gesamte Anzahl der Kind-Widgets für den Matrix. In unserem Beispiel bedeutet es, dass der erste Kind-Widget zur ersten Reihe hinzugefügt wird, der zweite zur zweiten Reihe und so weiter, wobei alle Kind-Widgets sich in einer Spalte befinden. In anderem Fall wird die Option MATRIX_BY_COLUMNS verwendet, wobei das Argument size die Anzahl der Spalten zeigt und die Anzahl der Reihen wird unterschiedlich.

FOX Beispielprogramm

Der folgende Code zeigt den kompletten Sourcecode von unserem Beispielprogramm. Den können Sie auch herunterladen fox-xmlviewer

#!/bin/env ruby -w
 
  require "fox"
  require "fox/responder"
 
  require "nqxml/treeparser"
  include Fox
 
 class XMLViewer < FXMainWindow

enjinclude Responder

enj# Define message identifiers for this class
enjID_ABOUT, ID_OPEN, ID_TREELIST =
enjoyenum(FXMainWindow::ID_LAST, 3)

enjdef createMenubar
enjoymenubar = FXMenubar.new(self, LAYOUT_SIDE_TOP|LAYOUT_FILL_X)
enjoyfilemenu = FXMenuPane.new(self)
enjoyFXMenuTitle.new(menubar, "&Datei", nil, filemenu)
enjoyFXMenuCommand.new(filemenu,
enjoyyou"&Öffnen...\tCtl-O\tOpen document file.", nil, self, ID_OPEN)
enjoyFXMenuCommand.new(filemenu,
enjoyyou"&Beenden\tCtl-Q\tQuit the application.", nil,
enjoyyogetApp(), FXApp::ID_QUIT, 0)

enjoyhelpmenu = FXMenuPane.new(self)
enjoyFXMenuTitle.new(menubar, "&Hilfe", nil, helpmenu, LAYOUT_RIGHT)
enjoyFXMenuCommand.new(helpmenu,
enjoyyou"&Über FOX...\t\tDisplay FOX about panel.",
enjoyyonil, self, ID_ABOUT, 0)
enjend

enjdef createTreeList
enjoylistFrame = FXVerticalFrame.new(@splitter,
enjoyyuLAYOUT_FILL_X|LAYOUT_FILL_Y|FRAME_SUNKEN|FRAME_THICK)
enjoy@treeList = FXTreeList.new(listFrame, 0, self, ID_TREELIST,
enjoyyu(LAYOUT_FILL_X|LAYOUT_FILL_Y|
enjoyyuTREELIST_SHOWS_LINES|TREELIST_SHOWS_BOXES|TREELIST_ROOT_BOXES))
enjend

enjdef createAttributesTable
enjoytableFrame = FXVerticalFrame.new(@splitter,
enjoyyuLAYOUT_FILL_X|LAYOUT_FILL_Y|FRAME_SUNKEN|FRAME_THICK)
enjoy@attributesTable = FXTable.new(tableFrame, 5, 2, nil, 0,
enjoyyu(TABLE_COL_SIZABLE|TABLE_ROW_SIZABLE|
enjoyyuFRAME_SUNKEN|FRAME_THICK|LAYOUT_FILL_X|LAYOUT_FILL_Y))
enjend

enjdef initialize(app)
enjoy# Initialize base class first
enjoysuper(app, "XML Editor", nil, nil, DECOR_ALL, 0, 0, 800, 600)

enjoy# Set up the message map
enjoyFXMAPFUNC(SEL_COMMAND, ID_ABOUT, "onCmdAbout")
enjoyFXMAPFUNC(SEL_COMMAND, ID_OPEN, "onCmdOpen")
enjoyFXMAPFUNC(SEL_COMMAND, ID_TREELIST, "onCmdTreeList")

enjoy# Create the menu bar
enjoycreateMenubar

enjoy@splitter = FXSplitter.new(self, LAYOUT_FILL_X|LAYOUT_FILL_Y)

enjoy# Create the tree list on the left
enjoycreateTreeList

enjoy# Attributes table on the right
enjoycreateAttributesTable

enjoy# Make a tool tip
enjoyFXTooltip.new(getApp(), 0)
enjend

enj# Create and show the main window
enjdef create
enjoysuper
enjoyshow(PLACEMENT_SCREEN)
enjend

enjdef loadDocument(filename)
enjoy@document = nil
enjoybegin
enjoyyu@document = NQXML::TreeParser.new(File.new(filename)).document
enjoyrescue NQXML::ParserError => ex
enjoyyuFXMessageBox.error(self, MBOX_OK, "Error",
enjoyyou"Couldn't parse XML document")
enjoyend
enjoyif @document
enjoyyo@treeList.clearItems()
enjoyyopopulateTreeList(@document.rootNode, nil)
enjoyend
enjend
enjdef populateTreeList(docRootNode, treeRootNode)
enjoyentity = docRootNode.entity
enjoyif entity.instance_of?(NQXML::Tag)
enjoyyotreeItem = @treeList.addItemLast(treeRootNode, entity.to_s, nil, nil, entity)
enjoyyodocRootNode.children.each do |node|
enjoyyoupopulateTreeList(node, treeItem)
enjoyyoend
enjoyelsif entity.instance_of?(NQXML::Text) && entity.to_s.strip.length != 0
enjoyyotreeItem = @treeList.addItemLast(treeRootNode, entity.to_s, nil, nil, entity)
enjoyend
enjend

enjdef onCmdOpen(sender, sel, ptr)
enjoydlg = FXFileDialog.new(self, "Öffnen")
enjoydlg.setPatternList([
enjoyyo"Alle Dateien (*)",
enjoyyo"XML Documents (*.xml)"])
enjoyif dlg.execute() != 0
enjoyyoloadDocument(dlg.getFilename())
enjoyend
enjoyreturn 1
enjend

enjdef onCmdTreeList(sender, sel, treeItem)
enjoyif treeItem
enjoyyoentity = treeItem.getData()
enjoyyoif entity.kind_of?(NQXML::NamedAttributes)
enjoyyoukeys = entity.attrs.keys.sort
enjoyyou@attributesTable.setTableSize(keys.length, 2)
enjoyyoukeys.each_index { |row|
enjoyyous@attributesTable.setItemText(row, 0, keys[row])
enjoyyous@attributesTable.setItemText(row, 1, entity.attrs[keys[row]])
enjoyyou}
enjoyyoend
enjoyend
enjoyreturn 1
enjend

enj# About box
enjdef onCmdAbout(sender, sel, ptr)
enjoyFXMessageBox.information(self, MBOX_OK, "�er XMLViewer",
enjoyyo"FXRuby Sample Application/Beispielprogramm")
enjoyreturn 1
enjend
enend

eif $0 == __FILE__
enj# Make application
enjapplication = FXApp.new("XMLViewer", "FoxTest")

enj# Open the display
enjapplication.init(ARGV)

enj# Make window
enjmainWindow = XMLViewer.new(application)

enj# Create the application windows
enjapplication.create

enj# Run the application
enjapplication.run
eend

Die FXRuby Version unseres Programm beginnt mit einer Einbindung der Module fox und fox/responder, welche der FOX Hauptbibliothek entsprechen und außerdem wird dadurch erreicht, dass die Widgets ihre Nachricht-Handler Methoden sozusagen bei der Bibliothek registrieren können:

 require "fox"
  require "fox/responder"
 
  require "nqxml/treeparser"
  include Fox
 
 class XMLViewer < FXMainWindow

enjinclude Responder

enj# Define message identifiers for this class
enjID_ABOUT, ID_OPEN, ID_TREELIST =
enjoyenum(FXMainWindow::ID_LAST, 3)

Wie man schon sieht, brauchen wir eine Hauptfensterklasse(XMLViewer) um auf drei Nachricht-ID's zu reagieren: ID_ABOUT, welche mit dem Über... Menübefehl zusammenhängt; ID_OPEN, welche mit dem Öffnen Menubefehl in der Beziehung steht; und ID_TREELIST, welche verwendet wird, wenn man ein Entrag in der Liste auswählt. Um Nachricht-Handler Funktionen für diese Klasse anzumelden, muss man auch mix-in Responder Module einbinden. Die enum Funktion ist sozusagen "Helfer", die von diesem Modul bereitgestellt wird und das baut einfach ein Array mit ganzen Zahlen auf. Der Array beginnt mit seinem ersten Input(FXMainWindow) und als zweites Argument wird die Anzahl der Nachricht-ID's übergeben. In unserem Fall stellen wir sicher, dass die Argumente für die enum Funktion mit FXMainWindow::ID_LAST beginnt, damit erreichen wir, dass es keine gegenseitige Störungen mit anderen FXMainWindow's Nachricht-ID's gibt. Und das ist eingentlich ein Standard-Vorgang für FXRuby Applikationen. Der erste Schritt in initialize Methode ist das Deklarieren der Basisklasse(FXMainWindow):

super(app, "XML Editor", nil, nil, DECOR_ALL, 0, 0, 800, 600)

Dabei ist es wichtig, diesen Schritt nicht zu unterlassen. Hier ist das zweite Argument zu FXMainWindow initialize Methode der Titel des Fensters("XML Editor"). Das fünfte Argument ist eine Reihe von Optionen, die Hinweise für Fenster's Manager beinhalten, welche Fenster's Dekorationen(z.B. ein Titelmenü oder eine Handhabung der Fenstergröße) gezeigt werden könnten. In unserem Fall wünschen wir uns alle mögliche Dekorationen(DECOR_ALL). Die letzte zwei Argumente setzen die anfängliche Breite und Höhe für Hauptfenster fest. Der nächste Schritt ist die Zuordnung einer Nachrichten-ID zu einer Methode:

FXMAPFUNC(SEL_COMMAND, ID_ABOUT, enj"onCmdAbout")
eFXMAPFUNC(SEL_COMMAND, ID_OPEN,enjoy"onCmdOpen")
eFXMAPFUNC(SEL_COMMAND, ID_TREELIST, "onCmdTreeList")

Die FXMAPFUNC Methode ist ein "mixed-in" aus dem Responder Modul. Die Methode hat drei Argumente und zwar Nachrichten-Typ, Nachrichten-ID und den Methodennamen. Der erste Aufruf, zum Beispiel, gibt es bekannt, wenn XMLViewer Objekt vom Nachrichtentyp SEL_COMMAND gefragt wird, wobei die Nachrichten-ID XMLViewer::ID_ABOUT verwendet wird, dass die onCmdAbout Methode aufgerufen wird. Der Rest der initialize Methode legt den Inhalt und den Layout des Hauptfensters fest. Die erste sozusagen interessante Stelle in der Applikation kommt, wenn die Menübar des Programmes erzeugt wird. Dafür haben wir die createMenubar Methode verwendet:

enjdef createMenubar
enjoymenubar = FXMenubar.new(self, LAYOUT_SIDE_TOP|LAYOUT_FILL_X)
enjoyfilemenu = FXMenuPane.new(self)
enjoyFXMenuTitle.new(menubar, "&Datei", nil, filemenu)
enjoyFXMenuCommand.new(filemenu,
enjoyyou"&Öffnen...\tCtl-O\tOpen document file.", nil, self, ID_OPEN)
enjoyFXMenuCommand.new(filemenu,
enjoyyou"&Beenden\tCtl-Q\tQuit the application.", nil,
enjoyyogetApp(), FXApp::ID_QUIT, 0)

enjoyhelpmenu = FXMenuPane.new(self)
enjoyFXMenuTitle.new(menubar, "&Hilfe", nil, helpmenu, LAYOUT_RIGHT)
enjoyFXMenuCommand.new(helpmenu,
enjoyyou"&Über FOX...\t\tDisplay FOX about panel.",
enjoyyonil, self, ID_ABOUT, 0)
enjend

Ein "FXMenubar" wird als ein horizontalorientierender Behälter, so dass er ein oder mehrere "FXMenuTitle"- Widgets enthalten kann. "FXMenuTitle" hat als Argument ein Textstring, der als ein Name für den Menütitel benutzt wird (wie "Datei") und außerdem wird beim Anklicken des Menütitels ein Popup-Fenster("FXMenuPane" )eröffnet, welches ein oder mehrere "FXMenuCommand" widgets enthält. Der Textstring für den Menütitel kann vorne das "Ampersandzeichen"("&"); enthalten, wenn das vorhanden ist, wird die erste Buchstabe unterstrichen, was den FOX veranlasst, den "Keybord-Beschleiniger" einsetzen, um das Menü mit der Tastekombination zu aktivieren. Zum Beispiel hat "FXMenuTitle" für das Datei Menü:

FXMenuTitle.new(menubar, "&Datei", nil, filemenu)

den Textstring "Datei" mit "D" unterstrichen, und wenn man die Alt+F Tastekombination verwendet, wird das Menü "Datei" benutzt. So ähnlich kann der Textstring für Menücommands spezielle Kontrollzeichen beinhalten:

FXMenuCommand.new(filemenu,
enjoyyou"&Öffnen...\tCtl-O\tOpen document file.", nil, self, ID_OPEN)

Das "Ampersandzeichen" vor der Buchastabe "Ö" in "Öffnen" legt "hot key" für den Menübefehl; wenn das Datei Menü schon geöffnet ist, kann man Ö Taste drücken um den "Öffnen..." Befehl zu aktivieren. Die Tabulatorzeichen("\t") werden bei FOX als Feldseparatoren erkannt. Das erste Feld ist der Haupttext, welcher in "FXMenuCommand" Widget gezeigt wird. Das zweite Feld ist ein optionaler String, der auf di Tastekombination hindeutet, die verwendet kann, um direkt auf den Befehl zugreifen, nur aber wenn das Menü schon geöffnet wurde. Und das letzte Feld ist auch optional, welches ein Textstring enthält, der uns einfach erklärt, was dahinter steckt.

Obwohl wir in unserem Beispiel keine Separatoren verwenden, ist est oft hilfreich, ein oder mehrere horizontalen Separatoren zu "Pulldown-Menü hinzuzufügen. Damit kann man zusammenhängende Menübefehle gruppieren. Um ein Separator zu "FXMenuPane" hinzuzufügen, erzeugt man einfach eine Instanz von der FXMenuSeparator Klasse in der gewünschten Position:

FXMenuSeparator.new(filemenu)

Die linke Seite des Hauptfensters unterbringt eine baumstrukturige Liste von XML Dokumentknoten; das wird durch die createTreeList erzeugt:

def createTreeList
enjoylistFrame = FXVerticalFrame.new(@splitter,
enjoyyuLAYOUT_FILL_X|LAYOUT_FILL_Y|FRAME_SUNKEN|FRAME_THICK)
enjoy@treeList = FXTreeList.new(listFrame, 0, self, ID_TREELIST,
enjoyyu(LAYOUT_FILL_X|LAYOUT_FILL_Y|
enjoyyuTREELIST_SHOWS_LINES|TREELIST_SHOWS_BOXES|TREELIST_ROOT_BOXES))
enjend

Da die FXTreeList Klasse nicht von FXFrame abgeleitet wird, hat man hier keine Framestyle's Optionen gesetzt. Um nun jetzt gut aussehende tiefliegende Kanten zu bekommen, müssen wir den FXTreeList Widget sozusagen umgeben, bzw. in ein von FXFrame-abgeleiteter Kind-Widget packen; hier werden wir FXVerticalFrame verwenden. Die dritte und vierte Argumente von new Methode für den FXTreeList Widget stellen das Hauptfenster(self) als Nachrichtenziel und auch deren Nachrichten-ID und zwar XMLViewer::ID_TREELIST. Wie Sie sich schon bestimmt erinnern, haben wir in der initialize Methode der Nachrichten-ID ID_TREELIST eine onCmdTreeList Methode zugeordnet, also wenn SEL_COMMAND Nachricht mit dieser Nachrichten-ID gesendet wird, veranlasst es, dass die onCmdTreeList aufgerufen wird:


enjdef onCmdTreeList(sender, sel, treeItem)
enjoyif treeItem
enjoyyoentity = treeItem.getData()
enjoyyoif entity.kind_of?(NQXML::NamedAttributes)
enjoyyoukeys = entity.attrs.keys.sort
enjoyyou@attributesTable.setTableSize(keys.length, 2)
enjoyyoukeys.each_index { |row|
enjoyyous@attributesTable.setItemText(row, 0, keys[row])
enjoyyous@attributesTable.setItemText(row, 1, entity.attrs[keys[row]])
enjoyyou}
enjoyyoend
enjoyend
enjoyreturn 1
enjend

Wenn der FXTreeList Widget den SEL_COMMAND Befehl zu seinem Nachrichtenziel sendet, ist die Nachricht(das dritte Argument, das an die "message handler methode" weitergeleitet wird) ein Verweis auf den ausgewählten Eintrag in der XML-Datei, wenn überhaupt. Angenommen, dass der ausgewählte Eintrag(als treeItem genannt) nicht nil ist, dann empfangen wir einen Verweis, der auf Angaben, die mit diesem Eintrag verbunden sind, hindeutet. Wie Sie später sehen werden, wenn wir die populateTreeList Methode besprechen werden, durch die die Einträge der XML-Datei dargestellt werden, also Angaben, die der User vornimmt, sind mit den Einträgen verbunden, die ihrerseits die XML-Entities repräsentieren. Wenn ein Eintrag mit ihm verbundene Attribute hat, modifizieren wir die Anzahl der Tabellenzeilen, erreichen wir das, wenn man die setTableSize Methode aufrufen und danach übergeben wir alle vorhandene Attribute, um den Inhalt in Tabellenzellen auf einen neuen Zustand zu bringen.

Die mit den Attributen gefüllte Tabelle auf der rechten Seite des Hauptfensters wird durch die createAttributesTable Methode erzeugt und besteht aus FXTable Widget, welcher ebenfalls in FXVerticalFrame Widget eingeschloßen wird:

enjdef createAttributesTable
enjoytableFrame = FXVerticalFrame.new(@splitter,
enjoyyuLAYOUT_FILL_X|LAYOUT_FILL_Y|FRAME_SUNKEN|FRAME_THICK)
enjoy@attributesTable = FXTable.new(tableFrame, 5, 2, nil, 0,
enjoyyu(TABLE_COL_SIZABLE|TABLE_ROW_SIZABLE|
enjoyyuFRAME_SUNKEN|FRAME_THICK|LAYOUT_FILL_X|LAYOUT_FILL_Y))
enjend

Wie Sie sich schon errinern können, haben einige von anderen GUI Toolkits den Namen table nur dafür, um an den Layout-Manager zu verweisen, der seine Kinder in Reihen und Spalten anordnet(was dafür FOX den FXMatrix Layout-Manager aufruft). Für FOX ist FXTable mehr als ein Tabellenkalkulationsähnlicher Widget. Wir haben anfangs den FXTable Widget mit 5 sichtbaren Zeilen und 2 sichtbaren Spalten erzeugt, obwohl, wir werden es später sehen, die Tabellengröße sich dynamisch ändern kann, während das Programm läuft. Die onCmdOpen Methode behandelt die SEL_COMMAND Nachricht, die erzeugt wird, sobald der User auf den Öffnen... Menübefehl aus dem Datei Menü klickt:

enjdef onCmdOpen(sender, sel, ptr)
enjoydlg = FXFileDialog.new(self, "Öffnen")
enjoydlg.setPatternList([
enjoyyo"Alle Dateien (*)",
enjoyyo"XML Documents (*.xml)"])
enjoyif dlg.execute() != 0
enjoyyoloadDocument(dlg.getFilename())
enjoyend
enjoyreturn 1
enjend

In dieser Methode wird ein neues FXFileDialog Objekt aufgebaut, dann wird seine Musterliste initialisiert und anschließend wird durch den Aufruf der execute Methode ein Dialogfenster angezeigt. Die execute Methode gibt "nicht-null" zurück, wenn der User den OK Button drückt, und das der Fall ist, werden wir nach einem Dateinamen gefragt, den wir entweder auswählen oder eingeben müssen. Daraufhin wird die loadDocument Methode aufgerufen, die eine XML-Datei laden kann:

enjdef loadDocument(filename)
enjoy@document = nil
enjoybegin
enjoyyu@document = NQXML::TreeParser.new(File.new(filename)).document
enjoyrescue NQXML::ParserError => ex
enjoyyuFXMessageBox.error(self, MBOX_OK, "Error",
enjoyyou"Couldn't parse XML document")
enjoyend
enjoyif @document
enjoyyo@treeList.clearItems()
enjoyyopopulateTreeList(@document.rootNode, nil)
enjoyend
enjend

Wenn der XML Parser eine Ausnahme auslöst, während der Versuchung ein NQXML::Document Objekt zu erzeugen, rufen wir eine FXMessageBox.error Singletone Methode auf, um ein einfaches Dialogbox zu zeigen, die dem User erklälrt, was geschehen ist. Das erste Argument von FXMessageBox.error identifiziert sozusagen den Besitzer der Dialogbox, andersgesagt wem die Dialogbox gehört. Die Box schwebt sozusagen über dem Besitzer, bis die abgewiesen wird. Das zweite Argument ist ein "flag", der darauf hinweist, welche Art von "Ende-Buttons" in dieser Dialogbox erzeugt werden könnte; es gibt dafür auch andere Optionen und zwar MBOX_OK_CANCEL, MBOX_YES_NO, MBOX_YES_NO_CANCEL, MBOX_QUIT_CANCEL und MBOX_QUIT_SAVE_CANCEL. Die FXMessageBox Klasse unterstützt ein paar anderen praktischen singleton Methoden, durch die nützliche Nachrichten wie information, question und warning angezeigt werden könnten. Wenn es keine Fehlermeldungen gibt, löschen wir den alten Inhalt der Liste und danach bauen durch den wiederholenden Aufruf der populateTreeList Methode neuen Listeninhalt auf:

enjdef populateTreeList(docRootNode, treeRootNode)
enjoyentity = docRootNode.entity
enjoyif entity.instance_of?(NQXML::Tag)
enjoyyotreeItem = @treeList.addItemLast(treeRootNode, entity.to_s, nil, nil, entity)
enjoyyodocRootNode.children.each do |node|
enjoyyoupopulateTreeList(node, treeItem)
enjoyyoend
enjoyelsif entity.instance_of?(NQXML::Text) && entity.to_s.strip.length != 0
enjoyyotreeItem = @treeList.addItemLast(treeRootNode, entity.to_s, nil, nil, entity)
enjoyend
enjend<

Hier verwenden wir eine addItemLast Methode, die zum FXTreeList Widget gehört, damit wird es erreicht, dass neue Listenenträge hinzugefügt werden. Das erste Argument von der addItemLast Methode ist ein Verweis auf einen Listeneintrag, es wird sozusagen ein "Vater" für diesen Eintrag erzeugt; die Listeneinträge in "top-level" erzeugt, man kann stattdesen nil für dieses Argument übergeben. Das zweite Argument für diese Methode ist ein Textstring, der angezeigt wird, wenn man auf den Entrag klickt, das dritte und vierte Argumente sind optional und das sind "plus" Icon für "öffnen" und "minus" Icon für "schließen". Die werden vor dem Textstring des Listeneintrages angezeigt. Wenn es keine Icons angeboten werden, wird der FXTreeList Widget anstatt der "plus" und "minus" Icons eine standartmäßige viereckige Icon darstellen. Das letzte Argument der addItemLast Methode ist eine optionale User's Angabe; das ist ein Ruby Objekt, welches mit dem frisch erzeugten XML-Eintrag verbunden ist. In unserem Fall speichern wir hier einen Verweis auf den XML-Entinity, welche durch diesen Eintrag repräsentiert wird.

Nun ist die Zeit zum Ausprobieren. Die unten ausgeführte Abbildung lief auf Windows OS:

Und das ist die Abbildung des Programms, das auf Linux OS ausprobiert war:

Anschließend noch Mal zur Erinnerung, dass man den aktuellen Stand FXRuby Projektes auf der jeweiligen Homeseite erfahren kann.

Valid XHTML 1.1! Valid CSS!