Weiter Zurück Inhaltsverzeichnis
Wie wir gesehen haben, haben wir KScribble bereits die Fähigkeit gegeben, Bilder mit der Dokumentklasse zu öffnen und zu
speichern, haben durch Überladen virtueller Methoden Interaktion ermöglicht, und wir haben die erste Funktionalität erreicht- wir
können auch Bilder zeichnen. Aber als wir die QPen
Instanz erzeugt haben, haben wir vordefinierte Werte für den Stift
verwendet; die Farbe ist Schwarz und die Breite ist auf 3 Pixel eingestellt. Da Sie für gewöhnlich diese Werte ändern können
wollen, müssen wir die Haupt-GUI um Funktionen, diese Werte zu setzen, erweitern, je nachdem welches Fenster gerade aktiv ist und
welches Dokument damit verbunden ist. Dieses Kapitel wird Ihnen daher zeigen:
Desweiteren fügen wir eine Methode hinzu, den Inhalt des gesamten Dokuments über das Menü zu löschen.
Wie der Name dieses Abschnittes schon sagt, werden wir hier ein Menü zum Setzen der Stiftwerte des Dokuments hinzufügen. Menüs,
die in die Menüleiste eingefügt werden, sind Instanzen von QPopupMenu
, und Sie können einen Blick darauf werfen, wie die
aktuelle Menüleiste aufgebaut ist, wenn Sie zur KScribbleApp
Klasse, Methode
initMenubar()
wechseln. Sie werden sehen, daß die Menüleistenelemente in der
Reihenfolge ihres Erscheinens auf der Menüleiste erzeugt wurden, aber das ist nicht
notwendigerweise so. Es gibt zwei Dinge, die für das Aussehen der Menüleiste wichtig sind:
Schließlich und endlich werden wir zuerst die Menüs erzeugen müssen, indem wir den Konstruktor aufrufen. Die Klassendeklaration enthält bereits die Zeiger auf die Popupmenüs, also werden wir zuerst das "Stift" Menü hier einfügen:
kscribbleapp.h class KScribbleApp { . . private: QPopupMenu* pPenMenu; }
Jetzt werden wir das Menü selbst erzeugen. Ändern Sie die Implementation der Methode KScribbleApp::initMenuBar()
und
editieren Sie die, mit dem Pfeil markierten Zeilen:
void KScribbleApp::initMenuBar() { .. -> /////////////////////////////////////////////////////////////////// -> // menuBar entry pen-Menu -> pPenMenu = new QPopupMenu(); -> pPenMenu->insertItem(i18n("&Color"), ID_PEN_COLOR); -> pPenMenu->insertItem(i18n("&Brush"), ID_PEN_BRUSH); menuBar()->insertItem(i18n("&Edit"), pEditMenu); -> menuBar()->insertItem(i18n("&Pen"), pPenMenu); menuBar()->insertItem(i18n("&View"), pViewMenu); -> connect(pPenMenu, SIGNAL(activated(int)), SLOT(commandCallback(int))); -> connect(pPenMenu, SIGNAL(highlighted(int)), SLOT(statusCallback(int))); }
Sie sehen, daß wir zunächst das Menü mit new QPopupMenu()
erzeugen. Dann verwenden wir
die insertItem Methode, zum Hinzufügen der Menüeinträge Color und Brush. Die sichtbaren
Kommandos werden mit der Methode i18n()
eingefügt, womit sichergestellt ist, daß Sie
Ihre Applikation internationalisieren können. Als generelle Regel kann gesagt werden, daß Sie
alle später sichtbaren Texte mit der Methode i18n()
deklarieren sollten. Nur-Qt Programme, die
Qt > 2.0 verwenden, benutzen dazu die Methode tr()
, da Qt eigene Verfahren hat,
Anwendungen zu internationalisieren. Das zweite Argument ist ein Makro, die ID des
Menüeintrages. Diese ID ist eine Nummer, die wir mit #define in der resource.h, in der Sie
auch die anderen bisher definierten ID's finden, setzen müssen. Es gibt auch den Weg, Menüs
direkt einzufügen, indem Sie einen Slot mit dem gewählten Eintrag verbinden, aber das Framework
dieser Anwendung verwendet ID's, um festzustellen, welche Aktion ausgewählt und markiert wurde.
Deshalb muß jeder Menüeintrag, unbhängig davon, in welchem Popupmenü er erscheint, eine
einzigartige Nummer besitzen, und da wir uns Nummern schlecht merken können, ist die Verwendung
eines #define für die ID eine gute Lösung. Das Popupmenü wird nun ebenfalls mit
insertItem()
der Menüleiste hinzugefügt, und zwar mit einem Zeiger auf das Menü als
zweitem Argument. Beachten Sie, daß wir das Popupmenü nach dem Edit- und vor dem Viewmenü
eingefügt haben, es wird also später zwischen diesen beiden Menüs erscheinen. Was auch wichtig
bei der Erstellung von Menüs ist, ist, daß sie dem Benutzer auch über Shortcuts zur Verfügung
gestellt werden sollten; normalerweise sehen Sie in Menüs einen unterstrichenen Buchstaben, der
zusammen mit ALT die entsprechende Funktion aufruft. Als Programmierer, müssen Sie diesen
Buchstaben durch ein vorangestelltes "&" setzen, das "Pen" Menü wird also später durch
drücken von ALT+P erreichbar sein. Innerhalb des Menüs kann der Benutzer auch nur den
Buchstaben eingeben, um den entsprechen Menüpunkt auszuführen, Sie sollten also für die anderen
Menüpunkte ebenfalls Tastaturkürzel zur Verfügung stellen. Achten Sie darauf, den Code zum
Einfügen von Einträgen in Gruppen zusammenzuschreiben, die Sie überblicken können, damit Sie
keine Kürzel doppelt verwenden (Dies ist auch für Ihre Übersetzer wichtig: In anderen Sprachen
kommt das gewählte Kürzel vielleicht nicht im Menütext vor, der Übersetzer muß also auch wieder
ein Kürzel auswählen).
In den letzten beiden Zeilen verbinden wir die Stiftmenüs mit zwei Slots: einen für das Menüsignal "aktiviert, Aktion ausführen" und einen für die Markierung des Menüeintrages. Dies ermöglicht es, eine Hilfemeldung in der Stauszeile anzuzeigen. Sie können sich die Methoden ansehen, die mit dem Menü verbunden sind. Sie enthalten Anweisungen, in denen die gesendete ID geprüft und die folgende Aktion aufgerufen wird. Was noch zu tun bleibt, ist die #define Anweisungen in die resource.h einzutragen:
resource.h /////////////////////////////////////////////////////////////////// // Pen-menu entries #define ID_PEN_COLOR 14010 #define ID_PEN_BRUSH 14020
Sie sehen, daß die Nummern für jeden Eintrag einzigartig sind. Sie müssen darauf achten, nicht zweimal dieselbe Nummer zu vergeben. Sollte es aber dennoch einmal versehentlich passieren, wird Sie der Compiler über die Redefinition informieren.
Das ist zum jetzigen Zeitpunkt alles, das Sie zum Hinzufügen eines neuen Menüs tun müssen. Die ausgeführten Aktionen sind: "Color" (Aufruf des Farbauswahldialogs) und "Brush" (Aufruf des Dialogs zum Setzen der Stiftbreite). Letzeren Dialog werden wir noch erstellen, aber zuerst werden wir im nächsten Kapitel auch die Werkzeugleiste um zwei Icons für diese Aktionen erweitern.
Wenn Sie neue Kommandos über Knöpfe in der Werkzeugleiste zur Verfügung stellen möchten, können
Sie dies leicht mit der initToolbar()
Methode der App
Klasse tun. Hier
entscheiden wir uns, je einen Knopf für die beiden Menüeinträge im Stiftmenü hinzuzufügen, aber
diese Knöpfe brauchen Icons. Sie können sie entweder im KDE Verzeichnis "toolbar" finden, oder,
wenn keins der Icons zu der Aktion paßt, sie selbst erzeugen. KIconEdit ist gut dafür geeignet
Icons zu erstellen, also werden wir sie zuerst erzeugen. Wählen Sie "Neu" aus dem KDevelop
Dateimenü, gehen Sie auf den Linux/KDE Tabulator und wählen Sie dann "Icon" als Dateityp. Das
erste Icon nennen wir "pencolor.xpm". Nun müssen wir sagen, wo das Icon erzeugt werden soll.
Drücken Sie den Verzeichnisauswahlknopf und wechseln Sie in das Verzeichnis mit Ihren
KScribble Quelldateien. Dort erzeugen Sie ein neues Verzeichnis "toolbar". Wechseln Sie in
dieses Verzeichnis und drücken Sie "OK". Das neue Icon wird erzeugt und automatisch mit
KIconEdit innerhalb von KDevelop geöffnet. Zeichnen Sie etwas, daß dem Benutzer den Sinn des
Knopfes zeigt, speichern Sie das Pixmap und wählen Sie dann den LFV / RFV in KDevelop. Wählen
Sie das Icon mit der rechten Maustaste aus und gehen Sie im Popupmenü auf "Eigenschaften". Sie
sehen, daß das Icon der Distribution hinzugefügt wurde, damit es aber später von Ihrem Programm
gefunden werden kann, müssen Sie das Installationsziel ebenfalls angeben. Markieren Sie die "installieren" Option und geben
Sie dann darunter ein:
$(kde_datadir)/kscribble/toolbar/pencolor.xpm
Damit wird das Pixmap im data Verzeichnis der KDE Dateisystemhierarchie installiert, in der jede Anwendung ihr Unterverzeichnis mit zusätzlich benötigten Dateien hat. Icons müssen in einem Unterverzeichnis namens toolbar abgelegt werden, damit der icon loader die Pixmaps für Ihr Programm finden kann.
Wenn Sie damit fertig sind, wiederholen Sie die gleichen Schritte für das Icon zum Setzen der Stiftweite. Nennen Sie dieses Icon "penwidth.xpm".
Nun müssen wir nur noch Knöpfe auf der Werkzeugleiste erzeugen; fügen Sie dazu die markierten Zeilen Ihrem Code hinzu:
void KScribbleApp::initToolBar() { .. toolBar()->insertButton(BarIcon("editcopy"), ID_EDIT_COPY, true, i18n("Copy")); toolBar()->insertButton(BarIcon("editpaste"), ID_EDIT_PASTE, true, i18n("Paste")); toolBar()->insertSeparator(); -> toolBar()->insertButton(BarIcon("pencolor"), ID_PEN_COLOR, true, i18n("Color") ); -> toolBar()->insertButton(BarIcon("penwidth"), ID_PEN_BRUSH, true, i18n("Width") ); -> toolBar()->insertSeparator(); toolBar()->insertButton(BarIcon("help"), ID_HELP_CONTENTS, SIGNAL(clicked()), .. }
Wir verwenden an dieser Stelle die Methoden von KToolBar um die Knöpfe einzufügen. Das erste
Argument, BarIcon()
, veranlaßt die Methode, das Icon für den Knopf zu laden. Was etwas
ungewöhnlich erscheint ist, daß wir uns nicht um die Dateierweiterung zu kümmern brauchen. Das
bevorzugte Format unter KDE 2 ist *.PNG, aber es funktioniert auch mit xpm's ( Sie könnten
ImageMagick für die Konvertierung der Icons nach PNG verwenden, oder, zu einem späteren
Zeitpunkt,KScribble !).
Das zweite Argument ist wiederum die ID. Die Kommandos werden dann automatisch aktiviert, da
toolBar()
bereits mit denselben Methoden verbunden ist wie die Menübar, über das Signal
activated()
. Das dritte Argument steht für "erreichbar" wenn wahr, "deaktiviert" wenn
falsch; da die Knöpfe verfügbar sein sollen, setzen wir es auf wahr. Schließlich setzen wir
noch eine Kurzinfo für die Knöpfe, die wir wieder in i18n()
einschließen, um die
Internationalisierung zu ermöglichen.
Nun sind Sie für's erste fertig. Die GUI ist, zumindest visuell, erweitert. Sie können KScribble wieder kompilieren und ausführen und sich anschauen, wie es aussieht- natürlich haben die neuen Elemente in der Menü- und in der Werkzeugleiste noch keine Funktion- dies wird sich aber im nächsten Abschnitt ändern. Sie werden vielleicht auch bemerken, daß die Icons, die wir hinzugefügt haben, nicht angezeigt werden. Dies liegt daran, daß wir KScribble nicht installiert haben, und so werden sie nicht gefunden. Alle anderen Icons werden bereits mit den KDE Bibliotheken geliefert, deshalb sind sie schon sichtbar.
Da wir schon die Menüleisten- und Werkzeugleistenkommandos erstellt haben, müssen wir jetzt unseren
ersten Dialog für das Setzen der Stiftweite erzeugen. Dazu wählen Sie "Neu" aus dem Dateimenü und
dann "Qt/KDE Dialog". Geben Sie als Dialognamen kpenbrushdlg
ein, die Erweiterung wird
automatisch angehängt. Wählen Sie "OK" und der Dialogeditor öffnet ein leeres Widget, das den
Hintergrund für unseren Dialog bilden wird. Wenn wir einen Dialog entwerfen, müssen wir uns
überlegen, was der Benutzer wirklich braucht; in unserem Fall brauchen wir ein Label, das
beschreibt, was geändert wird, eine Spinbox mit auf und ab Knöpfen zum Setzen der Stiftweite und
drei weitere Knöpfe: einen zum Zurücksetzen der Weite, einen zum Abbrechen des Dialogs und einen zum
Übernehmen des neuen Wertes - den "OK" Knopf. In dieser Reihenfolge werden wir die Elemente auch
hinzufügen, was wichtig ist, weil der Fokus der Tabulatortaste der Reihenfolge folgt, in der die
Widgets erstellt werden. D.h., wenn Sie mit dem "OK" Knopf anfangen, bringt uns das Drücken der
Tabtaste zuerst zur Spinbox und dann zum "Cancel" Knopf - was so nicht erwartet wird. Der Tabfokus
sollte den Elementen von oben nach unten und von links nach rechts folgen, also müssen wir den
Dialog auch in dieser Reihenfolge erstellen. Um Elemente zum Dialog hinzuzufügen, wählen Sie den
Widgets Tabulator im linken Panel. Dort werden alle verfügbaren Widgets durch Icons repräsentiert,
damit Sie Ihren Dialog erstellen können. Ein Druck auf ein Widget Icon erzeugt das neue Element in
der linken, oberen Ecke des Hauptwidgets. Von dort können Sie es mit der Maus zu der Position
ziehen, an der Sie das Element haben wollen. Desweiteren können Sie die Einstellungen für das
gewählte Widget im "Widgeteigenschaften" Panel auf der rechten Bilschirmseite vornehmen.
Das Label: Wählen Sie QLabel
aus dem Widgettabulator und plazieren Sie es an
Position x:50, y:20. Dann gehen Sie zum "General" Abschnitt in den Widgeteigenschaften. Ändern Sie den Text der Eigenschaft "Text"
von "Label:" nach "Pen Width:". Passen Sie die Breite in x-Richtung dem Text des Labels an, ein Wert von 120 sollte reichen.
Sie können dies entweder mit der Maus machen, oder den Wert im "Geometry" Abschnitt eingeben.
Die Spinbox: Wählen Sie QSpinbox
und plazieren Sie sie rechts neben dem eben
erstellten Label. Setzen Sie den Variablennamen im Abschnitt "C++ Code" auf "width_spbox". Die
Minimal- und Maximalwerte sind 1 und 100, was für unsere Stiftweite ausreichen sollte.
Die Knöpfe: Schließlich brauchen wir noch unsere drei Knöpfe. Der linke Knopf soll der
Standardknopf sein. Erzeugen Sie einen QPushbutton
und plazieren Sie ihn an einer günstigen
Stelle unten im Dialog, setzen Sie den Variablennamen auf "default_btn" und den Text auf "Default".
Machen Sie das gleiche mit dem "OK" Knopf, Variablenname "ok_btn" und dem "Cancel" Knopf,
Variablenname "cancel_btn". Setzen Sie die Knopftexte auf "&OK" und "&Cancel".
Wenn Sie mit Ihrem Layout zufrieden sind, wählen Sie "Alle Quellen erzeugen" aus dem "Erstellen"
Menü, geben Sie als Klassennamen "KPenBrushDlg" ein und wählen Sie QDialog
. Nachdem Sie
"OK" gedrückt haben, werden die Quellen für Ihren Dialog erzeugt und dem Projekt hinzugefügt. Sie
können nun wieder zur Editoransicht in KDevelop zurückkehren und wir können den Code hinzufügen,
der benötigt wird, damit der Dialog auch etwas macht.
Nachdem wie nun die GUI des Dialogs erstellt haben, müssen wir den Köpfen noch die Funktionalität geben, und Mittel zur Verfügung stellen, die Werte der Spinbox zu holen und zu setzen, weil wir wollen, daß der Dialog den aktuellen Wert anzeigt, wenn er aufgerufen wird, und wir möchten den eingestellten Wert auslesen, wenn der "OK" Knopf gedrückt wird.
In der erzeugten Klasse KPenBrushDlg
können Sie neben dem Konstruktor und dem Destruktor
noch die Methode initDialog()
finden. Diese Methode implementiert die gesamte GUI
Konstruktion, deshalb müssen wir uns darum nicht mehr kümmern, sondern können direkt die üblichen
Verbindungen für unsere Knöpfe erstellen. Fügen Sie die markierten Zeilen in den Konstruktor des
Dialogs ein:
KPenBrushDlg::KPenBrushDlg(int curr, QWidget *parent, const char *name) : QDialog(parent,name,true){ initDialog(); -> connect(default_btn, SIGNAL(clicked()), this, SLOT(slotDefault())); -> connect(ok_btn, SIGNAL(clicked()), this, SLOT(accept())); -> connect(cancel_btn, SIGNAL(clicked()), this, SLOT(reject())); }
Dies implementiert die Funktionalität für die Knöpfe, für den Fall, daß sie gedrückt werden. Als
erstes stellen wir den Standardknopf so ein, daß er den Slot slotDefault()
ausführt. Dieser
Slot, in dem wir den Standardwert für die Spinbox direkt setzen, muß noch implementiert werden.
Der zweite connect()
Aufruf verbindet den "OK" Knopf mit dem Slot accept()
, der
von QDialog
bereitgestellt wird, genauso wie der reject()
Slot, den wir mit dem
"Cancel" Knopf verbinden. "Cancel" wird sowohl den Dialog schließen, als auch den resultierenden
Wert setzen, den wir später, wenn wir die Methode implementieren, die den Dialog aufruft, dazu
verwenden werden, herauszufinden ob der neu eingestellte Wert verwendet oder die ganze Aktion
abgebrochen werden soll.
Jetzt werden wir die beiden Methoden zum Setzen und holen der Spinbox-Werte hinzufügen:
void setCurrent(int curr){ width_spbox->setValue(curr); } int width() { return width_spbox->value(); };
Fügen Sie diese Methoden in der Klassendeklaration mit dem Modifizierer "public" ein, da wir die
Werte setzen und holen wollen, wenn der Dialog angezeigt wird. Die Methode setCurrent()
wird zum Setzen des
aktuellen Stiftwertes verwendet, die width()
Methode liefert den Wert zurück, den die
Spinbox hat, wenn der Benutzer "OK" drückt.
Schließlich müssen wir noch die Methode slotDefault()
implementieren:
//kpenbrushdlg.h: //method declaration: public slots: void slotDefault(); //kpenbrushdlg.cpp: //method implementation: void KPenBrushDlg::slotDefault() { width_spbox->setValue(3); }
Dies setzt den Standardwert für die Stiftbreite auf 3 Pixel.
Wir sind nun mit unserem ersten Dialog fertig und können uns den anderen Klassen der Anwendung zuwenden, um noch einiges zu adaptieren und Methoden zu implementieren, die den Dialog aufrufen.
Wie Sie sich vielleicht schon denken können, bedeutet das Aufrufen der Dialoge nicht nur,
daß wir den Dialog zur Auswahl der Stiftgröße implementieren werden, sondern auch, daß wir
eine Methode zur Auswahl der Stiftfarbe hinzufügen. Doch eins nach dem anderen. Als erstes
erzeugen Sie eine Methode slotPenBrush()
in der Klasse KScribbleApp
:
void KScribbleApp::slotPenBrush() { slotStatusMsg(i18n("Setting brush width...")); // get one window with document for a current pen width QWidgetList windows = pWorkspace->windowList(); KScribbleView* m = (KScribbleView*)windows.at(0); KScribbleDoc* pDoc = m->getDocument(); int curr_width=pDoc->penWidth(); // create the dialog, get the new width and set the pen width for all documents KPenBrushDlg* dlg= new KPenBrushDlg(this); dlg->setCurrent(curr_width); if(dlg->exec()){ int width=dlg->width(); for ( int i = 0; i < int(windows.count()); ++i ) { m = (KScribbleView*)windows.at(i); if ( m ) { pDoc = m->getDocument(); pDoc->setPenWidth(width); } } } slotStatusMsg(i18n("Ready.")); }
Hier müssen wir zuerst auf die Fensterliste zugreifen und den Zeiger auf ein Dokument holen - das ein Dokument eines beliebigen Fensters sein kann, da alle Dokumente die gleiche aktuelle Stiftweite haben sollten. Dann legen wir eine Integervariable curr_width an, welche die aktuelle Stiftweite speichert.
Jetzt können wir den Dialog aufrufen, indem wir eine dlg Instanz von KPenBrushDlg
erzeugen. Dann setzen wir die aktuelle Stiftweite durch einen Aufruf der Methode
dlg->setCurrent()
, die wir ja dem Dialog hinzugefügt haben. Durch Aufruf von
dlg->exec()
zeigen wir den Dialog an. Die if()
Anweisung stellt sicher,
daß der folgende Code nur ausgeführt wird, wenn das Accept Flag gesetzt ist - das
bedeutet, der Code wird ausgeführt, wenn der Benutzer den "OK" Knopf gedrückt hat.
Davon ausgehend, daß der Benutzer den Wert geändert und "OK" gedrückt hat, müssen wir in
allen Dokumenten die neue Stiftweite einstellen. Dazu verwenden wir die for()
Schleife und setzen in jedem Dokument die entsprechende Variable, die wir zuvor mit
dlg->width()
geholt haben.
Wir haben die Methode setPenWidth()
noch nicht in der Dokumentklasse
implementiert, also werden wir das jetzt nachholen:
kscribbledoc.h: public: void setPenWidth( int w ){ pen.setWidth( w ); }
Was noch fehlt, damit überhaupt eine Aktion ausgeführt wird, ist die Methoden
hinzuzufügen, die aufgerufen werden sollen, wenn die Menüpunkte aktiviert oder die Knöpfe
in der Werkzeugleiste gedrückt werden. Dazu müssen wir die ID's im Slot
commandCallback()
eintragen, der die entsprechenden Methoden auswählt und
ausführt:
void KScribbleApp::commandCallback(int id_) { switch (id_) { case ID_PEN_BRUSH: slotPenBrush(); break; case ID_PEN_COLOR: slotPenColor(); break; .... } }
Diese Erweiterung fügt auch die Methode slotPenColor()
zum Setzen der Stiftfarbe
in die Liste der auszuführenden Methoden ein. Diese Methode werden wir nun implementieren:
void KScribbleApp::slotPenColor() { slotStatusMsg(i18n("Selecting pen color...")); QColor myColor; int result = KColorDialog::getColor( myColor, this ); if ( result == KColorDialog::Accepted ) { QWidgetList windows = pWorkspace->windowList(); KScribbleDoc* pDoc; KScribbleView* m; for ( int i = 0; i < int(windows.count()); ++i ) { m = (KScribbleView*)windows.at(i); if ( m ) { pDoc = m->getDocument(); pDoc->setPenColor(myColor); } } } slotStatusMsg(i18n("Ready.")); }
Wenn wir uns den Code ansehen, erkennen wir, daß wir eine weitere neue Methode von
KScribbleDoc
verwenden, um die Farbe des Stifts zu setzen. Diese Methode müssen
wir ebenfalls implementieren:
kscribbledoc.h: /** sets the pen color */ void setPenColor( const QColor &c ){ pen.setColor( c ); }
Denken Sie daran, die Deklarationen für slotPenBrush()
und
slotPenColor()
auch einzufügen, damit Ihre Klasse KScribbleApp
sie
überhaupt kennt.
Nun sind Sie fertig. Lassen Sie uns zusammenfassen, was wir in diesem Kapitel getan haben:
Mit dieser Struktur steht Ihnen ein allgemeiner Weg zur Verfügung, Ihre Anwendung um weitere Funktionalität zu erweitern und Einstellungen zu manipulieren, die das Verhalten des Dokumentes und die Interaktion mit den Ansichten beeinflussen.
Weiter Zurück Inhaltsverzeichnis