1542.book Seite 2 Montag, 4. Januar 2010 1:02 13
Liebe Leserin, lieber Leser, wir freuen uns, dass Sie sich für ein Galileo Computing-Buch entschieden haben! Jürgen Wolf, der Autor mehrerer Standardwerke zu C, C++, zur Shell und zur LinuxUNIX-Programmierung, gibt Ihnen in diesem Buch einen tiefen Einblick in die Entwicklung grafischer Oberflächen mit der populären C++-Klassenbibliothek Qt. In seinem gewohnt klaren, verständlichen Schreibstil vermittelt er Ihnen alle notwendigen Grundlagen der Qt-Programmierung und gibt Ihnen Anleitungen und Tipps für die konkrete Umsetzung Ihrer Projekte. Wie auch seine anderen Werke zeichnet sich dieses Buch durch eine klare Strukturierung aus, sodass Sie es sehr gut als Nachschlagewerk nutzen können. Für die Arbeit mit diesem Buch müssen Sie keine Entwicklungsumgebung installieren. Für das Nachvollziehen der Beispiele genügt ein einfacher ASCII-Editor. Wenn Sie dennoch eine Entwicklungsumgebung nutzen möchten, schauen Sie ins Kapitel 13. Dort wird die Arbeit mit dem Qt Creator vorgestellt. Es lohnt sich auch, einen Blick auf die Buch-DVD zu werfen. Sie finden dort neben den Beispielen mehrere Kapitel zu weiterführenden Themen aus anderen Büchern von Jürgen Wolf. Wenn Sie nach der Lektüre Fragen, Verbesserungsvorschläge oder auch Lob äußern wollen, so können Sie sich jederzeit an mich oder Jürgen Wolf wenden. Wir freuen uns immer über eine Rückmeldung!
Ihre Judith Stevens-Lemoine Lektorat Galileo Computing
[email protected] www.galileocomputing.de Galileo Press · Rheinwerkallee 4 · 53227 Bonn
1542.book Seite 3 Montag, 4. Januar 2010 1:02 13
Auf einen Blick 1
Einstieg in Qt ......................................................................
15
2
Signale und Slots ................................................................
31
3
Basisklassen und Bibliotheken von Qt ...............................
57
4
Dialoge, Layout und Qt-Widgets .......................................
71
5
Qt-Hauptfenster .................................................................
323
6
Ein-/Ausgabe von Daten .....................................................
417
7
Ereignisverarbeitung ...........................................................
627
8
Drag & Drop und Zwischenablage ......................................
647
9
Grafik und Drucken ............................................................
665
10
XML ....................................................................................
713
11
Internationale Anwendungen .............................................
733
12
Weiteres zu Qt ...................................................................
741
13
Anwendungen mit Qt Creator erstellen .............................
771
1542.book Seite 4 Montag, 4. Januar 2010 1:02 13
Der Name Galileo Press geht auf den italienischen Mathematiker und Philosophen Galileo Galilei (1564–1642) zurück. Er gilt als Gründungsfigur der neuzeitlichen Wissenschaft und wurde berühmt als Verfechter des modernen, heliozentrischen Weltbilds. Legendär ist sein Ausspruch Eppur se muove (Und sie bewegt sich doch). Das Emblem von Galileo Press ist der Jupiter, umkreist von den vier Galileischen Monden. Galilei entdeckte die nach ihm benannten Monde 1610. Gerne stehen wir Ihnen mit Rat und Tat zur Seite:
[email protected] bei Fragen und Anmerkungen zum Inhalt des Buches
[email protected] für versandkostenfreie Bestellungen und Reklamationen
[email protected] für Rezensions- und Schulungsexemplare Lektorat Judith Stevens-Lemoine, Anne Scheibe Korrektorat Dr. Monika Oertner, Konstanz Cover Barbara Thoben, Köln Titelbild Barbara Thoben, Köln Typografie und Layout Vera Brauner Herstellung Steffi Ehrentraut Satz SatzPro, Krefeld Druck und Bindung Bercker Graphischer Betrieb, Kevelaer Dieses Buch wurde gesetzt aus der Linotype Syntax Serif (9,25/13,25 pt) in FrameMaker. Gedruckt wurde es auf chlorfrei gebleichtem Offsetpapier.
Bibliografische Information der Deutschen Nationalbibliothek Die Deutsche Nationalbibliothek verzeichnet diese Publikation in der Deutschen Nationalbibliografie; detaillierte bibliografische Daten sind im Internet über http://dnb.d-nb.de abrufbar. ISBN
978-3-8362-1542-8
© Galileo Press, Bonn 2010 2., aktualisierte und erweiterte Auflage 2010 Das vorliegende Werk ist in all seinen Teilen urheberrechtlich geschützt. Alle Rechte vorbehalten, insbesondere das Recht der Übersetzung, des Vortrags, der Reproduktion, der Vervielfältigung auf fotomechanischem oder anderen Wegen und der Speicherung in elektronischen Medien. Ungeachtet der Sorgfalt, die auf die Erstellung von Text, Abbildungen und Programmen verwendet wurde, können weder Verlag noch Autor, Herausgeber oder Übersetzer für mögliche Fehler und deren Folgen eine juristische Verantwortung oder irgendeine Haftung übernehmen. Die in diesem Werk wiedergegebenen Gebrauchsnamen, Handelsnamen, Warenbezeichnungen usw. können auch ohne besondere Kennzeichnung Marken sein und als solche den gesetzlichen Bestimmungen unterliegen.
1542.book Seite 5 Montag, 4. Januar 2010 1:02 13
Inhalt Vorwort .......................................................................................................
11
1
Einstieg in Qt ..........................................................................
15
1.1 1.2 1.3
Was ist Qt? ................................................................................. Lizenzierung ............................................................................... Qt installieren ............................................................................. 1.3.1 Linux .............................................................................. 1.3.2 Mac OS X ....................................................................... 1.3.3 MS-Windows (XP/Vista/Windows 7) .............................. »Hallo Welt« mit Qt .................................................................... 1.4.1 »Hallo Welt« mit der Kommandozeile ............................ 1.4.2 »Hallo Welt« mit Qt Creator ........................................... 1.4.3 Troubleshooting: »Bei mir werden keine Icons angezeigt« ......................................................................
15 16 16 17 18 18 19 19 24
Signale und Slots ....................................................................
31
2.1 2.2 2.3 2.4
33 40 41
1.4
2
Signale und Slots ermitteln ......................................................... Gegenseitiges Signal- und Slot-Konzept ...................................... Argumentenlisten von Signal-Slot-Verbindungen ........................ Eigene Klasse mit Signalen und Slots definieren bzw. erweitern .................................................................................... Widget mit eigenem Slot ............................................................ Widget mit eigenem Signal ......................................................... Zusammenfassung .......................................................................
42 49 51 55
Basisklassen und Bibliotheken von Qt ...................................
57
3.1 3.2 3.3 3.4
57 57 60 61 64 65 65 65 66 66
2.5 2.6 2.7
3
29
Basisklasse: QObject ................................................................... Qt-Klassenhierarchie ................................................................... Speicherverwaltung von Objekten .............................................. Programm-Bibliotheken von Qt .................................................. 3.4.1 QtCore ........................................................................... 3.4.2 QtGui ............................................................................. 3.4.3 QtNetwork ..................................................................... 3.4.4 QtOpenGL ..................................................................... 3.4.5 QtSql ............................................................................. 3.4.6 QtSvg .............................................................................
5
1542.book Seite 6 Montag, 4. Januar 2010 1:02 13
Inhalt
3.4.7 QtXml ............................................................................ 3.4.8 Qt3Support .................................................................... 3.4.9 QtScript ......................................................................... 3.4.10 QtWebKit ...................................................................... 3.4.11 Phonon .......................................................................... 3.4.12 Der Rest ......................................................................... Meta-Include-Headerdatei ..........................................................
67 67 67 67 68 68 68
Dialoge, Layout und Qt-Widgets ...........................................
71
3.5
4
4.1 4.2 4.3 4.4
4.5
4.6
6
Eigene Widget-Klassen erstellen ................................................. Widgets anordnen – das Layout .................................................. 4.2.1 Grundlegende Layout-Widgets ....................................... Erstellen von Dialogen (QDialog) ................................................ 4.3.1 Benutzerfreundlichkeit von Dialogen .............................. Vorgefertigte Dialoge .................................................................. 4.4.1 QMessageBox – Nachrichtendialoge ............................... 4.4.2 QFileDialog – Dialoge zur Dateiauswahl ......................... 4.4.3 QInputDialog – Eingabedialog ........................................ 4.4.4 QFontDialog – Schriftauswahl ........................................ 4.4.5 QColorDialog – Farbauswahl .......................................... 4.4.6 QPrintDialog – Druckerdialog ......................................... 4.4.7 Dialoge – Übersicht ........................................................ Qt-Widgets ................................................................................ 4.5.1 Buttons – Basisklasse QAbstractButton ........................... 4.5.2 Container-Widgets ......................................................... 4.5.3 Widgets zur Zustandsanzeige ......................................... 4.5.4 Widgets für die Eingabe ................................................. 4.5.5 Item-View-Subklassen verwenden (Ansichts-Klassen) ..... 4.5.6 Exkurs: Model-View-Controller (MVC) ........................... 4.5.7 Vordefinierte Modelle .................................................... Online-Hilfen .............................................................................. 4.6.1 Statuszeilentipp .............................................................. 4.6.2 Tooltips .......................................................................... 4.6.3 Direkthilfe ...................................................................... 4.6.4 Einfache Dokumentation mit QTextBrowser ................... 4.6.5 QAssistantClient – Qt Assistant weiterverwenden ...........
71 74 74 100 110 113 113 122 127 131 132 132 133 133 133 155 181 199 263 302 303 316 316 316 317 318 321
1542.book Seite 7 Montag, 4. Januar 2010 1:02 13
Inhalt
5
Qt-Hauptfenster ..................................................................... 323 5.1 5.2
5.3 5.4
6
Aufbau eines Hauptfensters ........................................................ Die Klasse QMainWindow .......................................................... 5.2.1 Flags für QMainWindow ................................................ 5.2.2 Eine Menüleiste mit der Klasse QMenu und QMenuBar ..................................................................... 5.2.3 Eine Statusleiste mit der Klasse QStatusBar .................... 5.2.4 Eine Werkzeugleiste mit der Klasse QToolBar ................. 5.2.5 Verschiebbare Widgets im Hauptfenster mit QDockWidget ................................................................ 5.2.6 Einstellungen der Anwendung speichern mit QSettings ....................................................................... 5.2.7 Anwendungen mit MDI-Fenster erstellen (Klasse QWorkspace) ..................................................... 5.2.8 Übersicht zu den Methoden der Klasse QMainWindow .............................................................. Fenster aufteilen – QSplitter ....................................................... 5.3.1 Splitter-Handle – QSplitterHandle .................................. Scrolling Area – QScrollArea .......................................................
323 324 326 327 345 352 358 363 383 396 400 404 409
Ein-/Ausgabe von Daten ......................................................... 417 6.1 6.2 6.3
6.4 6.5 6.6 6.7 6.8
6.9
Schnittstelle für alle E/A-Geräte – QIODevice ............................. Die Datei – QFile ........................................................................ 6.2.1 Temporäre Datei – QTemporaryFile ................................ Streams ...................................................................................... 6.3.1 Binäre Daten – QDataStream ......................................... 6.3.2 Text Daten – QTextStream ............................................. Der Puffer – QBuffer ................................................................... Verzeichnisse – QDir ................................................................... Datei-Informationen – QFileInfo ................................................. Interprozesskommunikation – QProcess ...................................... Netzwerkkommunikation (Sockets) ............................................. 6.8.1 QAbstractSocket ............................................................ 6.8.2 Das HTTP-Protokoll – QHttp .......................................... 6.8.3 Das FTP-Protokol – QFtp ................................................ 6.8.4 Ein Proxy – QNetworkProxy ........................................... Multithreads – QThread .............................................................. 6.9.1 QMutex ......................................................................... 6.9.2 QMutexLocker ...............................................................
417 421 429 431 431 444 461 464 473 481 496 496 528 544 561 562 572 574
7
1542.book Seite 8 Montag, 4. Januar 2010 1:02 13
Inhalt
6.9.3 6.9.4 6.9.5 6.9.6
6.10
6.11
6.12
7
575 577 583 586 590 591 591 593 594 601 606 609 609 610 612 613 614 614 626
Ereignisverarbeitung ............................................................... 627 7.1 7.2 7.3 7.4
7.5 7.6
8
QReadWriteLock ............................................................ QSemaphore .................................................................. QWaitCondition ............................................................. Datenstrukturen an den Thread binden – QThreadStorage ............................................................. 6.9.7 Ausblick ......................................................................... Relationale Datenbanken – QtSql ............................................... 6.10.1 Die Treiber für QtSql ...................................................... 6.10.2 Ein Verbindung zur Datenbank herstellen – QSqlDatabase ................................................................ 6.10.3 SQL-Anweisungen ausführen – QSqlQuery ..................... 6.10.4 SQL-Anweisungen der höheren Ebene – QSqlTableModel ............................................................ 6.10.5 View-Klasse QTableView mit SQL verwenden ................ Klassen und Typen zum Speichern von Daten ............................. 6.11.1 Qt-eigene Typendefinitionen .......................................... 6.11.2 QString .......................................................................... 6.11.3 QChar ............................................................................ 6.11.4 QByteArray .................................................................... 6.11.5 QVariant ........................................................................ 6.11.6 Container und Algorithmen ............................................ Datum und Uhrzeit .....................................................................
Ereignisschleife (Event-Loop) ...................................................... Ereignishandler neu implementieren ........................................... 7.2.1 event() neu implementieren ........................................... Ereignisfilter implementieren ...................................................... Eingreifen in die Ereignisverwaltung ............................................ 7.4.1 QApplication::notify() .................................................... 7.4.2 eventFilter() – Ereignisfilter ............................................. 7.4.3 event() ........................................................................... 7.4.4 Ereignishandler .............................................................. 7.4.5 Weitergabe von Ereignissen ........................................... Ereignisverarbeitung für Threads ................................................. Ereignisverarbeitung optimieren .................................................
627 629 635 636 639 639 640 640 640 640 641 644
1542.book Seite 9 Montag, 4. Januar 2010 1:02 13
Inhalt
8
Drag & Drop und Zwischenablage .......................................... 647 8.1
8.2
9
Kodierung mit QMimeData ........................................................ 8.1.1 Drop-Seite ..................................................................... 8.1.2 Drag-Seite ...................................................................... 8.1.3 Benutzerdefinierte MIME-Typen für das Drag & Drop .... Zwischenablage – QClipboard .....................................................
648 651 656 660 661
Grafik und Drucken ................................................................. 665 9.1
9.2
9.3 9.4
9.5
Zeichnen mit Qt – QPainter ........................................................ 9.1.1 QPaintEvent ................................................................... 9.1.2 Einstellungen ................................................................. 9.1.3 Transformation des Koordinatensystems ........................ Bildbearbeitung – QImage .......................................................... 9.2.1 Speicher- und Bildformate .............................................. 9.2.2 Bild laden und speichern ................................................ 9.2.3 Bildinformationen und Bild-Transformation .................... 9.2.4 Pixel auslesen ................................................................. Drucken mit Qt – QPrinter .......................................................... OpenGL mit Qt ........................................................................... 9.4.1 Spezifikation .................................................................. 9.4.2 Anwendungsbeispiele in der Praxis von OpenGL ............ 9.4.3 Portabilität ..................................................................... 9.4.4 OpenGL mit Qt anwenden ............................................. Vektorgrafik – QSvgWidget .........................................................
665 666 670 673 683 683 684 684 685 691 701 702 702 703 703 710
10 XML ......................................................................................... 713 10.1 10.2
SAX-API von Qt verwenden ........................................................ 10.1.1 Default-Handler implementieren .................................... DOM-API von Qt verwenden ..................................................... 10.2.1 Elemente suchen ............................................................ 10.2.2 Weiteres ........................................................................
714 716 721 731 732
11 Internationale Anwendungen ................................................. 733 11.1 11.2 11.3 11.4
Voraussetzung für eine Übersetzung ........................................... 11.1.1 Fehlervermeidung und Kommentare .............................. Übersetzen mit Linguist .............................................................. Übersetzung verwenden ............................................................. char-Arrays internationalisieren ..................................................
733 734 735 738 740
9
1542.book Seite 10 Montag, 4. Januar 2010 1:02 13
Inhalt
12 Weiteres zu Qt ........................................................................ 741 12.1
Dynamische Bibliotheken erstellen ............................................. 12.1.1 Dynamische Bibliothek dynamisch nachladen ............... 12.1.2 Plugins erstellen ........................................................... 12.2 Qt Mobility (alias Qt Extended (ehemals Qtopia)) ....................... 12.3 Debugging-Ausgabe ................................................................... 12.3.1 Fehlerbehebung ........................................................... 12.4 Qt Styles ..................................................................................... 12.5 QApplication, QCoreApplication und die Kommandozeile .......... 12.6 QtWebKit-Module ..................................................................... 12.7 Das Qt-Ressourcen-System ......................................................... 12.8 Qt Phonon .................................................................................. 12.9 Animation Framework ................................................................ 12.10 Weitere Klassen im Schnelldurchlauf ........................................... 12.10.1 Multitouch- und Gestensteuerung ............................... 12.10.2 State Machine Framework ............................................ 12.10.3 Qt für Symbian S60 ......................................................
741 744 746 747 747 751 752 754 756 763 765 766 770 770 770 770
13 Anwendungen mit Qt Creator erstellen ................................. 771 13.1 13.2 13.3
13.4
13.5
Die Arbeitsoberfläche von Qt Creator ......................................... Qt-Beispiele verwenden .............................................................. Der Editor von Qt Creator ........................................................... 13.3.1 Schneller durch den Code navigieren mit dem Locator 13.3.2 Tastenkombinationen ................................................... Anwendungen mit dem Qt Designer entwerfen .......................... 13.4.1 Ein Dialogfenster erstellen ............................................ 13.4.2 Ein Hauptfenster mit dem Designer entwerfen ............. Mehrere Versionen von Qt verwenden .......................................
771 772 774 775 777 777 778 799 808
Index ........................................................................................................... 811
10
1542.book Seite 11 Montag, 4. Januar 2010 1:02 13
Vorwort
Ich freue mich, Ihnen mein nächstes Buch zur GUI-Programmierung mit Qt präsentieren zu dürfen. Besonders schön finde ich, nun über eine komplette Büchersammlung zu verfügen. Wie soll man das verstehen? Meine bisherigen Bücher (bspw. »C von A bis Z«, »C++ von A bis Z«, »Linux-UNIX-Programmierung« oder »Shell-Programmierung«) waren alle eher für Konsolen-Programme gedacht (auch wenn sie einzelne GUI-Kapitel enthielten). Natürlich sind dies alles Grundlagenbücher der Programmierung und als solche unverzichtbar. Viele Leser wollten allerdings wissen, wie sie Programme mit einer grafischen Oberfläche erstellen, ohne gleich vom System abhängig zu sein und ohne sofort horrende Lizenzgebühren zu zahlen. Das vorliegende Buch füllt eine Lücke im bestehenden Angebot.
Warum Qt? Sicherlich stellen Sie sich zunächst die Frage, warum ausgerechnet Qt bzw. warum ich mich für Qt entschieden habe? Warum nicht MFC von Microsoft? Einen Vergleich mit anderen GUI-Frameworks anzustellen, ist meist wenig sinnvoll. Ich habe mich für Qt entschieden, weil dieses Framework in der Softwareszene mittlerweile die Rolle eines Platzhirsches einnimmt. Top-Software wie u. a. Google Earth, der Opera-Browser oder Skype wurde mit Qt erstellt. Die Liste der Firmen, die Qt verwenden, ist lang und beeindruckend. Natürlich bedeutet es noch nicht das Nonplusultra, wenn Firmen wie Synopsys, Motorola, Skype, Volvo, Adobe, Google, Samsung, Walt Disney Feature Animation, NASA usw. dieses Framework verwenden – aber »es hat schon was«. Den Großteil der mit Qt erstellten Software bekommt man ohnehin nie zu Gesicht, weil es sich dabei meist um Programme handelt, die speziell für Firmen erstellt wurden. Aber auch von der technischen Seite her hat Qt eine Menge zu bieten. Das Framework ist sehr flexibel und kann auf vielen gängigen Systemen eingesetzt werden. Neben den »großen« wie MS-Windows, Linux, Unix, BSD oder Mac OS X lässt sich Qt auch auf »kleinen« Systemen wie Handys oder PDAs einsetzen. Neben dem portablen Quellcode ist natürlich auch der Reichtum an Funktionali-
11
1542.book Seite 12 Montag, 4. Januar 2010 1:02 13
Vorwort
tät ein entscheidender Grund, Qt zu verwenden (davon wollen wir Sie mit unserem Buch noch überzeugen). Bei der gewaltigen Vielfalt, die Qt bietet, wurde trotzdem beachtet, dass sich das Framework auch einfach anwenden lässt. Auch die Dokumentation ist erste Sahne. Bei der Lizenzierung ist Qt sehr fair. Solange Sie Ihre Anwendungen im Open-Source-Bereich verwenden wollen, entstehen Ihnen keinerlei Unkosten. Mehr zur Lizenzierung erfahren Sie in Abschnitt 1.2. Natürlich könnte ich Ihnen das Blaue vom Himmel erzählen, schließlich verdiene ich ja Geld mit meinem Buch. Am besten wird aber sein, Sie überzeugen sich selbst von den Stärken des Qt-Frameworks.
Voraussetzungen für Qt Außer fundierten und guten C++-Kenntnissen mit all ihren Facetten wird eigentlich nicht allzu viel vorausgesetzt, um in die Qt-Bibliothek einzusteigen. Sollten Sie Defizite in C++ aufweisen, kann ich Ihnen (Achtung, Schleichwerbung!) wärmstens mein Buch »C++ von A bis Z« (im selben Verlag erschienen) empfehlen. Was die technische Seite angeht, genügt ein Rechner mit beliebigem Betriebssystem (Linux, Unix, Windows, Mac OS X etc.). Natürlich sind für ein Selbststudium entsprechende Disziplin und Eigenmotivation nötig. Der Lernende ist selbst für seinen Fortschritt verantwortlich, niemand wird diesen Fortschritt überwachen.
Ziel und Zielgruppe des Buches Die Zielgruppe des Buches sind eindeutig Leser, die sich die Grundlagen der C++Programmierung angeeignet haben und endlich »echte« professionelle Programme mit einer grafischen Oberfläche erstellen wollen. Es soll dem Leser helfen, sich die Grundlagen der GUI-Programmierung mit Qt zu erarbeiten. Weil das Qt-Framework noch viel mehr bietet als das Erstellen von Anwendungen mit einer grafischen Oberfläche gehen wir natürlich auch darauf ein. Das Buch wird so manchem recht umfangreich erscheinen, doch soll es kein Ersatz für die wirklich tolle Dokumentation von Qt sein (siehe Buch-DVD). Daher sollte man parallel immer die Dokumentation von Qt verwenden, die natürlich auf vieles wesentlich detaillierter eingeht, als es mit einem Buch wie dem vorliegenden überhaupt möglich ist.
12
1542.book Seite 13 Montag, 4. Januar 2010 1:02 13
Vorwort
Schnellübersicht zum Buch Die ersten fünf Kapitel behandeln die Grundlagen der Programmierung mit Qt. Sollten Sie über keinerlei Grundkenntnisse in Qt verfügen, empfehle ich Ihnen, diese fünf Kapitel der Reihe nach durchzuarbeiten. Nach einer allgemeinen Übersicht zu Qt (Kapitel 1) wird in Kapitel 2 das Signal- und Slot-Konzept von Qt behandelt, welches an Stelle der Callback-Funktionen aus anderen Frameworks verwendet wird. Kapitel 3 bietet eine Zusammenstellung der Bibliotheken und Klassen-Hierarchien von Qt. Kapitel 4 behandelt erst die Dialoge, dann folgt ein umfangreicher Überblick der Widgets von Qt mit vielen Beispielen. Die Erstellung eines Hauptfensters mit allen dazugehörigen Facetten wird in Kapitel 5 beschrieben. Kapitel 6 steht ganz im Zeichen der Daten: wie man Daten mit den Qt-StreamKlassen verwendet (Speichern, Eingabe, Ausgabe), binär ebenso wie ASCII. Neben der Speicherung von Daten in Dateien oder dem Verwenden von Verzeichnissen wird auch auf die Interprozesskommunikationen (synchron, asynchron) eingegangen. Auch die Netzwerkkommunikation (Sockets (TCP, UDP), HTTP, FTP) beschreiben wir dabei ausführlich. Des Weiteren werden auch Themen wie Multithreading oder die Verwendung des SQL-Moduls behandelt. Kapitel 7 beschreibt die Ereignisverarbeitung und stellt auch die Grundlage der beiden nächsten Kapitel dar – Drag & Drop und Zwischenablage (Kapitel 8), Grafikprogrammierung und Drucken (Kapitel 9). In Kapitel 10 gehen wir auf das XML-Modul von Qt ein (Qt unterstützt hier sowohl die SAX-API als auch die DOM-API). Was Sie bei internationalen Anwendungen beachten müssen, erklärt Kapitel 11. Kapitel 12 geht auf einzelne Features ein, die es wert sind, erwähnt zu werden. Hierzu gehören u. a. das Erstellen dynamischer Bibliotheken oder die Verwendung des Designers von Qt. Kapitel 13 behandelt dann die Entwicklungsumgebung Qt Creator und wie Sie sinnvoll damit arbeiten können.
Danksagung Ein Buch nebenbei in der Freizeit zu schreiben, ist nicht immer einfach – was besonders die Mitmenschen in meiner Umgebung zu spüren bekamen. Ein besonderer Dank gilt natürlich meiner Familie, die meine grummelige Laune wieder einmal überstanden hat. Ein Dankeschön geht wieder an Martin Conrad, der mich trotz eines Projekts nicht unter Druck gesetzt hat und geduldig mit mir war.
13
1542.book Seite 14 Montag, 4. Januar 2010 1:02 13
Vorwort
Ebenfalls ein großer Dank an Judith (Stevens-Lemoine), meine Lektorin, die mir dieses Buch-Projekt, das mir am Herzen lag, ermöglichte – mein fünftes Buch mit Judith. Alles klappte wie immer. Reibungslos. Viel Spaß beim Lesen. Zudem möchte ich mich recht herzlich bei Carsten Lehbrink von der Firma Trolltech bedanken, der mir alle meine Fragen beantwortet hat.
Jürgen Wolf
P.S.: Sollten Sie beim Einstieg mit den ersten Listings Probleme bekommen, so können Sie mich gerne per E-Mail kontaktieren (
[email protected]).
14
1542.book Seite 15 Montag, 4. Januar 2010 1:02 13
Anfang = der wichtigste Teil der Arbeit. – Platon (Wir könnten auch sagen: der Entschluss. Was Platon damit nicht sagen wollte, war: Der Anfang ist schon die halbe Miete. Das glaube ich keineswegs. Es gibt viele Leute, die packen vieles an und bringen nichts zu Ende. Mein Credo ist: Eins nach dem anderen. Zügig, entschlossen, zielorientiert. Es gibt viel zu tun – packen wir es an!)
1
Einstieg in Qt
1.1
Was ist Qt?
Ich denke, wer dieses Buch erworben hat, wird wissen, worauf er sich einlässt. Allerdings gibt es ja schließlich auch noch den Leser, der solche Kulturgüter in einem stationären Sortimentsbuchhandel erwirbt und sich zuvor die ersten Seiten durchliest. Ok, genug davon. Qt ist eine immer beliebter werdende Klassenbibliothek, die zur plattformunabhängigen Programmierung grafischer Benutzeroberflächen (kurz und englisch auch: GUI = Graphical User Interface) unter C++ verwendet wird. Verantwortlich für Qt ist die norwegische Firma Trolltech (ehemals Quasar Technologies). Die Qt-Bibliothek ist mittlerweile für die verschiedensten Betriebssysteme und Grafikplattformen wie X11 (Linux-/Unix-Derivate), Mac OS X, Windows oder auch als PDA-Version erhältlich. Anfang 2008 wurde das Unternehmen Trolltech von Nokia aufgekauft. Seitdem wird die Entwicklung unter dem Namen Qt Development Frameworks fortgeführt. Qt ist allerdings nicht nur eine Bibliothek, die zur Entwicklung von grafischen Benutzeroberflächen verwendet werden kann. Bei dieser Bibliothek handelt es sich vielmehr um ein mächtiges Framework, welches Dinge wie XML, Datenbanken, Internationalisierung, Netzwerke, Datei-Ein-/Ausgabe, Interprozesskommunikation, Multithreading und noch einiges mehr anbietet. Somit kann man sagen, dass sich mit Qt eigentlich alles machen lässt, und man keine weiteren Bibliotheken zusätzlich benötigt. Ein Rundum-sorglos-Paket eben.
15
1542.book Seite 16 Montag, 4. Januar 2010 1:02 13
1
Einstieg in Qt
Zwar verwendet Qt eine Erweiterung der Programmiersprache C++, aber es gibt auch Implementierungen für C, C#, Java, Perl, Python und Ruby. Allerdings werden diese Erweiterungen nicht von Nokia gepflegt. Qt oder QT Qt (ursprünglich Quasar toolkit) wird stets mit einem kleinen »t« geschrieben. Davon zu unterscheiden ist QT, was für Apples Multimediasoftware QuickTime steht. Allerdings wird Qt heute nicht mehr als Kürzel für Quasar toolkit verstanden, sondern will offiziell wie das englische Worte cute ausgesprochen werden.
1.2
Lizenzierung
Es ist doch immer wieder überraschend, wie schnell sich die Uhr in der IT-Branche dreht. Während ich in der ersten Auflage an dieser Stelle noch geschrieben hatte, dass Qt unter einer dualen Lizenzierung (der GPL und der QPL) stehe, so trifft dies heute schon nicht mehr zu. Mit einer neuen Lizenzierung wurde Qt ab März 2009 unter die dritte Version der GPL (genauer LGPL) gestellt. Durch diese Lizenzierung können Sie mit Qt endlich auch ohne eine kostenpflichtige Lizenz proprietäre Software entwickeln, ohne dass Sie den Quellcode veröffentlichen müssen. Lediglich für Änderungen an Qt selbst und für den technischen Support brauchen Sie noch eine kostenpflichtige Lizenz. Sicher ist sicher Wie bereits erwähnt, wandelt sich die IT-Welt relativ schnell. Daher empfehle ich Ihnen dringend, sich mit der Lizenzierung von Qt zu befassen (unter http:// qt.nokia.com/), bevor Sie sich an die Aufgabe machen, eine eigene proprietäre Software zu entwickeln.
1.3
Qt installieren
Um die Beispiele im Buch auch tatsächlich verwenden zu können, müssen Sie Qt auf Ihrem Rechner installieren. Zunächst sollten Sie sich entscheiden, ob Sie die kommerzielle Version oder die Open-Source-Version von Qt installieren wollen. Für das Nachvollziehen der Beispiele im Buch spielt dies keine Rolle. Meine Testumgebungen für das Buch Die Listings im Buch wurden erfolgreich auf Windows XP, Windows Vista, Windows 7, Linux (Ubuntu 9.10) und Mac OS X (10.6) getestet.
16
1542.book Seite 17 Montag, 4. Januar 2010 1:02 13
Qt installieren
Zum Zeitpunkt der Drucklegung dieses Buches war die Qt-Version 4.6 aktuell. Wer die Entwicklung von Qt beobachtet, wird feststellen, dass hier recht viel Bewegung drin ist. Eine Version 5.x ist jedoch im Moment noch nirgendwo angedacht. Alle Versionen sollen allerdings abwärtskompatibel sein, so dass es immer möglich ist, bereits erstellten Code in zukünftigen Programmversionen zu verwenden. Wer den Schritt von Version 3.x zu 4.x mitbekommen hat, wird festgestellt haben, dass die Kompatibilitätszusage dabei gebrochen wurde, weil sich dies aus architektonischen Gründen nicht vermeiden ließ. Allerdings wird ein solcher Kompatibilitätsbruch nur dann in Kauf genommen, wenn überhaupt keine andere Lösung mehr offensteht. Die aktuellste Version von Qt beziehen Sie am besten von der Webseite selbst (http://qt.nokia.com/). Hier finden Sie auch weitere Produkte wie z. B. Qt für Symbian S60, mit dem Sie Anwendungen für Symbian-Systeme (Mobiltelefone etc.) erstellen können. SDK auf der Buch-DVD Auf der Buch-DVD haben wir Ihnen die zum Zeitpunkt der Drucklegung des Buches aktuellste Open-Source-Version des Qt-SDKs zur bequemen Installation gleich mitgeliefert. Neben der Bibliothek umfasst das Qt-SDK auch eine Menge unverzichtbarerer Demos (Qt-Demos), Qt Assistant, das als Referenz und Dokumentation unverzichtbar ist, wenn Sie sich mit Qt befassen wollen, eine komplette Entwicklungsumgebung namens Qt Creator mit Designer (RAD-Tool) und Linguist (für die Lokalisierung). Es gibt das Qt Framework auch separat, also beschränkt auf die benötigten Bibliotheken zum Download, für den Fall, dass Sie z. B. Microsoft Visual Studio oder Eclipse zur Entwicklung Ihrer Qt-Anwendungen verwenden wollen. Wie Sie Qt in andere Entwicklungsumgebungen einbinden können, wird aber in diesem Buch nicht beschrieben. Hier wird lediglich gezeigt, wie Sie mit dem hauseigenen Qt SDK Anwendungen entwickeln können. Alles andere würde den Rahmen des Buches sprengen. Auf der Webseite http://qt.nokia.com/downloads finden Sie übrigens auch ein Visual-Studio-Add-in und eine Eclipse-Integration zum Download.
1.3.1
Linux
In der Praxis empfiehlt es sich, bei Linux immer die vorkompilierten Pakete der jeweiligen Distributionen zu verwenden, welche häufig mitgeliefert und nachträglich über den entsprechenden Paketmanager nachinstalliert werden können. Sinnvoll ist, die neuesten Pakete der entsprechenden Distribution online zu beziehen.
17
1.3
1542.book Seite 18 Montag, 4. Januar 2010 1:02 13
1
Einstieg in Qt
Um eine komplette Qt-Umgebung mithilfe eines Paketmanagers zu erstellen, benötigen Sie auf jeden Fall die Pakete libqt4-core, libqt4-dev und qt-dev-tools. Mit dieser Qt-Umgebung könnten Sie bereits Anwendungen in der Kommandozeile übersetzen. Anschließend können Sie die Entwicklungsumgebung Qt Creator, genauer das Paket qt-creator, aus den Quellen nachinstallieren. Alternativ können Sie auch das SDK (eine BIN-Datei) von der Buch-DVD verwenden oder aus dem Web herunterladen. Beachten hierbei, dass Sie die Installation noch ausführbar (chmod u+x) machen müssen. Der Vorteil des fertigen SDKs ist ein typischer Installer mit einer grafischen Oberfläche, wie man sie beispielsweise von Windows-Betriebssystemen her kennt.
1.3.2
Mac OS X
Die Installation des SDKs beim Mac gestaltet sich ebenfalls recht einfach. Auch hier genügt ein einfaches Anklicken des DMG-Images von der Buch-DVD, oder Sie laden sich die neueste Version aus dem Web herunter. Anschließend brauchen Sie nur noch den Anweisungen auf dem Bildschirm zu folgen. Gewöhnlich wird das komplette SDK beim Mac ins Wurzelverzeichnis Developer/Applications/Qt installiert. Xcode Tools für Mac OS X Zusätzlich müssen unter Mac OS X noch die Xcode Tools von Apple installiert sein. Diese liegen gewöhnlich Ihrer Apple-Installations-DVD bei oder können bei Apple (http://developer.apple.com/TOOLS/Xcode/) kostenlos heruntergeladen werden.
1.3.3
MS-Windows (XP/Vista/Windows 7)
Für Windows finden Sie wie gewöhnlich einen kompletten Installer vor, der Ihnen alles automatisch installiert. Glücklicherweise muss man sich bei der Installation des SDKs nicht mehr um den Compiler und dessen systemweite Konfiguration kümmern. Qt verwendet beim SDK den MinGW-Compiler unter Windows. Am Ende der Installation sollten Sie im Start-Menü einen neuen Ordner Namens Qt SDK by Nokia v2010.01 (Open Source) vorfinden, in dem sich die Kommandozeile zum Übersetzen der Anwendungen, die Entwicklungsumgebung Qt Creator, die Demos (Beispiele), Qt Assistant (Dokumentation, Referenz) und Qt Linguist befinden.
18
1542.book Seite 19 Montag, 4. Januar 2010 1:02 13
»Hallo Welt« mit Qt
1.4
»Hallo Welt« mit Qt
Ich werde Ihnen zwei Möglichkeiten zeigen, wie Sie Ihre Software mit Qt erstellen können: einerseits mittels der Kommandozeile und andererseits mittels der Entwicklungsumgebung Qt Creator. Bei den Beispielen, die im Buch vorkommen, reicht für das Bauen der Anwendungen die Kommandozeile im Grunde völlig aus. In der Praxis werden Sie allerdings mit einer Entwicklungsumgebung wie Qt Creator erheblich effektiver, schneller und vor allem mit weniger Fehlern ans Ziel kommen. RAD-Tool versus Kommandozeile Das Buch wurde so konzipiert, dass Sie die Beispiele unabhängig von einer Entwicklungsumgebung erstellen können. Zwar umfasst das SDK auch eine hammermäßige Entwicklungsumgebung mit integriertem RAD-Tool, aber häufig wird der Einsteiger durch die Vielzahl der Funktionen förmlich erschlagen. Da Entwicklungsumgebungen mit RAD-Tools aber der Garant für schnelles und zuverlässiges Entwickeln sind, widmet sich Kapitel 13 ausschließlich Qt Creator. Gerne können Sie nach dem ersten Kapitel in jenes Kapitel springen und erst danach mit dem zweiten Kapitel fortfahren.
1.4.1
»Hallo Welt« mit der Kommandozeile
Sie benötigen keine Entwicklungsumgebung oder sonstige Software, um Programme mit dem Qt Framework zu erstellen. Es genügt, dass Sie Qt –genauer gesagt die Bibliothek – richtig installiert haben. Ein einfacher ASCII-Editor reicht aus, um den Quelltext für die Programme zu erstellen. Besser wäre natürlich ein Editor, der die Syntax von C++ hervorhebt. Da das kein Einsteiger-Buch für C++Programmierer ist, gehe ich davon aus, das Sie wissen, was man verwendet um einen Quelltext zu erstellen und diesen abzuspeichern. Hier folgt zunächst der Quellcode zum Hallo-Welt-Programm mit Qt. Im Anschluss wird er näher erläutert. 01 // beispiele/hallo/main.cpp 02 #include
03 #include 04 int main(int argc, char *argv[]) { 05 QApplication app(argc, argv); 06 QPushButton hello("Hallo Welt"); 07 hello.resize(100, 30); 08 hello.show(); 09 return app.exec(); 10 }
19
1.4
1542.book Seite 20 Montag, 4. Januar 2010 1:02 13
1
Einstieg in Qt
Der Kommentar in Zeile 1 zeigt an, wo Sie den Quelltext auf der Buch-DVD wiederfinden. Für dieses Beispiel heißt das: Ausgehend vom Wurzelverzeichnis des CD- bzw. DVD-Laufwerks (Bezeichnung ist abhängig vom System) finden Sie diesen Quellcode im Verzeichnis beispiele/hallo unter dem Dateinamen main.cpp. Sie finden diese erste Zeile bei jedem Beispiel des Buches, sodass Sie es schnell auf der Buch-DVD finden. In Zeile 2 und 3 fügen wir die entsprechenden Headerdateien für die im Beispiel verwendeten Qt-Klassen ein. Im Beispiel benutzen wir die Klassen QApplication und QPushButton. Dem neuen C++-Standard gemäß verzichtet auch Qt auf die Dateinamenserweiterung .h bei den Headerdateien. Genau genommen enthalten die verwendeten Headerdateien im Code nur eine Include-Zeile zur passenden .h-Datei. So beinhaltet die Headerdatei QApplication lediglich folgende Zeile: // Headerdatei: QApplication #include "qapplication.h"
In Zeile 4 finden Sie die altbekannte Hauptfunktion main() mit ihren zwei Argumenten, den Kommandozeilen-Argumenten. argc entspricht hierbei der Anzahl der Argumente und argv ist ein String-Array mit Argumenten der Kommandozeile. Dies sind beides Eigenschaften von Standard-C++, weshalb hier nicht näher darauf eingegangen werden soll. Die beiden Argumente benötigen wir in Zeile 5, wo ein QApplication-Objekt angelegt wird. Das QApplication-Objekt wird für jede Qt-Anwendung benötigt, die eine grafische Oberfläche verwendet. In Zeile 6 wird ein QPushButton-Objekt mit dem Text »Hallo Welt« darauf erzeugt. Aus dem Namen der Klasse lässt sich schon herauslesen, dass es sich hierbei um einen Button (zu deutsch: Schaltfläche) handelt. In Zeile 7 legen wir die Größe der Schaltfläche mit der Methode resize() fest. Und damit die Schaltfläche auch angezeigt wird, muss in der Zeile 8 die Methode show() aufgerufen werden. Der eigentliche Start der Anwendung geschieht in Zeile 9 mit dem Aufruf der Methode exec(). Mit diesem Aufruf starten Sie eine Ereignisschleife (englisch: Event-Loop). Diese Schleife wartet auf Ereignisse der Anwendung (z. B. Mausoder Tastaturereignisse) und leitet diese an die entsprechende Klasse weiter. In diesem Beispiel wurde allerdings nichts eingerichtet, um auf ein Ereignis zu reagieren. Trotzdem wird, wenn Sie die Anwendung beenden, die quit()-Methode an QApplication gesendet. Wird quit() aufgerufen, dann beendet sich die Ereignisschleife, und auch das Programm wird beendet.
20
1542.book Seite 21 Montag, 4. Januar 2010 1:02 13
»Hallo Welt« mit Qt
Ein ausführbares Qt-Programm erstellen Ich denke, das kleine Beispiel von gerade eben haut keinen von den Socken. Auch wird darin noch nicht im Detail auf die einzelnen Klassen und deren Methoden eingegangen. Dazu werden wir im Verlauf des Buches noch mehr Gelegenheit haben, als uns lieb ist. Um aus der Quelldatei ein ausführbares Programm zu machen, muss man sich vor Augen halten, dass es verschiedene Systeme gibt, bei denen die Übersetzung jeweils unterschiedlich abläuft. Ein Beispiel gefällig, wie eine solche wüste Übersetzung unter Windows aussehen könnte? C:\Qt\hallo > g++ -c -O2 -O2 -frtti -fexceptions –Wall -DUNICODE -DQT_LARGEFILE_SUPPORT -DQT_DLL -DQT_NO_DEBUG -DQT_GUI_LIB -DQT_CORE_LIB -DQT_THREAD_SUPPORT -DQT_NEEDS_QMAIN -I"C:/Qt/4.6.0/include/QtCore" -I"C:/Qt/4.6.0/include/QtCore" -I"C:/Qt/4.6.0/include/QtGui" -I"C:/Qt/4.6.0/include/QtGui" -I"C:/Qt/4.6.0/include" -I"." -I"C:/Qt/4.6.0/include/ActiveQt" -I"tmp\moc\release_shared" -I"." -I"..\mkspecs\win32-g++" –o tmp\obj\release_shared\main.o main.cpp C:\Qt\hallo > g++ -mthreads -Wl,-enable-stdcall-fixup -Wl,-enable-auto-import -Wl,-enable-runtime-pseudo-reloc -Wl,-s -Wl,-s -Wl,-subsystem,windows -o "release\hallo.exe" tmp\obj\release_shared\main.o -L"c:\Qt\4.6.0\lib" -L"c:\Qt\4.6.0\lib" -lmingw32 -lqtmain -lQtGui4 -lQtCore4
Unter den verschiedenen Linux-Systemen und Mac OS X sind die Pfade und teilweise auch die Schalter wiederum anders. Zum Glück hat sich Trolltech unserer erbarmt und uns mit qmake ein Tool zur Verfügung gestellt, welches zur plattformunabhängigen Projekterstellung dient. Mit qmake wird eine Makefile gebaut, das alle nötigen Informationen enthält, um den Quelltext für die entsprechende Plattform zu übersetzen. Umgebungsvariable PATH und Kommandozeile Unter Windows sollten Sie die Kommandozeile QT COMMAND PROMPT verwenden, weil hierbei gleich sämtliche Umgebungsvariablen gesetzt sind, die Sie ansonsten selbst in der Umgebungsvariablen PATH setzen müssten.
21
1.4
1542.book Seite 22 Montag, 4. Januar 2010 1:02 13
1
Einstieg in Qt
Wechseln Sie also zunächst in den Pfad, unter dem Sie die Quelldatei main.cpp gespeichert haben, und führen Sie qmake folgendermaßen aus: $ > cd hallo $ > qmake -project
Mit dem Schalter -project erzeugen Sie eine Projektdatei hallo.pro. Diese Projektdatei hat folgenden Inhalt: ################################## # Automatically generated by qmake ################################## TEMPLATE = app TARGET = DEPENDPATH += . INCLUDEPATH += . # Input SOURCES += main.cpp
Der Eintrag TEMPLATE gibt an, ob eine Applikation (app) oder eine Bibliothek (lib) erstellt werden soll. Zusätzlich könnte man hier auch noch ein Plugin (plugin) erstellen. Welche Dateien zum Projekt gehören, wird mit SOURCES angegeben. Die Projektdatei hallo.pro genügt, um auf dem jeweiligen System ein Makefile zu bauen. Hierzu ist ein einfacher Aufruf von qmake ausreichend: $ > qmake
Unter Windows werden hiermit drei Makefiles (Makefile, Makefile.Debug und Makefile.Release) generiert, wobei Makefile als Meta-Datei auf die beiden anderen Makefiles verweist. Auf Linux/Unix und Mac OS X hingegen wird nur die ein Makefile erzeugt. Soll nur eine Debug-Version des Makefiles gebaut werden, brauchen Sie in der Projektdatei hallo.pro lediglich folgende Zeile hinzuzufügen: CONFIG += debug_and_release
Debug-Version von Qt Um die Debug-Version von Qt wirklich verwenden zu können, muss diese auch installiert sein. Bei Linux kann es sein, dass Sie die Bibliothek nachinstallieren müssen. Ihre Kennzeichnung ist zumeist ein Paketname wie qt-debug, libqt4-debug oder libqt4-debug-dev.
22
1542.book Seite 23 Montag, 4. Januar 2010 1:02 13
»Hallo Welt« mit Qt
Mac OS X, Makefile und X-Code-Projektdatei Je nach Einstellungen kann es sein, dass beim Mac mit qmake lediglich eine native X-Code-Projektdatei (*.xcodeproj) erzeugt wird und somit das anschließende make seinen Dienst verweigert bzw. keine Arbeit findet. Zwar können Sie jetzt durch Anklicken der X-Code-Projektdatei das Beispiel mit Xcode übersetzen. Wenn Sie dies jedoch nicht wollen, müssen Sie statt eines leeren qmake noch die Option für macx-g++ setzen, damit ein echtes Makefile gebaut wird. Statt qmake müssen Sie also eintippen: qmake –spec macx-g++. Dann klappt es auch anschließend mit dem makeAufruf.
Jetzt können Sie make ohne weitere Argumente aufrufen, und die Qt-Anwendung wird übersetzt. Wollen Sie gezielt eine Debug-Version erstellen, müssen Sie make folgendermaßen einsetzen: $ > make debug
Natürlich lässt sich hierbei auch gezielt eine Release-Version erstellen: $ > make release
Jetzt finden Sie die entsprechende Version im Projekt-Verzeichnis hallo unter dem Verzeichnis debug oder/und release bereit zur Ausführung. Windows, make und MinGW Unter Windows müssen Sie eventuell statt make den Namen mingw32-make eingeben, sofern Sie den MinGW-Compiler verwenden.
Das Programm starten Das Programm kann jetzt durch das einfache Anklicken der ausführbaren Datei gestartet werden. Natürlich lässt sich das Programm auch über die Kommandozeile starten, was viele Linux-Anwender nach wie vor bevorzugen. DLL-Hölle unter Windows Sofern Sie die Qt-Beispiele unter MS-Windows aus jedem beliebigen Verzeichnis heraus mit einem Mausklick ausführen wollen, müssen natürlich auch die entsprechenden DLLs von Qt für das Programm vorhanden sein. Die DLLs finden Sie gewöhnlich im bin-Verzeichnis von Qt (z. B. C:\Qt\2009.04\qt\bin). Sollten Sie einen Fehlermeldung bekommen, wie »der Einsprungspunkt von Qt…dll konnte nicht gefunden werden«, dann kopieren Sie einfach sämtliche DLLs ins Systemverzeichnis von MS-Windows (z. B. in C:\Windows\system), falls dies bei der Installation nicht schon passiert ist.
23
1.4
1542.book Seite 24 Montag, 4. Januar 2010 1:02 13
1
Einstieg in Qt
Und so sieht das erste Beispiel bei der Ausführung aus:
Abbildung 1.1
»Hallo Welt« mit Qt unter Windows 7
Abbildung 1.2
»Hallo Welt« mit Qt unter Linux (Ubuntu 9.04)
Abbildung 1.3
»Hallo Welt« mit Qt unter Mac OS X (10.6 )
1.4.2
»Hallo Welt« mit Qt Creator
Das Hallo-Welt-Listing lässt sich natürlich noch einfacher und schneller mit Qt Creator, der Entwicklungsumgebung von Qt, erstellen. Starten Sie zuerst Qt Creator.
Abbildung 1.4
24
Die Entwicklungsumgebung Qt Creator nach dem Start
1542.book Seite 25 Montag, 4. Januar 2010 1:02 13
»Hallo Welt« mit Qt
1. Wählen Sie jetzt beim Willkommensbildschirm von Qt Creator den Reiter Develop aus. Hier finden Sie auch gleich die Schaltfläche Create New Project... , welche Sie anklicken.
Abbildung 1.5
Über den Willkommensbildschirm können Sie ein neues Projekt anlegen.
2. Im nächsten Dialog müssen Sie die Art des neuen Projektes auswählen. In unserem Fall reicht die einfachste Variante, Empty Qt4 Project, vorerst völlig aus. Weiter geht es, indem Sie auf OK klicken.
Abbildung 1.6
Art des neuen Projekts auswählen
3. Im nächsten Dialog werden Sie nach dem Namen und Speicherort des neuen Projekts gefragt. Der Name ist hierbei gleichzeitig auch der Verzeichnisname des Projekts. Weiter geht es mit der Schaltfläche Next.
25
1.4
1542.book Seite 26 Montag, 4. Januar 2010 1:02 13
1
Einstieg in Qt
Abbildung 1.7
Vergabe des Projektnamens und Speicherorts
4. Im nächsten Dialog Project management können Sie bei einem leeren Projekt nichts mehr ändern. Sie bekommen hierbei lediglich noch den Hinweis, dass eine Projektdatei (hier halloWelt.pro) zum Projekt hinzugefügt wurde. Klicken Sie auf die Schaltfläche Finish, und es wird das neue Projekt halloWelt im Editor-Modus angezeigt. Im Projektverzeichnis links oben befindet sich im Augenblick nur die Projektdatei halloWelt.pro. 5. Klicken Sie jetzt auf das Projektverzeichnis halloWelt mit der rechten Maustaste und wählen Sie im Kontextmenü Add New... aus. Alternativ können Sie hierbei natürlich auch gleich die Quelldatei über Add Existing Files... hinzufügen, wenn Sie diese bereits erstellt haben bzw. wenn diese bereits vorhanden ist (z. B. auf der Buch-DVD).
Abbildung 1.8 Über einen rechten Mausklick auf das Projektverzeichnis öffnen Sie viele neue Kommandos.
26
1542.book Seite 27 Montag, 4. Januar 2010 1:02 13
»Hallo Welt« mit Qt
6. Im sich öffnenden Dialog können Sie jetzt auswählen, was für eine Art von Datei Sie zum Projekt hinzufügen wollen. Im Beispiel reicht uns ein C++ Source File aus.
Abbildung 1.9
Art der zu erstellenden Datei auswählen
7. Jetzt können Sie den Dateinamen der Quelldatei eingeben und gegebenenfalls einen anderen Speicherort auswählen, wenn Sie mit der Standardeinstellung nicht einverstanden sind. Weiter geht es mit der Schaltfläche Next.
Abbildung 1.10
Name der Quelldatei und das Verzeichnis auswählen
27
1.4
1542.book Seite 28 Montag, 4. Januar 2010 1:02 13
1
Einstieg in Qt
8. Im letzten Dialog Project managment sollten Sie das Häkchen vor Add to project gesetzt lassen. Auch die Einstellungen der Projektdatei sollten Sie hier so lassen, wie sie sind. Mit einem Klick auf die Schaltfläche Finish schließen Sie das Hinzufügen einer neuen Datei ab.
Abbildung 1.11
Im Projektmanagement sollten Sie die Einstellungen belassen, wie sie sind.
Weitere Dateien zum Projekt hinzufügen So wie Sie in Schritt 5 bis 8 vorgegangen sind, können Sie auch verfahren, wenn Sie anschließend in den Beispielen weitere Quell- oder Headerdateien hinzufügen. In Schritt 6 wählen Sie dabei aus, ob es sich um eine Headerdatei (*.h) oder um eine Quelldatei (*.cpp) handelt.
Die vorgegebene(n) Zeile(n) der sich jetzt im Editor öffnenden Datei main.cpp können Sie entfernen und das klassische Hallo-Welt-Programm eintippen, wie Sie es in Abschnitt 1.4.1 bei der Methode via Kommandozeile kennengelernt haben. Nachdem Sie die Datei gespeichert haben (z. B. mit (Strg)+(S)), können Sie diese mit der kleinen, dreieckigen, grünen Schaltfläche bzw. (Strg)+(R) übersetzen und starten. Windows und seine DLLs – wie immer ... Natürlich gilt auch hier: Sofern Sie die Qt-Beispiele unter MS-Windows aus einem Verzeichnis heraus mit einem Mausklick ausführen wollen, müssen die entsprechenden DLLs von Qt für das Programm vorhanden sein. Sollten Sie eine Fehlermeldung bekommen, wie »der Einsprungspunkt von Qt…dll konnte nicht gefunden werden«, dann kopieren Sie doch einfach sämtliche DLLs ins Systemverzeichnis von MS-Windows (z. B. in C:\Windows\system), falls dies bei der Installation nicht schon passiert ist.
28
1542.book Seite 29 Montag, 4. Januar 2010 1:02 13
»Hallo Welt« mit Qt
Abbildung 1.12
1.4.3
Das »Hallo-Welt«-Listing nach der Übersetzung und bei der Ausführung
Troubleshooting: »Bei mir werden keine Icons angezeigt«
Betrachten Sie diesen Abschnitt als eine Art Vorschau darauf, welche Probleme Sie beim Starten von Anwendungen bekommen können. Wenn Sie das in der Überschrift dieses Abschnitts genannte Problem später ereilen sollte, blättern Sie einfach hierher zurück. Unter Linux/Unix macht es in diesem Zusammenhang einen Unterschied, ob Sie das Programm von der Kommandozeile oder mit einem Mausklick auf das Icon starten, da es sich hierbei um zweierlei Umgebungen mit unterschiedlichen Werten handelt (vor allem mit unterschiedlichen Arbeitsverzeichnissen). Der Unterschied macht sich erst bemerkbar, wenn Sie externe Dateien wie z. B. Grafiken im Programm verwenden und dynamisch zur Laufzeit nachladen wollen. Was beim Starten aus der Kommandozeile noch angezeigt wurde, wird beim Mausklick auf die Anwendung nicht mehr angezeigt, weil hier der Wert für das Arbeitsverzeichnis ein anderer ist. Ein Icon fügen Sie zu einem Button wie folgt hinzu: button->setIcon("./images/icon.png");
29
1.4
1542.book Seite 30 Montag, 4. Januar 2010 1:02 13
1
Einstieg in Qt
In diesem Beispiel wird davon ausgegangen, dass sich das Icon im aktuellen Arbeitsverzeichnis in einem Verzeichnis namens images befindet. Dies trifft zu, wenn Sie, wie bereits erwähnt, das Programm aus der Kommandozeile heraus starten. Beim Mausklick ist das aktuelle Arbeitsverzeichnis hingegen nicht dasselbe, und daher kann logischerweise das Icon nicht mehr angezeigt werden. Ein portabler und möglicher Weg, das Problem zu umgehen, ist es, das aktuelle Arbeitsverzeichnis direkt im Programm mit anzugeben und den Pfad zur Grafik hinzuzufügen. Sie ermitteln quasi zur Laufzeit des Programms den absoluten Pfad zum Arbeitsverzeichnis. So etwas wird mit der statischen Methode QCoreApplication::applicationDirPath() ermittelt. Zusammengebastelt würde ein solcher zur Laufzeit angegebener dynamischer Pfad wie folgt aussehen (machen Sie sich hier keine Gedanken über den Code): button01->setIcon( QIcon(QString("%1 %2") .arg(QCoreApplication::applicationDirPath()) .arg("/images/icon.png") ) );
Wenn Sie Grafiken (oder auch andere Dateien) nicht zur Laufzeit laden wollen, bietet der Ressourcenmechanismus von Qt eine alternative Vorgehensweise an. Dieser Mechanismus wird in Abschnitt 12.7, »Das Qt-Ressourcensystem«, beschrieben.
30
1542.book Seite 31 Montag, 4. Januar 2010 1:02 13
Wer sich schon mit der GUI-Programmierung befasst hat, wird bei dieser Überschrift an sogenannte Callback-Funktionen denken. Doch Qt geht hier einen anderen Weg und verwendet ein Signal-Slot-Konzept. Wie dieses genau funktioniert, wollen wir im folgenden Kapitel näher beschreiben.
2
Signale und Slots
Im Grunde verläuft die GUI-Programmierung immer nach demselben Prinzip: Wenn sich bspw. ein Widget verändert hat (bspw. bei Anklicken eines Buttons), will man ein anderes Widget darüber informieren. Widgets werden also im Programm mit einer Funktion verknüpft, die ausgeführt wird, sobald der Anwender dieses etwa durch einen Mausklick aktiviert hat. Drückt der Anwender z. B. auf einen »Öffnen«-Button, wird eine entsprechende Funktion aufgerufen, die ggf. einen neuen Dialog zum Öffnen einer Datei präsentiert. Widgets Das Wort ins Deutsche zu übersetzen ist wohl wenig sinnvoll (Widget = »Dingsbums«). Als Widgets (Fensterkontrollelemente) bezeichnet man alle Interaktions- bzw. Steuerelemente in grafischen Benutzeroberflächen. Abhängig von der GUI-Bibliothek gibt es hierbei einfache Widgets, woraus sich der Programmierer seine eigenen Dialoge zusammenbasteln kann. Häufig findet man auch komplexere Widgets wie bspw. einen fertigen Dialog zum Öffnen/Speichern einer Datei.
Viele grafische Toolkits verwenden zur Kommunikation zwischen den Widgets oft eine Callback-Funktion. Ein solcher Callback ist nichts als ein simpler Zeiger auf eine Funktion. Allerdings haben solche Callbacks zwei kleine Makel. Zum einen sind sie nicht typensicher (man ist nie sicher, dass die gerade ausführende Funktion den Callback mit den richtigen Argumenten aufruft). Zweitens ist der Callback mit der auszuführenden Funktion fest verbunden, weil die auszuführende Funktion wissen muss, welcher Callback aufgerufen werden soll. Mit dem Signal-und-Slot-Konzept geht Qt hier einen etwas anderen Weg. Dieses Konzept hat den Vorteil, dass Qt die Verbindung automatisch trennt, wenn eines der kommunizierenden Objekte zerstört wird. Dadurch vermeidet man viele Abstürze, weil Versuche, auf ein nichtvorhandenes Objekt zuzugreifen, nicht mehr möglich sind.
31
1542.book Seite 32 Montag, 4. Januar 2010 1:02 13
2
Signale und Slots
Am einfachsten lässt sich das Signal-und-Slot-Konzept an unserem »Hallo Welt«Beispiel beschreiben. Das Beispiel wollen wir nun erweitern, indem wir beim Anklicken des Buttons die Anwendung beenden. 01 // beispiele/signalslot1/main.cpp 02 #include 03 #include 04 int main(int argc, char *argv[]) { 05 QApplication app(argc, argv); 06 QPushButton hello("Ende"); 07 hello.resize(100, 30); 08 hello.show(); 09 QObject::connect( &hello, SIGNAL( clicked() ), &app, SLOT( quit() ) ); 10 return app.exec(); 11 }
Verglichen mit dem ersten Beispiel wurde hier, abgesehen von einer anderen Bezeichnung des Buttons, mit der Zeile 9 eine neue statische Methode aufgerufen. Und zwar connect() von der Klasse QObject. connect() von Qt und Berkley Die Funktion connect() von der Klasse QObject hat nichts mit der gleichnamigen Socketfunktion von Berkeley zu tun. connect() ist eine statische (static) Methode, die eine Verbindung zwischen einem Signal und einem Slot herstellt. Hierzu die genaue Syntax: bool QObject::connect ( const QObject * sender, const char * signal, const QObject * receiver, const char * method, Qt::ConnectionType type = Qt::AutoCompatConnection ) [static]
Mit den ersten beiden Argumenten (sender, signal) bezeichnet man das Objekt, das ein Signal aufnimmt und an den Empfänger sendet – das Sender-Objekt also. Die beiden anderen Argumente (receiver, method) legen das Objekt des Empfängers des entgegenzunehmenden Slots fest. Genauer gesagt: Empfängt das Objekt sender das Signal signal, wird es an den Slot gebunden. Dies nimmt das Objekt receiver vom Slot entgegen und führt method aus. Auf die folgende Zeile bezogen, heißt das:
32
1542.book Seite 33 Montag, 4. Januar 2010 1:02 13
Signale und Slots ermitteln
QObject::connect( &hello, SIGNAL( clicked() ), &app, SLOT( quit() ) );
Hiermit verbinden Sie das Objekt hello von der Klasse QPushButton mit dem Objekt app von der Klasse QApplication. Sollte beim Button das Signal clicked() eingehen, legen Sie als Aktion fest, dass bei der Klasse QApplication die Methode quit() ausgeführt wird, was in diesem Fall das Ende des Programms bedeuten würde (siehe Abbildung 2.1).
QPushButton hello
QApplication app
clicked()
quit()
Abbildung 2.1
Zwei Objekte verbinden
Die Funktion QObject::connect() gibt bei erfolgreicher Verbindung true, ansonsten false zurück. Außerdem müssen Sie die Makros SIGNAL() und SLOT() verwenden, wenn Sie das Signal und die Methode festlegen wollen, weil für die beiden Argumente eine Zeichenkette vorgesehen und diese beiden Makros dafür sorgen, dass eine korrekte Zeichenkette eingesetzt wird. Letztes Argument von connect() Das letzte Argument type von QObject::connect() ist optional und beschreibt den Typ der Verbindung, auf dem diese aufbaut.
Somit handelt es sich bei den Slots um ganz normale Methoden einer Klasse die auf Signale einer anderen Klasse reagieren, wobei die Signale im Grunde wiederum nur einfache Methoden einer Klasse sind. Natürlich ist es auch möglich, einen Sender mit mehreren Empfängern und umgekehrt zu verknüpfen. Ist bspw. ein Signal mit zwei oder mehreren Slots verbunden, werden die Slots der Reihe nach ausgeführt, so wie sie im Code geschrieben wurden. Jede Unterklasse von QObject kann somit solche Signale und Slots definieren.
2.1
Signale und Slots ermitteln
Sicherlich stellt sich für Sie jetzt die Frage: Woher kommen die Signale und Slots? Hierzu sind tiefere Kenntnisse von Qt und deren Klassen von Vorteil, aber die einzelnen Signale und Slots kann man unmöglich auswendig kennen. Für solche
33
2.1
1542.book Seite 34 Montag, 4. Januar 2010 1:02 13
2
Signale und Slots
Fälle ist es ratsam, die Dokumentation von Qt zu verwenden, welche bei der QtDistribution mitinstalliert wurde (siehe Abbildung 2.2). Um die Dokumentation von Qt zu verwenden, müssen Sie Qt Creator (oder Qt Assistant) ausführen. Über das Menü Help können Sie mit Contents die vorhandenen Referenzen auflisten. Schneller können Sie die Referenzen beim Qt Creator mit (Strg)+(5) anzeigen.
Abbildung 2.2 Die Dokumentation von Qt mit vielen Tutorials ist bestens für eine intensive Recherche geeignet.
Wollen Sie bspw. ermitteln, welche Signale bei der Klasse QPushButton auftreten können, müssen Sie zunächst im Indexverzeichnis von Qt Assistant nach der entsprechenden Klasse suchen (siehe Abbildung 2.3). Das Indexverzeichnis können Sie über das Menü Help mit Index aufrufen.
Abbildung 2.3 Mithilfe des Indexverzeichnisses auf der Suche nach der entsprechenden Klasse
34
1542.book Seite 35 Montag, 4. Januar 2010 1:02 13
Signale und Slots ermitteln
Als Ergebnis erhalten Sie die Klassen-Referenz von QPushButton mit all ihren Eigenschaften, Funktionen, Signalen und Slots. Wichtig hierbei ist auch der Inhalt von vererbten Mitgliedern (siehe Abbildung 2.4).
Abbildung 2.4
Die Klassen-Referenz von QPushButton
Bei der Durchsicht der Klassen-Referenz von QPushButton fällt auf, dass sich hier zwar öffentliche Slots befinden, aber keine direkten Signale. Erst bei den geerbten Mitgliedern finden Sie einen Eintrag (mit Verweis) wie bspw. 4 signals inherited from QAbstractButton. Also folgen Sie dem Link zur Klasse QAbstractButton und siehe da, wir finden die gewünschten Signale, die ja auch an QPushButton weitervererbt wurden (da public). Neben clicked() finden Sie hier auch noch die Signale pressed(), released() und toogled(). Des Weiteren finden Sie hier nochmals zwei weitere Signale, die jeweils von QWidget und QObject geerbt wurden (siehe Abbildung 2.5).
Abbildung 2.5
Vererbte Signale von QAbstractButton an QPushButton
35
2.1
1542.book Seite 36 Montag, 4. Januar 2010 1:02 13
2
Signale und Slots
Wollen Sie noch mehr zu dem Signal erfahren, folgen Sie wieder einfach dem entsprechenden Verweis. Im Beispiel zuvor haben wir auch einen Slot (quit()) der Klasse QApplication verwendet. Um auch hier den oder die entsprechenden Slot(s) einer Klasse zu ermitteln, gehen Sie genauso vor wie eben bei den Signalen beschrieben. Auf der Klassen-Referenz von QApplication finden Sie dann unter dem Eintrag »Public Slots« die entsprechenden Slots (siehe Abbildung 2.6).
Abbildung 2.6
Klassen-Referenz von QApplication
Zwar sind hier sieben vorhandene Slots definiert, aber der Slot quit() ist nicht direkt in der Klasse QApplication definiert. Auch hier wurde der Slot quit() von einer anderen Klasse, hier von QCoreApplication, geerbt. Dies können Sie aus dem entsprechenden Verweis 1 public slot inherited from QCoreApplication entnehmen.
Abbildung 2.7
In der Referenz von QcoreApplication haben wir den Slot quit() gefunden.
Die Verwendung der Referenz von Qt ist unerlässlich für jeden ernsten Qt-Programmierer. Das Prinzip ist immer dasselbe: Man sucht nach der entsprechenden Klasse (Widget) im Indexverzeichnis und dann eben nach dem entsprechenden Signal, Slot, der Eigenschaft oder eben der entsprechenden Methode, die man mit einem Widget ausführen will. Hierzu soll ein weiteres Beispiel erstellt werden, das folgendes Signal-Slot-Konzept verwendet (siehe Abbildung 2.8).
36
1542.book Seite 37 Montag, 4. Januar 2010 1:02 13
Signale und Slots ermitteln
QPushButton but1
QApplication app
clicked()
aboutQt()
quit() QPushButton but2
clicked() QWidget win QPushButton but3 showNormal() clicked() showMaximized()
QPushButton but4
showMinimized()
clicked()
Abbildung 2.8
Signale und Slots verbinden
Die Zeichnung liest sich einfach. Auf der linken Seite wurden alle Objekte angegeben, die ein Signal empfangen und es an die Objekte der rechten Seite senden. Auf der linken Seite befinden sich also die Signale (mit deren Klassen) und auf der rechten Seiten die Slots (mit deren Klassen). Das Beispiel verwendet vier Buttons der Klasse QPushButton, ein Widget namens QWidget, welches den Rahmen darstellt, und natürlich die QApplication-Klasse, ohne die keine GUI-Anwendung auskommt. Der Button but1 wurde hier mit zwei Slots verbunden. Erhält dieser Button das Signal clicked(), wird zunächst der Slot aboutQt() der Klasse QApplication ausgeführt und anschließend der Slot showNormal() von der Klasse QWidget. aboutQt() ist eine einfache Nachrichtenbox über Qt und showNormal() entspricht dem Systemmenüeintrag Wiederherstellen des Fensters. Wurde bspw. die Größe des Fensters verändert, sorgt dieser Slot dafür, dass das Fenster wieder in die ursprüngliche Größe versetzt wird. Zwei weitere Buttons wurden ebenfalls mit Slots der Klasse QWidget verbunden. Erhält der Button but2 das Signal clicked(), wird bei QWidget der Slot
37
2.1
1542.book Seite 38 Montag, 4. Januar 2010 1:02 13
2
Signale und Slots
showMaximized() ausgeführt. Hierbei wird das Fenster in der Größe des Desktops maximiert. Selbiges gilt für Button but3, nur dass hierbei das Fenster minimiert wird. Der letzte Button (but4) wurde wieder mit QApplication und dessen Slot quit() verbunden. Empfängt also but4 das Signal clicked(), wird die Anwendung beendet. Im Folgenden das komplette Listing zu Abbildung 2.8: 00 01 02 03 04
// beispiele/signalslot2/main.cpp #include #include #include #include
05 int main(int argc, char *argv[]) { 06 QApplication app(argc, argv); 07 QWidget* win = new QWidget; 08 QVBoxLayout* layout = new QVBoxLayout(win); 09 QPushButton* but1 = new QPushButton("Wiederherstellen"); 10 QPushButton* but2 = new QPushButton("Maximieren"); 11 QPushButton* but3 = new QPushButton("Minimieren"); 12 QPushButton* but4 = new QPushButton("Schließen"); 13 14 15 16 17
// Größe der Buttons but1->resize(100, 30); but2->resize(100, 30); but3->resize(100, 30); but4->resize(100, 30);
18 19 20 21 22
// Widgets zur vertikalen Box hinzufügen layout->addWidget(but1); layout->addWidget(but2); layout->addWidget(but3); layout->addWidget(but4);
23 24
// Signal-und-Slot-Verbindungen herstellen QObject::connect( but1, SIGNAL( clicked() ), &app, SLOT( aboutQt() ) ); QObject::connect( but1, SIGNAL( clicked() ), win, SLOT( showNormal() ) ); QObject::connect( but2, SIGNAL( clicked() ), win, SLOT( showMaximized() ) ); QObject::connect( but3, SIGNAL( clicked() ), win, SLOT( showMinimized() ) ); QObject::connect( but4, SIGNAL( clicked() ), &app, SLOT( quit() ) );
25 26 27 28
38
1542.book Seite 39 Montag, 4. Januar 2010 1:02 13
Signale und Slots ermitteln
29 30 31 32 }
// Fenster anzeigen win->show(); return app.exec();
Neben dem bereits erwähnten neuen Widget QWidget wurde hier auch die Klasse QVBoxLayout verwendet, womit einzelne GUI-Elemente vertikal angeordnet hinzugefügt werden können. Als Argument erhält der Konstruktor die Adresse des Eltern-Widgets, was im Beispiel das Hauptfenster der Anwendung (QWidget) ist. Die einzelnen Widgets können Sie anschließend mit der Methode addWidget() und dem Widget als Argument hinzufügen. Das Thema Layout und die Anordnung einzelner Widgets ist ein sehr wichtiges Thema, so dass Sie hierzu einen eigenen Abschnitt (4.2) finden. Hier nun das Programm bei seiner Ausführung:
Abbildung 2.9
Abbildung 2.10
Ein Fenster mit vier Buttons
Button »Wiederherstellen« wurde angeklickt
39
2.1
1542.book Seite 40 Montag, 4. Januar 2010 1:02 13
2
Signale und Slots
2.2
Gegenseitiges Signal- und Slot-Konzept
Außerdem kommt es häufig vor, dass zwei Widgets voneinander abhängig sind. Wird bspw. ein Widget verändert, muss das andere ebenfalls an diese Veränderung angepasst werden – und umgekehrt genauso. Am besten sehen Sie sich hierzu folgende Abbildung (2.11) an.
QSpinBox spin
QSlider slider
valueChanged(int)
valueChanged(int)
setValue(int)
setValue(int)
Abbildung 2.11
Anpassen von zwei Widgets bei Veränderungen
In diesem Beispiel haben Sie ein Klasse QSpinBox und eine Klasse QSlider, die beide jeweils bei einer Veränderung den Wert des anderen Widgets anpassen. Wurde bspw. bei der Spinbox der Wert verändert, wird das Signal valueChanged() ausgelöst. Ist dies der Fall wird der Schieberegler (QSlider) entsprechend der Spinbox mit dem Slot setValue() angepasst. Anders betrachtet, haben Sie denselben Fall. valueChanged() ist jeweils das Signal und setValue() der Slot der beiden Widgets. Wie Sie weitere Signale und Slots in Erfahrung bringen können, wurde in Abschnitt 2.1 mit dem Assistant von Qt bereits beschrieben. Erklärungen folgen … Es lässt sich leider nicht ganz vermeiden, dass ich ein wenig auf spätere Kapitel vorgreife, anders lässt sich das Signal-Slot-Konzept nicht beschreiben.
Um die beiden Widgets miteinander zu verbinden, sind im Grunde nur zwei Verbindungen mit connect() nötig. Hier das entsprechende Programmbeispiel: 00 01 02 03 04
// beispiele/signalslot3/main.cpp #include #include #include #include
05 int main(int argc, char *argv[]) 06 QApplication app(argc, argv);
40
{
1542.book Seite 41 Montag, 4. Januar 2010 1:02 13
Argumentenlisten von Signal-Slot-Verbindungen
07 08 09 10
QWidget* win QVBoxLayout* layout QSpinBox* spin QSlider* slider
11 12 13 14 15 16 17 18 19 20 21
// Minimum-Maximum Wert für Spinbox spin->setMinimum(0); spin->setMaximum(100); // Minimum-Maximum Wert für Slider slider->setMinimum(0); slider->setMaximum(100); // Widgets hinzufügen layout->addWidget(spin); layout->addWidget(slider); // Signal-und-Slot Verbindung QObject::connect( spin, SIGNAL( valueChanged(int) ), slider, SLOT( setValue(int) ) ); QObject::connect( slider, SIGNAL( valueChanged(int) ), spin, SLOT( setValue(int) ) ); win->show(); return app.exec();
22 23 24 25 }
= = = =
new new new new
QWidget; QVBoxLayout(win); QSpinBox; QSlider(Qt::Horizontal);
Das Programm bei der Ausführung:
Abbildung 2.12
Die beiden Widgets, immer up to date
Verändern Sie hierbei den Schieberegler, wird automatisch auch die Spinbox mit dem entsprechenden Wert versehen/verändert. Verändern Sie die Spinbox mit den Pfeilen oder als gültigen Wert durch die Tastatureingabe, wird auch der Schieberegler an die Position entsprechend angepasst.
2.3
Argumentenlisten von Signal-Slot-Verbindungen
Im Beispiel zuvor hatten die Signal-Slot-Verbindungen beide dieselben Argumente. Also sowohl valueChanged(int) und setValue(int) waren vom Typ int. Dies muss unbedingt beachtet werden, weil bei einer solchen Signal-SlotVerbindung keine automatische Typenumwandlung erfolgt.
41
2.3
1542.book Seite 42 Montag, 4. Januar 2010 1:02 13
2
Signale und Slots
Benötigen Sie bspw. einen String anstelle eines Integer-Werts, müssen Sie von der Klasse ableiten und den entsprechenden Slot implementieren. Hierbei konvertiert man im Grunde nur das Argument und ruft anschließend den eigentlichen Slot auf. Seit Qt 4 kann man nicht mehr einfach beliebige Funktionen als Slots verwenden. Slots müssen Sie als solche kennzeichnen um von Qt akzeptiert zu werden. Sollte der Slot weniger Argumente enthalten als das Signal, ist dies ebenfalls gestattet. Überflüssige Argumente werden hierbei ignoriert. Bspw. ist es möglich, eine Verbindung zwischen signalFunc(int) und slotFunc() aufzubauen, obwohl der Slot keine Argumente enthält. Bei mehr als einem Parameter des Signals gilt dieselbe Regel wie bei der Funktionsüberladung von C++. So lässt sich bspw. mit signalFunc(int, float) mit den Slots slotFunc(), slotFunc(int) und slotFunc(int, float) eine Verbindung aufbauen. Nicht aber mit dem Slot slotFunc(float). Anders herum ist es nicht möglich, eine Verbindung herzustellen, wenn ein Slot mehr Argumente erwartet, als das Signal beinhaltet. Einfachstes Beispiel: QObject::connect( button, SIGNAL( clicked() ), label, SLOT( setText("Neuer Text") ) );
Hier wird versucht, eine Verbindung zwischen einem Button und einem Textlabel herzustellen. Sollte der Button gedrückt werden, dann soll der Textlabel mit einem neuen Text versehen werden. Allerdings funktioniert dies nicht, weil das Signal clicked() keine Argumente enthält, der Slot setText()aber eines erwartet. Ärgerlich ist hierbei die Tatsache, dass der Compiler Ihnen dies ohne Probleme übersetzt.
2.4
Eigene Klasse mit Signalen und Slots definieren bzw. erweitern
Klassen über Klassen Ab diesem Punkt muss ich anmerken, dass das Thema für den Einstieg schon recht komplex ist, zumal die Basisklassen von Qt bis jetzt noch nicht genauer beschrieben wurden. Allerdings wollte ich das Thema »Signale und Slots« nicht im ganzen Buch verstreuen, so dass Sie als Leser jederzeit beim Nachschlagen alles in einem Kapitel wiederfinden und nicht im ganzen Buch danach suchen müssen. Ggf. können Sie zu diesem Abschnitt später zurückkehren, sollten Sie diesen Abschnitt nicht auf Anhieb verstehen oder nur überfliegen.
42
1542.book Seite 43 Montag, 4. Januar 2010 1:02 13
Eigene Klasse mit Signalen und Slots definieren bzw. erweitern
Um eine Klasse mit eigenen Signalen und Slots zu versehen, gilt es folgende Regeln einzuhalten: 왘
Es kann keine eigene Ereignisklasse definiert werden, die nur aus Signalen und Slots besteht. Die Signale und Slots müssen immer einen Teil der Klasse darstellen.
왘
Eigene Signale und Slots können nur in Klassen definiert werden, die von der Klasse QObject abgeleitet sind.
왘
In der neu definierten Klasse muss das Makro Q_OBJECT gesetzt werden (mehr dazu in Kürze).
왘
Standard-Parameter sind für eigene Klassen bei den Signalen und Slots nicht erlaubt.
C++-Grundlagen Für den Fall, dass Ihnen Themen wie das Ableiten von Klassen in C++ fremd sind, ist es nun an der Zeit, dieses Wissensdefizit zu beheben, weil im Grunde die gesamte QtProgrammierung darauf basiert.
Um zu beschreiben, wie Sie eigene Signale und Slots in einer abgeleiteten Klasse definieren, soll zunächst die folgende normale C++-Klasse als Vergleich herhalten: // typische Form einer einfachen C++-Klasse class MyClass { public: // Konstruktor MyClass(); // Zugriffsmethoden int value() const { return val; } void setValue(int); private: int val; };
Damit daraus eine waschechte Qt-Klasse entsteht, sind folgende (fett hervorgehobene) Änderungen nötig: 00 // eine echte Qt-Klasse 01 class MyClass : public QObject { 02 Q_OBJECT 03 public: 04 // Konstruktor 05 MyClass();
43
2.4
1542.book Seite 44 Montag, 4. Januar 2010 1:02 13
2
Signale und Slots
06 // Zugriffsmethoden 07 int value() const { return val; } 08 public slots: 09 void setValue(int); 10 signals: 11 void valueChanged(int); 12 private: 13 int val; 14 };
Nachdem Sie Ihre Klasse in Zeile 1 von QObject abgeleitet haben, können Sie Signale und Slots im Grunde wie normale Methoden deklarieren. Allerdings dürfen die deklarierten Signal- und Slot-Elementefunktionen keinen Rückgabetyp haben und müssen daher void sein. Als Argumente können Sie hingegen eine beliebige Anzahl und auch beliebige Typen verwenden. Wichtig ist auch das Makro Q_OBJECT aus Zeile 2 (Achtung, ohne Semikolon!). In der Deklaration der Signal- und Slot-Elementfunktionen werden auch die neuen Spezifizierer signals (Zeile 10) und slots (Zeile 8) verwendet. Je nach erwünschter Sichtbarkeit nach außen bzw. der Weitervererbbarkeit werden noch die Spezifizierer private, public und protected vor signals und slots gesetzt. In Ihrer Eigenschaft als Programmierer definieren Sie nur die Slots als gewöhnliche Elementfunktionen (Methoden). Einsteiger in Qt, die Quellcode studieren, sind häufig verwirrt, weil sie die Definition zur Signal-Elementefunktion nicht finden. Der Code für Signal-Elementfunktionen wird aber nicht vom Programmierer, sondern vom Meta Object Compiler (kurz MOC) geschrieben. Hierzu ein Beispiel, das zunächst allerdings noch keine GUI-Elemente verwendet. Wir erstellen eine einfache Klasse mit einem Integerwert als Eigenschaft und implementieren in dieser Klasse das Signal valueChanged(int), welches ausgelöst wird, sobald man den Integerwert des Objekts verändert. Natürlich implementieren wir hierbei auch den entsprechenden Slot setValue(int), um zwei Objekte der Klasse mit connect() verknüpfen zu können. Hier zunächst die Deklaration der Klasse MyClass: 00 01 02 03
// beispiele/signalslot4/myclass.h #ifndef MY_CLASS_H #define MY_CLASS_H #include
04 // Eine Klasse, die Signals und Slots besitzt. 05 class MyClass : public QObject { 06 Q_OBJECT 07 public:
44
1542.book Seite 45 Montag, 4. Januar 2010 1:02 13
Eigene Klasse mit Signalen und Slots definieren bzw. erweitern
08 MyClass(); // Konstruktor 09 int value() const { return val; } 10 public slots: 11 // Der Wert von "val" wird geändert. 12 void setValue( int ); 13 signals: 14 // Das Signal soll ausgesandt werden, 15 // wenn "val" geändert wird. 16 void valueChanged( int ); 17 private: 18 int val; 19 }; 20 #endif
Die Definition der Klasse ist ebenso einfach. Da wir das Signal nicht definieren müssen, reichen in diesem Fall der Konstruktor und die Slot-Elementefunktion setValue(int) aus. 00 // beispiele/signalslot4/myclass.cpp 01 #include "myclass.h" 02 MyClass::MyClass() { 03 val = 0; 04 } 05 void MyClass::setValue( int v ) { 06 // val wird nur neu gesetzt, 07 // wenn tatsächlich ein anderer Wert übergeben wird. 08 if ( v != val ) { 09 val = v; 10 emit valueChanged(v); 11 } 12 }
Auffällig ist hier die Zeile 10 mit dem emit-Signalbezeichner. Mit dem Bezeichner emit machen Sie deutlich, dass es sich bei dem Aufruf um keinen normalen Funktionsaufruf handelt, sondern um einen Signalaufruf. Allerdings erreichen Sie selbiges auch ohne Bezeichner emit. Der Bezeichner ist nur dafür gedacht, sofort erkennen zu können, ob es sich um einen Signalaufruf handelt. Hierzu folgt noch die Hauptfunktion, die abgesehen von den Nachrichtenboxen (QMessageBox) auch ohne GUI-Elementen auskommt und das eigene Signal-SlotKonzept demonstriert. 00 // beispiele/signalslot4/main.cpp 01 #include "myclass.h"
45
2.4
1542.book Seite 46 Montag, 4. Januar 2010 1:02 13
2
Signale und Slots
02 #include 03 #include 04 #include 05 // simple Nachrichten-Box 06 void MyMessageBox(MyClass& a,MyClass& b,QString title) { 07 QString Qstr, Qval; 08 // String zusammenbasteln 08 Qstr.append(title); Qstr.append("\na: "); 09 Qval.setNum(a.value()); Qstr.append(Qval); 10 Qstr.append("\nb: "); Qval.setNum(b.value()); 11 Qstr.append(Qval); 12 QMessageBox::information( NULL, "MyClass Information", Qstr, QMessageBox::Ok); 13 } 14 int main(int argc, char *argv[]) 15 QApplication app(argc, argv); 16 // Zwei MyClass-Objekte 17 MyClass *a = new MyClass(); 18 MyClass *b = new MyClass();
{
19 20 21
// Das Signal des einen wird mit dem Slot // des anderen Objekts verbunden. QObject::connect( a, SIGNAL( valueChanged(int) ), b, SLOT( setValue(int) ) );
22 23 24 25 27 28 29 30 31 32 }
// b.val bekommt den Wert 100 b->setValue( 100 ); MyMessageBox(*a, *b, "b->setValue(100)"); // a.val bekommt den Wert 99. Durch die Signal-Slot// Verknüpfung bekommt jetzt auch b.val den Wert 99 a->setValue( 99 ); // Der Beweis MyMessageBox(*a, *b, "a->setValue(99)"); return 0;
Die Zeilen 5 bis 13 können Sie hierbei ignorieren. Damit wird nur eine Nachrichtenbox auf dem Bildschirm ausgegeben mit dem Inhalt der Integerwerte der beiden Objekte, die in den Zeilen 17 und 18 neu erzeugt werden. In Zeile 21 verknüpfen wir diese Objekte. Verändert man den Wert von Objekt a, wird das Signal valueChanged() ausgelöst und mit Objekt b und dem Slot setValue() verbunden. Sobald Sie also den Wert von Objekt a verändern, wird
46
1542.book Seite 47 Montag, 4. Januar 2010 1:02 13
Eigene Klasse mit Signalen und Slots definieren bzw. erweitern
der Wert von Objekt b mit unserem selbst erstellten Slot setValue() angepasst. In diesem Beispiel erhält das Objekt b durch setValue() denselben Wert wie Objekt a. In der Zeile 23 bekommt das Objekt b mit 100 einen neuen Wert zugewiesen. Wir haben keine Signal-Slot-Verknüpfung eingerichtet, a und b besitzen folgende Werte (siehe Abbildung 2.13):
Abbildung 2.13
Nachdem Objekt b einen Wert erhalten hat
In der Zeile 28 weisen wir dann dem Objekt a mit 99 einen neuen Wert zu. Und hierbei wird auch gleich unsere eingerichtete Signal-Slot-Verbindung aktiv, so dass auch Objekt b denselben Wert erhält, wie Objekt a. Dadurch ergeben sich folgende Werte (siehe Abbildung 2.14):
Abbildung 2.14
Signal wurde ausgelöst
MOC – Meta Object Compiler Dank qmake brauchen Sie sich nicht mehr groß um den Meta Object Compiler (MOC) zu kümmern. Mit dem folgenden typischen Dreisatz muss man sich um (fast) nichts mehr Gedanken machen: $ qmake –project $ qmake $ make qmake erstellt uns hier den entsprechenden Makefile, der auch den Meta Object
Compiler automatisch verwendet. Normalerweise müssen Sie MOC also nicht direkt aufrufen.
47
2.4
1542.book Seite 48 Montag, 4. Januar 2010 1:02 13
2
Signale und Slots
Dennoch ist es theoretisch möglich, den MOC anders zu verwenden. Hierzu kann man entweder die mit dem MOC erzeugte Datei der Klassendefinition in einem Schritt zu einer Objektdatei kompilieren und beim Linken der ausführbaren Datei hinzufügen. Der Vorgang würde in etwa wie folgt aussehen: $ moc myclass.h -o moc_myclass.cpp $ g++ moc_myclass.cpp -o moc_myclass.o ---( zunächst wird die moc Datei gesondert übersetzt )--$ g++ myclass.cpp -o myclass.o $ g++ myclass.o moc_myclass.o main.o -o main -L... -l...
Man kann aber auch das mit MOC erzeugte Programm der Klassendefinition mittels einer Include-Anweisung in den Quellcode mit einfügen. Meta Object Compiler Wollen Sie mehr über den Meta Object Compiler erfahren, müssen Sie bei der Qt Reference Documentation von Qt in der Sektion Tools den Link All Tools anklicken. Dort finden Sie neben der Beschreibung weiterer Werkzeuge auch eine zum Meta Object Compiler.
Als Programmierer werden Sie sich sicherlich fragen, wozu denn überhaupt ein zusätzlicher Meta Object Compiler nötig ist, um ein eigenes Signal-Slot-Konzept zu realisieren. Nun – das liegt vor allem daran, dass das Signal-Slot-Konzept kein reines C++ ist, sondern eher eine Erweiterung vom C++-Standard. Der Meta Object Compiler verwandelt daher Signale und Slots in echten C++-konformen Standard. Somit ist MOC also ein Programm, das einen C++-Quelltext einliest und die C++-Erweiterung von Qt verwaltet. Trifft MOC auf eine oder mehrere Deklarationen mit dem Makro Q_OBJECT, produziert dieser eine andere C++-Quelltextdatei. Der so produzierte Code enthält dann den Meta-Object Code für die Klassen, die das Q_OBJECT benutzen. Dieser Meta-Object Code wird für den Signal-Slot-Mechanismus, für Typeninformationen zur Laufzeit und das dynamische Eigenschaftssystem (Property-System) benötigt. Kurz gesagt, benötigt man MOC immer dann, wenn QObject als Basisklasse dient. Der MOC muss dabei jede Datei, die eine Klassendefinition einer entsprechenden Klasse enthält, bearbeiten (ähnlich wie der Präprozessor), bevor der C++-Compiler den Code erhält. Alle von MOC erzeugten Dateien weisen das Präfix moc_auf. In unserem Beispiel wird bspw. aus den Dateien myclass.h und myclass.cpp die Datei moc_myclass.cpp erzeugt. Diese Quelltextdatei muss noch extra kompiliert und mit der Implementierung der Klasse verlinkt werden. qmake erledigt dies alles zum Glück automatisch.
48
1542.book Seite 49 Montag, 4. Januar 2010 1:02 13
Widget mit eigenem Slot
2.5
Widget mit eigenem Slot
Häufig wird man zu seinen Widgets eigene Slots hinzufügen wollen. Ein gerne zitiertes Beispiel zeigt der folgende Codeausschnitt: QObject::connect( button, SIGNAL( clicked() ), label, SLOT( setText("neuer Text") ) );
Hierbei haben wir eine versuchte Signal-Slot-Verknüpfung, die keinerlei Effekt haben wird. Der Programmierer wollte hier wohl erreichen, dass beim Anklicken eines Buttons (bspw. QPushButton) der Text des Labels (QLabel) verändert wird. Der Button verwendet dabei das Signal clicked() und der Label den Slot setText(). Warum dies nicht funktioniert, wurde in Abschnitt 2.3 bereits näher beschrieben. Die Argumentenliste von clicked() und setText() stimmt nicht überein. clicked() gibt hierbei keine Argumente an den Slot setText(). Um das folgende Beispiel zu realisieren, leiten wir unsere neue Klasse MyWindow von QWidget ab. Zunächst die Headerdatei mit der Klasse: 00 01 02 03 04 05 06 07 08
// beispiele/signalslot5/mywindow.h #ifndef MYWINDOW_H #define MYWINDOW_H #include #include #include #include #include #include
09 class MyWindow : public QWidget { 10 Q_OBJECT 11 public: 12 MyWindow(QWidget *parent = 0); 13 private: 14 QLabel *label; 15 QPushButton *button0; 16 QPushButton *button1; 17 QVBoxLayout* layout; 18 private slots: 19 void setText(); 20 }; 21 #endif
Verglichen mit dem Beispiel zuvor, in dem wir eigene Signale und Slots verwendeten, finden Sie auch hier nichts Neues, außer dass wir jetzt von der Klasse QWidget (Zeile 9) – statt von QObject – ableiten und hier nur ein Slot (Zeile 18
49
2.5
1542.book Seite 50 Montag, 4. Januar 2010 1:02 13
2
Signale und Slots
und 19) verwendet wird. Die Definition des Konstruktors und der Slot-Ereignisfunktion finden Sie in der folgenden Quelldatei wieder: 00 // beispiele/signalslot5/mywindow.cpp 01 #include "mywindow.h" 02 MyWindow::MyWindow(QWidget *parent): QWidget(parent) { 03 label = new QLabel("alter Text"); 04 button0 = new QPushButton ("Label aktualisieren"); 05 button1 = new QPushButton ("Beenden"); 06 layout = new QVBoxLayout(this); 07 layout->addWidget(button0); 08 layout->addWidget(button1); 09 layout->addWidget(label); 10 setLayout(layout); 11 connect( button0, SIGNAL( clicked() ), this, SLOT( setText() ) ); 12 connect( button1, SIGNAL( clicked() ), qApp, SLOT( quit() ) ); 13 } 14 void MyWindow::setText() { 15 label->setText("neuer Text"); 16 }
Zunächst werden wie gewohnt die einzelnen Widgets wie Buttons (QPushButton) und Label (QLabel) erzeugt und der vertikalen Box (QVBoxLayout) hinzugefügt. Anschließend (Zeile 11 und 12) werden die Signal-Slot-Verknüpfungen eingerichtet. Ein besonderes Augenmerk sei hierbei auf den Slot von Zeile 11 geworfen. Hierbei handelt es sich nicht mehr um den von QLabel zur Verfügung gestellten Slot setText(), sondern um unseren selbst definierten Slot ohne Argumente, dessen Implementierung Sie in Zeile 14 bis 16 vorfinden. Klicken Sie nun im Beispiel auf den Button mit dem Label »alter Text«, wird das Signal clicked() ausgelöst. Dieses Signal ist allerdings nicht mit dem Label verbunden, sondern mit dem Fenster bzw. QWidget. Daher finden Sie hierbei auch den this-Zeiger vor. Erst mit der Slot-Ereignisfunktion MyWindow::setText() wird der Text-Label verändert. Jetzt fehlt nur noch die Hauptfunktion, die kaum noch Code enthält: 00 // beispiele/signalslot5/main.cpp 01 #include 02 #include "mywindow.h" 03 int main(int argc, char *argv[])
50
{
1542.book Seite 51 Montag, 4. Januar 2010 1:02 13
Widget mit eigenem Signal
04 05 06 07 08 }
QApplication app(argc, argv); MyWindow* window = new MyWindow; window->show(); return app.exec();
Das Programm bei der Ausführung:
Abbildung 2.15
Programm beim Start
Abbildung 2.16
Nach dem Drücken von »Label aktualisieren«
2.6
Widget mit eigenem Signal
Natürlich will ich Ihnen Ähnliches auch mit einem Signal demonstrieren. Hierfür wollen wir die QPushButton-Klasse um das Signal clicked(int) erweitern. Ursprünglich gibt es bei dieser Klasse ja nur das Signal clicked() ohne Argument. Wir wollen ein neues clicked(int) mit Argument hinzufügen, welches beim Drücken eines Buttons ein Signal mit einer Identifikationsnummer (ID) des Buttons mitschickt. Hierzu zunächst die Klasse MyButton, die wir von der Klasse QPushButton ableiten: 00 01 02 03 04 05
// beispiele/signalslot6/mybutton.h #ifndef MYBUTTON_H #define MYBUTTON_H #include #include #include
06 class MyButton : public QPushButton {
51
2.6
1542.book Seite 52 Montag, 4. Januar 2010 1:02 13
2
Signale und Slots
07 08 09
10
Q_OBJECT public: MyButton( const QString& text, int id, QWidget* parent = NULL ); MyButton( const QString& text, QWidget* parent = NULL ); private: static int nextId; int m_id; public slots: //click() überschreiben void click(); signals: //ein neues Signal hinzufügen void clicked(int id);
11 12 13 14 15 16 17 18 19 20 }; 21 #endif
Zusätzlich zu einem neuen Signal, das hier in Zeile 17 bis 19 festgelegt wird, überschreiben wir den in der Klasse QAbstractButton definierten Slot click(). Der Slot click() führt wortwörtlich einen Klick durch. Natürlich benötigen Sie nach wie vor keine Definition des Signals, da dies, wie bereits beschrieben, der Meta Object Compiler für uns übernimmt. Um den Vorgang besser zu verstehen, benötigen Sie die Implementierung der Klasse MyButton: 00 // beispiele/signalslot6/mybutton.cpp 01 #include "mybutton.h" 02 int MyButton::nextId = 0; 03 MyButton::MyButton( const QString& text, int id, QWidget* parent ) : QPushButton(text, parent), m_id(id) { 04 connect(this, SIGNAL(clicked()), this, SLOT(click())); 05 } 06 MyButton::MyButton(const QString& text, QWidget* parent ) : QPushButton(text, parent), m_id(nextId++) { 07 connect(this, SIGNAL(clicked()), this, SLOT(click())); 08 } 09 void MyButton::click() { 10 emit clicked(m_id); 11 }
52
1542.book Seite 53 Montag, 4. Januar 2010 1:02 13
Widget mit eigenem Signal
Außerdem fallen die beiden connect()in den Konstruktoren auf (Zeile 04 und 07). Hiermit verbinden wir das alte (oder nennen Sie es das echte) clicked()Signal mit dem eigenen, hausgebackenen Slot click() der Klasse MyButton. Hierbei ist maßgebend entscheidend, dass wirklich die Slot-Elementfunktion von MyButton (Zeile 9 bis 11) ausgeführt wird. Würde die Supermethode QPushButton::click() verwendet, hätte dies eine Endlos-Rekursion zur Folge, weil QPushButton::click() wieder das Signal clicked() auslöst. Sobald der Slot MyButton::click() ausgelöst wurde, können Sie das neue Signal mit der Identifikationsnummer (ID) verschicken (Zeile 10). Um aus diesem Beispiel ein stilvolles Qt-Programm zu machen, wollen wir hierzu noch eine Klasse MyWindow (abgeleitet von QWidget) mit unseren eigenen Buttons (MyButton) und einem Slot implementieren, der sich um die Ausgabe auf dem Bildschirm kümmert. Hier die Headerdatei unserer Klasse mywindow.h: 00 01 02 03 04
// beispiele/signalslot6/mywindow.h #ifndef MYWINDOW_H #define MYWINDOW_H #include #include "mybutton.h"
05 class MyWindow : public QWidget { 06 Q_OBJECT 07 public: 08 MyWindow(QWidget* parent = NULL); 09 private: 10 MyButton* myButton[3]; 11 public slots: 12 // Slot für die Ausgabe 13 void myMessageBox(int id); 14 }; 15 #endif
Unseren Button finden Sie in Zeile 10 MyButton mit eigenem implementierten Signal wieder. In Zeile 11 bis 13 deklarieren wir noch einen eigenen Slot für die Klasse MyWindow. Die Slot-Elementfunktion ist im Grunde eine einfache Nachrichtenbox (QMessageBox), welche die Identifikationsnummer des Signals clicked(int) unserer Buttons zurückgibt. Hierzu die Implementierung der Klasse MyWindow: 00 01 02 03 04
// beispiele/signalslot6/mywindow.cpp #include "mywindow.h" #include "mybutton.h" #include #include
53
2.6
1542.book Seite 54 Montag, 4. Januar 2010 1:02 13
2
Signale und Slots
05 MyWindow::MyWindow(QWidget* parent) : QWidget(parent) { 06 myButton[0] = new MyButton(tr("Drück mich")); 07 myButton[1] = new MyButton(tr("Mich auch")); 08 myButton[2] = new MyButton(tr("Und mich")); 09 QVBoxLayout* layout = new QVBoxLayout(this); 10 for (int i = 0; i < 3; ++i) { 11 layout->addWidget(myButton[i]); 12 connect( myButton[i], SIGNAL( clicked(int) ), this, SLOT( myMessageBox(int) ) ); 13 } 14 } 15 void MyWindow::myMessageBox(int id) { 16 QMessageBox::information( this, tr("Ein Button wurde betätigt"), QString(tr("Button ID: %1")).arg(id), QMessageBox::Ok ); 17 }
In Zeile 12 wird unser neues clicked(int)-Signal mit dem Ausgabe-Slot myMessageBox verbunden. Sobald also unser neues Signal eintrifft, wird eine Nachrichtenbox mit der ID des Buttons ausgegeben. Zum Schluss noch unser Hauptprogramm zur Demonstration: // beispiele/signalslot6/main.cpp #include #include "mywindow.h" #include "mybutton.h" int main(int argc, char *argv[]) { QApplication app(argc, argv); MyWindow* window = new MyWindow; window->show(); return app.exec(); }
Das Programm bei der Ausführung:
Abbildung 2.17
54
Programm nach dem Start
1542.book Seite 55 Montag, 4. Januar 2010 1:02 13
Zusammenfassung
Abbildung 2.18
2.7
Button »Mich auch« mit ID 1 wurde betätigt
Zusammenfassung
Der Umfang dieses Kapitels war zwar nicht enorm, dafür aber sehr detailreich, so dass wir hier die wichtigsten Regeln zusammenfassen, die in Bezug auf das Signal-Slot-Konzept im Allgemeinen zutreffen. 왘
Die Deklaration von Signalen und Slots ist nur in Klassen erlaubt. Eine Deklaration außerhalb von Klassen würde auch nicht in das Klassenkonzept von C++ passen.
왘
Klassen, die eigene Signale bzw. Slots enthalten, müssen von QObject abgeleitet sein. Da bei Qt die meisten GUI-Klassen von QWidget abgeleitet sind, muss man sich diesbezüglich keine großen Gedanken machen, da QWidget wiederum von QObject abgeleitet ist (mehr dazu im nächsten Kapitel).
왘
Verwenden Sie Klassen mit eigenen Signalen bzw. Slots, müssen Sie das Makro Q_OBJECT verwenden. Hinter dem Makro wird kein Semikolon verwendet.
왘
Slots können Sie wie eine gewöhnliche C++-Methode deklarieren und implementieren. Im Grunde sind Slots ja gewöhnliche Methoden, die sich auch außerhalb einer Signal-Slot-Verknüpfung mit connect() direkt verwenden lassen.
왘
Bei der Definition von Slots müssen Sie zuvor das Schlüsselwort slots mit entsprechendem Spezifizierer (private, public) hinzufügen. Wollen Sie einen Slot als virtual deklarieren, können Sie auch protected vor dem Schlüsselwort slots setzen.
왘
Es ist nicht gestattet, Slots als static zu deklarieren, was bei gewöhnlichen C++-Methoden verwendet werden darf.
왘
Wie Methoden können auch Slots Parameter besitzen. Hierbei muss beachtet werden, dass bei einem mit connect() angegebenen Signal dieselben Parametertypen wie beim entsprechenden Slot verwendet werden. Wie bei einer Funktionsüberladung ist auch erlaubt, dass ein Slot weniger Parameter haben darf als das Signal.
55
2.7
1542.book Seite 56 Montag, 4. Januar 2010 1:02 13
2
Signale und Slots
왘
Bei der Definition von Signalen in einer Klasse muss zuvor das Schlüsselwort signals angegeben werden. Ansonsten entspricht auch die Deklaration von Signalen der Deklaration gewöhnlicher Methoden. Wichtig ist hierbei allerdings, dass Signale nur deklariert und niemals direkt implementiert werden dürfen.
왘
Zum Senden von Signalen in einer Klassen-Komponente wird gewöhnlich das Qt-Schlüsselwort emit platziert. emit dient nur der besseren Erkennbarkeit und muss nicht verwendet werden.
왘
Die Signale und Slots werden mit der statischen Funktion QObject:: connect() verbunden.
왘
Wichtig: Bei den Routinen der Makros SIGNAL und SLOT sind nur Typen und keine Werte als Parameter erlaubt. Sie können also nicht bei einer Signalroutine wie SIGNAL(valueChanged( 5 )) einen Wert als Parameter verwenden, sondern dürfen hierzu nur einen Typ wie SIGNAL(valueChanged( int )) verwenden.
Hinweis Natürlich ist es auch möglich, ein Signal von seiner Verbindung, die mittels connect() erstellt wurde, mit QObject::disconnect() wieder zu lösen.
56
1542.book Seite 57 Montag, 4. Januar 2010 1:02 13
Das Qt-Framework umfasst mittlerweile weit über 400 Klassen. Hierbei die Übersicht zu behalten, ist für Einsteiger zunächst nicht ganz einfach. Qt basiert auf einer Ableitungshierarchie. Um sich mit Qt eingehender zu befassen, müssen wir daher erst die Wurzeln und den Aufbau des Frameworks kennen. Sobald Sie wissen, wie Qt »tickt«, werden Sie sich schnell zurechtfinden. Dieses theoretische Kapitel geht also kurz auf den Aufbau des Qt-Frameworks ein.
3
Basisklassen und Bibliotheken von Qt
Wie Sie bereits ein Kapitel zuvor erfahren haben, fordert auch das Signal-SlotKonzept, dass die beteiligten Klassen von der Klasse QObject abstammen. Aber auch viele andere nichtgrafische Klassen basieren auf QObject als Basisklasse. Bspw. werden Klassen, die der Kommunikation zwischen den Prozessen dienen (z. B. QThread), von QObject abgeleitet, damit diese über Signale und Slots kommunizieren können.
3.1
Basisklasse: QObject
QObject wurde im vorigen Kapitel im Zusammenhang mit dem Signal-Slot-Kon-
zept bereits erwähnt. Dabei werden Sie sicherlich festgestellt haben, dass es sich hierbei um eine ziemlich wichtige Klasse in Qt handelt. In der Tat ist QObject eine Basisklasse vieler Qt-Klassen. Alle auf dem Bildschirm dargestellten Widgets leiten bspw. von der Klasse QWidget ab. QWidget wiederum wurde von der Klasse QObject abgeleitet.
3.2
Qt-Klassenhierarchie
Natürlich gibt es auch viele weitere Klassen, die nicht von QObject abgeleitet wurden. Dies sind im Grunde alle Klassen, die kein Signal-und-Slot-Konzept und keine automatische Speicherverwaltung benötigen. Bekanntester Vertreter dürfte hier QString sein. QString ist das, was für den C++-Programmierer die Klasse string und für die Verwendung von Zeichenketten zuständig ist.
57
1542.book Seite 58 Montag, 4. Januar 2010 1:02 13
3
Basisklassen und Bibliotheken von Qt
QString vs. string Es wird empfohlen, in Qt die Klasse QString der Standardklasse string vorzuziehen. QString speichert und verarbeitet den Text im Unicode-Format und erlaubt somit, fast alle Schriftsysteme dieser Erde zu verwenden. Oder kurz: Mit QString müssen Sie sich nicht um die Dekodierung des Zeichensatzes kümmern.
Den besten Überblick über die Klassenhierarchie von Qt bekommen Sie hierbei wieder mit der Dokumentation von Qt. Sie finden die Übersicht im Abschnitt »API Reference« unter »Inheritance Hierarchy« (siehe Abbildung 3.1). Hierbei werden Sie feststellen, dass QObject den größten Anteil von abgeleiteten Klassen besitzt.
Abbildung 3.1
Klassenhierarchie von Qt
Trotzdem werden Sie Klassen wie QPaintDevice entdecken, wovon auch QWidget wiederum einen Teil ererbt hat und somit bei den sichtbaren GUI-Elementen wieder eine Rolle spielt. Ebenso sieht dies etwa bei der Klasse QLayout aus. Diese Klasse wurde bspw. ebenfalls von QObject abgeleitet, hat aber auch die Superbasisklasse QLayoutItem. Die Klasse QLayout beinhaltet Widgets, die zur Anordnung der GUI-Elemente auf dem Fenster benötigt werden. In QVBoxLayout haben Sie bereits einen solchen Abkömmling beider Klassen kennengelernt. Der folgenden Abbildung (3.2) können Sie einen kurzen Überblick zum Aufbau der Qt-Klassenhierarchie entnehmen. Hierbei erkennt man leicht, dass viele Klassen zum Teil von mehreren Klassen abgeleitet wurden. Andere Klassen wiederum, wie bspw. QString, haben gar keine Basisklasse.
58
1542.book Seite 59 Montag, 4. Januar 2010 1:02 13
Qt-Klassenhierarchie
QLayoutItem
QObject
QLayout
QPaintDevice
QWidget
QString
QPrinter
... ...
... QBoxLayout
...
QDialog
... Abbildung 3.2
Schematischer Überblick der Qt-Klassenhierarchie
Gerade auf Einsteiger wirken solch gewaltige Frameworks zunächst ziemlich abschreckend. Sie erfuhren aber bereits in Abschnitt 2.1, wie man die ganze Klassenhierarchie mit der Referenz von Qt nach oben arbeiten kann. Um nochmals auf den Button QPushButton zurückzukommen: Bei der Klassenreferenz von QPushButton im Assistant finden Sie ganz zu Beginn eine Zeile wie »Inherits QAbstractButton«, also von QabstractButton abgeleitet. Klicken Sie auf den Verweis von QAbstractButton. Bei dieser Klassenreferenz erfahren Sie auch gleich, welche Kinder-Widgets aus QAbstractButton erstellt wurden. Dies steht in »Inherited by«, was in diesem Fall die Klassen Q3Button, QCheckBox, QPushButton, QRadioButton und QToolButton wären. Das Eltern-Widget (Inherits) ist hingegen QWidget. Auch finden Sie hier (bei QWidget) gleich endlos viele GUI-Elemente, die aus QWidget abgeleitet wurden. QWidget wiederum wurde von QObject und QPaintDevice (siehe Abbildung 3.3) abgeleitet. QObject
QPaintDevice
QWidget
QAbstractButton
QPushButton
Abbildung 3.3
Klassenhierarchie von QPushButton
59
3.2
1542.book Seite 60 Montag, 4. Januar 2010 1:02 13
3
Basisklassen und Bibliotheken von Qt
Dieses Durchlaufen der Klassen mit der Referenz von Qt ist unerlässlich, wenn es darum geht, nach Funktionen, Methoden, Eigenschaften, Slots oder Signalen bestimmter Klassen zu suchen oder eben um eigene Widgets zu implementieren. Ich weiß, dass ich mich wiederhole, doch kann man dies nicht oft genug erwähnen.
3.3
Speicherverwaltung von Objekten
Da C++ keine automatische Speicherverwaltung wie bspw. Java besitzt, haben wir in den Beispielen des vorigen Kapitels des Öfteren den new-Operator zum Erzeugen neuer Objekte verwendet. Im Abschnitt zuvor lernten Sie die Klassenhierarchie von Qt kennen. Sie sahen (Abbildung 3.3), wie aus QObject abgeleitete Klassen eine Baumhierarchie gebildet wird. Durch eine solche Hierarchie entsteht eine Eltern-Kind-Beziehung. Wird bspw. ein Eltern-Widget gelöscht, werden alle davon abgeleiteten KinderObjekte ebenfalls gelöscht. Die Kinder dieser Kinder-Objekte wiederum löschen auch ihre Nachkommen. Auf diese Weise nimmt uns Qt einen Teil der Speicherverwaltung ab, und Sie müssen nicht jedes einzelne Objekt mit delete per Hand löschen. Damit man die Qt-Klassen verwenden kann, wird bei der Initialisierung immer die Übergabe eines Zeigers auf das aufrufende Elternobjekt verlangt. Dadurch ist gewährleistet, dass die Elternklasse Kenntnis über ihre Kindobjekte besitzt und diese bei der eigenen Löschung auch aus dem Speicher zerstört. Aus diesem Grund muss jedes Qt-Objekt im Heap erzeugt werden, was der Operator new ja leistet. Mit einem Klassen-Konstruktor auf dem Stack gelegte Objekte werden nach der Abarbeitung wieder gelöscht. Das Widget wird zwar kurz erzeugt, aber nie angezeigt. Vergleichsfall Als Beispiel nenne ich Ihnen ein altbekanntes Problem: die Rückgabe einer globalen Variablen aus einer normalen Funktion.
Hierzu bspw. folgende Zeilen: 01 02 03 04 05
60
QWidget* win = new QWidget; QVBoxLayout* layout = new QVBoxLayout(win); QPushButton* but01 = new QPushButton("Label"); // Widgets zur vertikalen Box hinzufügen layout->addWidget(but01);
1542.book Seite 61 Montag, 4. Januar 2010 1:02 13
Programm-Bibliotheken von Qt
QVBoxLayout wird hier als Kind von QWidget gezeugt. Der Button hingegen ist bei der Zeugung noch elternlos und würde jetzt noch nicht angezeigt werden. Erst beim Aufruf von addWidget() wird dafür gesorgt, dass jemand die Elternschaft für den Button übernimmt. Allerdings kommt dabei oft auch ein wenig Verwirrung auf, da nicht die Klasse QVBoxLayout die Elternschaft von QPushButton übernimmt, sondern QWidget. Betrachtet man die Widget-Hierarchie, wird man feststellen, dass QPushButton und QVBoxLayout in der Hierarchie etwa gleichgestellt sind. Dabei würde die ganze Speicherverwaltung natürlich nicht mehr funktionieren. Deswegen müssen die Kinderelemente eines Widgets immer GUI-Elemente eines übergeordneten Widgets sein. Durch den Codeausschnitt ergibt sich die in Abbildung 3.4 abgedruckte Hierarchie.
QWidget
QPushButton
Abbildung 3.4
QVBoxLayout
Eltern-Kind-Elemente
Allerdings ist es auch möglich, das Eltern-Widget gleich bei der Erzeugung mit anzugeben. Bezogen auf den eben gezeigten Codeausschnitt würde dies folgendermaßen aussehen: 01 02 03
QWidget* win = new QWidget; // Eltern-Widget als zweites Argument angeben QPushButton* but01 = new QPushButton( "Label", win );
3.4
Programm-Bibliotheken von Qt
Wenn Sie mit dem Qt-Assistant ein wenig die Klassen betrachtet haben, werden Sie feststellen, dass Qt in mehrere Pakete (Module) aufgeteilt ist. Neben der Erstellung grafischer Oberflächen bietet Qt ja noch viel mehr für die Entwicklung von Applikationen. Hier zunächst ein Überblick über die in den nächsten Abschnitten näher erörterten Qt-Bibliotheken: 왘
QtCore enthält die grundlegenden (core) Funktionen und Klassen ohne grafische Oberfläche, die von den anderen Bibliothken verwendet werden.
왘
QtGui erweitert die Bibliothek QtCore und umfasst die gesamte GUI-Funktionalität.
왘
QtNetwork enthält alle Klassen und Funktionen, die nötig sind, um bspw. TCP/IP (und UDP) Server- und/oder Client-Anwendungen zu schreiben.
61
3.4
1542.book Seite 62 Montag, 4. Januar 2010 1:02 13
3
Basisklassen und Bibliotheken von Qt
왘
QtOpenGL enthält Klassen, die es einfach machen, die OpenGL-Schnittstelle in Qt-Anwendungen zu verwenden.
왘
QtSql zuständig für die Einbindung von SQL-Datenbanken in den Qt-Anwendung.
왘
QtSvg stellt Klassen zum Anzeigen von SVG-Grafiken (Vektorformat) zur Verfügung.
왘
QtXml enthält C++-Implementationen von SAX- und DOM-Klassen.
왘
QtDesigner beinhaltet Klassen, mit denen Sie Plugins (Widgets) für den QtDesigner erstellen können.
왘
QtUiTools – dieser Teil der Bibliothek enthält Klassen (im Augenblick eigentlich nur eine), die es Ihnen ermöglichen, Standalone-Anwendungen (Benutzerschnittstellen; User Interfaces (UI)) dynamisch zur Laufzeit zu erzeugen und in einer .ui-Datei abzuspeichern.
왘
QtAssistant ermöglicht die Verwendung der Online-Hilfe des Assistant von Qt.
왘
Qt3Support enthält Klassen, um Anwendungen von Version 3 auf Version 4 zu portieren.
왘
QtTest wird verwendet, um Qt-Anwendungen und -Bibliotheken zu testen.
왘
QtScript und QtScriptTools enthalten Klassen zur Verarbeitung von ECMAScript (= JavaScript). QtScriptTools enthält hierfür noch weitere zusätzliche Klassen.
왘
QtWebKit enthält Klassen zur Darstellung und Verarbeitung von Webseiten (seit Qt 4.4).
왘
Phonon beinhaltet Klassen zur Verwendung von Multimedia-Inhalten wie Audio oder Video (seit Qt 4.4).
왘
QtMultimedia beinhaltet Multimedia-Funktionalitäten auf der niedrigeren Ebene (seit Qt 4.6).
Die kommerzielle Version von Qt unter Windows bietet mit QAxContainer und QAxServer außerdem zwei Programm-Bibliotheken für ActiveX-Controls und ActiveX-Server an. Für alle Unix-Plattformen (und Qt-Editionen) stehen mit der Bibliothek QtDBus noch Klassen für die Interprozesskommunikation mit D-BUS zur Verfügung. D-BUS D-BUS ist ein Software-System, das mehrere Möglichkeiten anbietet, Programme miteinander kommunizieren zu lassen.
62
1542.book Seite 63 Montag, 4. Januar 2010 1:02 13
Programm-Bibliotheken von Qt
Wenn Sie qmake verwenden, um Ihre Projekte zu bauen, werden QtCore (core) und QtGui (gui) standardmäßig hinzugefügt. Wollen Sie bspw., dass bei Ihrem Projekt nur QtCore gelinkt wird, müssen Sie in der .pro-Datei (Projektdatei) folgende Zeile ändern bzw. hinzufügen: QT -= gui
Die Verwendung entspricht den erweiterten Operatoren von C/C++. Wollen Sie bspw. dem Projekt die Bibliothek QtNetwork hinzufügen, müssen Sie nur Folgendes in die .pro-Datei einfügen: QT += network
Die zu linkende Bibliothek legt qmake mit QT fest. Wenn Sie eine Qt-Bibliothek hinzufügen wollen, müssen Sie nur den +=-Operator vor der gewünschten Bibliothek setzen. Soll eine Bibliothek entfernt werden, dann erledigen Sie dies mit -=-Operator. In der folgenden Tabelle (3.1) finden Sie die möglichen Werte, die Sie für die QTVariable der .pro-Datei (Projektdatei) verwenden können, und welche Bibliothek Sie dabei hinzulinken. QT-Wert (Option)
Bibliothek
core (per Standard implementiert)
QtCore
designer
QtDesigner
gui (per Standard implementiert)
QtGui
network
QtNetwork
multimedia
QtMultimedia
opengl
QtOpenGL
phonon
Phonon
qt3support
Qt3Support
qtestlib
QtTest
script
QtScript
scripttools
QtScriptTools
sql
QtSql
svg
QtSvg
uitools
QtUiTools
webkit
QtWebKit QtXml
xml
Tabelle 3.1
Bibliotheken hinzulinken
63
3.4
1542.book Seite 64 Montag, 4. Januar 2010 1:02 13
3
Basisklassen und Bibliotheken von Qt
Natürlich ist es auch möglich, mehrere Bibliotheken dem Projekt hinzuzulinken. Zu diesem Zweck muss man der .pro-Datei nur mehrere Bibliotheken, getrennt von einem Leerzeichen, hinzufügen: QT += network opengl sql svg xml qt3support
Hiermit hätten Sie praktisch alle Bibliotheken (gui und core sind per Standard dabei) hinzugelinkt.
3.4.1
QtCore
QtCore ist die grundlegende Basisbibliothek, auf der Qt basiert, und enthält im Grunde nur Klassen, die mit keiner grafischen Ausgabe verbunden sind. QtCore beinhaltet auch die von C++-Programmierern so bezeichnete STL (Standard Template Library) – und zwar mitsamt der Funktionalität und der Art der Verwendung. Alle Qt-eigenen Container-Klassen (und auch Strings) verwenden hierbei die Methode »copy on write«. Damit lassen sich die Klassen ohne großen Aufwand als Funktionswert zurückgeben. Auch die Klasse QString (zur Behandlung von Zeichenketten) ist ein Teil von QtCore. Es wurde bereits erwähnt, dass die einzelnen Zeichen hierbei im Unicode-Standard (UTF-16) kodiert werden. Anstelle eines char kommt hierbei ein short int (16 Bits) zum Zuge. Natürlich enthält QString auch alles Nötige für Konvertierungen unterschiedlicher Zeichensatzsysteme wie ASCII, UTF, verschiedener ISO-Standards usw. QtCore ist auch für die Klassen QObject und QCoreApplication (Basisklasse für QApplication) verantwortlich. Insgesamt beinhaltet QtCore mehr als 200 Klas-
sen. Die grundlegenden und in der Praxis gängigsten seien hier stichpunktartig notiert: 왘 왘
die grundlegenden Datentypen QString und QByteArray die grundlegenden Datenstrukturen (wie bei STL) QList, QVector, QHash QMap, usw.
왘
Klassen für die Ein-/Ausgabe wie QFile, QDir, QTextStream usw.
왘
Klassen, um Multithreads wie QThread, QMutex, QWaitCondition usw. zu programmieren
왘
Klassen für Datum und Uhrzeit wie QDate, QTime und QDateTime
왘
Laden von Bibliotheken und Plugins zur Laufzeit wie QLibrary und QPluginLoader
왘
64
Die Klasse QObject, Hauptträger vieler Konzepte wie Kommunikation zwischen Signalen und Slots, Speicherverwaltung und Objekteigenschaften.
1542.book Seite 65 Montag, 4. Januar 2010 1:02 13
Programm-Bibliotheken von Qt
QtCore wird häufig auch alleine ohne QtGui verwendet, um Client-/Server-Programme mit Multithreads oder Sockets zu schreiben. Häufig ist hierfür keine grafische Oberfläche nötig.
3.4.2
QtGui
Zunächst setzt QtGui die Bibliothek QtCore voraus. QtGui enthält alle Klassen, die etwas auf dem Bildschirm zeichnen. Dabei wird die Klasse QtWidget erweitert. QtWidget allein zeichnet im Grunde nur ein leeres Rechteck auf den Bildschirm. Auch neue GUI-Elemente werden mithilfe von QWidget erstellt, indem man die neue Klasse davon ableitet. QtGui ist wohl der Hauptgrund, weshalb viele Anwender Qt verwenden und wird wohl auch den Großteil des Buches ausmachen. Daher zählen wir im Folgenden die gängigsten Teile von QtGui stichpunktartig auf 왘
Klassen, um vorgefertigte Dialoge zu verwenden (bspw. QFileDialog (Dateiauswahl), QPrinterDialog (Dokument drucken), QColorDialog (Farbauswahl), usw.);
왘
Klassen, um die Widgets anzuordnen – sogenannte Layout-Klassen (bspw. QVBoxLayout, QHBoxLayout, QGridLayout, usw.);
왘
die Klasse QWidget und unzählige davon abgeleitete GUI-Klassen;
왘
Klassen zum Zeichnen auf dem Bildschirm (bspw. QBrush, QPainter, QPen, usw.);
왘
die Klasse QApplication.
3.4.3
QtNetwork
Die Klasse QtNetwork bietet eine Menge Klassen an, mit denen Sie TCP/IP (sowie UDP)-Client-/Server-Anwendungen schreiben können. Neben den Klassen QTcpSocket, QTcpServer und QUdpSocket, die ja in einer tieferen Ebene operieren, bietet QtNetwork mit QHttp und QFtp Klassen der höheren Ebene an, womit das HTTP- und FTP-Protokoll verwendet werden kann. QtNetwork benötigt ebenfalls die Bibliothek QtCore, nicht aber QtGui.
3.4.4
QtOpenGL
Mit dieser Bibliothek können Sie die OpenGL-API in Ihrer Qt-Anwendung verwenden. OpenGL ist eine Portable Standard-Schnittstelle zum Rendern von 3DGrafik (ähnlich wie DirectX). Mit QtOpenGL können Sie OpenGL verwenden wie jedes andere Qt-Widget.
65
3.4
1542.book Seite 66 Montag, 4. Januar 2010 1:02 13
3
Basisklassen und Bibliotheken von Qt
3.4.5
QtSql
Die Bibliothek QtSql beinhaltet Klassen für die Unterstützung SQL-Datenbanken. In der folgenden Tabelle (3.2) sind Client-APIs aufgelistet, für die Qt einen passenden Treiber zur Verfügung stellt. Treibername
Datenbanksystem
QIBASE
Borland InterBase
QMYSQL
MySQL
QODBC
Open Database Connectivity (ODBC) (wird von Microsoft SQL-Server verwendet)
QPSQL
PostgresSQL
QSQLITE2
SQLite (Version 2)
QSQLITE
SQLite (Version 3)
Tabelle 3.2
Datenbanken, für die Qt einen Treiber mitliefert
Diese Treiber sind auf jeden Fall in der Open-Source-Edition von Qt enthalten. Weitere Datenbanksysteme bzw. Treiber, die sich mit GPL nicht unter einen Hut bringen lassen, stehen nur bei der kommerziellen Version von Qt zur Verfügung (siehe Tabelle 3.3). Treibername
Datenbanksystem
QDB2
IBM DB2
QOCI
Oracle Call Interface Treiber
QTDS
Sybase Adaptive Server
Tabelle 3.3
DBMS (nur in der kommerziellen Version von Qt)
Besonders beeindruckend an QtSql ist die Tatsache, dass man nur ein entsprechendes Datenbanksystem auszuwählen braucht und gleich danach mit dem QtCode auf jede DB-Schnittstelle zugreifen kann.
3.4.6
QtSvg
Die Bibliothek QtSvg unterstützt Klassen, mit denen sich Vektorgrafiken mit dem SVG-Format anzeigen lassen. SVG (Scalable Vector Graphics, »Skalierbare Vektorgrafiken«) ist ein Standard zur Beschreibung zweidimensionaler Vektorgrafiken in der XML-Syntax. SVG wurde im September 2001 vom W3C als Empfehlung veröffentlicht. Viele aktuelle Webbrowser sind von Haus aus in der Lage, einen Großteil des Sprachumfangs darzustellen, wie z. B. Mozilla Firefox oder
66
1542.book Seite 67 Montag, 4. Januar 2010 1:02 13
Programm-Bibliotheken von Qt
Opera. Bisher unterstützt QtSvg die Profile SVG-Basic und SVG-Tiny. ECMAScripts und DOM-Manipulationen werden von Qt zurzeit noch nicht unterstützt.
3.4.7
QtXml
Die Bibliothek QtXml stellt einen einfachen XML-Parser zur Verfügung. Dieser lässt sich direkt über die SAX2-Schnittstelle (SAX = Simple API for XML) verwenden. In QtXml ist außerdem der DOM-Standard (DOM = Document Object Model) implementiert. Damit lässt sich ein XML-Dokument parsen und die entsprechende Baumstruktur verändern bzw. anpassen. Das so veränderte Dokument kann natürlich wiederum als XML-Dokument ausgegeben werden. DOM erlaubt es sogar, ein neues XML-Dokument daraus zu erstellen.
3.4.8
Qt3Support
Da Qt4 gegenüber Qt3 enorm erweitert und verbessert wurde und tlw. inkompatibel geworden ist, liefert Trolltech diese Bibliothek mit. Wir beschreiben allerdings Qt Version 4 und gehen nicht näher darauf ein.
3.4.9
QtScript
Mit dieser Bibliothek können Sie eine zu ECMA-262 kompatible Scriptsprache (bspw. JavaScript) in C++-Anwendungen einbetten. Damit wird dann eine Umgebung angeboten, die zur dynamischen Interaktion mit Qt-Objekten verwendet werden kann. QtScript deaktiviert Ab der Version 4.6 wird QtScript auf allen Plattformen deaktiviert, wenn diese QtWebKit nicht unterstützen. Trotzdem kann man QtScript auf diesen Plattformen wieder aktivieren.
3.4.10 QtWebKit QtWebKit ist eine HTML-Rendering-Bibliothek, die alles mitliefert, womit Sie quasi einen eigenen Webbrowser entwickeln könnten. WebKit wurde ursprünglich von Apple entwickelt und als Grundlage für den Webbrowser Safari verwendet. Mittlerweile setzen mehrere Hersteller auf dieses Webkit. Bekannte Software auf Basis der Bibliothek sind Adobe AIR, Google Chrome oder Omniweb, um einige zu nennen. Aber auch viele Browser auf mobilen Endgeräten wie iPhone, Palm Pre, Nokias S60-Serie oder Googles Android sind mithilfe von Webkit aufgebaut. Die Grundlagen von Webkit stammen ursprünglich vom KDE-Projekt.
67
3.4
1542.book Seite 68 Montag, 4. Januar 2010 1:02 13
3
Basisklassen und Bibliotheken von Qt
3.4.11 Phonon Wie auch das Webkit ist Phonon (hieß früher KDEMM) eine Multimedia-API vom KDE-Projekt und seit der Version 4.4 auch bei Qt als Multimediaschnittstelle an Bord. Phonon bietet hierbei eine einheitliche (Wrapper-)Schnittstelle für Audio- und Video-Anwendungen ähnlich wie DirectShow unter Windows oder QuickTime unter Mac OS X.
3.4.12 Der Rest Auf die weiteren Bibliotheken wie QtMultimedia, QtDesigner, QtUiTools, QtTest und die kommerziellen Bibliotheken wird in diesem Buch nicht näher eingegangen. Es existiert zudem noch eine Migrationslösung in Qt4 für MFC-, Motif- und Xt-basierende Anwendungen. Auch hierbei handelt es sich um eine kommerzielle Lösung, die Trolltech separat unter dem Namen QtSolution anbietet.
3.5
Meta-Include-Headerdatei
Alle Programmbibliotheken aus Abschnitt 3.4 zuvor sind auch als Meta-IncludeHeaderdatei enthalten, um bspw. die einzelnen Klassen aus der GUI-Bibliothek QtGui wie folgt zu inkludieren: #include #include #include #include #include #include ...
Statt der vielen Inkludierungen der Headerdateien können Sie auch die MetaHeaderdatei QtGui inkludieren: #include
Wenn Sie sich diese Meta-Include-Datei ansehen, werden Sie feststellen, dass alle Klassen der GUI-Bibliothek inkludiert sind. Ein Nachteil dieser Inkludierung kann sein, dass die Kompilierung etwas mehr Zeit in Anspruch nimmt. Im Grunde trifft dies aber nur auf ältere Compiler zu, die keine vorkompilierten Headerdateien unterstützen. In den Buch-Beispielen verwenden wir zunächst noch die Klassen-Headerdateien und führen dann allmählich die Meta-Include-Datei ein. Im Prinzip entspricht der Klassenname bei Qt auch dem Headernamen. Lautet der Klassenname bspw.
68
1542.book Seite 69 Montag, 4. Januar 2010 1:02 13
Meta-Include-Headerdatei
QPushButton, müssen Sie nur den Include hinzufügen (oder eben die Meta-Include-Datei ).
Für alle anderen Bibliotheken gilt dasselbe wie für die Bibliothek QtGui mit ihrem Meta-Include. Somit können Sie der Bibliothek QtXml anstelle der unzähligen XML-Include-Dateien auch nur den Meta-Include hinzufügen.
69
3.5
1542.book Seite 70 Montag, 4. Januar 2010 1:02 13
1542.book Seite 71 Montag, 4. Januar 2010 1:02 13
In Ihrem ersten grundlegenden Kapitel zur GUI-Programmierung mit Qt gehen wir zunächst auf die Dialoge ein. Sie werden eigene Dialoge mit Unterstützung der Layout-Klassen erstellen. Anschließend erörtern wir die fertigen Dialoge von Qt. Im zweiten Teil des Kapitels behandeln wir die zahlreichen Widgets von Qt eingehender.
4
Dialoge, Layout und Qt-Widgets
4.1
Eigene Widget-Klassen erstellen
Im Kapitel zu den Signalen und Slots wurde Ihnen ab Abschnitt 2.4, in dem es darum ging, eine Klasse um eigene Signale und Slots zu erweitern, vor Augen geführt, wie man dies über die Ableitung einer Klasse erreicht. Der dabei demonstrierte Vorgang ist eigentlich ein üblicher Weg bei der Qt-Programmierung. Man leitet dabei normalerweise von einer in der Hierarchie etwas höher angesiedelten Klasse ab (bspw. meistens QWidget oder QDialog) und platziert darin die für das Teil-Objekt benötigten Widgets. In der Praxis werden die Anwendungen dann aus mehreren solchen Teilobjekten zusammengesetzt. Dieser Weg hat viele Vorteile. Will man bspw. das Teilobjekt verbessern, verändern oder erweitern, genügt es in der Regel, nur den Code dieser Klasse zu verändern. Die Hauptfunktion bleibt unverändert. Insgesamt entspricht dies den Vorteilen, die man auch mit C++ und den entsprechenden Klassen hat. Im Grunde erstellt man sein eigenes Widget meist in folgenden Schritten (wobei hier »erweitertes« eher zutrifft): 왘
Widget von der gewünschten Klasse ableiten
왘
GUI-Elemente erzeugen, die im Widget angezeigt werden sollen
왘
angezeigte GUI-Elemente anordnen (Layout)
왘
Signal- und Slot-Verbindungen einrichten
Hierzu wollen wir ein einfaches Beispiel in Form einer einfachen Nachrichtenbox mit einem Button erstellen. Das Ganze soll am Ende folgendermaßen aussehen (Abbildung 4.1):
71
1542.book Seite 72 Montag, 4. Januar 2010 1:02 13
4
Dialoge, Layout und Qt-Widgets
Abbildung 4.1
Einfache Nachrichtenbox
Anstatt also im Hauptprogramm einen Label, einen Button und ein Widget für das Layout zu erzeugen, erstellt man hier ein eigenes Widget, das sich jederzeit ohne großen Aufwand im Hauptprogramm erzeugen und anzeigen lässt. Die komfortablere Alternative: QMessageBox Qt bietet natürlich mit QMessageBox eine erheblich einfachere und komfortablere Alternative, Nachrichtenboxen zu verwenden.
Als Erstes erstellen wir das Grundgerüst der Klasse. Hierzu wird alles als Grundgerüst in einer Headerdatei zusammengefasst, was man für eine Nachrichtenbox benötigt. In diesem Beispiel wurde folgendes Gerüst erstellt: 00 01 02 03 04 05 06
// beispiele/simple/mywidget.h #ifndef MYWIDGET_H #define MYWIDGET_H #include #include #include #include
07 class MyWidget : public QWidget { 08 public: 09 MyWidget(const char* qstr = "Bitte Button betätigen", const char* but = "Ende", QWidget *parent = 0 ); 10 private: 11 QPushButton *button0; 12 QLabel* label; 13 QVBoxLayout* layout; 14 }; 15 #endif
In diesem Beispiel werden keine Methoden oder eigene Signale bzw. Slots implementiert. Der erste Parameter im Konstruktor (Zeile 9) wird für das Text-Label (QLabel) verwendet. Gibt der Anwender nichts anderes an, steht hier der vorgegebene Text in der Nachrichtenbox (hier »Bitte Button betätigen«). Gleiches gilt
72
1542.book Seite 73 Montag, 4. Januar 2010 1:02 13
Eigene Widget-Klassen erstellen
für das zweite Argument des Konstruktors in Bezug auf den Button (QPushButton). Als zusätzliche Widgets in der Klasse MyWidget wurden hier noch QPushButton, QLabel und QVBoxLayout (Zeile 11 bis 13) implementiert. Nun zur eigentlichen Definition der Klasse MyWidget: 00 // beispiele/simple/mywidget.cpp 01 #include "mywidget.h" 02 #include 03 // neue Widget-Klasse vom eigentlichen Widget ableiten 04 MyWidget::MyWidget( const char* lab, const char* but, QWidget *parent): QWidget(parent) { 05 // Elemente des Widgets erzeugen 06 button0 = new QPushButton (but); 07 layout = new QVBoxLayout(this); 08 label = new QLabel(lab); 09 // Elemente des Widgets anordnen/anpassen 10 layout->addWidget(label); 11 layout->addWidget(button0); 12 // Signale des Widgets einrichten 13 connect( button0, SIGNAL( clicked() ), qApp, SLOT( quit() ) ); 14 }
Zunächst erzeugen wir die GUI-Elemente des neuen Widgets (Zeile 6 bis 8), die anschließend einem Layout entsprechend angeordnet werden (Zeile 10 bis 11). Am Ende richten wir noch eine Signal-Slot-Verbindung (Zeile 13) ein. Dabei dürfte sich wohl gleich die Frage stellen, was qApp hier zu suchen hat. qApp ist ein Zeiger auf eine globale Variable in . Dieser verweist auf die eindeutige Instanz der Anwendung. Wir brauchen diesen globalen Zeiger, um die Anwendung bzw. in diesem Fall die Nachrichtenbox wieder zu beenden. So – oder so ähnlich – sind im Grunde alle Qt-Anwendungen aufgebaut. Fehlt eigentlich nur noch eine Hauptfunktion, um die Klasse bei der Ausführung zu demonstrieren: 00 // beispiele/simple/main.cpp 01 #include 02 #include "mywidget.h" 03 int main(int argc, char *argv[]) { 04 QApplication app(argc, argv); 05 MyWidget* window = new MyWidget("Programm mit Ende beenden");
73
4.1
1542.book Seite 74 Montag, 4. Januar 2010 1:02 13
4
Dialoge, Layout und Qt-Widgets
06 07 08 }
4.2
window->show(); return app.exec();
Widgets anordnen – das Layout
Ein weiteres sehr wichtiges Kapitel ist das Layout der Anwendungen, womit die Anordnung der Widgets gemeint ist. Bspw., ob die einzelnen Widgets horizontal oder vertikal angeordnet werden sollen. Oder: Was passiert, wenn der Anwender die Größe des Fensters verändert, usw. Der ein oder andere Leser wird mir nun entgegnen, das Thema Layout sei doch Sache des Designers von Qt (ein RAD-Tool). Im Grunde trifft das auch zu. Sie werden wohl kaum das Layout Zeile für Zeile selbst schreiben. Hierfür verwendet man bei einem vernünftigen Framework immer ein RAD-Tool. Allerdings ist es für den Qt-Einsteiger unerlässlich zu wissen, welche Layout-Widgets es gibt und wie man sie verwendet. Wenn die Grundlagen dieser Widgets bekannt sind, ist auch der Designer von Qt Ihr Freund (siehe Kapitel 13).
4.2.1
Grundlegende Layout-Widgets
Bevor wir das grundlegende Qt-Layout-Konzept näher erläutern, wollen wir uns zunächst die Klassenhierarchie davon ansehen (Abbildung 4.2).
QObject
QStackedLayout
QVBoxLayout
Abbildung 4.2
QLayoutItem
QLayout
QSpacerItem
QBoxLayout
QGridLayout
QWidgetItem
QHBoxLayout
Klassenhierarchie für das Qt-Layout
In der Praxis werden hierbei die Klassen QGridLayout, QStackedLayout, QVBoxLayout und QHBoxLayout am meisten (und auch direkt) verwendet. Alle Klassen stammen von der Basisklasse QLayout ab, die wiederum von der SuperBasisklasse QObject und QLayoutItem abgeleitet ist. Von QLayoutItem alleine
74
1542.book Seite 75 Montag, 4. Januar 2010 1:02 13
Widgets anordnen – das Layout
wurden die beiden Klassen QSpaceItem und QWidgetItem abgeleitet, die allerdings beide normalerweise nicht direkt verwendet werden. QBoxLayout wiederum lässt sich direkt verwenden, was aber ebenfalls eher selten der Fall ist, weil hierbei die beiden Klassen QVBoxLayout und QHBoxLayout die meisten Fälle abdecken. QGridLayout, QVBoxLayout und QHBoxLayout Der einfachste und schnellste Weg, Ihrer Anwendung ein gutes Layout zu verpassen, dürften die eingebauten Widgets (auch Layout-Manager genannt) QHBoxLayout, QVBoxLayout und QGridLayout sein. Alle drei Klassen sind von QLayout (siehe Abbildung 4.2) abgeleitet. Die einzelnen Layouts lassen sich recht schnell beschreiben: 왘
QVBoxLayout – ordnet die Widgets in einer vertikalen Spalte von oben nach
왘
QHBoxLayout – ordnet die Widgets in einer horizontalen Reihe von links nach
unten an; rechts an (in Ländern, wo die Schriftzeile von rechts nach links verläuft, ist auch die Anordnung entsprechend gestaltet); 왘
QGridLayout – ordnet die Widgets in einem zweidimensionalen Raster an.
Man kann sich dies ähnlich wie bei einer Tabellenkallkulation vorstellen. Ein Widget kommt bspw. in Zeile 1 und Reihe 3, wobei die Zählung von 0 anfängt. In der folgenden Abbildung (4.3) können Sie alle drei grundlegenden Layouts von Qt näher betrachten.
Abbildung 4.3
QVBoxLayout, QHBoxLayout und QGridGroup
75
4.2
1542.book Seite 76 Montag, 4. Januar 2010 1:02 13
4
Dialoge, Layout und Qt-Widgets
Natürlich will ich Ihnen nicht nur eine entsprechende Abbildung zeigen, sondern auch ein Beispiel liefern. Der Einfachheit halber verwenden wir zunächst nur Buttons (QPushButton) als Widgets. Am einfachsten wird es sein, Ihnen als Erstes das Grundgerüst, die Headerdatei, zu zeigen: 00 01 02 03 04 05 06 07 08
// beispiele/layout1/mylayout.h #ifndef MYLAYOUT_H #define MYLAYOUT_H #include #include #include #include #include #include
09 class MyLayoutEx : public QWidget { 10 public: 11 MyLayoutEx(); 12 private: 13 QPushButton *but01[3], *but02[3], *but03[3], *but04[3]; 14 QVBoxLayout* AllBox; 15 QVBoxLayout* VBox; 16 QHBoxLayout* HBox; 17 QGridLayout* Grid; 18 QGroupBox* VGroup; 19 QGroupBox* HGroup; 20 QGroupBox* GridGroup; 21 }; 22 #endif
Neben den Buttons finden Sie hier die drei Layout-Widgets QVBoxLayout, QHBoxLayout und QGridLayout. Dass hier zweimal Klassen vom Typ QVBoxLayout vorhanden sind, hat damit zu tun, dass die einzelnen Layout-Widgets auch wieder im Fenster angeordnet werden müssen. Wie Sie Abbildung 4.3 entnehmen können, haben wir die Layout-Widgets vertikal angeordnet. Bevor wir die einzelnen Layout-Widgets allerdings in die vertikale Box einfügen, stecken wir die Layouts mit den Buttons in eine Gruppenbox (QGroupBox). Im Grunde ist dies ein Widget, das seinerseits Widgets beinhalten kann und zusätzlich über einen Rahmen um diese Widgets mit einem Text-Label verfügt. Auch hier erscheint das Ganze wieder komplizierter, als es tatsächlich ist. Am besten sehen Sie sich dazu den entsprechenden Konstruktor und die anschließende Erläuterung an:
76
1542.book Seite 77 Montag, 4. Januar 2010 1:02 13
Widgets anordnen – das Layout
00 // beispiele/layout1/mylayout.cpp 01 #include "mylayout.h" 02 #include 03 // neue Widget-Klasse vom eigentlichen Widget ableiten 04 MyLayoutEx::MyLayoutEx() { 05 // 3 mal 4 Buttons erzeugen 06 for( int j = 0; j < 3; ++j ) { 07 but01[j] = new QPushButton("Button01"); 08 but02[j] = new QPushButton("Button02"); 09 but03[j] = new QPushButton("Button03"); 10 but04[j] = new QPushButton("Button04"); 11 } 12 // Buttons vertikal anordnen 13 VBox = new QVBoxLayout; 14 VBox->addWidget(but01[0]); 15 VBox->addWidget(but02[0]); 16 VBox->addWidget(but03[0]); 17 VBox->addWidget(but04[0]); 18 // eine Box mit Label um die Buttons 19 VGroup = new QGroupBox("QVBoxLayout"); 20 // die vertikal Box mit den Buttons 21 // in die Group-Box stecken 22 VGroup->setLayout(VBox); 23 // Buttons horizontal anordnen 24 HBox = new QHBoxLayout; 25 HBox->addWidget(but01[1]); 26 HBox->addWidget(but02[1]); 27 HBox->addWidget(but03[1]); 28 HBox->addWidget(but04[1]); 29 // eine Box mit Label um die Buttons 30 HGroup = new QGroupBox("QHBoxLayout"); 31 // die horizont. Box mit den Buttons 32 // in die Group-Box stecken 33 HGroup->setLayout(HBox); 34 // Buttons auf einen Raster anordnen 35 Grid = new QGridLayout; 36 Grid->addWidget(but01[2], 0, 0); 37 Grid->addWidget(but02[2], 0, 1); 38 Grid->addWidget(but03[2], 1, 0); 39 Grid->addWidget(but04[2], 1, 1); 40 // eine Box mit Label um die Buttons 41 GridGroup = new QGroupBox("QGridGroup"); 42 // die Raster-Box mit den Buttons 43 // in die Group-Box stecken
77
4.2
1542.book Seite 78 Montag, 4. Januar 2010 1:02 13
4
Dialoge, Layout und Qt-Widgets
44 45 46 47 48 49 50 60 61 62 63 64 }
GridGroup->setLayout(Grid); // eine vertikale Box erzeugen AllBox = new QVBoxLayout(this); // alle Group-Boxen in die vertik. Box stecken AllBox->addWidget(VGroup); AllBox->addWidget(HGroup); AllBox->addWidget(GridGroup); // Layout setzen setLayout(AllBox); // Fenstertitel angeben setWindowTitle("Basic Layouts");
In den Zeilen 6 bis 11 erzeugen wir zunächst jeweils 3 mal 4 Buttons der Klasse QPushButton, anschließend in Zeile 13 eine vertikale Layout-Box (QVBoxLayout), der in den Zeilen 14 bzw. 17 jeweils 4 Buttons mit addWidget() hinzugefügt werden. Jetzt wird in Zeile 19 eine Group-Box (QGroupBox) mit dem Text-Label »QGroupBox« erzeugt, worin in Zeile 22 dann die vertikale Box (QVBoxLayout) mitsamt der Buttons platziert wird. Selbiger Vorgang jetzt auch in den Zeilen 24 bis 33, nur eben bezogen auf eine horizontale Box (QHBoxLayout). Zum Schluss geschieht Ähnliches mit dem RasterLayout (QGridLayout). Allerdings gestaltet sich hier das Hinzufügen der einzelnen Widgets (hier Buttons) mit addWidget() ein wenig anders. Hierzu nochmals der Codeausschnitt, jetzt mit Kommentaren versehen: // Zeile 0; Spalte 0 Grid->addWidget(but01[2], // Zeile 0; Spalte 1 Grid->addWidget(but02[2], // Zeile 1; Spalte 0 Grid->addWidget(but03[2], // Zeile 1; Spalte 1; Grid->addWidget(but04[2],
0, 0); 0, 1); 1, 0); 1, 1);
Natürlich ist QGridLayout wesentlich vielseitiger, als es den Anschein hat. Verwendet man bspw. folgende Rasterpunkte, ergibt sich Folgendes: // Zeile 0; Spalte 0; Grid->addWidget(but01[2], // Zeile 1; Spalte 0; Grid->addWidget(but02[2], // Zeile 1, Spalte 1; Grid->addWidget(but03[2], // Zeile 2; Spalte 1 Grid->addWidget(but04[2],
78
0, 0); 1, 0); 1, 1); 2, 1);
1542.book Seite 79 Montag, 4. Januar 2010 1:02 13
Widgets anordnen – das Layout
Verwendet man dieses Raster, erhält man folgende Abbildung:
Abbildung 4.4
QGridLayout im Einsatz (1)
Unschön ist hierbei, dass »Button01« und »Button04« hier relativ verloren herumstehen. Wollen Sie, dass auch die restliche Fläche mit dem Button ausgefüllt wird, müssen Sie bei den Buttons zusätzlich angeben wie weit sich die Abgrenzung der Buttons erstrecken soll. Dies erledigen Sie mit der folgenden Angabe: Grid->addWidget(but01[2], Grid->addWidget(but02[2], Grid->addWidget(but03[2], Grid->addWidget(but04[2],
0, 1, 1, 2,
0, 1, 2); 0); 1); 0, 1, 2);
Bezogen auf die erste Zeile addWidget(but01[2], 0, 0, 1, 2), bedeutet dies: Leg den Button in Zeile 0 und Spalte 0. Der Button soll sich dabei nur über die eine Zeile, aber über zwei Spalten erstrecken. Durch diese weiteren Angaben bei addWidget() ergibt sich folgende Abbildung:
Abbildung 4.5
QGridLayout im Einsatz (2)
Nun zurück zur Implementierung der Klasse MyLayoutEx. Am Ende des Beispiels erzeugen wir in Zeile 46 erneut eine vertikale Box, der in den Zeilen 48 bis 50 die Group-Box hinzugefügt wird. Am Schluss setzen wir das Layout (Zeile 61) und den Fenstertitel (Zeile 63). Jetzt benötigen wir noch eine Hauptfunktion, die dieses Programm in der Praxis demonstriert, damit Sie die Abbildung 4.3 erhalten: 00 // beispiele/layout1/main.cpp 01 #include 02 #include "mylayout.h" 03 int main(int argc, char *argv[])
{
79
4.2
1542.book Seite 80 Montag, 4. Januar 2010 1:02 13
4
Dialoge, Layout und Qt-Widgets
04 05 06 07 08 }
QApplication app(argc, argv); MyLayoutEx* window = new MyLayoutEx; window->show(); return app.exec();
Allgemeine Methoden für das Layout (QLayout) Natürlich gibt es zu den einzelnen Klassen, angefangen bei QLayout, eine Menge Methoden und Funktionen, das Layout anzupassen. Auf alle hier einzugehen, wäre wohl zu viel des Guten und ist eher Aufgabe des Qt-Assistant. Beginnen wir mit Methoden die in der Klasse QLayout als public gekennzeichnet sind und somit allen anderen davon abgeleiteten Klassen zur Verfügung stehen. Methode
Beschreibung
SizeConstraint sizeConstraint () const;
Damit können Sie das Verhalten des Layouts abfragen. Was passiert, wenn die Größe verändert wird?
void setSizeConstraint (SizeConstraint );
Damit legen Sie das Verhalten des Layouts fest. Was passiert, wenn die Größe verändert wird?
int margin () const;
Damit fragen Sie die Weite eines (Leer-)Raums außerhalb des Rahmens vom Layout ab. Standardwerte sind hierbei meist 9 für Kinder-Widgets und 11 für Fenster.
void setMargin ( int );
Damit setzen Sie die Weite eines (Leer-)Raums außerhalb des Layout-Rahmens.
int spacing () const;
Damit können Sie den Abstand zwischen den Widgets innerhalb des Layouts ermitteln. Der Standardwert ist gewöhnlich –1, was bedeutet, dass dieser Abstand vom Eltern-Layout geerbt wird.
void setSpacing ( int );
Hiermit setzen Sie den Abstand zwischen den Widgets innerhalb des Layouts.
Tabelle 4.1
Wichtige Methoden der Klasse QLayout
sizeConstraint Mögliche Werte für SizeConstraint, wenn die Größe verändert wurde, finden Sie in der Tabelle 4.2 aufgelistet. Virtuelle Methoden der Klasse QWidget Die in der Tabelle 4.2 erwähnten Methoden minimumSize(), maximumSize() und sizeHint() sind virtuelle Methoden der Klasse QWidget.
80
1542.book Seite 81 Montag, 4. Januar 2010 1:02 13
Widgets anordnen – das Layout
Konstante
Bedeutung
QLayout::SetDefaultConstraint
Die Größe der Widgets wird hierbei auf ein Minimum mit minimumSize() gesetzt, außer das Widget besitzt bereits die minimale Größe.
QLayout::SetFixedSize
Die Größe der Widgets lässt sich nicht verändern. Intern wird hierfür die Methode sizeHint() verwendet.
QLayout::SetMinimumSize
Die Widgets werden mit minimumSize() auf die kleinste Größe gesetzt. Kleiner geht es nicht mehr.
QLayout::SetMaximumSize
Die Widgets werden mit maximumSize() auf die maximale Größe gesetzt. Es geht nicht mehr größer.
QLayout::SetMinAndMaxSize
Die minimale Größe wird mit minimumSize() und die max. Größe mit maximumSize() gesetzt.
QLayout::SetNoConstraint
Hiermit setzen Sie überhaupt keine Begrenzung.
Tabelle 4.2
Mögliche Werte für SizeConstraint
Bezieht man dies bspw. auf das Beispiel mit der Klasse MyLayoutEx und der horizontalen Box (QHBoxLayout) mit den darin enthaltenen Buttons, würde eine Größenveränderung die Breite der Buttons in die Länge ziehen – was nicht sehr schön aussieht (siehe Abbildung 4.6).
Abbildung 4.6
Größenänderung ohne Vorkehrungen
Verwenden Sie hingegen die Methode setSizeConstraint() mit dem Wert QLayout::SetFixedSize folgendermaßen: HBox->setSizeConstraint(QLayout::SetFixedSize);
Damit bleiben auch bei einer Größenveränderung alle Widgets der horizontalen Box in einer festen Mindestgröße erhalten (siehe Abbildung 4.7).
Abbildung 4.7
Größenänderung und setSizeConstraint(QLayout::SetFixedSize)
margin Die Beschreibung in der Tabelle 4.1 zu margin lässt sich theoretisch nicht so leicht erkennen. Daher demonstrieren wir anhand eines Beispiels, was hier mit
81
4.2
1542.book Seite 82 Montag, 4. Januar 2010 1:02 13
4
Dialoge, Layout und Qt-Widgets
Margin und dem Raum gemeint ist. Greifen wir zunächst wieder das Beispiel MyLayoutEx und die vertikale Box auf:
Abbildung 4.8
QVBoxLayout ohne Margin
Fügen wir nun nach dem Erzeugen der Box und dem Hinzufügen der Buttons folgende Zeile hinzu: VBox->setMargin(100);
Somit erhalten Sie mittels dieser Zeile folgendes Bild:
Abbildung 4.9
QVBoxLayout mit setMargin(100)
Hierbei wird also der Bereich zwischen der Group-Box (QGroupBox) und der vertikalen Box (QVBoxLayout) mit 100 Pixel in allen Richtungen gepolstert. spacing Im Gegensatz zu margin legen Sie mit spacing einen leeren Raum innerhalb eines Widgets fest. Dies wollen wir wiederum auf unsere Klasse MyLayoutEx anwenden. Diesmal soll das Raster-Layout (QGridLayout) dazu verwendet werden, welches bisher folgendermaßen aussieht:
82
1542.book Seite 83 Montag, 4. Januar 2010 1:02 13
Widgets anordnen – das Layout
Abbildung 4.10
QGridLayout ohne Spacing
Auch hier fügen Sie nach dem Erzeugen von QGridGroup und dem Hinzufügen der Buttons die folgende Zeile ein: Grid->setSpacing(50);
Dank dieser Zeile sieht das Raster-Layout nun wie folgt aus:
Abbildung 4.11
QGridLayout mit Spacing
Jetzt haben alle Widgets innerhalb der Klasse QGridLayout eine Polsterung von 50 Pixel. Wollten Sie außen herum ebenfalls einen Leerraum von 50 Pixel hinzufügen, müssten Sie nur die Methode setMargin() in der nächsten Zeile folgendermaßen anwenden: Grid->setMargin(50);
Abbildung 4.12
QGridLayout mit Spacing und Margin
Methoden für das Layout (QBoxLayout) – horizontales und vertikales Layout Die eben gezeigten Methoden von QLayout stehen somit allen Layout-Widgets zur Verfügung. Eine Klasse tiefer von QLayout finden Sie u. a. QBoxLayout. Diese Klasse bietet wiederum einige nützliche Methoden, um das Layout der einzelnen
83
4.2
1542.book Seite 84 Montag, 4. Januar 2010 1:02 13
4
Dialoge, Layout und Qt-Widgets
Widgets anzuordnen. Auch hier werden in einer Tabelle die wichtigsten und gängigsten Methoden beschrieben und anschließend etwas näher in der Praxis erläutert. Alle Methoden sind public und stehen somit auch der Klasse QVBoxLayout und QHBoxLayout zur Verfügung. Methode
Beschreibung
void addSpacing ( int size );
Fügt einen Leerraum mit size Pixel am Ende der Box hinzu. Damit lässt sich praktisch ein Leerraum zwischen zwei Widgets einer Box hinzufügen.
void addStretch ( int stretch = 0 );
Fügt einen auf das Widget bezogenen Ausdehnungsraum am Ende der Box hinzu. Damit lässt sich quasi verhindern, dass sich ein Widget bei einer Größenveränderung ausdehnt.
void addWidget ( QWidget * widget, int stretch = 0, Qt::Alignment alignment = 0 );
Fügt ein Widget am Ende der Box hinzu. Hierzu lassen sich noch ein Ausdehnungsraum wie mit addStrech() und die Ausrichtung (vertikal oder horizontal) angeben. Wird diese Methode in Bezug auf QHBoxLayout verwendet, ist die Ausrichtung horizontal und bei QVBoxLayout eben vertikal.
void setDirection ( Direction direction );
Legt die Anordnung der Widgets für das Layout fest. Hierbei kann man eben von rechts nach links, links nach rechts, oben nach unten oder unten nach oben setzen.
Direction direction () const ;
Fragt die Anordnung der Widgets ab (siehe setDirection()).
bool setStretchFactor ( QWidget * widget, int stretch );
Legt einen Ausdehnungsraum für ein bestimmtes Widget fest (siehe auch addStretch()).
void insertSpacing ( int index, int size );
Fügt einen Leerraum zwischen dem Widget mit dem index-1 und index+1 ein (index beginnt mit 0).
void insertStretch ( int index, int stretch = 0 );
Fügt einen Ausdehungsraum zwischen dem Widget mit dem index-1 und index+1 ein (0 für index bedeutet Am Anfang einfügen).
void insertWidget ( int index, QWidget * widget, int stretch = 0, Qt::Alignment alignment = 0 );
Fügt ein neues Widget zwischen index-1 und index+1 mit einer vertikalen oder horizontalen Ausrichtung in das Box-Layout ein (0 für index bedeutet Am Anfang einfügen).
Tabelle 4.3
Wichtige Methoden der Klasse QBoxLayout
Natürlich will ich einige dieser Methoden auch in der Praxis mit Abbildungen verdeutlichen. Bilder sagen gewöhnlich mehr als Worte.
84
1542.book Seite 85 Montag, 4. Januar 2010 1:02 13
Widgets anordnen – das Layout
Spacing Mit den beiden Methoden addSpacing() und insertSpacing() können Sie auch hier einen Leerraum einfügen. Im Gegensatz zum Spacing der Klasse QLayout fügen Sie allerdings den Leerraum immer am Ende der Box ein. Somit gilt das Spacing innerhalb von QBoxLayout immer für einen Leerraum zwischen Widgets und nicht für die gesamte Box. Fügen Sie bspw. in das Listing mit der Klasse MyLayoutEx folgende Zeilen ein: VBox = new QVBoxLayout; VBox->addWidget(but01[0]); VBox->addWidget(but02[0]); VBox->addWidget(but03[0]); VBox->addWidget(but04[0]); // Leerraum zwischen but02[0] und but03[0] einfügen VBox->insertSpacing( 2, 50 ); ... HBox = new QHBoxLayout; HBox->addWidget(but01[1]); HBox->addWidget(but02[1]); // Leerraum einfügen HBox->addSpacing(50); HBox->addWidget(but03[1]); HBox->addWidget(but04[1]);
Diese beiden Zeilen ergeben folgende Leerräume:
Abbildung 4.13
Verwendung von insertSpacing() und addSpacing()
Stretch Wenn Sie die Größe eines Fensters verändern, dehnen sich leider häufig auch die Widgets aus. Bezogen auf unsere Klasse MyLayoutEx ergibt sich bei einer Ausdehnung bspw. folgendes Bild:
85
4.2
1542.book Seite 86 Montag, 4. Januar 2010 1:02 13
4
Dialoge, Layout und Qt-Widgets
Abbildung 4.14
Buttons in QHBox werden in die Länge gezogen
Fügen Sie nun Stretches wie folgt zwischen die Buttons ein: HBox = new QHBoxLayout; HBox->addWidget(but01[1]); HBox->addStretch(); HBox->addWidget(but02[1]); HBox->addStretch(); HBox->addWidget(but03[1]); HBox->addStretch(); HBox->addWidget(but04[1]);
Nun ergibt sich folgende Abbildung:
Abbildung 4.15
Die Verwendung von addStretch() zwischen den Buttons
Wollen Sie bspw., dass bei einer Größenänderung »Button01« von den anderen Buttons getrennt wird, müssten Sie nur einen Stretch zwischen »Button01« und »Button02« einbauen. Nun bezieht sich der komplette Stretch nur auf diesen Raum, bspw.: HBox->addWidget(but01[1]); HBox->addStretch(); HBox->addWidget(but02[1]); HBox->addWidget(but03[1]); HBox->addWidget(but04[1]);
Dadurch ergibt sich Abbildung 4.16. Alternativ zu addStretch() können Sie auch die Methode insertStretch() verwenden. Hierbei können Sie zusätzlich den Index der Position in der Box angeben, wo der Stretch eingefügt werden soll.
Abbildung 4.16
86
Die Verwendung von addStretch() zwischen »Button01« und »Button02«
1542.book Seite 87 Montag, 4. Januar 2010 1:02 13
Widgets anordnen – das Layout
Wollen Sie hingegen, dass im Gegensatz zu Abbildung 4.16 der Button den Rest des Raumes zwischen Button01 und Button02 einnimmt, brauchen Sie nur die Methode setStretchFactor() zu verwenden: HBox->addWidget(but01[1]); HBox->addWidget(but02[1]); HBox->addWidget(but03[1]); HBox->addWidget(but04[1]); HBox->setStretchFactor(but01[1], 100);
Hierdurch dehnt sich der »Button01« wie folgt aus:
Abbildung 4.17
Verwendung von setStretchFactor()
Anordnung der Widgets (Direction) Interessant ist auch die Möglichkeit, die Anordnung der Widgets zu ändern. Bezogen auf Abbildung 4.17 sind die einzelnen Buttons von links nach rechts bzw. auf Abbildung 4.9 von oben nach unten angeordnet. Dies lässt sich mit den Methoden setDirection() und direction() auch ändern bzw. abfragen. Für Direction können Sie hierbei folgende Werte setzen bzw. abfragen (Tabelle 4.4). Konstante
Ausrichtung
QBoxLayout::LeftToRight
horizontal von links nach rechts
QBoxLayout::RightToLeft
horizontal von rechts nach links
QBoxLayout::TopToBottom
vertikal von oben nach unten
QBoxLayout::BottomToTop
vertikal von unten nach oben
Tabelle 4.4
Mögliche Werte zur Ausrichtung der Widgets mit QBoxLayout
Bezogen auf die Abbildung 4.17 lässt sich die Anordnung der Buttons von links nach rechts folgendermaßen in von rechts nach links ändern: HBox = new QHBoxLayout; // Anordnung der Buttons von rechts-nach-links HBox->setDirection(QBoxLayout::RightToLeft); HBox->addWidget(but01[1]); HBox->addWidget(but02[1]); HBox->addWidget(but03[1]); HBox->addWidget(but04[1]);
87
4.2
1542.book Seite 88 Montag, 4. Januar 2010 1:02 13
4
Dialoge, Layout und Qt-Widgets
Durch die Änderung der Anordnung ergibt sich folgende Abbildung:
Abbildung 4.18
Anordnung geändert von rechts nach links
Widgets hinzufügen (addWidget) Mit der Methode addWidget() fügen Sie ein Widget am Ende der Layout-Box hinzu. Hierbei kann man optional einen Stretch-Faktor und die Anordnung angeben. Der Stretch-Faktor wird nur bei entsprechender Anordnung (Direction) von QBoxLayout hinzugefügt und ist relativ zu den anderen Boxen und Widgets in diesem QBoxLayout. Widgets und Boxen mit einem höheren Stretch-Faktor dehnen sich auch mehr aus. Bspw. folgender Codeausschnitt: HBox = new QHBoxLayout; HBox->addWidget(but01[1], 100); HBox->addWidget(but02[1]); HBox->addWidget(but03[1]); HBox->addWidget(but04[1]);
Damit erreichen Sie dasselbe wie mit setStretchFactor() (Abbildung 4.17). Sollte der Stretch-Faktor 0 sein – in QBoxLayout hat nichts einen Stretch-Faktor größer als 0 –, wird der Raum für jedes Widget gemäß QWidget::sizePolicy() gefüllt. Methoden für das Layout (QGridLayout) – Raster-Layout QGridLayout bietet ebenfalls einige Methoden an, um das Layout der Widgets zu verwalten. Auch hier ein kurzer Überblick zu den gängigen Methoden von QGridLayout. Methode
Beschreibung
void setColumnMinimumWidth ( int column, int minSize );
Setzt die Mindestweite der Spalte column auf minSize Pixel.
int columnMinimumWidth ( int column ) const;
Gibt die Mindestweite der Spalte column in Pixel zurück.
Tabelle 4.5
88
Methoden der Klasse QGridLayout
1542.book Seite 89 Montag, 4. Januar 2010 1:02 13
Widgets anordnen – das Layout
Methode
Beschreibung
void setColumnStretch ( int column, int stretch );
Setzt den Ausdehnungsfaktor der Spalte column auf den Wert stretch. Die erste Spalte beginnt mit der Nummer 0. Der Stretchfaktor ist relativ zu den anderen Spalten im Grid-Layout. Spalten mit einem höheren Stretchfaktor nehmen auch mehr Platz ein. Der Standardwert des Stretchfaktors ist hierbei 0.
int columnStretch ( int column ) const;
Ermittelt den Ausdehnungsfaktor der Spalte column.
void setOriginCorner ( Qt::Corner corner );
Setzt den Ausgangspunkt der Ecke auf corner. Hierfür kann man die in der Tabelle 5.32 angegebenen Werte verwenden.
Qt::Corner originCorner() const;
Ermittelt den Ausgangspunkt der Ecke. Mögliche Rückgabewerte siehe Tabelle 5.32.
void setRowMinimumHeight ( int row, int minSize );
Setzt die Mindesthöhe der Zeile row auf minSize Pixel.
int rowMinimumHeight ( int row ) const;
Gibt die Mindesthöhe der Zeile row in Pixel zurück.
void setRowStretch ( int row, int stretch );
Setzt den Stretch-Faktor der Zeile row auf den Wert stretch. Die erste Zeile beginnt mit 0. Der Stretch-Faktor ist wiederum relativ zu den anderen Zeilen im Grid-Layout. Zeilen mit einem höheren Stretchfaktor nehmen auch mehr Platz ein. Der Standardwert ist auch hier wieder 0.
int rowStretch (int row) const;
Gibt den Stretch-Faktor der Zeile row zurück.
int columnCount () const;
Gibt die Anzahl der Spalten zurück.
int rowCount () const;
Gibt die Anzahl der Zeilen zurück.
void addWidget ( QWidget * widget, int row, int column, Qt::Alignment alignment = 0 );
Fügt ein neues Element in die Zeile row und Spalte column ein. Optional kann hierbei die Ausrichtung des Widgets angegeben werden.
void addWidget ( QWidget * widget, int fromRow, int fromColumn, int rowSpan, int columnSpan, Qt::Alignment alignment = 0 );
Dito, nur ist außerdem die Angabe möglich, über welche Spalten und Zeilen sich ein Widget erstrecken kann (wurde im Programmlisting gezeigt und erklärt).
Tabelle 4.5
Methoden der Klasse QGridLayout (Forts.)
89
4.2
1542.book Seite 90 Montag, 4. Januar 2010 1:02 13
4
Dialoge, Layout und Qt-Widgets
Stretch Zum
Verändern
und
Abfragen
des
Stretchfaktors
stehen
Ihnen
mit
setColumnStretch(), setRowStretch(), columnStretch() und rowStretch()
drei Methoden zur Verfügung. Beginnen wir mit dem Stretch-Faktor der Spalte. Fügen Sie Ihrem Code folgende Zeile hinzu: Grid = new QGridLayout; Grid->setColumnStretch(0, Grid->addWidget(but01[2], Grid->addWidget(but02[2], Grid->addWidget(but03[2], Grid->addWidget(but04[2],
100); 0, 0); 0, 1); 1, 0); 1, 1);
Auf diese Weise setzen Sie den Stretch-Faktor der ersten Spalte auf 100. Hiermit werden die Widgets der zweiten Spalte mit ihrer minimalen Größe erzeugt und angezeigt, während die Widgets der ersten Spalte den Rest auffüllen. Dadurch ergibt sich folgende Abbildung:
Abbildung 4.19
QGridLayout und setColumnStretch()
Um jetzt auch beim Stretch-Faktor für die Zeile mit setRowStretch() etwas zu sehen, müssen wir die maximal erlaubte Größe der Buttons ein wenig verändern. Dies erledigen Sie folgendermaßen: ... but01[2]->setMaximumSize ( 300, 100 ); but02[2]->setMaximumSize ( 300, 100 ); but03[2]->setMaximumSize ( 300, 100 ); but04[2]->setMaximumSize ( 300, 100 ); // Buttons auf einem Raster anordnen Grid = new QGridLayout; Grid->addWidget(but01[2], 0, 0); ... setMaximumSize() ist eine Methode der Klasse QWidget und legt die maximal erlaubte Größe für ein Widget fest. Nun können Sie den Stretch-Faktor für die Zeile wie folgt verwenden: Grid = new QGridLayout; Grid->setRowStretch(0, 100);
90
1542.book Seite 91 Montag, 4. Januar 2010 1:02 13
Widgets anordnen – das Layout
Grid->addWidget(but01[2], Grid->addWidget(but02[2], Grid->addWidget(but03[2], Grid->addWidget(but04[2],
0, 0, 1, 1,
0); 1); 0); 1);
Wenn Sie das Programm jetzt ausführen, erkennen Sie zunächst noch nichts. Erst wenn Sie die Größe des Fensters verändern, dehnen sich die Buttons in der ersten Zeile um den Faktor 100 aus, wie die folgende Abbildung zeigt:
Abbildung 4.20
QGridLayout und setRowStretch()
Mindestgrößen festlegen Mit
den
Methoden
setColumnMinimumWidth(),
setRowMinimumHeight(),
columnMinimumWidth() und rowMinimumHeight() haben Sie die Möglichkeit, die
Mindesthöhe- bzw. -breite zu setzen bzw. abzufragen. Um auch hier etwas zu sehen, belassen Sie bitte die maximal erlaubte Größe der Buttons so, wie dies beim Stretch-Faktor für die Zeile demonstriert wurde. Zunächst wollen wir eine Mindestgröße für die Spalte setzen: Grid = new QGridLayout; Grid->setColumnMinimumWidth( Grid->addWidget(but01[2], 0, Grid->addWidget(but02[2], 0, Grid->addWidget(but03[2], 1, Grid->addWidget(but04[2], 1,
0, 200); 0); 1); 0); 1);
Dadurch setzen Sie die Mindestbreite der ersten Spalte auf 200 Pixel. Egal, wie breit das Fenster wird, die erste Spalte muss mindestens 200 Pixel breit sein. Dadurch ergibt sich folgende Abbildung:
Abbildung 4.21
QGridLayout und setColumnMinimumWidth()
91
4.2
1542.book Seite 92 Montag, 4. Januar 2010 1:02 13
4
Dialoge, Layout und Qt-Widgets
Selbiges ist natürlich auch mit einer Zeile wie folgt möglich: Grid = new QGridLayout; Grid->setRowMinimumHeight( 0, 100 ); Grid->addWidget(but01[2], 0, 0); Grid->addWidget(but02[2], 0, 1); Grid->addWidget(but03[2], 1, 0); Grid->addWidget(but04[2], 1, 1);
Damit setzen Sie die Mindesthöhe für die erste Zeile auf 100 Pixel. Im Beispiel ist der Bereich um die Buttons in der ersten Reihe immer mindestens 100 Pixel. Egal wie hoch das Fenster ist.
Abbildung 4.22
QGridLayout und setRowMinimumHeight()
Stapel-Layout (QStackedLayout) Das Stapel-Layout der Klasse QStackedLayout lagert die einzelnen Widgets übereinander. Bei den bisher gezeigten Layouts wurden die einzelnen Widgets sichtbar auf einer Ebene angeordnet. In der Praxis verwendet man dieses Layout recht häufig bei Konfigurations-Dialogen. Am besten lässt sich dies wohl wieder anhand einer Abbildung beschreiben (Abbildung 4.23).
Abbildung 4.23
Stapel-Layout (1)
Die Abbildung zeigt auf der linken Seite eine Combo-Box und auf der rechten Seiten eine Group-Box mit Buttons. Die sich überlagernden Widgets befinden sich auf der rechten Seite und werden hier über die Combo-Box ausgewählt. Wählen Sie bspw. bei der Combo-Box »Seite 2« aus, wird die Group-Box mit den Buttons
92
1542.book Seite 93 Montag, 4. Januar 2010 1:02 13
Widgets anordnen – das Layout
von einem anderen, an eben dieser Position bzw. an diesem Index befindlichen Widget überlagert (siehe bspw. Abbildung 4.24).
Abbildung 4.24
Stapel-Layout (2)
Neben einer Combo-Box (QComboBox) wird für die Steuerung des Stapel-Layouts häufig auch die Listenform (QListWidget) verwendet (siehe Abbildung 4.25).
Abbildung 4.25
Steuerung des Stapel-Layouts mit QListWidget
Die Klasse QStackedLayout lässt sich eigentlich recht einfach verwenden. Auch hier findet man die bekannte Funktion addWidget() zum Hinzufügen neuer Widgets. Um auf ein bestimmtes Widget im Stapel zugreifen bzw. um es anzeigen zu können, verwendet man meist den Slot setCurrentIndex(int) verwendet. Diese Slot-Methode erwartet den Index (angefangen bei 0) des entsprechenden Widgets. Zusätzlich gibt es den Slot setCurrentWidget(QWidget*) der statt eines Integer-Werts einen Zeiger auf eine von QWidget abgeleitete Klasse erwartet. Um sich den Aufwand der Verwaltung des Layouts von QStackedLayout zu ersparen, das meistens selbst erzeugt werden muss, bietet Ihnen Qt die Klasse QStackedWidget an. QStackedWidget besitzt dieselbe Schnittstelle wie QStackedLayout. Außerdem ist QStackedWidget intern bereits ein Widget mit Stapel-Layout. Hierzu soll wieder ein Listing erstellt werden, mit dem Sie ein Beispiel wie in den Abbildungen 4.23 und 4.24 erstellen können. Hierbei müssen wir allerdings erneut mit QComboBox auf eine Klasse vorgreifen, die noch nicht behandelt wurde. Zunächst wieder das Grundgerüst, die Headerdatei:
93
4.2
1542.book Seite 94 Montag, 4. Januar 2010 1:02 13
4
Dialoge, Layout und Qt-Widgets
00 01 02 03 04 05
// beispiele/stapellayout/stapelwidget.h #ifndef STAPELWIDGET_H #define STAPELWIDGET_H #include #include #include
06 07 08 09 10 11 12 13 14
class StapelWidget : public QWidget { public: StapelWidget(QWidget *parent = 0); void addStapel(const QString& title, QWidget *wid); private: QStackedWidget *widgetStack; QComboBox *pageComboBox; }; #endif
Den wichtigsten Teil in dieser Klasse übernimmt die Methode addStapel() (Zeile 9). Mit ihr fügen Sie dem Stapel-Widget ein neues Widget (oder auch eine neue »Seite«) hinzu. Genaueres wird die Implementierung unserer Klasse StapelWidget zum Vorschein bringen: 00 // beispiele/stapellayout/stapelwidget.cpp 01 #include 02 #include "stapelwidget.h" 03 StapelWidget::StapelWidget( QWidget *parent ) : QWidget(parent) { 04 QHBoxLayout *lay = new QHBoxLayout(this); 05 pageComboBox = new QComboBox; 06 widgetStack = new QStackedWidget; 07 // Widgets zur horizont. Box hinzufügen 08 lay->addWidget(pageComboBox); 09 lay->addWidget(widgetStack); 10 // Signal-Slot-Verbindung einrichten 11 connect( pageComboBox, SIGNAL( activated( int ) ), widgetStack, SLOT( setCurrentIndex( int ) )); 12 setWindowTitle("Stapel Layout"); 13 } 14 void StapelWidget::addStapel( const QString& title, QWidget *wid ) { 15 pageComboBox->addItem(title); 16 widgetStack->addWidget(wid); 17 }
94
1542.book Seite 95 Montag, 4. Januar 2010 1:02 13
Widgets anordnen – das Layout
Nachdem in Zeile 5 und 6 eine Combo-Box und ein Stapel-Widget erzeugt wurden, packen wir diese beiden Widgets in Zeile 8 und 9 in die in Zeile 4 erzeugte horizontale Box. Anschließend wird in Zeile 11 die Signal-Slot-Verbindung eingerichtet. Hierfür wurde das Signal activated(int) der Combo-Box mit dem Slot setCurrentIndex(int) des Stapel-Widgets verbunden. Dabei sendet activated() die Indexnummer des aktivierten Elements in der Combo-Box (angefangen bei 0). Der Slot setCurrentIndex() verwendet diesen erhaltenen Indexwert zur Anzeige des entsprechenden Widgets (gerne auch: der entsprechenden Seite) im Stapel. Der erste Parameter der Methode addStapel() (Zeile 14 bis 17) wird für den Titel (bzw. den Text) der Combo-Box verwendet. Der zweite Parameter ist das dem Stapel hinzuzufügende Widget und wird somit angewählt, wenn der entsprechende Text der Combo-Box (Parameter 1) ausgewählt wurde. Beide Elemente haben somit denselben für die Signal-Slot-Verbindung benötigten Indexwert. Hierzu
noch
ein
Beispiel-Code
(das
Hauptprogramm),
der
die
Klasse
StapelWidget in der Praxis zeigen soll: 00 // beispiele/stapellayout/main.cpp 01 #include 02 #include "stapelwidget.h" 03 int main(int argc, char *argv[]) { 04 QApplication app(argc, argv); 05 // neues Stapelwidget erzeugen 06 StapelWidget *w = new StapelWidget; 07 // neue Seite mit einem Label hinzufügen 08 w->addStapel("Seite 1", new QLabel("Huhu ich bin die erste Seite") ); 09 // neue Seite mit einem Button hinzufügen 10 w->addStapel("Seite 2", new QPushButton("dont't touch me")); 11 12 13 14 15 16 17 18 19
// Group-Box mit drei Buttons erzeugen ----- Anfang QPushButton *buttons[3]; for( int j = 0; j < 3; ++j ) { buttons[j] = new QPushButton("Button"); } QVBoxLayout *VBox = new QVBoxLayout; for( int j = 0; j < 3; ++j ) { VBox->addWidget(buttons[j]); }
95
4.2
1542.book Seite 96 Montag, 4. Januar 2010 1:02 13
4
Dialoge, Layout und Qt-Widgets
20 21 22 23
// eine Box mit Label um die Buttons QGroupBox *VGroup = new QGroupBox("Seite 3"); VGroup->setLayout(VBox); // Group-Box mit drei Buttons erzeugen ----- Ende
24 25 26 27 28 29 }
// neue Seite mit der Group-Box hinzufügen w->addStapel("Seite 3", VGroup); // alles Anzeigen w->show(); return app.exec();
Zunächst wird in Zeile 6 unser Stapel-Widget erzeugt. In Zeile 8 wird das erste Widget dem Stapel hinzugefügt. Der Name, mit dem das Widget im Stapel über die Combo-Box aufgerufen werden kann, lautet »Seite«, und das Widget ist ein einfaches Textlabel (QLabel). Dasselbe geschieht in Zeile 10 mit einem Button. Praxisnäher wird es dann zwischen den Zeilen 11 bis 22. Hier verwenden wir eine vertikale Box, die drei Buttons enthält, außerdem in eine Group-Box gesteckt und anschließend (Zeile 25) dem Stapel hinzugefügt wird. Natürlich können Sie auch eigene Klassen erzeugen – alles, was irgendwie mit QWidget verwandt ist. Weitere indirekte Layout-Klassen (QSpacerItem und QWidgetItem) Beide Layout-Klassen werden normalerweise nie direkt verwendet. Die Klasse QSpacerItem erzeugt einen leeren Raum in einem Layout. Qt’s eingebaute Layout-Manager verwenden im Allgemeinen folgende Methoden, um den Leerraum im Layout zu verändern: Klasse
Methoden
QBoxLayout, QVBoxLayout, QHBoxLayout,
addSpacing(), addStretch(), insertSpacing(), insertStretch()
QGridLayout
setRowMinimumHeight(), setRowStretch(), setColumnMinimumWidth(), setColumnStretch()
Tabelle 4.6
Direkte Alternativen für QSpacerItem
Ähnlich wie die Klasse QSpacerItem wird die Klasse QWidgetItem nie direkt genutzt. QWidgetItem ist eine Layout-Klasse, die ein Widget repräsentiert. Auch hier werden die Built-in-Layout-Manager verwendet, um das Layout der Widgets zu verändern (siehe Tabelle 4.7).
96
1542.book Seite 97 Montag, 4. Januar 2010 1:02 13
Widgets anordnen – das Layout
Klasse
Methode
QBoxLayout, QVBoxLayout, QHBoxLayout
addWidget(), insertWidget(), setStretchFactor()
QGridLayout
addWidget()
QStackedLayout
addWidget(), insertWidget(), widget(), currentWidget(), setCurrentWidget()
Tabelle 4.7
Direkte Alternativen für QWidgetItem
Qt::Alignment Viele Layout-Manager verfügen über die Option, zusätzlich einen Parameter Qt::Alignment zu verwenden, um weitere Anpassungen vornehmen zu können. Dieser enum-Typ enthält horizontale und vertikale Flags, die sich auch mit dem bitweisen ODER (falls sinnvoll) kombinieren lassen. Die horizontalen Flags sind: Konstante
Beschreibung
Qt::AlignLeft
zur linken Ecke ausgerichtet
Qt::AlignRight
zur rechten Ecke ausgerichtet
Qt::AlignHCenter
zentriert im vorhandenen Platz
Qt::AlignJustify
einen Text bei vorhandenem Platz im Block ausrichten
Tabelle 4.8
Horizontale Flags
Nun zu den vertikalen Flags: Konstante
Beschreibung
Qt::AlignTop
nach oben ausgerichtet
Qt::AlignBottom
nach unten ausgerichtet
Qt::AlignVCenter
vertikal zentriert im vorhandenen Platz
Tabelle 4.9
Vertikale Flags
Außerdem gibt es ein Flag, das Sie für horizontale und vertikale Ausrichtungen verwenden können: Konstante
Beschreibung
Qt::AlignCenter
Entspricht AlignVCenter | AlignHCenter und zentriert in beide Richtungen.
Tabelle 4.10
Horizontale und vertikale Flags
97
4.2
1542.book Seite 98 Montag, 4. Januar 2010 1:02 13
4
Dialoge, Layout und Qt-Widgets
Eigene Layout-Manager erstellen Als Alternative zu den bisher erwähnten und gezeigten Layout-Managern können Sie natürlich auch Ihren eigenen erstellen. Zu diesem Zweck müssen Sie Folgendes definieren: 왘
Die neue von QLayout abgeleitete Klasse.
왘
Jedes Element, das hinzugefügt wird, ist Typ der Klasse QLayoutItem.
왘
Um ein neues Element hinzuzufügen, benötigen Sie eine Methode addItem(), die als Parameter QLayoutItem erhält.
왘
Eine Methode setGeometry() zum Aufführen des Layouts.
왘
Eine Methode sizeHint(), die die Größe des Layouts bearbeitet.
왘
Eine Methode itemAt().
왘
Eine Methode takeAt(), um ein Layoutelement zu entfernen.
왘
Meistens auch die Methode minimumSize().
Ein Beispiel kann ich mir hier ersparen, da Qt mit »Border Layout« und »Flow Layout« zwei Beispiele mitliefert, die das Beschriebene in der Praxis demonstrieren (siehe auch Buch-DVD). Manuelles Layout Bei den vielen Layout-Hilfen, die Qt anbietet, stellt sich sicherlich die Frage, wie die klassische oder gerne auch manuelle Version zur Erstellung von Layouts funktioniert. Zu diesem Zweck legt man ein Eltern-Widget der Klasse QWidget als Basisklasse mit einer festen Größe (Höhe und Breite) an. In QWidget ist die Methode setGeometry() definiert, um ein Widget auf das Eltern-Widget zu »kleben«. Die Methode setGeometry() hat vier Parameter: Die beiden ersten entsprechen der relativen x- und y-Position im Eltern-Widget. Parameter drei und vier entsprechen der Höhe und Breite, die das Widget im Eltern-Widget einnehmen soll. In diesem Zusammenhang ein einfaches Beispiel: ein simples Fenster mit vier Buttons. Das Grundgerüst: 00 01 02 03 04
// beispiele/manLayout/mylayout.h #ifndef MYLAYOUT_H #define MYLAYOUT_H #include #include
05 class ManualLayout : public QWidget { 06 public: ManualLayout();
98
1542.book Seite 99 Montag, 4. Januar 2010 1:02 13
Widgets anordnen – das Layout
07 }; 08 #endif
Nun zur Implementierung des manuellen Layouts der Klasse ManualLayout: 00 // beispiele/manLayout/mylayout.cpp 01 #include "mylayout.h" 02 // neue Widget-Klasse vom eigentlichen Widget ableiten 03 ManualLayout::ManualLayout() { 04 setFixedSize(250, 200 ); 05 QPushButton *button01 = new QPushButton("Button01", this); 06 QPushButton *button02 = new QPushButton("Button02", this); 07 QPushButton *button03 = new QPushButton("Button03", this); 08 QPushButton *button04 = new QPushButton("Button04", this); 09 button01->setGeometry( 20, 20, 100, 20 ); 10 button02->setGeometry( 20, 50, 100, 20 ); 11 button03->setGeometry( 130, 20, 100, 20 ); 12 button04->setGeometry( 130, 50, 100, 20 ); 13 setWindowTitle("Manuelles Layout"); 14 }
Zunächst weisen Sie dem Layout mit setFixedSize() (Zeile 4) eine feste und unveränderliche Größe zu. Anschließend platzieren wir die vier Buttons auf dieser Fläche. Die linke obere Ecke von »button01« befindet sich 20 Pixel vom linken und 20 Pixel vom oberen Seitenrand entfernt. Der Button wird 100 Pixel breit und 20 Pixel hoch. Gleiches geschieht mit den drei anderen Buttons, nur mit anderen Werten. Dieses manuelle Layout ergibt folgende Abbildung:
Abbildung 4.26
Manuelles Layout
99
4.2
1542.book Seite 100 Montag, 4. Januar 2010 1:02 13
4
Dialoge, Layout und Qt-Widgets
Hierzu noch eine main-Funktion zum Testen des Programmbeispiels: 00 // beispiele/manLayout/main.cpp 01 #include 02 #include "mylayout.h" 03 int main(int argc, char *argv[]) { 04 QApplication app(argc, argv); 05 ManualLayout* window = new ManualLayout; 06 window->show(); 07 return app.exec(); 08 }
Dem einen oder anderen mag die manuelle Erstellung von Layouts einfach und praktisch erscheinen, doch ist es auf Anhieb recht schwierig. Meistens artet dieser Versuch in eine wilde Probiererei aus. Kommt ein neues Widget hinzu, müssen oft alle anderen Widgets ebenfalls neu positioniert werden. Außerdem fehlt häufig die Möglichkeit, die Widgets schrumpfen oder wachsen zu lassen, was vielleicht bei Button-Widgets nicht wichtig erscheint, bei vielen anderen Widgets aber mehr oder minder »Pflicht« ist.
4.3
Erstellen von Dialogen (QDialog)
Ein Dialogfenster ist ein Top-Level-Fenster, das man gewöhnlich für kurze Anwendereingaben, Einstellungen (Konfigurationen) oder auch nur Informationen für den Anwender verwendet. Es basiert auf der Klasse QDialog, die wiederum von QWidget abgeleitet ist (siehe Abbildung 4.27). Qt bietet hierfür natürlich auch eine Menge vordefinierter Dialoge wie bspw. User-Eingaben von Zahlen oder Text, Farbauswahl, Dateiauswahl, Schriftauswahl usw. Wir gehen darauf allerdings erst in Abschnitt 4.4 näher ein. Zunächst beschreiben wir die »Basisklasse« aller Dialoge mit QDialog.
QObject
QPaintDevice
QWidget
QDialog
Abbildung 4.27
100
Klassenhierarchie von QDialog
1542.book Seite 101 Montag, 4. Januar 2010 1:02 13
Erstellen von Dialogen (QDialog)
Ein solcher Dialog mit QDialog kann modal oder eben nichtmodal sein. Modal bedeutet, dass der Dialog immer vor dem Elternfenster liegt und das Elternfenster so lange blockiert, bis der Dialog geschlossen ist. Damit zwingt man den Anwender sozusagen, die Interaktion erst auszuführen, bevor er Zugriff auf andere Fenster der Anwendung hat. Der übliche Weg, einen modalen Dialog anzuzeigen, besteht darin, die exec()Funktion zu verwenden. Bei QDialog ist diese Methode auch als Slot implementiert. Wenn der Anwender den Dialog schließt, liefert exec() einen Rückgabewert zurück. Üblicherweise wird ein Standard-Button (bspw. Ok) mit accept() und ein weiterer Button mit reject() (bspw. Abbrechen) verknüpft. Modales Dialogfenster Alternativ zu exec() kann auch die Methode setModal(true) und dann show() aufgerufen werden, um ein modales Dialogfenster anzuzeigen. Im Gegensatz zeigt man ein nichtmodales Dialogfenster an, indem man die Methode setModal(false) verwendet und anschließend show() aufruft.
Hierzu wieder ein einfaches Beispiel: Wir erstellen ein simples Fenster (QWidget) mit einem Button. Betätigt man den Button, wird ein Dialogfenster angezeigt. Zunächst das Grundgerüst für das Top-Level-Fenster: 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20
// beispiele/dialog1/mywidget.h #ifndef MYWIDGET_H #define MYWIDGET_H #include #include #include #include #include "mydialog.h" class MyWidget : public QWidget { Q_OBJECT public: MyWidget( const char* qstr ="Bitte Button betätigen", QWidget *parent = 0 ); private slots: void checkInputDialog(); private: QPushButton *button0; QLabel* label1, *label2; QVBoxLayout* layout; MyDialog* dialog; }; #endif
101
4.3
1542.book Seite 102 Montag, 4. Januar 2010 1:02 13
4
Dialoge, Layout und Qt-Widgets
Nach dem Grundgerüst des Hauptfensters folgt das Grundgerüst der Headerdatei mydialog.h (Zeile 7) für den Dialog, den Sie in Zeile 18 von mywidget.h vorfinden: 00 01 02 03 04 05 06
// beispiele/dialog1/mydialog.h #ifndef MYDIALOG_H #define MYDIALOG_H #include #include #include #include
07 08 09 10 11 12
class MyDialog : public QDialog { Q_OBJECT public: MyDialog(); }; #endif
Natürlich müssen Sie hierbei auch das Makro Q_OBJECT (Zeile 8) verwenden, wie für alle direkten (und indirekten) Ableitungen von Basisklassen (hier QObject), da sonst das Signal-Slot-Konzept nicht funktionieren verwendet. Dies ist »beliebter« Fehler, der oft nicht gleich bemerkt wird, weil weder Compiler noch Linker eine Fehlermeldung ausgeben. Eingerichtete Signal-Slot-Verbindungen werden ohne Makros einfach ignoriert. Weiter mit der Implementierung der Klasse MyWidget, unserem Top-LevelFenster: 00 01 02 03
// beispiele/dialog1/mywidget.cpp #include "mywidget.h" #include "mydialog.h" #include
04 // neue Widget-Klasse vom eigentlichen Widget ableiten 05 MyWidget::MyWidget( const char* lab, QWidget *parent): QWidget(parent) { 06 // Elemente des Widgets erzeugen 07 button0 = new QPushButton ("Dialog starten"); 08 layout = new QVBoxLayout(this); 09 label1 = new QLabel(lab); 10 label2 = new QLabel; 11 dialog = new MyDialog; 12 // Programm nicht beenden, wenn Dialog zerstört wird 13 dialog->setAttribute(Qt::WA_QuitOnClose);
102
1542.book Seite 103 Montag, 4. Januar 2010 1:02 13
Erstellen von Dialogen (QDialog)
14 15 16 17
// Elemente des Widgets anordnen/anpassen layout->addWidget(label1); layout->addWidget(button0); layout->addWidget(label2);
18 19
// Signal-Slot-Verbindungen einrichten connect( button0, SIGNAL( clicked() ), dialog, SLOT( exec() ) ); connect( dialog, SIGNAL( accepted() ), this, SLOT( checkInputDialog() ) ); connect( dialog, SIGNAL( rejected() ), this, SLOT( checkInputDialog() ) ); setWindowTitle("Hauptfenster – Anwendung");
20 21 22 23 }
24 void MyWidget::checkInputDialog() { 25 int val = dialog->result(); 26 if( val == QDialog::Accepted ) { 27 label2->setText("\"Ok\" wurde gewählt"); 28 } 29 else if( val == QDialog::Rejected ) { 30 label2->setText("\"Abbrechen\" wurde gewählt"); 31 } 32 }
In Zeile 11 erzeugen wir das neue Dialogfenster. Mit dem Attribut Qt::WA_QuitOnClose, das in Zeile 13 gesetzt wird, legen wir fest, dass beim Beenden des Dia-
logfensters nicht gleich die ganze Anwendung endet. Bei der Ausführung sieht dieses Fenster folgendermaßen aus:
Abbildung 4.28
Das Hauptfenster bei der Ausführung
Die Signal-Slot-Verbindung für den Button legen wir in Zeile 19 fest. Erhält der Button das Signal clicked(), wird der Slot exec() von QDialog ausgeführt. Damit zeigt man ein modales Dialogfenster an (siehe Abbildung 4.29). In Zeile 20 und 22 richten wir zwei weitere Signal-Slot-Verbindungen ein. Die erste SignalSlot-Verbindung von Zeile 20 wird aktiv, wenn beim Dialogfenster das Signal accepted() ausgelöst wurde. Die andere Signal-Slot-Verbindung (Zeile 22) wird beim Signal rejected() aktiv. Für beide Signale wird unsere eigene Slot-
103
4.3
1542.book Seite 104 Montag, 4. Januar 2010 1:02 13
4
Dialoge, Layout und Qt-Widgets
Methode checkInputDialog() aufgerufen. Wie die Signale ausgelöst werden, erfahren Sie in Kürze. In unserer eigenen Slot-Methode checkInputDialog() werten wir in Zeile 25 das Ergebnis des Signals mit der Methode result() aus. Hierbei wird entweder der enum-Typ Accepted oder Rejected von QDialog::DialogCode zurückgegeben. Entsprechend dem Rückgabewert von result() wird das Label in Zeile 27 oder 30 gesetzt. Um mehr Licht ins Dunkel zu bringen, sehen wir uns die Implementierung des Dialogfensters an: 00 // beispiele/dialog1/mydialog.cpp 01 #include "mydialog.h" 02 // neue Widget-Klasse vom eigentlichen Widget ableiten 03 MyDialog::MyDialog() { 04 setFixedSize ( 150, 100 ); 05 QVBoxLayout *vbox = new QVBoxLayout; 06 QLabel *label = new QLabel("Bitte Button betätigen"); 07 QPushButton *button01 = new QPushButton("Ok"); 08 QPushButton *button02 = new QPushButton("Abbrechen"); 09 10 11 12 13 14 15
button01->setDefault(true); vbox->addWidget(label); vbox->addWidget(button01); vbox->addWidget(button02); setLayout(vbox); connect( button01, SIGNAL( clicked() ), this, SLOT( accept() ) ); connect( button02, SIGNAL( clicked() ), this, SLOT( reject() ) );
16 }
Bei der Ausführung sieht dieser Dialog folgendermaßen aus:
Abbildung 4.29
Der Dialog bei der Ausführung
In Zeile 9 legen wir den Ok-Button als Standard-Button fest. Dies bedeutet: Drückt der Anwender auf (¢), wird der Ok-Button als gedrückt ausgelöst.
104
1542.book Seite 105 Montag, 4. Januar 2010 1:02 13
Erstellen von Dialogen (QDialog)
In Zeile 14 wird die Signal-Slot-Verbindung des Ok-Buttons eingerichtet. Erhält dieser Button das clicked()-Signal, wird der Slot accept() von QDialog ausgeführt – was bei der Klasse MyWidget das Signal accepted() der entsprechenden Signal-Slot-Verbindung auslöst. Gleiches gilt für die Signal-Slot-Verbindung in Zeile 15, doch gilt es hier für das Signal rejected() auf der anderen Seite. In beiden Fällen wird hierbei die SlotMethode checkInputDialot() von MyWidget aufgerufen und entsprechend ausgewertet. Auch der Slot reject() wird ausgeführt, wenn der Anwender die Escape-Taste betätigt oder das Fenster schließt. Beidem entspricht hierbei das »Anklicken« des Abbrechen-Buttons. Wurde bspw. der Ok-Button betätigt, wird das Dialogfenster wieder geschlossen und im Hauptfenster das entsprechende Label gesetzt (siehe Abbildung 4.30).
QObject
QPaintDevice
QWidget
QDialog
Abbildung 4.30
Der Ok-Button wurde beim Dialog ausgewählt
Beachten Sie, dass das Dialogfenster hierbei nur »versteckt« und nicht zerstört wird. Nochmals genauer: Wurde der Ok-Button betätigt, werden das Signal clicked() ausgelöst und der Slot accept() ausgeführt. Bei MyWidget wiederum löst das Signal accepted() aus, und der eigene Slot checkInputDialog() startet. Darin überprüft die Methode result(), ob Accepted oder Rejected betätigt wurde. Accepted wurde in unserem Fall an den Ok-Button und Rejected an den Abbrechen-Button des Dialogs vergeben. Analoges geschieht beim Betätigen des Abbrechen-Buttons, nur eben mit reject() und rejected(). Hierzu noch eine main()-Funktion, womit Sie das Ganze in der Praxis testen können: 00 // beispiele/dialog1/main.cpp 01 #include 02 #include "mywidget.h" 03 int main(int argc, char *argv[])
{
105
4.3
1542.book Seite 106 Montag, 4. Januar 2010 1:02 13
4
Dialoge, Layout und Qt-Widgets
04 05 06 07 08 }
QApplication app(argc, argv); MyWidget* window = new MyWidget; window->show(); return app.exec();
In unserem Beispiel wurde der Dialog folgendermaßen gestartet: 11 12 13 ... 18 19
dialog = new MyDialog; // Programm nicht beenden, wenn Dialog zerstört wird dialog->setAttribute(Qt::WA_QuitOnClose); // Signal-Slot-Verbindungen einrichten connect( button0, SIGNAL( clicked() ), dialog, SLOT( exec() ) );
Wurde der Button betätigt, tritt das Signal clicked() ein. Letzteres wurde mit dem Objekt dialog und dem Slot exec() verbunden. Einen Dialog können Sie auch folgendermaßen starten (was durchaus gängige Praxis ist): 01 02 03 04 05 06
dialog = new MyDialog; // Programm nicht beenden, wenn Dialog zerstört wird dialog->setAttribute(Qt::WA_QuitOnClose); // Dialog in eigener Event-Loop eintreten lassen int status = dialog->exec(); // ... ist Dialog fertig, geht’s hier weiter
Nachdem hier eine Instanz des Dialogs erzeugt wurde, lassen wir diesen in Zeile 5 mittels exec() in eine Ereignisschleife eintreten. Schließt jetzt der Benutzer den Dialog bzw. das Fenster, kehrt die Ereignisschleife der Anwendung zurück – genauer: Die Anwendung des Programms fährt hinter dem exec() (im Code-Ausschnitt Zeile 6) fort. Was zurückgegeben wird, bestimmt auch hier wieder der Slot; wie bereits erläutert, bietet QDialog hierbei wiederum accept() und reject() vordefiniert an. Hierzu nun ein Überblick über die gängigsten Methoden, Signale und Slots der Klasse QDialog. Methode
Beschreibung
bool isSizeGripEnabled() const;
Überprüft, ob sich in der unteren rechten Ecke ein QSizeGrip-Widget befindet, womit die Größe des Dialogfensters verändert werden kann. Bei true trifft dies zu, ansonsten wird false zurückgegeben.
Tabelle 4.11
106
Gängige Methoden von QDialog
1542.book Seite 107 Montag, 4. Januar 2010 1:02 13
Erstellen von Dialogen (QDialog)
Methode
Beschreibung
void setSizeGripEnabled(bool);
Damit können Sie ein QSizeGrip-Widget zum Verändern der Größe an der rechten unteren Seite des Dialogfensters mit true hinzufügen bzw. mit false entfernen.
void setModal(bool modal);
Damit können Sie einen Dialog mit true auf Modal bzw. mit false auf Nichtmodal setzen.
Tabelle 4.11
Gängige Methoden von QDialog (Forts.)
Weiter mit den öffentlichen Slots von QDialog: Slot
Beschreibung
virtual void accept ();
Versteckt den modalen Dialog und setzt den Rückgabewert auf Accepted (1).
virtual void done(int r);
Schließt den Dialog und setzt den Rückgabewert auf r. Wird der Dialog mit exec() angezeigt, beendet done() die Ereignisschleife, und exec() gibt r zurück.
int exec();
Zeigt einen modalen Dialog und blockiert, bis der Anwender diesen schließt. Diese Funktion gibt einen Dialog-Rückgabewert wie Accepted oder Rejected zurück.
virtual void reject ();
Gegenstück zu accept(). Versteckt den modalen Dialog und setzt den Rückgabewert auf Rejected (0).
Tabelle 4.12
Slots von QDialog
Zum Schluss noch eine Auflistung möglicher Signale von QDialog. Signal
Beschreibung
void accepted();
Dieses Signal wird ausgelöst, wenn der Dialog akzeptiert wurde. Dies wird gewöhnlich über accept() oder done() mit QDialog::Accepted als Argument ausgelöst.
void finished(int result);
Wird gesendet, wenn der Rückgabewert des Dialogs gesetzt wurde; normalerweise mit accept(), reject() oder done() ausgelöst.
void rejected();
Wird ausgelöst, wenn der Dialog abgelehnt (Rejected) wurde; normalerweise mit reject() oder done() und QDialog::Reject als Argument ausgelöst.
Tabelle 4.13
Signale von QDialog
107
4.3
1542.book Seite 108 Montag, 4. Januar 2010 1:02 13
4
Dialoge, Layout und Qt-Widgets
Den einen oder anderen dürfte jetzt brennend interessieren, wie man einen anderen Wert als Accepted und Rejected aus einem Dialogfenster zurückgibt. Erweitern Sie am besten dazu unseren Dialog MyDialog um einen Slot: 00 01 02 03 04 05 06
// beispiele/dialog1_version2/mydialog.h #ifndef MYDIALOG_H #define MYDIALOG_H #include #include #include #include
07 08 09 10 11 12 13 14
class MyDialog : public QDialog { Q_OBJECT public: MyDialog(); public slots: void mySetResult(); }; #endif
Fügen Sie nun einen neuen Button bei der Definition der Klasse der vertikalen Layout-Box hinzu (siehe Abbildung 4.31): // beispiele/dialog1_version2/mydialog.cpp ... QPushButton *button03 = new QPushButton("Ignorieren"); ... vbox->addWidget(button03); ...
Abbildung 4.31 Ein weiterer Button »Ignorieren« wurde hinzugefügt
Für diese Buttons richten Sie nun eine Signal-Slot-Verbindung zu unserem neu implementierten Slot ein: // beispiele/dialog1_version2/mydialog.cpp ...
108
1542.book Seite 109 Montag, 4. Januar 2010 1:02 13
Erstellen von Dialogen (QDialog)
connect( button03, SIGNAL ( clicked()), this, SLOT( mySetResult() ) ); ...
Die Implementierung des Slots, der aufgerufen wird, wenn der neue Button betätigt wurde, sieht folgendermaßen aus: // beispiele/dialog1_version2/mydialog.cpp ... void MyDialog::mySetResult() { int result = 99; emit done(result); }
Hierbei rufen Sie die echte Slot-Methode done() mit dem Rückgabewert 99 auf, womit der Dialog geschlossen wird. Jetzt fügen wir in mywidget.h einen weiteren Slot hinzu – genauer: wir überladen einfach den alten Slot mit einem Integer als Argument: // beispiele/dialog1_version2/mywidget.h ... private slots: void checkInputDialog(); void checkInputDialog(int); ...
Natürlich richten wir auch hier eine neue Signal-Slot-Verbindung ein, die auf den Slot done(int) der Klasse MyDialog reagiert. Hierfür eignet sich das Signal finished(int), das ebenfalls einen Integer als Parameter hat. Als Slot für das Signal verwenden wir wieder unsere selbst implementierte und überladene SlotMethode: // beispiele/dialog1_version2/mywidget.cpp ... connect( dialog, SIGNAL ( finished( int )), this, SLOT( checkInputDialog( int ) ) ); ...
Nun gilt es noch die Definition der überladenen Slot-Methode hinzuzufügen: // beispiele/dialog1_version2/mywidget.cpp ... void MyWidget::checkInputDialog(int val) { //val = dialog->result(); if( val == 99 ) {
109
4.3
1542.book Seite 110 Montag, 4. Januar 2010 1:02 13
4
Dialoge, Layout und Qt-Widgets
label2->setText("\"Ignorieren\" wurde gewählt"); } }
Geschafft! Mehr ist nicht nötig, um auch auf andere Rückgabewerte als Accepted oder Rejected zu reagieren. Im Beispiel wurde auf den Rückgabewert 99 reagiert.
Abbildung 4.32 Reagiert nun auch auf andere Rückgabewerte
Listing auf Buch-DVD Sie finden das Beispiel ebenfalls im Verzeichnis beispiele/dialog1, aber in einem weiteren Ordner namens version2.
4.3.1
Benutzerfreundlichkeit von Dialogen
Viele Programmierer tendieren dazu, in ihre Dialoge etwas mehr zu packen, als erforderlich ist. Viele Optionen und Erweiterungen werden aber nur selten verwendet. Hierbei sollte man bereits zu Beginn darauf achten, die Dialogfenster mit einem vernünftigen Standardwert vorzubelegen. Benötigt der Anwender dann doch weitere selten genutzte Optionen, bietet Qt hierzu über die Schnittstelle von QDialog Erweiterungen (Extensions) an. Als Beispiel habe ich unsere Klasse MyDialog hierzu nochmals um den folgenden Button erweitert:
Abbildung 4.33
110
Button »Weiteres« hinzugefügt
1542.book Seite 111 Montag, 4. Januar 2010 1:02 13
Erstellen von Dialogen (QDialog)
Bei diesem Dialog sehen Sie außer den bereits bekannten Buttons einen Button »Weiteres«. Wird er betätigt, sieht der Anwender die Erweiterung des Dialogs (vgl. Abbildung 4.34).
Abbildung 4.34
Erweiterung des Dialogs ausgefahren
Dazu bedarf es im Grunde nicht viel. Sie müssen nur das oder die entsprechenden Widget(s) (ausgehend vom Eltern-Widget) als Parameter in der Funktion setExtensions() verwenden. Sehen Sie sich hierzu einfach den Quellcode an. Im Beispiel musste hierfür nur die Datei mydialog.cpp erweitert werden: 00 // beispiele/dialog1_version3/mydialog.cpp 01 #include "mydialog.h" 02 #include 03 // neue Widget-Klasse vom eigentlichen Widget ableiten 04 MyDialog::MyDialog() { 05 setFixedSize ( 200, 180 ); 06 07 08 09 10 11 12
QVBoxLayout *vbox = new QVBoxLayout; QVBoxLayout *VBox = new QVBoxLayout; QLabel *label = new QLabel("Bitte Button auswählen"); QPushButton *button01 = new QPushButton("Ok"); QPushButton *button02 = new QPushButton("Abbrechen"); QPushButton *button03 = new QPushButton("Ignorieren"); QPushButton *button04 = new QPushButton("Weiteres>>");
13 14
QPushButton *but01 = new QPushButton("Button01"); QPushButton *but02 = new QPushButton("Button02");
111
4.3
1542.book Seite 112 Montag, 4. Januar 2010 1:02 13
4
Dialoge, Layout und Qt-Widgets
15 16
QPushButton *but03 = new QPushButton("Button03"); QPushButton *but04 = new QPushButton("Button04");
17 18 19 20 21 22 23 24 25
// Buttons vertikal anordnen VBox = new QVBoxLayout; VBox->addWidget(but01); VBox->addWidget(but02); VBox->addWidget(but03); VBox->addWidget(but04); // eine Box mit Label um die Buttons QGroupBox* VGroup = new QGroupBox("Erweitert"); VGroup->setLayout(VBox);
26 27
setExtension(VGroup); setOrientation(Qt::Vertical);
28 29
button02->setDefault(true); button04->setCheckable(true);
30 31 32 33 34 35
vbox->addWidget(label); vbox->addWidget(button01); vbox->addWidget(button02); vbox->addWidget(button03); vbox->addWidget(button04); setLayout(vbox);
36
connect( button01, SIGNAL( clicked() ), this, SLOT( accept() ) ); connect( button02, SIGNAL( clicked() ), this, SLOT( reject() ) ); connect( button03, SIGNAL ( clicked()), this, SLOT( mySetResult() ) ); connect( button04, SIGNAL( toggled(bool) ), this, SLOT( showExtension(bool) ) );
37 38 39 40 }
41 void MyDialog::mySetResult() { 42 int result = 99; 43 emit done(result); 44 }
Zunächst erzeugen Sie hierbei einen gewöhnlichen Button (Zeile 12), von Zeile 13 bis 25 vier weitere Buttons, die Sie in eine vertikale Box stecken. Letztere packen Sie in eine Group-Box. In Zeile 26 erklären Sie diese Group-Box mittels
112
1542.book Seite 113 Montag, 4. Januar 2010 1:02 13
Vorgefertigte Dialoge
setExtension() als eine Erweiterung (Extensions). In Zeile 27 (setOrientation()) geben Sie an, zu welcher Seite die Erweiterung ein-/ausgeklappt wer-
den soll. In diesem Beispiel erstellen wir eine vertikale Erweiterung. In Zeile 29 erklären wir unseren Button als Toggle-Button, was bedeutet, dass er bei seiner Betätigung in diesem Zustand (toggled == geschaltet) verharrt. Er kommt natürlich wie ein gewöhnlicher Button zu unserem Dialog (Zeile 34). In Zeile 39 verbinden wir den Button dann mit unserem Dialogfenster. Hierbei reagieren wir auf das Signal toogled(bool) – ob sich der Button also in einem gedrückten (bool=true) oder ungedrückten (bool=false) Zustand befindet. Als Slot verwendet wird showExtension(bool), ebenfalls ein Slot von QDialog. Befindet sich der Button dann im gedrückten Zustand, wird vom Signal toggled() true an showExtension() übergeben. In diesem Fall wird der Dialog erweitert gezeigt. Andersherum eben umgekehrt.
4.4
Vorgefertigte Dialoge
In Abschnitt 4.3 erwähnten wir, dass Qt eine Menge vorgefertigter Dialoge anbietet. Üblicherweise findet man in einem Buch zu einer GUI zuerst die Widgets beschrieben und anschließend die vorgefertigten Dialog-Boxen. Wir möchten genau umgekehrt verfahren. Bevor Sie die einzelnen Widgets kennenlernen, mit denen wir auch eigene Dialoge entwerfen, wollen wir Ihnen die vorgefertigten Dialog von Qt auflisten und auch demonstrieren, um bereits Bekanntes nicht zu wiederholen und zu verhindern, dass Sie einen Dialog erstellen, den es in ähnlicher Form schon gibt.
4.4.1
QMessageBox – Nachrichtendialoge
Relativ häufig wird die von QDialog abgeleitete Klasse QMessageBox eingesetzt, um dem Anwender kurze Informationen zu übermitteln oder ihn zu veranlassen, bestimmte Entscheidungen zu treffen. Gewöhnlich wird dieser modale Dialog mit einer kurzen Nachricht, einem Icon und Buttons angezeigt. Das Aussehen der Icons bzw. des Buttons hängt vom aktuellen Fenster-Stil und z. T. auch vom System ab. Der einfachste Weg, eine solche Nachrichtenbox anzuzeigen, besteht darin, die statischen Funktionen QMessageBox::information(), QMessageBox::question(), QMessageBox::critical() und QMessageBox::warning() zu verwenden. Bspw. folgende Nachrichtenbox:
113
4.4
1542.book Seite 114 Montag, 4. Januar 2010 1:02 13
4
Dialoge, Layout und Qt-Widgets
Abbildung 4.35
QMessage::warning()
Diese Nachrichtenbox (Abbildung 4.35) anzuzeigen, setzt Folgendes voraus: int ret = QMessageBox::warning( this, "Beenden?", "Wollen Sie die Anwendung wirklich beenden?", QMessageBox::Yes | QMessageBox::No );
Alternativ können Sie den Konstruktor von QMessageBox verwenden, um eine Nachrichtenbox mit Icon und Button(s) zu erzeugen. Bezogen auf Abbildung 4.35 sähe dies folgendermaßen aus: QString caption("Beenden?"); QString text("Wollen Sie die Anwendung wirklich beenden?"); QMessageBox msg( QMessageBox::Warning, caption, text, QMessageBox::Yes | QMessageBox::No ); msg.exec();
Mit dem ersten Argument geben Sie hier das Icon für die Nachrichtenbox an. Hierbei können Sie unter den folgenden vier Symbolen auswählen (von denen Sie QMessageBox::Warning bereits kennen): Konstante
Beschreibung
QMessageBox::Critical
Wird verwendet, um schwerwiegende Fehler anzuzeigen.
QMessageBox::NoIcon
Soll kein Icon angezeigt werden, können Sie diese Konstante verwenden.
QMessageBox::Information
Wird verwendet, um eine Information anzuzeigen.
QMessageBox::Question
Wird verwendet, wenn bei einer Dialogbox Fragen gestellt werden.
QMessageBox::Warning
Sollte eine gefährliche Aktion ausgeführt werden, können Sie dieses Symbol verwenden.
Tabelle 4.14
Icon-Konstanten für QMessageBox
Sollten Sie ein eigenes Icon hinzufügen wollen, steht Ihnen dafür die Methode setIconPixmap() zur Verfügung.
114
1542.book Seite 115 Montag, 4. Januar 2010 1:02 13
Vorgefertigte Dialoge
Mit den Argumenten zwei und drei von QMessageBox geben Sie den Fenstertitel und den eigentlichen Nachrichtentext an. Der Nachrichtentext kann, wie übrigens alle Qt-Dialoge, mit HTML formatiert werden. Ändern Sie bspw. den String text folgendermaßen ab: QString text( "Wollen Sie die " "Anwendung wirklich beenden?" );
Dadurch erhält der Nachrichtentext in der Nachrichtenbox folgendes Aussehen:
Abbildung 4.36 Mit HTML formatierte Nachrichten
Mit den weiteren Argumenten legen Sie die verschiedenen Buttons fest. Die möglichen Werte und deren Bedeutung finden Sie in Tabelle 4.15 aufgelistet. Konstante
Bedeutung
QMessageBox::Ok
Ok
QMessageBox::Open
Open (Öffnen )
QMessageBox::Save
Save (Speichern)
QMessageBox::Cancel
Cancel (Abbrechen)
QMessageBox::Close
Close (Schließen)
QMessageBox::Discard
Discard (Verwerfen)
QMessageBox::Apply
Apply (Anlegen)
QMessageBox::Reset
Reset (Zurücksetzen)
QMessageBox::RestoreDefaults
RestoreDefaults (Wiederherstellen)
QMessageBox::Help
Help (Hilfe)
QMessageBox::SaveAll
Save all (Alles Speichern)
QMessageBox::Yes
Yes (Ja)
QMessageBox::YesToAll
Yes to all (Ja zu allem)
QMessageBox::No
No (Nein)
QMessageBox::NoToAll
No to all (Nein, zu allem)
QMessageBox::Abort
Abort (Aussteigen)
Tabelle 4.15
Mögliche Texte für den Button
115
4.4
1542.book Seite 116 Montag, 4. Januar 2010 1:02 13
4
Dialoge, Layout und Qt-Widgets
Konstante
Bedeutung
QMessageBox::Retry
Retry (Wiederholen)
QMessageBox::Ignore
Ignore (Ignorieren)
QMessageBox::NoButton
–
Tabelle 4.15
Mögliche Texte für den Button (Forts.)
Dialog auch mit leerer QMessageBox Es ist auch möglich, mit einem leeren QMessageBox-Konstruktor einen Nachrichtendialog zu erstellen. Hierbei stehen dem Anwender dann verschiedene set-Zugriffsmethoden zur Verfügung, um Icon, Titel oder Buttons nachträglich anzugeben.
Nachrichtendialog auswerten Auch das Auswerten der Nachrichtendialoge ist recht einfach. Im Grunde müssen Sie einfach überprüfen, ob der Button mit der entsprechenden Konstante (siehe Tabelle 4.15) gedrückt wurde. Um Ihnen das Ganze auch praxisnah zu vermitteln, erstellen wir hierzu wieder ein Beispiel. Zunächst erstellen wir wieder ein einfaches Fenster mit einem Button. Das Grundgerüst: 00 01 02 03 04 05 06 07 08
// beispiele/qmessagebox/mywidget.h #ifndef MYWIDGET_H #define MYWIDGET_H #include #include #include #include #include #include
09 10 11 12 13 14 15 16 17 18 19
class MyWidget : public QWidget { Q_OBJECT public: MyWidget( const char* qstr ="Bitte Button betätigen", const char* but = "Ende", QWidget *parent = 0 ); private: QPushButton *button0; QLabel* label; QVBoxLayout* layout; public slots:
116
1542.book Seite 117 Montag, 4. Januar 2010 1:02 13
Vorgefertigte Dialoge
20 void myquit(); 21 }; 22 #endif
Dieses Beispiel haben Sie in ähnlicher Form bereits verwendet. Die Auswertung des Dialogs wird mit der eigenen Slot-Methode myquit() (Zeile 19 und 20) realisiert. Hierzu nun die Implementierung der Klasse MyWidget: 00 // beispiele/qmessagebox/mywidget.cpp 01 #include "mywidget.h" 02 #include 03 // neue Widget-Klasse vom eigentlichen Widget ableiten 04 MyWidget::MyWidget( const char* lab, const char* but, 05 QWidget *parent): QWidget(parent) { 05 // Elemente des Widgets erzeugen 06 button0 = new QPushButton (but); 07 layout = new QVBoxLayout(this); 08 label = new QLabel(lab); 09 // Elemente des Widgets anordnen/anpassen 10 layout->addWidget(label); 11 layout->addWidget(button0); 12 // Signale des Widgets einrichten 13 connect( button0, SIGNAL( clicked() ), this, SLOT( myquit() ) ); 14 } 15 void MyWidget::myquit() { 16 int ret = QMessageBox::warning( 17 this, "Beenden?", 18 "Wollen Sie die Anwendung wirklich beenden?", 19 QMessageBox::Yes | QMessageBox::No ); 20 if( ret == QMessageBox::Yes ) 21 close(); 22 }
Die entscheidende Zeile finden Sie hier zunächst in Zeile 13, wo Sie für den Button des Hauptfensters eine Signal-Slot-Verbindung einrichten. Drückt man den Button (erhält das Signal clicked()), wird der eigene Slot myquit() ausgeführt. In der Slot-Methode myquit() (Zeile 15 bis 22) erzeugen wir zunächst mit der statischen Funktion QMessageBox::warning einen Nachrichtendialog, wie Sie ihn von Abbildung 4.35 her kennen. Die statischen Funktionen von QMessageBox geben als Rückgabewert einen Integer zurück. Welcher Wert bzw., genauer, welcher Button gedrückt wurde, werten wir in Zeile 20 aus. Entspricht der Rück-
117
4.4
1542.book Seite 118 Montag, 4. Januar 2010 1:02 13
4
Dialoge, Layout und Qt-Widgets
gabewert in ret der Konstante QMessageBox::Yes, wurde der Yes-Button (bzw. Ja-Button) betätigt und, wir beenden die Anwendung mit close(). Ansonsten wurde der No-Button (bzw. Nein-Button) gedrückt, was in diesem Fall allerdings nicht mehr ausgewertet werden muss. Ähnlich funktioniert dies natürlich mit Objekten von QMessageBox. Das gleiche Beispiel lässt sich auch folgendermaßen erstellen: // beispiele/qmessagebox/mywidget.cpp ... void MyWidget::myquit() { QString caption("Beenden?"); QString te("Wollen Sie die Anwendung wirklich beenden?"); QMessageBox msg( QMessageBox::Warning, caption, te, QMessageBox::Yes | QMessageBox::No ); if( msg.exec() == QMessageBox::Yes ) close(); }
Sollten Sie mehrere Buttons verwenden und überprüfen wollen, lässt sich auch ein switch()-Schalter verwenden. Bspw.: switch ( msg.exec() ) { case QMessageBox::Yes: // yes wurde betätigt break; case QMessageBox::No: // no wurde betätigt break; default: // Fehler, hierher sollte es nicht gehen break; }
Eigene Schaltflächen für QMessageBox Sollten die vordefinierten Konstanten für die Knöpfe nicht ausreichen, können Sie gerne einen eigenen Button mit der mehrfach überladenen Methode addButton() hinzufügen, dem Button einen eigenen Text geben und diesen mit einer vordefinierten Button-Funktion versehen. Folgende Funktionen können dabei einem Button übergeben werden, was auch nötig ist, um ihn auswerten zu können.
118
1542.book Seite 119 Montag, 4. Januar 2010 1:02 13
Vorgefertigte Dialoge
Konstante
Beschreibung
QMessageBox::InvalidRole
Der Button ist ungültig.
QMessageBox::AcceptRole
Bei Betätigung des Buttons wird der Dialog als akzeptiert (accepted) gewertet (bspw. Ok).
QMessageBox::RejectRole
Bei Betätigung des Buttons wird der Dialog als abgelehnt (rejected) gewertet (bspw. Cancel).
QMessageBox::DestructiveRole
Bei Betätigung des Buttons wird der Dialog als verworfen gewertet (bspw. Discard Changes).
QMessageBox::YesRole
Yes-ähnlicher Button
QMessageBox::NoRole
No-ähnlicher Button
Tabelle 4.16
Einige vordefinierte Button-Funktionen
Keine Signale und Slots QMessageBox besitzt keine Signale und Slots, weshalb Sie auf die vordefinierten But-
ton-Funktion neu zugreifen müssen, sofern Sie eigene verwenden wollen.
In der Praxis wird die Methode addButton() folgendermaßen verwendet: void MyWidget::myquit() { QString caption("Beenden?"); QString te("Wollen Sie die Anwendung wirklich beenden?"); QMessageBox msg( QMessageBox::Warning, caption, te ); QPushButton* yesButton = msg.addButton("Jep", QMessageBox::YesRole); QPushButton* noButton = msg.addButton("Nee", QMessageBox::NoRole); msg.exec(); if( msg.clickedButton() == yesButton ) close(); else if( msg.clickedButton() == noButton ) ;// Nein, wurde ausgewählt }
Wenn Sie wie hier im Beispiel eine Instanz von QMessageBox mit eigenen Buttons verwenden, können Sie den Wert des gedrückten Buttons mit der Methode clickedButton() nach dem Aufruf von exec() abfragen.
119
4.4
1542.book Seite 120 Montag, 4. Januar 2010 1:02 13
4
Dialoge, Layout und Qt-Widgets
So sieht es dann aus:
Abbildung 4.37
Eigene Schaltflächen für QMessageBox
Zum Schluss noch ein kurzer Überblick über die gängigsten Methoden der Klasse QMessageBox. Methode
Beschreibung
QMessageBox ( QWidget * parent = 0 );
Konstruktor. Erzeugt eine Nachrichtenbox ohne Text und ohne Buttons sowie (optional) mit parent als Elternwidget.
QMessageBox ( Icon icon, const QString & title, const QString & text, StandardButtons buttons = NoButton, QWidget * parent = 0, Qt::WindowFlags f = Qt::Dialog | Qt:: MSWindowsFixedSizeDialogHint);
Konstruktor. Erzeugt eine Nachrichtenbox mit Icon icon, Titel title, Text text, Buttons buttons, Elternwidget parent und Fensterflags f.
~QMessageBox ()
Destruktor. Zerstört eine Nachrichtenbox.
void addButton( QAbstractButton * button, ButtonRole role );
Fügt den übergebenen Button button an den Nachrichtendialog mit der Funktion role (Tabelle 4.16).
QPushButton * addButton ( const QString & text, ButtonRole role );
Erzeugt einen Button mit dem übergebenen String text und der Funktion role (Tabelle 4.16) und fügt diese dem Nachrichtendialog hinzu. Zurückgegeben wird der erzeugte Button.
QPushButton * addButton ( StandardButton button );
Fügt einen Standard-Button (Tabelle 4.15) dem Nachrichtendialog hinzu und gibt diesen als Rückgabewert zurück.
QAbstractButton * button ( StandardButton which ) const;
Gibt einen Zeiger auf dem vorbelegten Standardbutton which zurück. Existiert dieser Button nicht im Nachrichtendialog, wird 0 zurückgegeben.
Tabelle 4.17
120
Gängige Methode für QMessageBox
1542.book Seite 121 Montag, 4. Januar 2010 1:02 13
Vorgefertigte Dialoge
Methode
Beschreibung
QAbstractButton* clickedButton() const;
Gibt den vom Anwender gedrückten Button zurück, oder 0, wenn er die Escape-Taste gedrückt hat und kein Escape-Button gesetzt wurde.
QPushButton * defaultButton () const;
Gibt den Button zurück, der im Nachrichtendialog als Standard-Button vorbelegt ist (wenn bspw. (¢) gedrückt wird). Wurde kein Standard-Button gesetzt, wird 0 zurückgegeben.
QString detailedText () const
Gibt den detaillierten Text innerhalb des Nachrichtendialogs zurück.
QAbstractButton* escapeButton() const;
Gibt den Button zurück, der im Nachrichtendialog als Escape-Button vorbelegt ist (wenn bspw. (¢) gedrückt wird). Wurde kein Standard-Button gesetzt, wird 0 zurückgegeben.
Icon icon () const;
Gibt das aktuelle Icon des Nachrichtendialogs zurück.
QPixmap iconPixmap () const;
Gibt das aktuelle Icon-Pixmap des Nachrichtendialogs zurück.
void removeButton ( QAbstractButton * button );
Entfernt den Schalter button aus dem Nachrichtendialog.
void setDefaultButton ( QPushButton * button );
Setzt den Schalter button als Standard-Button im Nachrichtendialog.
void setDetailedText ( const QString & text );
Setzt den detaillierten Text innerhalb des Nachrichtendialogs auf text.
void setEscapeButton ( QAbstractButton * button );
Setzt den Schalter button als Escape-Button im Nachrichtendialog.
void setIcon ( Icon );
Setzt das Icon im Nachrichtendialog auf Icon.
void setIconPixmap ( const QPixmap & pixmap );
Setzt das Icon-Pixmap im Nachrichtendialog auf pixmap.
void setText ( const QString & text );
Setzt den Text des Nachrichtendialogs auf text.
void setWindowModality ( Qt::WindowModality windowModality );
Setzt die Modalität des Nachrichtendialogs auf einen Wert der enum-Variablen Qt::WindowModality.
void setWindowTitle ( const QString & title );
Setzt den Fenster-Titel des Nachrichtendialogs auf title.
QString text () const;
Gibt den Text des Nachrichtendialogs zurück.
Tabelle 4.17
Gängige Methode für QMessageBox (Forts.)
121
4.4
1542.book Seite 122 Montag, 4. Januar 2010 1:02 13
4
Dialoge, Layout und Qt-Widgets
4.4.2
QFileDialog – Dialoge zur Dateiauswahl
Die Klasse QFileDialog wurde ebenfalls von QDialog abgeleitet und zeigt einen Dialog an, mit dem es möglich ist, eine Datei oder ein Verzeichnis auszuwählen. Wie auch schon bei QMessageBox bietet QFileDialog zwei Wege an: Zum einen sind auch hier wieder vorgefertigte statische Methoden zur Erzeugung vorhanden, und zum anderen ist es natürlich auch hiermit wieder möglich, eine Instanz der Klasse über den Konstruktor zu erzeugen. Der einfachste Weg, einen solchen Dateiauswahl-Dialog zu erzeugen, bietet wieder die statische Möglichkeit an. Je nach Betriebssystem wird hierbei immer der native Dateiauswahl-Dialog angezeigt. Das bedeutet: Damit bekommt der Anwender den Dialog zu sehen, den er auf seinem System gewohnt ist. Es wird also der Dateiauswahl-Dialog des Betriebssystems verwendet. Dies kann ggf. auch vermieden werden, wenn die Option QtFileDialog::DontUseNativeDialog gesetzt wird. Am besten hierzu ein einfaches Beispiel, in dem wir wieder die Slot-Methode MyWidget::myquit() aus dem Beispiel zuvor umschreiben: // beispiele/qfiledialog/mywidget.cpp ... #include #include ... void MyWidget::myquit() { QString file = QFileDialog::getOpenFileName( this, "Bitte eine Datei auswählen", QDir::homePath(), "Dokumente (*.pdf *ps *doc)" ); if( !file.isNull() ) { QString info("Folgende Datei wurde ausgewählt:\n"); info.append(file); QMessageBox::information( this, "Ihre Auswahl", info, QMessageBox::Ok ); } }
Je nach System, auf dem Sie das Beispiel ausführen, sieht der Dateiauswahl-Dialog folgendermaßen aus (siehe Abbildung 4.38). Sollten Sie eine Datei (hier eine mit der Endung PDF, PS oder DOC) ausgewählt haben (wird überprüft mit !file.isNull()), wird dies im folgenden Nachrichtendialog angezeigt. Hierzu eine etwas genauere Beschreibung.
122
1542.book Seite 123 Montag, 4. Januar 2010 1:02 13
Vorgefertigte Dialoge
Abbildung 4.38
Dateiauswahl-Dialog (QFileDialog::getOpenFileName)
Nachdem Sie in QFileDialog::getOpenFileName mit dem ersten Parameter das Eltern-Widget angegeben haben, wird mit dem zweiten Parameter der Titel des Fensters vergeben. Beachten Sie Folgendes: Wenn Sie beim ersten Parameter 0 angeben, wird der Dialog nichtmodal angezeigt. Mit dem dritten Parameter können Sie das Startverzeichnis angeben. Hierbei sind natürlich auch relative und absolute Pfadnamen erlaubt. Im Beispiel haben wir mit QDir::homePath() eine statische Methode der Klasse QDir verwendet. Unter Linux/Unix und auch Mac OS X wird hierbei die Umge-
bungsvariable HOME verwendet. Ist dies nicht gesetzt, wird das Wurzelverzeichnis verwendet. Unter MS-Windows wird ebenfalls nach einer Umgebungsvariablen HOME gesucht. Existiert diese nicht, wird die Umgebungsvariable USERPROFILE verwendet. Existiert auch diese nicht, verwendet Qt HOMEDRIVE und HOMEPATH. QDir bietet hierzu noch weitere statische Methoden an, welche in der Tabelle 4.18 aufgelistet sind. Statische Methode
Beschreibung
QDir::currentPath()
Gibt das aktuelle Verzeichnis der Anwendung zurück.
QDir::rootPath()
Gibt das oberste Verzeichnis zurück. Unter Windows ist dies bspw. C:\, und unter Linux/Unix/Mac OS X wird / zurückgegeben.
QDir::tempPath()
Gibt den Pfad zum temporären Verzeichnis zurück.
Tabelle 4.18
Statische Methoden von QDir
123
4.4
1542.book Seite 124 Montag, 4. Januar 2010 1:02 13
4
Dialoge, Layout und Qt-Widgets
Der letzte Parameter von QFileDialog::getOpenFileName ist ein Dateifilter. Die Syntax hierzu ist ganz einfach: "Bezeichner (*.ext1 *.ext2 *.ext3)"
Der »Bezeichner« ist frei wählbar. Dann verwenden Sie die Dateiendungen, die Sie im Filter mit einbeziehen wollen, genauer: die im Dateiauswahl-Dialog angezeigt werden sollen. Wollen Sie mehrere Dateifilter verwenden, müssen Sie nur am Ende der geklammerten Filter zwei Semikolons setzen. Bspw: QString file = QFileDialog::getOpenFileName( this, "Bitte eine Datei auswählen", QDir::homePath(), "Dokumente (*.pdf *ps *doc);;" "Bilder (*.jpg *.png *.gif);;" "Quelldateien (*.c *cc *cpp);;" "Alle Dateien (*.*)");
Nun können Sie über eine Dropdown-Liste die hinzugefügten Filter verwenden (siehe Abbildung 4.39).
Abbildung 4.39
Mehrere Dateifilter verwenden
Neben der statischen Methode QFileDialog::getOpenFileName gibt es weitere, die Tabelle 4.19 auflistet. Statische Methode
Beschreibung
QString getExistingDirectory ( QWidget * parent = 0, const QString& caption = QString(), const QString & dir = QString(), Options options = ShowDirsOnly );
Damit erstellen Sie einen Dateiauswahl-Dialog, der nur Vereichnisse anzeigt und keine Dateien. Daher benötigt diese statische Methode keinen Filter. Zusätzlich wird überprüft, ob ein Verzeichnis tatsächlich existiert.
QStringList getOpenFileNames ( QWidget * parent = 0, const QString & caption = QString(),
Mit dieser Methode können Sie mehrere Dateien auf einmal auswählen (bspw. mit gedrückter (Strg)-Taste). Zurückgegeben wird QStringList (eine Klasse für eine Liste von Strings) mit den ausgewählten Dateien.
Tabelle 4.19
124
Weitere statische Methoden für einen Dateiauswahl-Dialog
1542.book Seite 125 Montag, 4. Januar 2010 1:02 13
Vorgefertigte Dialoge
Statische Methode
Beschreibung
const QString & dir = QString(), const QString & filter = QString(), QString * selectedFilter = 0, Options options = 0 ); QString getSaveFileName ( QWidget * parent = 0, const QString & caption = QString(), const QString & dir = QString(), const QString & filter = QString(), QString * selectedFilter= 0, Options options = 0 );
Tabelle 4.19
Erstellt einen Dateiauswahl-Dialog zum Speichern und Anlegen von Dateien. Existiert diese Datei, wird vor dem Überschreiben nachgefragt. Sie können dies mit der Option QFileDialog:: DontConfirmOverwrite übergehen.
Weitere statische Methoden für einen Dateiauswahl-Dialog (Forts.)
Natürlich ist es auch möglich, einen eigenen QFileDialog ohne statische Funktionen zu erzeugen. Bezogen auf das statische Beispiel, sieht die Verwendung mit dem Konstruktor der Klasse QFileDialog folgendermaßen aus: // beispiele/qfiledialog2/mywidget.cpp ... void MyWidget::myquit() { QFileDialog* filedlg = new QFileDialog( this, "Bitte eine Datei auswählen"); QStringList filters; filters << "Dokumente (*.pdf *ps *doc)" << "Bilder (*.jpg *.png *.gif)" << "Quelldateien (*.c *cc *cpp)" << "Alle Dateien (*.*)"; filedlg->setFileMode(QFileDialog::ExistingFiles); filedlg->setDirectory(QDir::homePath()); filedlg->setFilters(filters); filedlg->setViewMode(QFileDialog::Detail); QStringList fileNames; if (filedlg->exec()) fileNames = filedlg->selectedFiles(); QString info("Ihr Auswahl:\n"); info.append(fileNames.join("\n"));
125
4.4
1542.book Seite 126 Montag, 4. Januar 2010 1:02 13
4
Dialoge, Layout und Qt-Widgets
QMessageBox::information( this, "Ihre Auswahl", info, QMessageBox::Ok ); }
Mit der Methode setFileMode() geben Sie an, was der Anwender auswählen muss. Mit QFileDialog::ExistingFiles legen Sie fest, dass mehrere vorhandene Dateien ausgewählt werden dürfen. Weitere Modi hierzu: Konstante
Beschreibung
QFileDialog::AnyFile
der Name einer Datei, egal, ob diese existiert oder nicht
QFileDialog::ExistingFile
der Name einer einzelnen existierenden Datei
QFileDialog::Directory
Der Name eines Verzeichnisses. Trotzdem werden Dateien und Verzeichnisse angezeigt.
QFileDialog::DirectoryOnly
Der Name eines Verzeichnisses. Außerdem werden auch nur Verzeichnisse angezeigt.
QFileDialog::ExistingFiles
der Name einer oder mehrerer existierender Datei(en)
Tabelle 4.20
Modi für die Methode QFileDialog::setFileMode()
Mit der Methode setDirectory() geben Sie das Verzeichnis vor, wo der Dateiauswahl-Dialog anfängt. Einen Filter setzen Sie mit der Methode setFilter() oder mit setFilters(), wenn es mehrere sein sollten. Der Dateiauswahl-Dialog hat zwei Ansichten: QFileDialog::List und QFileDia-log::Detail. QFileDialog::List zeigt den Inhalt des aktuellen Verzeichnisses mit den Dateien und Verzeichnissen an. QFileDialog::Detail hingegen zeigt ebenfalls die Dateien und Verzeichnisse an. Hierbei werden zusätzlich Details wie die Größe, der Dateityp und das Veränderungsdatum angezeigt (siehe Abbildung 4.40). Die entsprechende Ansicht wird mit der Methode setViewMode() gesetzt.
Abbildung 4.40
126
Dateiauswahl-Dialog und die Ansicht (QFileDialog::Detail)
1542.book Seite 127 Montag, 4. Januar 2010 1:02 13
Vorgefertigte Dialoge
Wenn Sie einen eigenen Dateiauswahl-Dialog erzeugen, ist selectFiles() (es gibt auch selectFile() für eine einzelne Auswahl) die wichtigste Funktion. In unserem Beispiel wird der modale Dialog zunächst mit exec() angezeigt. Wenn der User Ok (oder Open bzw. Öffnen) anklickt, stehen die Dateinamen im Beispiel in der String-Liste QStringList fileNames. Da wir unseren Dialog mit dem Modus QFileDialog::ExistingFiles erzeugt haben, können hierbei durchaus mehrere Dateien ausgewählt werden (mit gehaltener (Strg)-Taste). Wir teilen diese String-Liste mit der Methode QStringList::join() in einzelne Happen auf und hängen diese zeilenweise (mit QString::append()) an den QString info. Die ausgewählten Dateien werden anschließend mit dem Nachrichtendialog ausgegeben.
4.4.3
QInputDialog – Eingabedialog
Für die Eingaben von Zahlen und Zeichenketten bietet Qt mit QInputDialog (ebenfalls von QDialog abgeleitet) auch vorgefertigte statische Eingabe-Dialoge an. In diesem Fall gibt es nur die statischen Methoden und keine Möglichkeit, eigene Instanzen dieser Klasse zu erzeugen (was allerdings auch gar nicht nötig ist). Alle Dialoge bieten jeweils den Button Ok und Abbrechen (bzw. Cancel) an. In der folgenden Tabelle (4.21) finden Sie die vier statischen Methoden aufgelistet. Statische Methode
Beschreibung
double QInputDialog::getDouble ( QWidget * parent = 0, const QString & title, const QString & label, double value = 0, double minValue = –2147483647, double maxValue = 2147483647, int decimals = 1, bool * ok = 0, Qt::WindowFlags f = 0 );
Liest eine Gleitpunktzahl vom Anwender ein. Mit parent geben Sie das Eltern-Widget an, und title ist der in der Titelleiste angezeigte Text. label ist der Text im Dialog, den der Anwender zu sehen bekommt. Mit value können Sie einen Standardwert vorbelegen. Den Mindest- bzw. Maximalwert, den der Anwender eingeben kann, legen Sie mit minValue bzw. maxValue fest. Mit decimal legen Sie die Stellen hinter dem Komma fest. Ist der Wert ok nach dem Anklicken eines Buttons wahr (true), hat der Anwender OK gedrückt. Bei false wurde »Abbrechen« (bzw. Cancel) gedrückt. Der Dialog wird modal angezeigt und verwendet außerdem das Widget-Flag f. Zurückgegeben wird im Erfolgsfall der eingegebene double-Wert.
int QInputDialog::getInteger ( QWidget * parent = 0, const QString & title, const QString & label, int value = 0,
Liest eine Ganzzahl vom Anwender ein. Mit parent geben Sie das Eltern-Widget an, und title ist der in der Titelleiste angezeigte Text. label ist der Text im Dialog, den der Anwender zu sehen bekommt. Mit value können Sie einen Standardwert vorbelegen.
Tabelle 4.21
Statische Methoden für Eingabedialoge
127
4.4
1542.book Seite 128 Montag, 4. Januar 2010 1:02 13
4
Dialoge, Layout und Qt-Widgets
Statische Methode int minValue = –2147483647, int maxValue = 2147483647, int step = 1, bool * ok = 0, Qt::WindowFlags f = 0 );
(Forts.)
Beschreibung Den Mindest- bzw. Maximalwert, den der Anwender eingeben kann, legen Sie mit minValue bzw. maxValue fest. Mit step legen Sie den Wert fest, der erhöht bzw. reduziert wird, wenn die Pfeil-Buttons gedrückt werden. Ist der Wert ok nach dem Anklicken eines Buttons wahr (true), hat der Anwender OK gedrückt. Bei false wurde »Abbrechen« (bzw. Cancel) gedrückt. Der Dialog wird modal angezeigt und verwendet zudem das Widget-Flag f. Zurückgegeben wird im Erfolgsfall der eingegebene Integerwert.
QString QInputDialog::getItem ( const QString & title, const QString & label, const QStringList & list, int current = 0, bool editable = true, bool * ok = 0, QWidget * parent = 0, const char * name = 0, Qt::WindowFlags f = 0 );
Lässt den Anwender eine Zeichenkette aus mehreren vorgegebenen Strings auswählen. Mit parent geben Sie das Eltern-Widget an, und title ist der in der Titelleiste angezeigte Text. label ist der Text im Dialog, den der Anwender zu sehen bekommt. Mit list können Sie eine Liste von Strings angeben, aus denen der Anwender wählen kann. Mit current legen Sie den String in der Liste fest, der beim Start des Dialogs vorbelegt ist. Setzen Sie editable auf true, erlauben Sie dem Anwender, dass dieser auch einen eigenen Text eingeben kann. Bei false kann der Anwender nur die vorgegebenen Strings wählen. Ist der Wert ok nach dem Anklicken eines Buttons wahr (true), hat der Anwender OK gedrückt. Bei false wurde »Abbrechen« (bzw. Cancel) gedrückt. Der Dialog wird modal angezeigt und verwendet zudem das Widget-Flag f. Zurückgegeben wird im Erfolgsfall das ausgewählte Element.
QString QInputDialog::getText ( const QString & title, const QString & label, QLineEdit::EchoMode echo = QLineEdit::Normal, const QString & text = QString(), bool * ok = 0, QWidget * parent = 0, const char * name = 0, Qt::WindowFlags f = 0 );
Liest einen eingegebenen String vom Anwender. Mit parent geben Sie das Eltern-Widget an, und title ist der in der Titelleiste angezeigte Text. label ist der Text im Dialog, den der Anwender zu sehen bekommt. Mit echo können Sie die Anzeige der Eingabe festlegen (siehe Tabelle 4.22). Im Beispiel ist der Text im Klartext lesbar. Mit text können Sie im Editierfeld einen markierten String vorbelegen. Ist der Wert ok nach dem Anklicken eines Buttons wahr (true), hat der Anwender OK gedrückt. Bei false wurde Abbrechen (bzw. Cancel) gedrückt. Der Dialog wird modal angezeigt und verwendet zudem das Widget-Flag f. Zurückgegeben wird bei Erfolg der eingegebene String.
Tabelle 4.21
128
Statische Methoden für Eingabedialoge (Forts.)
1542.book Seite 129 Montag, 4. Januar 2010 1:02 13
Vorgefertigte Dialoge
Hierzu noch eine Tabelle, mit dem enum-Typ QLineEdit::EchoMode, die beschreibt, wie der Inhalt in einem Editierfeld angezeigt werden soll. Konstante
Beschreibung
QLineEdit::Normal
Zeigt die eingegebenen Zeichen im Klartext an.
QLineEdit::NoEcho
Bei der Eingabe wird gar nichts angezeigt. Lässt sich bspw. für die Eingabe von Passwörtern verwenden, wenn die Länge des Passwortes geheim bleiben soll.
QLineEdit::Password
Bei der Eingabe werden nur Sternchen für jedes Zeichen im Editierfeld angezeigt.
QLineEdit::PasswordEchoOnEdit
Vorbelegte Zeichen werden mit Sternchen angezeigt. Wird der Text editiert, werden die Zeichen allerdings im Klartext angezeigt.
Tabelle 4.22
Konstanten für die Ausgabe der Eingabe
Hierzu wieder unsere Slot-Methode myquit(), die alle vier Eingabedialoge mitsamt Auswertung in der Praxis demonstrieren soll: // beispiele/qinputdialog/mywidget.cpp void MyWidget::myquit() { bool ok; // QInputDialog::getText() --- Anfang QString text = QInputDialog::getText( this, "QInputDialog::getText()", "Ihr Name :", QLineEdit::Normal, "Name eingeben", &ok); if (ok && !text.isEmpty()) QMessageBox::information( this, "Ihr Eingabe", text, QMessageBox::Ok ); // QInputDialog::getText() --- Ende // QInputDialog::getDouble --- Anfang double dvalue = QInputDialog::getDouble( this, "QInputDialog::getDouble()", "Wert eingeben :", 55.555, 0, 100, 3, &ok ); if(ok) { QString qsdvalue = QString("%1").arg(dvalue); QMessageBox::information( this,"Ihr double-Wert ",qsdvalue,QMessageBox::Ok); } // QInputDialog::getDouble --- Ende // QInputDialog::getInteger --- Anfang int ivalue = QInputDialog::getInteger(
129
4.4
1542.book Seite 130 Montag, 4. Januar 2010 1:02 13
4
Dialoge, Layout und Qt-Widgets
this, "QInputDialog::getInteger()", "Wert eingeben :", 10, 0, 20, 1, &ok ); if(ok) { QString qsivalue = QString("%1").arg(ivalue); QMessageBox::information( this, "Ihr Integer-Wert ", qsivalue, QMessageBox::Ok ); } // QInputDialog::getInteger --- Ende // QInputDialog::getItem --- Anfang QStringList items; items << "Sonne" << "Regen" << "Nebel" << "Wind" << "Schnee"; QString itemvalue = QInputDialog::getItem( this,"QInputDialog::getItem()","Ihr Lieblingswetter:", items, 0, false, &ok ); if(ok) { QMessageBox::information( this, "Ihr Lieblingswetter", itemvalue, QMessageBox::Ok ); } // QInputDialog::getItem --- Ende }
Das Programm bei der Ausführung (alle vier Eingabedialoge):
Abbildung 4.41 Einlesen einer Zeichenkette (QInputDialog::getText())
Abbildung 4.42 Einlesen eines Double-Werts (QInputDialog::getDouble())
130
1542.book Seite 131 Montag, 4. Januar 2010 1:02 13
Vorgefertigte Dialoge
Abbildung 4.43
Einlesen eines Integer-Werts (QInputDialog::getInteger())
Abbildung 4.44
Einlesen eines Elements (QInputDialog::getItem())
4.4.4
QFontDialog – Schriftauswahl
Für die Auswahl einer Schriftart bietet Qt die von QDialog abgeleitete Klasse QFontDialog (siehe Abbildung 4.45) an. Die Schriftart selbst wird mit der Klasse QFont beschrieben.
Abbildung 4.45
Schriftart auswählen (QFontDialog)
Die Verwendung von QFontDialog an dieser Stelle wäre recht gehaltlos, weshalb dieser Dialog erst später (beim QTextEdit-Widget) zum Einsatz kommt, wo er sinngemäß verwendet wird.
131
4.4
1542.book Seite 132 Montag, 4. Januar 2010 1:02 13
4
Dialoge, Layout und Qt-Widgets
4.4.5
QColorDialog – Farbauswahl
Für die Auswahl einer Farbe bietet Qt die von QDialog abgeleitete Klasse QColorDialog (siehe Abbildung 4.46) an. Die Farbe selbst wird mit der Klasse QColor beschrieben.
Abbildung 4.46 Farbe auswählen (QColorDialog)
Auch die Verwendung von QColorDialog an dieser Stelle ist recht trocken, weshalb dieser Dialog erst im Abschnitt zu QTextEdit (S. 243ff.) zum Einsatz kommt.
4.4.6
QPrintDialog – Druckerdialog
Für die Auswahl des Druckers wird die von QAbstractPrintDialog (ihrerseits wiederum von QDialog) abgeleitete Klasse QPrintDialog (siehe Abbildung 4.47) verwendet. Der Drucker selbst wird mit der Klasse QPrinter beschrieben – genauer: QPrinter ist für das Zeichnen auf den Drucker verantwortlich.
Abbildung 4.47
132
Drucker-Dialog (QPrintDialog)
1542.book Seite 133 Montag, 4. Januar 2010 1:02 13
Qt-Widgets
Die Verwendung von QPrintDialog und QPrint besprechen wir gesondert in Abschnitt 9.3.
4.4.7
Dialoge – Übersicht
Zum Schluss noch eine Hierarchie-Übersicht aller vorgefertigten Dialoge von Qt und dessen Stammbaum.
QObject
QPaintDevice
QWidget
QDialog
QMessageBox
QFontDialog QFileDialog QColorDialog
QInputDialog
QAbstractPrintDialog
QPrintDialog
Abbildung 4.48
4.5
Klassenhierarchie der vorgefertigten Dialoge
Qt-Widgets
Nachdem die Dialoge von Qt behandelt wurden, können Sie mit den grundlegenden Widgets, die Qt anbietet, fortfahren. Um den Rahmen des Kapitels nicht zu sprengen, gehen wir hierbei wieder nur auf die gängigsten und wichtigsten Signale, Slots und Methoden der einzelnen Widgets ein. Für weitere Details empfehlen wir wie immer den Assistant von Qt.
4.5.1
Buttons – Basisklasse QAbstractButton
Die Basisklasse QAbstractButton (abgeleitet von QWidget) ist die abstrakte Basisklasse der Widgets für Schaltflächen (Buttons) und stellt die grundlegenden Funktionen, Signale und Slots für alle andere Buttonarten zur Verfügung. QAbstractButton unterstützt neben den gewöhnlichen Buttons (QPushButton)
auch Check-Buttons (QCheckBox) und Radio-Buttons (QRadioButton). Check-But-
133
4.5
1542.book Seite 134 Montag, 4. Januar 2010 1:02 13
4
Dialoge, Layout und Qt-Widgets
tons sind Auswählkästchen, die gewöhnlich zu einer Gruppe zusammengefasst werden, wo mehrere ausgewählt werden können. Radio-Buttons sind im Grunde dasselbe, bloß dass hierbei in der Regel nur ein Element der Gruppe ausgewählt werden kann.
QAbstractButton
QRadioButton
Abbildung 4.49
Zu
QCheckBox
QPushButton
QToolButton
Basisklasse QAbstractButton
den
gewöhnlichen Buttons (QPushButton) gehört auch die Klasse QToolButton. Beide unterstützen natürlich auch den angewählten (gedrückten) Zustand (engl. toggled). QAbstractButton vergibt den Text und das Icon an die Buttons. Der Text wird mit setText() gesetzt (es sei denn, dieser wird beim Erzeugen mit dem Konstruktor vergeben) und das Icon mit setIcon(). Bspw. lässt sich ein Icon folgendermaßen auf den Button setzen: button01->setIcon(QIcon("images/icon.png"));
In diesem Beispiel wird das Icon icon.png, welches sich im Verzeichnis images des laufenden Programms befindet, dem Button hinzugefügt. Ist das Icon größer als der Button, wird die Größe automatisch angepasst. Alle Buttons in Qt unterstützen sowohl Text und/oder die Anzeige von Icons. Wollen Sie dem Button ein Tastaturkürzel verleihen, stehen Ihnen zwei Möglichkeiten zur Verfügung. Wenn Sie das Ampersand-Zeichen (&) verwenden, erzeugt QAbstractButton automatisch ein Tastaturkürzel für das Zeichen vor dem Ampersand-Zeichen. Bspw.: button2 = new QPushButton ("Q&CheckButton");
Hiermit können Sie mit dem Tastaturkürzel (Alt)+(C) den Button betätigen. Auf diese Weise wird übrigens der Slot animateClick() ausgeführt. Außerdem
können
Sie
ein
solches
Tastaturkürzel
mit
der
Methode
setShortcut() setzen. Bspw.: button0->setShortcut(tr("Alt+F1"));
Damit können Sie den Button mit der Tastenkombination (Alt)+(F1) betätigen (clicked()).
134
1542.book Seite 135 Montag, 4. Januar 2010 1:02 13
Qt-Widgets
Hierzu ein Überblick zu den gängigen Methoden der abstrakten Basisklasse QAbstractButton, die den abgeleiteten Klassen QCheckBox, QRadioButton, QPushButton und QToolButton zur Verfügung stehen. Methode
Beschreibung
QIcon icon () const;
Gibt das Icon zurück, das der Button enthält.
QSize iconSize () const;
Gibt die Größe des im Button enthaltenen Icons zurück
bool isCheckable () const;
Überprüft, ob der Button ankreuzbar (bspw. bei einem Radio- bzw. Checkbutton) bzw. festsetzbar (bei QPushButton (toggled)) ist.
bool isChecked () const;
Überprüft, ob der Button angekreuzt oder festgesetzt (toogled) ist. Natürlich können nur »checkable« Buttons diesen Zustand aufweisen.
bool isDown () const;
Überprüft, ob ein Button gedrückt ist.
void setCheckable ( bool );
Setzt einen Button mit true auf ankreuzbar bzw. festsetzbar (toggled). Per Standard ist ein Button nicht »checkable«.
void setDown ( bool );
Mit true wird der Button gedrückt angezeigt. Per Standard ist ein Button nicht gedrückt.
void setIcon ( const QIcon & icon );
Platziert das Icon icon auf dem Button.
void setShortcut ( const QKeySequence & key );
Vergibt ein Tastaturkürzel key an den Button.
void setText ( const QString & text );
Setzt den Text text auf dem Button (neu).
QKeySequence shortcut () const;
Gibt die Tastaturkürzel für einen Button zurück.
QString text () const;
Gibt den Text von einem Button zurück.
Tabelle 4.23
Gängige Methoden von QAbstractButton
isDown() vs. isChecked() Zum Unterschied zwischen isDown() und isChecked(): Wenn der Anwender einen drückbaren (isChecked()) Button betätigt, wird der Button gedrückt und bleibt anschließend in einem gedrückten Zustand. Klickt der Anwender den Button erneut an, ist Letzterer wieder im ursprünglichen Zustand (nicht gedrückt). Wird hingegen der gedrückte Button (isDown()) betätigt, befindet sich dieser in einem nicht mehr gedrückten Zustand und – der Hauptunterschied – kann auch nicht mehr in einen gedrückten Zustand gebracht werden (toggled). Dieser Button ist nach einmal Betätigen somit ein normaler Button.
135
4.5
1542.book Seite 136 Montag, 4. Januar 2010 1:02 13
4
Dialoge, Layout und Qt-Widgets
Folgende Signale werden von QAbstractButton und für den darunter liegenden Klassen zur Verfügung gestellt (Tabelle 4.24). Signal
Beschreibung
void clicked ( bool checked = false );
Dieses Signal wird ausgelöst, wenn der Button betätigt wurde (bspw. gedrückt und losgelassen, wenn der Mauscursor innerhalb des Buttons war).
void pressed ();
Wird ausgelöst, wenn der Button gedrückt wurde.
void released ();
Wird erst ausgelöst, wenn der Button losgelassen wurde.
void toggled ( bool checked );
Wird ausgelöst, wenn ein ankreuzbarer oder dauerhaft drückbarer Button seinen Zustand verändert. checked ist true, wenn der Button angekreuzt oder gedrückt ist. Das Gegenteil bei false.
Tabelle 4.24
Signale von QAbstractButton
Folgende Slots werden von der Klasse QAbstractButton bereitgestellt: Slots
Beschreibung
void animateClick ( int msec = 100 );
Damit wird ein »animierter« Klick ausgeführt. Dies bedeutet: Der Button wird gedrückt und erst msec Millisekunden später losgelassen. Der Standard hierbei ist 100 ms.
void click ();
Führt einen Klick aus. Dies bedeutet, dass alle relevanten Signale das Signal clicked() ausgelöst bekommen.
void setChecked ( bool );
Ankreuzbare Buttons werden hiermit angekreuzt bzw. gedrückt, wenn der Parameter true ist; bei false bleibt nichts angekreuzt bzw. gedrückt.
void setIconSize ( const QSize & size );
Setzt die Größe des Icons auf size.
void toggle ();
Knebelt den Status eines ankreuzbaren bzw. drückbaren Buttons.
Tabelle 4.25
Slots von QAbstractButton
Um jetzt die einzelnen Buttons in der Praxis zu demonstrieren, haben wir ein Programm vorbereitet. Bis auf die Klasse QToolButton sollen hierbei alle ButtonKlassen behandelt werden. Für jede Klasse wurde eine extra Header- und Quelldatei verwendet. Um die Übersicht zu wahren, folgt ein kurzer Überblick. Alle Dateien im Überblick:
136
1542.book Seite 137 Montag, 4. Januar 2010 1:02 13
Qt-Widgets
왘
main.cpp – das Hauptprogramm
왘
mywidget1.cpp und mywidget1.h – das Grundgerüst, mit dem die einzelnen
왘
mypushbuttondialog.h und mypushbuttondialog.cpp – demonstriert die
왘
mycheckbuttondialog.h und mycheckbuttondialog.cpp – demonstriert die
왘
myradiobuttondialog.h und myradiobuttondialog.cpp – demonstriert die
Button-Demonstrationen (oder auch -Klassen) aufgerufen werden Klasse QPushButton. Klasse QCheckButton. Klasse QRadioButton. Listings auf Buch-DVD Natürlich finden Sie alle Beispiele auf der Buch-DVD wieder, falls Sie den Überblick verlieren.
Zunächst also das Hauptprogramm main.cpp: 00 // beispiele/buttondemo/main.cpp 01 #include 02 #include "mywidget1.h" 03 int main(int argc, char *argv[]) { 04 QApplication app(argc, argv); 05 MyWidget* window = new MyWidget; 06 window->show(); 07 return app.exec(); 08 }
Als Nächstes folgt das Grundgerüst der Klasse MyWidget, womit alle anderen Klassen gestartet werden. 00 01 02 03 04 05 06 07 08 09
// beispiele/buttondemo/mywidget1.h #ifndef MYWIDGET_H #define MYWIDGET_H #include #include #include #include #include "myqpushbuttondialog.h" #include "mycheckbuttondialog.h" #include "myradiobuttondialog.h"
10 class MyWidget : public QWidget { 11 Q_OBJECT
137
4.5
1542.book Seite 138 Montag, 4. Januar 2010 1:02 13
4
Dialoge, Layout und Qt-Widgets
12 public: 13 MyWidget( const char* qstr = "Bitte wählen: ", QWidget *parent = 0 ); 14 private: 15 QPushButton *button0, *button1, *button2, *button3; 16 QLabel* label1; 17 QVBoxLayout* layout; 18 MyQPushDialog* pushDialog; 19 MyQCheckBoxDialog* checkDialog; 20 MyQRadioBoxDialog* radioDialog; 21 public slots: 22 void qpush_exec(); 23 void qcheck_exec(); 24 void qradio_exec(); 25 }; 26 #endif
In Zeile 18, 19 und 20 finden Sie schon mal die Deklarationen zu den noch folgenden Buttonklassen. In den Zeilen 22, 23 und 24 finden Sie eigene Slots, die ausgeführt werden, wenn Sie ein entsprechendes Button-Demo starten (wurde in unserem Beispiel über einen Button ausgewählt (clicked())). In diesen Slots wird jeweils ein Dialog mit der entsprechenden Button-Klasse gestartet und auch ausgewertet. Hierzu jetzt die Implementation des Grundgerüsts: 00 01 02 03 04 05 06
// beispiele/buttondemo/mywidget1.cpp #include "mywidget1.h" #include "myqpushbuttondialog.h" #include "mycheckbuttondialog.h" #include "myradiobuttondialog.h" #include #include
07 // neue Widget-Klasse vom eigentlichen Widget ableiten 08 MyWidget::MyWidget( const char* lab, QWidget *parent): QWidget(parent) { 09 // Elemente des Widgets erzeugen 10 button0 = new QPushButton ("QPushButton"); 11 button2 = new QPushButton ("Q&CheckButton"); 12 button3 = new QPushButton ("Q&RadioButton"); 13 layout = new QVBoxLayout(this); 14 label1 = new QLabel(lab); 15
button0->setShortcut(tr("Alt+F1"));
16
// Elemente des Widgets anordnen/anpassen
138
1542.book Seite 139 Montag, 4. Januar 2010 1:02 13
Qt-Widgets
17 18 19 20
layout->addWidget(label1); layout->addWidget(button0); layout->addWidget(button2); layout->addWidget(button3);
21 22
// Signale des Widgets einrichten connect( button0, SIGNAL( clicked() ), this, SLOT( qpush_exec() ) ); connect( button2, SIGNAL( clicked() ), this, SLOT( qcheck_exec() ) ); connect( button3, SIGNAL( clicked() ), this, SLOT( qradio_exec() ) ); setWindowTitle("Button – Demo");
23 24 25 26 }
27 void MyWidget::qpush_exec() { 28 pushDialog = new MyQPushDialog; 29 int status = pushDialog->exec(); 30 31
32 33
34 35
36 37
if( status == QDialog::Accepted ) QMessageBox::information( this, "qpush_exec()", "Acceped zurückgegeben (\"Flat\"-Button)", QMessageBox::Ok ); else if( status == QDialog::Rejected ) QMessageBox::information( this, "qpush_exec()", "Rejected zurückgegeben (\"Default\"-Button)", QMessageBox::Ok ); else if( status == 100) QMessageBox::information( this, "qpush_exec()", "\"Normal\"-Button betätigt: " "\"Checkable\"-Button war aktiv", QMessageBox::Ok ); else if( status == 50 ) QMessageBox::information( this, "qpush_exec()", "\"Normal\"-Button betätigt:" " \"Checkable\"-Button war nicht aktiv", QMessageBox::Ok );
38 } 39 void MyWidget::qcheck_exec() { 40 checkDialog = new MyQCheckBoxDialog;
139
4.5
1542.book Seite 140 Montag, 4. Januar 2010 1:02 13
4
Dialoge, Layout und Qt-Widgets
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
int status = checkDialog->exec(); QString score = "Aktiv waren: [Label 4] "; if ( status != 0 ) { if( status / 100 ) score.append(" [Label 3] "); status%=100; if( status / 10 ) score.append(" [Label 2] "); status%=10; if( status / 1 ) score.append(" [Label 1] "); } else { score.append(" Kein Label war aktiv " ); } QMessageBox::information( this, "qcheck_exec()", score, QMessageBox::Ok );
57 }
58 void MyWidget::qradio_exec() { 59 radioDialog = new MyQRadioBoxDialog; 60 int status = radioDialog->exec(); 61 QString score = "Aktiver Radio-Button: "; 62 if( status == 1 ) score.append("1"); 63 if( status == 2 ) score.append("2"); 64 if( status == 3 ) score.append("3"); 65 QMessageBox::information( this, "qradio_exec()", score, QMessageBox::Ok ); 66 }
Und so sieht das Grundgerüst aus:
Abbildung 4.50
Auswahl des Button-Demos
Auf die einzelnen Slots in diesem Beispiel gehen wir in den entsprechenden Klassen noch näher ein.
140
1542.book Seite 141 Montag, 4. Januar 2010 1:02 13
Qt-Widgets
QPushButton Die Klasse QPushButton wurde bereits des Öfteren in diesem Buch verwendet. Sie verkörpert die klassische Schaltfläche (sowie das am meisten angewendete Widget), die beim Anklicken bspw. mit einer Maus ein Kommando oder sonstige Aktionen ausführt. Ein solcher Button besitzt gewöhnlich einen Text und optional auch ein Icon. Um einen Text bzw. ein Icon nachträglich zu setzen, greift man auf die Methoden setIcon() und setText() der Basisklasse QAbstractButton zurück. QPushButton hat keine eigenen Signale definiert und verwendet die in QAbstractButton definierten Signale (siehe Tabelle 4.24). Dasselbe gilt für die Slots, die ebenfalls von der darüber liegenden Klasse QAbstractButton verwendet werden. Abgesehen vom Slot showMenu(), mit dem man ein entsprechendes Popup-Menü anzeigen kann, sofern eines existiert. Darauf gehen wir hier aber nicht ein.
Im Grunde besitzt ein gewöhnlicher QPushButton wenige wirklich wichtige Methoden und greift immer auf die Methoden der Basisklasse zurück. Eine Methode, mit der Sie einen Button flach (flat) darstellen können, ist setFlat(bool). Dies ist allerdings weniger vorteilhaft, weil schlecht sichtbar ist, ob es sich um einen Button oder um ein bloßes Textlabel handelt. Wenn Sie einen gedrückten Button benötigen, verwenden Sie die Methode setCheckable(). Hierzu nun ein Beispiel, um die Klasse MyQPushDialog (alias QPushButton) näher zu beschreiben. Zunächst das Grundgerüst: 00 01 02 03 04 05 06 07 08 09
// beispiele/buttondemo/myqpushbuttondialog.h #ifndef MYQPUSHBUTTONDIALOG_H #define MYQPUSHBUTTONDIALOG_H #include #include #include #include #include #include #include
10 class MyQPushDialog : public QDialog { 11 Q_OBJECT 12 public: 13 MyQPushDialog(); 14 QVBoxLayout *vbox; 15 QPushButton *button01; 16 QPushButton *button02;
141
4.5
1542.book Seite 142 Montag, 4. Januar 2010 1:02 13
4
Dialoge, Layout und Qt-Widgets
17 QPushButton *button03; 18 QPushButton *button04; 19 QPushButton *button05; 20 public slots: 21 void checktoogled(bool iftoogled); 22 void mySetResult(); 23 }; 24 #endif
Eine Erläuterung hierzu kann ich mir ersparen und fahre gleich mit der Implementation dieser Klasse fort: 00 // beispiele/buttondemo/myqpushbuttondialog.cpp 01 #include "myqpushbuttondialog.h" 02 // neue Widget-Klasse vom eigentlichen Widget ableiten 03 MyQPushDialog::MyQPushDialog() { 04 setFixedSize ( 200, 180 ); 05 06 07 08 09 10
vbox = new button01 = button02 = button03 = button04 = button05 =
11 12 13
// Attribute setzen button01->setFlat(true); button01->setIcon( QIcon(QString("%1 %2") .arg(QCoreApplication::applicationDirPath()) .arg("/images/icon.png") ) ); button04->setCheckable(true); button05->setDown(true);
14 15 16 17 18 19 20 21 22 23
142
QVBoxLayout; new QPushButton("Flat"); new QPushButton("Default"); new QPushButton("Normal"); new QPushButton("Checkable"); new QPushButton("Down");
vbox->addWidget(button01); vbox->addWidget(button02); vbox->addWidget(button03); vbox->addWidget(button04); vbox->addWidget(button05); setLayout(vbox); connect( button01, SIGNAL( clicked() ), this, SLOT( accept() ) ); connect( button02, SIGNAL( clicked() ), this, SLOT( reject() ) );
1542.book Seite 143 Montag, 4. Januar 2010 1:02 13
Qt-Widgets
24 25
connect( button03, SIGNAL ( clicked()), this, SLOT( mySetResult() ) ); connect( button04, SIGNAL( toggled(bool) ), this, SLOT( checktoogled(bool) ) );
26 } 27 // Zustand des Buttons hat sich verändert 28 void MyQPushDialog::checktoogled(bool iftoogled) { 29 if( iftoogled ) 30 QMessageBox::information( this, "checktoogled()", "Button \"Checkable\" ist aktiv", QMessageBox::Ok ); 31 else 32 QMessageBox::information( this, "checktoogled()", "Button \"Checkable\" ist nicht aktiv", QMessageBox::Ok ); 33 } 34 // Rückgabewert vom "Checkable" Button abhängig machen 35 void MyQPushDialog::mySetResult() { 36 int result; 37 if (button04->isChecked() ) 38 result = 100; 39 else 40 result = 50; 41 emit done(result); 42 }
Gestartet wird das Demo über der Klasse MyWidget mit dem »QPushButton«. Hierfür haben wir eine Signal-Slot-Verbindung eingerichtet, mit der beim Anwählen des Buttons (clicked()) der Slot qpush_exex() ausgeführt wird: // beispiele/buttondemo/mywidget1.cpp ... 27 void MyWidget::qpush_exec() { 28 pushDialog = new MyQPushDialog; 29 int status = pushDialog->exec(); 30 if( status == QDialog::Accepted ) 31 QMessageBox::information( this, "qpush_exec()", "Acceped zurückgegeben (\"Flat\"-Button)", QMessageBox::Ok ); 32 else if( status == QDialog::Rejected )
143
4.5
1542.book Seite 144 Montag, 4. Januar 2010 1:02 13
4
Dialoge, Layout und Qt-Widgets
33
34 35
36 37
QMessageBox::information( this, "qpush_exec()", "Rejected zurückgegeben (\"Default\"-Button)", QMessageBox::Ok ); else if( status == 100) QMessageBox::information( this, "qpush_exec()", "\"Normal\"-Button betätigt: " "\"Checkable\"-Button war aktiv", QMessageBox::Ok ); else if( status == 50 ) QMessageBox::information( this, "qpush_exec()", "\"Normal\"-Button betätigt:" " \"Checkable\"-Button war nicht aktiv", QMessageBox::Ok );
38 }
In diesem Slot wird mit exec() der Dialog gestartet. Sie erhalten folgende Dialogbox:
Abbildung 4.51
Demo zu QPushButton bei der Ausführung
Der erste Button wurde mit dem Attribut setFlat() »flach« gemacht und erhält ein Icon mit der Methode setIcon(): button01->setFlat(true); button01->setIcon(QIcon(QString("%1 %2") .arg(QCoreApplication::applicationDirPath()) .arg("/images/icon.png")));
Grafiken hinzufügen Wem unsere Art des Hinzufügens mit der Ermittlung des absoluten Pfades zum Arbeitsverzeichnis und dem Anhängen des Verzeichnisses mit der Grafik zu umständlich ist, der kann auch auf das Ressourcen-System von Qt zurückgreifen. Hierauf gehen wir in Abschnitt 12.7 näher ein. Warum hier dieser Weg eingeschlagen wurde, erläuterten wir bereits kurz in Abschnitt 1.4.3.
144
1542.book Seite 145 Montag, 4. Januar 2010 1:02 13
Qt-Widgets
Bei Betätigung des Buttons (Signal: clicked()),wird der Slot accepted() ausgeführt, wobei der Slot MyWidget::qpush_exec() eine entsprechende Auswertung (Zeile 30 und 31) durchführt in einem Nachrichtendialog anzeigt:
Abbildung 4.52
Der Button mit dem Label »Flat« wurde betätigt.
Selbiges passiert mit dem Button »Default«, nur dass hierbei der Slot reject() ausgeführt und beim Slot eben Entsprechendes (Zeile 32 und 33) ausgewertet wird. Wenn Sie den Button »Normal« betätigt haben, wird der Slot mySetResult() ausgeführt, womit der Button auf Eintreten des Signals clicked() verbunden wurde. Bei diesem Slot überprüfen wir zunächst, ob sich der Button »Checkable« in gedrücktem Zustand befindet (button04->isChecked()) oder nicht. Je nach Zustand übergeben wir den Wert an das Signal done() das am Ende des Slots ausgelöst wird, worauf der Slot MyWidget::qpush_exec() entsprechend reagiert (Zeile 34 bis 37). Der Button »Checkable« stellt einen klassischen toggled Button dar, der beim Niederdrücken in diesem Zustand verharrt und durch erneutes Drücken wieder gelöst werden kann. Dieser Button wurde mit der Methode setCheckable(true) eingerichtet. Natürlich hat man auch hierzu eine entsprechende Signal-Slot-Verbindung eingerichtet. Sobald der Button betätigt wurde, wird das Signal toogled() ausgelöst. Um den Booleschen Parameter des Signals auszuwerten, haben wir einen eigenen Slot checktoogled() erstellt. Der Letzte im Bunde ist ein Button, der mit setDown() zunächst in der gedrückten Version ins Bild kommt. Wird dieser Zustand (durch Anklicken) gelöst, verwandelt sich der Button in einen ganz normalen Button (keine toggled Version, wie häufig angenommen wird). QCheckBox Die Klasse QCheckBox stellt einen Button dar, der sich mit einem Häkchen oder einem Kreuz aktivieren oder deaktivieren lässt. Solche Buttons setzt man gerne in Situationen ein, um bestimmte Optionen ein- oder auszuschalten. Wird der Zustand eines Check-Buttons verändert, löst man das Signal stateChanged(int) aus. Ansonsten enthält QCheckBox keine eigenen Signale und
145
4.5
1542.book Seite 146 Montag, 4. Januar 2010 1:02 13
4
Dialoge, Layout und Qt-Widgets
Slots. Allerdings kann auch hier bei Bedarf wieder auf die Signale und Slots von QAbstractButton zurückgegriffen werden. Die gängigsten Methoden von QCheckBox finden Sie in der folgenden Tabelle aufgelistet. Methode
Beschreibung
Qt::CheckState checkState () const;
Gibt den Zustand des Check-Buttons zurück (siehe Tabelle 4.27).
bool isTristate () const;
Überprüft, ob der Check-Button drei Zustände erlaubt (siehe setTristate()).
void setCheckState ( Qt::CheckState state );
Setzt den Check-Button auf den Zustand state (siehe Tabelle 4.27).
void setTristate ( bool y = true );
Damit ist ein dritter Zustand des Check-Buttons möglich, in dem Sie diesen in einen ausgegrauten Zustand setzen. Diesen ausgegrauten Zustand können Sie entweder aktivieren oder eben nicht. Dies soll anzeigen, dass dieser Check-Button nicht mehr zu ändern ist.
Tabelle 4.26
Methoden von QCheckBox
Mögliche Werte für den enum-Wert Qt::CheckState: Konstante
Beschreibung
Qt::Unchecked
Das Element ist deaktiviert (nicht angekreuzt bzw. abgehakt).
Qt::PartiallyChecked
Das Element ist teilweise aktiviert.
Qt::Checked
Das Element ist aktiviert (angekreuzt bzw. abgehakt).
Tabelle 4.27
Zustände eines Check-Buttons
Hierzu jetzt die Klasse MyQCheckBoxDialog (alias QCheckBox) in der Praxis. Zunächst wieder das Grundgerüst: 00 01 02 03 04 05 06 07 08 09
146
// beispiele/buttondemo/mycheckbuttondialog.h #ifndef MYQCHECKBOXDIALOG_H #define MYQCHECKBOXDIALOG_H #include #include #include #include #include #include #include
1542.book Seite 147 Montag, 4. Januar 2010 1:02 13
Qt-Widgets
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
// Grundgerüst der Klasse MyCheckBoxDialog() class MyQCheckBoxDialog : public QDialog { Q_OBJECT public: MyQCheckBoxDialog(); QVBoxLayout *vbox; QCheckBox *check01; QCheckBox *check02; QCheckBox *check03; QCheckBox *check04; QPushButton *button01; public slots: void isChecked(int state); void cantChecked(); void myResult(); }; #endif
Die Definition der Klasse sieht folgendermaßen aus: 00 // beispiele/buttondemo/mycheckbuttondialog.cpp 01 #include "mycheckbuttondialog.h" 02 // Neue Widget-Klasse vom eigentlichen Widget ableiten 03 MyQCheckBoxDialog::MyQCheckBoxDialog() { 04 setFixedSize ( 200, 180 ); 05 vbox = new QVBoxLayout; 06 check01 = new QCheckBox("Label 1"); 07 check02 = new QCheckBox("Label 2"); 08 check03 = new QCheckBox("Label 3"); 09 check04 = new QCheckBox("Label 4"); 10 button01 = new QPushButton("Auswerten"); 11 12 13
check02->setCheckState(Qt::Checked); check04->setTristate(); check04->setCheckState(Qt::PartiallyChecked);
14 15 16 17 18 19
vbox->addWidget(check01); vbox->addWidget(check02); vbox->addWidget(check03); vbox->addWidget(check04); vbox->addWidget(button01); setLayout(vbox);
20
connect( check01, SIGNAL( stateChanged ( int ) ), this, SLOT( isChecked( int ) ) );
147
4.5
1542.book Seite 148 Montag, 4. Januar 2010 1:02 13
4
Dialoge, Layout und Qt-Widgets
21 22 23 24
connect( check02, SIGNAL( stateChanged ( int ) ), this, SLOT( isChecked( int ) ) ); connect( check03, SIGNAL( stateChanged ( int ) ), this, SLOT( isChecked( int ) ) ); connect( check04, SIGNAL( clicked() ), this, SLOT( cantChecked( ) ) ); connect( button01, SIGNAL( clicked() ), this, SLOT( myResult( ) ) );
25 } 26 // Zustand eines Buttons hat sich verändert 27 void MyQCheckBoxDialog::isChecked( int state ) { 28 if( state == Qt::Checked ) 29 QMessageBox::information( this, "isChecked", "Eine Checkbox wurde aktiviert", QMessageBox::Ok ); 30 else if( state == Qt::Unchecked ) 31 QMessageBox::information( this, "isChecked", "Eine Checkbox wurde deaktiviert", QMessageBox::Ok ) ; 32 } 33 // Zustand eines Buttons hat sich verändert 34 void MyQCheckBoxDialog::cantChecked( ) { 35 QMessageBox::information( 36 this, "cantChecked", "Sorry, Sie können diese Checkbox nicht ändern", QMessageBox::Ok ) ; 37 check04->setCheckState(Qt::PartiallyChecked); 38 } 39 // alle Checkboxen auswerten 40 void MyQCheckBoxDialog::myResult() { 41 int result=0; 42 if (check01->checkState() == Qt::Checked ) 43 result+=1; 44 if (check02->checkState() == Qt::Checked ) 45 result+=10; 46 if( check03->checkState() == Qt::Checked ) 47 result+=100; 48 emit done(result); 49 }
148
1542.book Seite 149 Montag, 4. Januar 2010 1:02 13
Qt-Widgets
Diese Klasse wird über die Klasse MyWidget mit dem Button »QCheckBox« gestartet. Wurde der Button betätigt, wird der dafür eingerichtete Slot qcheck_exec() ausgeführt: // beispiele/buttondemo/mywidget1.cpp ... 39 void MyWidget::qcheck_exec() { 40 checkDialog = new MyQCheckBoxDialog; 41 int status = checkDialog->exec(); 42 QString score = "Aktiv waren: [Label 4] "; 43 if ( status != 0 ) { 44 if( status / 100 ) 45 score.append(" [Label 3] "); 46 status%=100; 47 if( status / 10 ) 48 score.append(" [Label 2] "); 49 status%=10; 50 if( status / 1 ) 51 score.append(" [Label 1] "); 52 } 53 else { 54 score.append(" Kein Label war aktiv " ); 55 } 56 QMessageBox::information( this, "qcheck_exec()", score, QMessageBox::Ok ); 57 }
Auch hierbei wird mit exec() der Dialog mit folgendem Ergebnis gestartet:
Abbildung 4.53
QCheckBox in der Praxis
Bei den Buttons mit den Textlabels »Label 1« und »Label 3« wurde überhaupt nichts unternommen. Der Button »Label 2« wurde mit der Methode setCheckState() folgendermaßen abgehackt: check02->setCheckState(Qt::Checked);
149
4.5
1542.book Seite 150 Montag, 4. Januar 2010 1:02 13
4
Dialoge, Layout und Qt-Widgets
Der letzte Button »Label 4« kann mit setTristate() den dritten Zustand (ausgegraut) annehmen. Diesen Zustand wollen wir auch gleich mit setCheckState() und dem Zustand Qt::PartiallyChecked als vorausgewählt und im Beispiel als nicht mehr veränderbar verwenden: check04->setTristate(); check04->setCheckState(Qt::PartiallyChecked);
Wenn sich bei den ersten drei Check-Buttons etwas verändert, wird das Signal stateChanged() ausgelöst, das wir in diesem Beispiel mit unserem eigenen Slot isChecked() verbunden haben (Zeile 26 bis 33). Dieser Slot macht nichts anderes als zu überprüfen, ob der Zustand abgehackt (Qt::Checked) oder nicht abgehackt (Qt::Unchecked) ist. Je nach verändertem Zustand des Check-Buttons wird ein entsprechender Nachrichtendialog angezeigt. Versucht man hingegen, den Check-Button »Label 4« zu verändern, wird zwar auch das Signal stateChanged() abgefangen, aber als Slot wurde hier (wieder ein eigener) cantChecked() ausgeführt (Zeile 33 bis 38). Dieser macht wiederum nichts anderes, als den Anwender zu informieren, dass sich der Zustand des Check-Buttons nicht verändern lässt. Da ja der Zustand im Grunde doch verändert werden kann, setzen wir am Ende des Slots diesen wieder mit setCheckState() auf Qt::PartiallyChecked. Wird hingegen der Button »Auswerten« betätigt (Signal: clicked()), wird der Slot myResult() ausgeführt (Zeile 39 bis 49). Der Slot myResult() sendet dann den aktuellen Zustand aller Buttons zurück an int status=checkDialog->exec() im Slot MyWidget::qcheck_exec(). Die Rechnerei in myResult() ist wohl eher ein Eigengebäck von mir und kann selbstverständlich auch ganz anders gelöst werden. Die bitweisen Operatoren würden sich wohl besser eignen. Ist result bspw. 0, ist außer dem »Label 4«, das ja nicht verändert werden kann, kein weiterer Check-Button angehackt gewesen. Ist result 1, dann ist »Label 1« abgehackt. Ist result bspw. 101, dann sind »Label 1« und »Label 3« angekreuzt. Bei 111 sind alle Check-Buttons aktiviert und mit 11 nur »Label 1« und »Label 2«. Das Ergebnis »schneiden« wir im Slot MyWidget::qcheck_exec() mit dem Modulo- und Divisions-Operator in Scheibchen und erhalten einen entsprechenden Nachrichtendialog. Die Auswertung sieht bezogen auf Abbildung 4.53 folgendermaßen aus:
Abbildung 4.54
150
Check-Buttons auswerten
1542.book Seite 151 Montag, 4. Januar 2010 1:02 13
Qt-Widgets
QRadioButton Die Klasse QRadioButton ist der Klasse QCheckBox recht ähnlich, nur wird normalerweise nur eine Auswahl aus einer Gruppe von Buttons getroffen und nicht mehrere. Die Radio-Buttons werden in der Praxis auch etwas anders als die Buttons der Klasse QCheckBox angezeigt. Anstelle eines Hakens oder eines Kreuzes wird hierbei gewöhnlich ein Punkt verwendet. Dies ist allerdings vom Betriebssystem und vom verwendeten Fenster-Manager (Desktop) abhängig. Radio-Buttons sind per Voreinstellung immer autoExclusive. Dies bedeutet, dass Sie aus mehreren Radio-Buttons immer nur einen auswählen können, auch wenn Sie vielleicht mehrere Gruppen im Auge haben. Wenn Sie mehrere Gruppen von Radio-Buttons benötigen, müssen Sie diese in QButtonGroup oder QGroupBox stecken. QRadioButton verfügt über keine eigenen Signale, Slots oder Methoden. Wählt
man einen Radio-Button an, wird das Signal toggled() (von der Basisklasse QAbstractButton) ausgelöst. Wollen Sie überprüfen, ob ein Button angewählt wurde oder nicht, wird die Methode isChecked() verwendet. Da uns zu QRadioButton im Augenblick nichts mehr einfällt, sollen die RadioButtons auch hier mit einer Klasse MyQRadioBoxDialog demonstriert werden. Zunächst das Grundgerüst: 00 01 02 03 04 05 06 07 08 09
// beispiele/buttondemo/myradiobuttondialog.h #ifndef MYQRADIOBOXDIALOG_H #define MYQRADIOBOXDIALOG_H #include #include #include #include #include #include #include
10 class MyQRadioBoxDialog : public QDialog { 11 Q_OBJECT 12 public: 13 MyQRadioBoxDialog(); 14 QVBoxLayout *vbox; 15 QGroupBox* groupBox; 16 QRadioButton* radio01; 17 QRadioButton* radio02; 18 QRadioButton* radio03; 19 QPushButton *button01;
151
4.5
1542.book Seite 152 Montag, 4. Januar 2010 1:02 13
4
Dialoge, Layout und Qt-Widgets
20 public slots: 21 void changeRadio(bool status); 22 void myResult(); 23 }; 24 #endif
Nun die Definitionen der Klasse: 00 // beispiele/buttondemo/myradiobuttondialog.cpp 01 #include "myradiobuttondialog.h" 02 // neue Widget-Klasse vom eigentlichen Widget ableiten 03 MyQRadioBoxDialog::MyQRadioBoxDialog() { 04 setFixedSize ( 200, 180 ); 05 groupBox = new QGroupBox("Radio Button Demo"); 06 07 08 09 10 11
radio01 = new QRadioButton("Radio 1"); radio02 = new QRadioButton("Radio 2"); radio03 = new QRadioButton("Radio 3"); button01 = new QPushButton("Auswerten"); // radio02 vorbelegen radio02->setChecked(true);
12 13 14 15 16 17 18
vbox = new QVBoxLayout; vbox->addWidget(radio01); vbox->addWidget(radio02); vbox->addWidget(radio03); vbox->addWidget(button01); vbox->addStretch(1); groupBox->setLayout(vbox);
19 20 21
QVBoxLayout* myBox = new QVBoxLayout; myBox->addWidget(groupBox); setLayout(myBox);
22
connect( radio01, SIGNAL( toggled(bool) ), this, SLOT( changeRadio(bool) ) ); connect( radio02, SIGNAL( toggled(bool) ), this, SLOT( changeRadio(bool) ) ); connect( radio03, SIGNAL( toggled(bool) ), this, SLOT( changeRadio(bool) ) ); connect( button01, SIGNAL( clicked() ), this, SLOT( myResult( ) ) );
23 24 25 26 }
152
1542.book Seite 153 Montag, 4. Januar 2010 1:02 13
Qt-Widgets
27 // Zustand eines Buttons hat sich verändert 28 void MyQRadioBoxDialog::changeRadio(bool status) { 29 if( status ) 30 QMessageBox::information( this, "changeRadio", "Radiobutton wurde geändert", QMessageBox::Ok ) ; 31 } 32 // Radio-Buttons auswerten 33 void MyQRadioBoxDialog::myResult() { 34 int result=0; 35 if( radio01->isChecked()) result = 1; 36 if( radio02->isChecked()) result = 2; 37 if( radio03->isChecked()) result = 3; 38 emit done(result); 39 }
Gestartet wird diese Klasse (wie bei den anderen beiden Button-Klassen) über die Klasse MyWidget mit dem eigens dafür eingerichteten Slot qradio_exec(): // beispiel/buttondemo/mywidget1.cpp ... 58 void MyWidget::qradio_exec() { 59 radioDialog = new MyQRadioBoxDialog; 60 int status = radioDialog->exec(); 61 QString score = "Aktiver Radio-Button: "; 62 if( status == 1 ) score.append("1"); 63 if( status == 2 ) score.append("2"); 64 if( status == 3 ) score.append("3"); 65 QMessageBox::information( this, "qradio_exec()", score, QMessageBox::Ok ); 66 }
Nachdem der Dialog mit exec() gestartet wurde, erhalten Sie folgendes Bild:
Abbildung 4.55
QRadioButton in der Praxis
153
4.5
1542.book Seite 154 Montag, 4. Januar 2010 1:02 13
4
Dialoge, Layout und Qt-Widgets
In diesem Beispiel haben wir für jeden Radio-Button das Signal toggled() mit dem eigenen Slot changeRadio() verbunden. Dieser Slot überprüft nur, ob das entsprechende Radio-Element aktiviert wurde, und gibt eine entsprechende Meldung zurück (Zeile 27 bis 31). Klicken (Signal: clicked()) Sie auf den Button »Auswerten«, wird der eigene Slot myResult() ausgeführt. Dieser überprüft die einzelnen Radio-Buttons, ob sie aktiviert sind. Je nachdem, welcher Radio-Button aktiviert ist, wird wiederum mit dem Signal done() an den exec()-Aufruf im Slot MyWidget::qradio_exec() zurückgegeben, entsprechend ausgewertet und mit einem Nachrichtendialog ausgegeben:
Abbildung 4.56
Auswerten der Radio-Buttons
QToolButton Die Klasse QToolButton ist eine weitere Klasse für Buttons, die üblicherweise innerhalb einer Werkzeugleiste (QToolBar) verwendet wird.
Abbildung 4.57
QToolButton in einer Toolbar
Im Gegensatz zu normalen Buttons enthalten solche Tool-Buttons gewöhnlich keinen Text-Label sondern ein Icon. Da die Klasse allein hier zunächst keinen Sinn ergibt, wollen wir darauf erst in Abschnitt 5.2.4 näher eingehen, wo wir auch eine Werkzeugleiste mit Tool-Buttons verwenden. QButtonGroup Die Klasse QButtonGroup ist zwar keine direkt abgeleitete Klasse von QAbstractButton, sollte aber dennoch kurz hier erwähnt werden. QButtonGroup
ist von QObject abgeleitet und wird für die Verwaltung mehrerer Gruppen von Button-Widgets verwendet. QButtonGroup verwaltet alle Arten von Buttons die von QAbstractButtons abgeleitet sind. Es gilt zu beachten, dass es sich bei QButtonGroup um keine Container-Klasse handelt, die die visuellen Aspekte der Buttons beinhaltet (wie bspw. QGroupBox).
154
1542.book Seite 155 Montag, 4. Januar 2010 1:02 13
Qt-Widgets
QButtonGroup dient vielmehr der Verwaltung des Zustands jedes Buttons in der
Gruppe. Mehr dazu entnehmen Sie bitte dem Qt-Assistant.
4.5.2
Container-Widgets
Unter Container-Widget verstehen wir Klassen, die andere Widgets als »Behälter« verwalten. Im Grunde könnte man auch QDialog und QWidget als ContainerWidgets bezeichnen. Allerdings sind die hier beschriebenen Widgets nicht ohne die Klassen QDialog oder QWidget sichtbar. Somit sind Container-Widgets weitere Sammelbehälter innerhalb von QDialog oder QWidget. Abgesehen von der Klasse QToolBox, sind alle Container-Widgets von der Basisklasse QWidget abgeleitet (siehe Abbildung 4.58).
QWidget
QGroupBox
QFrame
QTabWidget
QToolBox
Abbildung 4.58
Hierarchie der Container-Widgets
QGroupBox Die schon des Öfteren verwendete Klasse QGroupBox ist ein Widget, das über einen Rahmen mit Titel um eine Gruppe von Widgets verfügt. Zusätzlich kann auch hier mit dem Ampersandzeichen ein Tastaturschnellzugriff eingerichtet werden, so dass der Fokus auf eines der Kinder-Widgets in der Box fällt. Hierfür die gängigsten Methoden der Klasse QGroupBox (Tabelle 4.28). Methode
Beschreibung
Qt::Alignment alignment() const;
Gibt die Ausrichtung des Titels der Box zurück (siehe Tabelle 4.29).
bool isCheckable() const;
Gibt true zurück, wenn die Box einen Checkbutton im Titel hat (siehe setCheckable()). Per Standard hat eine solche Box keinen Checkbutton. Ansonsten wird false zurückgegeben.
Tabelle 4.28
Gängige Methoden von QGroupBox
155
4.5
1542.book Seite 156 Montag, 4. Januar 2010 1:02 13
4
Dialoge, Layout und Qt-Widgets
Methode
Beschreibung
bool isChecked () const;
Gibt true zurück, wenn die Box einen Checkbutton hat und dieser abgehakt ist. Ansonsten wird false zurückgegeben.
bool isFlat () const;
Gibt true zurück, wenn die Box keinen Rahmen hat (siehe setFlat()). Ansonsten wird false zurückgegeben.
void setAlignment ( int alignment );
Setzt die Ausrichtung des Titels der Box auf alignment (siehe Tabelle 4.29).
void setCheckable ( bool checkable );
Wird checkable für true verwendet, hat die Box im Titel einen Checkbutton. Mit ihm können die Widgets in der Box deaktiviert (ausgegraut) bzw. aktiviert werden.
void setFlat ( bool flat );
Wird für flat der Wert true verwendet, hat die Box keinen Rahmen.
void setTitle ( const QString & title );
Setzt den Titel des Rahmens. Mit dem Ampersandzeichen können Sie außerdem ein Tastaturkürzel einrichten.
QString title () const;
Gibt den Titel der Box zurück.
Tabelle 4.28
Gängige Methoden von QGroupBox (Forts.)
Hierzu noch die möglichen Ausrichtungen (alignment) für den Titel der Box, wobei standardmäßig Qt::AlignLeft eingestellt ist. Konstante
Beschreibung
Qt::AlignLeft
Der Text des Titels ist auf der linken Seite der Box ausgerichtet.
Qt::AlignRight
Der Text des Titels ist auf der rechten Seite der Box ausgerichtet.
Qt::AlignHCenter
Der Text des Titels ist zentriert auf der Box ausgerichtet.
Tabelle 4.29
Ausrichten des Titels von QGroupBox
Sofern die Box einen Checkbutton neben Titel verwendet, gibt es zwei Signale, die ausgelöst werden, wenn der Checkbutton betätigt wurde: Signal
Beschreibung
clicked(bool checked=false)
Das Signal wird ausgelöst, wenn der Checkbutton neben dem Titel betätigt wurde.
toggled(bool on)
Das Signal wird ausgelöst, wenn der Checkbutton angehakt bzw. angekreuzt wurde.
Tabelle 4.30
156
Signal von QGroupBox, wenn der Checkbutton aktiviert ist
1542.book Seite 157 Montag, 4. Januar 2010 1:02 13
Qt-Widgets
Die Methode setCheckable() (siehe Tabelle 4.28) können Sie zudem auch als Slot verwenden. Zwar wurde die Klasse QGroupBox schon des Öfteren verwendet, doch soll das Feature, eine solche Box zu aktivieren und zu deaktivieren, hier doch gezeigt werden. Zunächst wieder das Grundgerüst: 00 01 02 03 04 05 06 07
// beispiele/groupbox/mywidget.h #ifndef MYWIDGET_H #define MYWIDGET_H #include #include #include #include #include
08 class MyWidget : public QWidget { 09 Q_OBJECT 10 public: 11 MyWidget( QWidget *parent = 0); 12 private: 13 QRadioButton *rbutton1, *rbutton2, *rbutton3; 14 QRadioButton *rbutton4, *rbutton5, *rbutton6; 15 QVBoxLayout* vBox1, *vBox2; 16 QGroupBox* groupBox1, *groupBox2; 17 QButtonGroup *group1, *group2; 18 }; 19 #endif
Nun die Implementierung dieser Klasse: 00 01 02 03
// beispiele/groupbox/mywidget.cpp #include "mywidget.h" #include #include
04 // neue Widget-Klasse vom eigentlichen Widget ableiten 05 MyWidget::MyWidget( QWidget *parent): QWidget(parent) { 06 // Radio-Elemente des Widgets erzeugen 07 rbutton1 = new QRadioButton ("Radio 1"); 08 rbutton2 = new QRadioButton ("Radio 2"); 09 rbutton3 = new QRadioButton ("Radio 3"); 10 rbutton4 = new QRadioButton ("Radio 1a"); 11 rbutton5 = new QRadioButton ("Radio 2a"); 12 rbutton6 = new QRadioButton ("Radio 3a");
157
4.5
1542.book Seite 158 Montag, 4. Januar 2010 1:02 13
4
Dialoge, Layout und Qt-Widgets
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 }
// Attribute setzen rbutton1->setChecked(true); rbutton4->setChecked(true); // zwei neue Group-Boxen erzeugen groupBox1 = new QGroupBox("Radio-Auswahl &1"); groupBox2 = new QGroupBox("Radio-Auswahl &2"); // Attribute setzen groupBox2->setCheckable(true); groupBox2->setChecked(false); // Layout erzeugen vBox1 = new QVBoxLayout; vBox2 = new QVBoxLayout; // Elemente ins Layout einfügen vBox1->addWidget(rbutton1); vBox1->addWidget(rbutton2); vBox1->addWidget(rbutton3); vBox2->addWidget(rbutton4); vBox2->addWidget(rbutton5); vBox2->addWidget(rbutton6); // Attribute des Layouts setzen vBox1->addStretch(1); vBox2->addStretch(1); // Layout zu den Group-Boxen hinzufügen groupBox1->setLayout(vBox1); groupBox2->setLayout(vBox2); // noch ein Layout für die Group-Boxen QVBoxLayout* layout = new QVBoxLayout; layout->addWidget(groupBox1); layout->addWidget(groupBox2); setLayout(layout); setWindowTitle("QGroupBox – Demo");
In der Zeile 20 setzen Sie die zweite Group-Box auf ankreuzbar, und eine Zeile später deaktivieren Sie den Checkbutton. Ein Hauptprogramm fehlt noch dazu: 00 // beispiele/groupbox/main.cpp 01 #include 02 #include "mywidget.h" 03 int main(int argc, char *argv[]) { 04 QApplication app(argc, argv); 05 MyWidget* window = new MyWidget; 06 window->show(); 07 return app.exec(); 08 }
158
1542.book Seite 159 Montag, 4. Januar 2010 1:02 13
Qt-Widgets
Das Programm bei der Ausführung:
Abbildung 4.59
QGroupBox mit Checkbutton im Titel
QTabWidget Die Klasse QTabWidget stellt ein Register-Widget zur Verfügung. Das Prinzip ist einem Ringordner recht ähnlich, in dem sich einzelne Trennblätter befinden, mit denen man versucht, dem Blätterchaos Herr zu werden. Ähnlich funktioniert das Tab-Widget, das einem ggf. hilft, ein Widget-Chaos zu vermeiden. Ein Tab-Widget wird in die Tab-Leiste und einen Seitenbereich aufgeteilt. Die Tab-Leiste verwendet man, um dem Inhalt des Seitenbereichs einen Zusammenhang zu geben. So können Sie bspw. eine Tab-Leiste mit dem Text »Ausgaben« und eine Tab-Leiste mit »Einnahmen« verwenden. In den Seitenbereichen dieser Tab-Leisten können Sie jetzt die jeweiligen Funktionalitäten mit passenden Widgets einbauen. Standardmäßig wird die Tab-Leiste über dem Seitenbereich angezeigt, was aber geändert werden kann. Natürlich kann immer nur ein Seitenbereich, dessen TabLeiste angewählt wurde, auf einmal gezeigt werden. Hierbei können Sie wiederum ein Tastaturkürzel mit dem Ampersandzeichen einrichten, so dass Sie mit dem (Alt)+Zeichen auf die Tab-Leiste und somit den Seitenbereich zugreifen. QTabWidget vs. QTabBar und QStackedWidget QTabWidget ist ein sehr gutes Ready-to-use-Widget, das im Allgemeinen für Zufriedenheit sorgt. Wer noch flexibler sein will und Tab-Leiste sowie Seitenbereich separat verwalten möchte, kann die Widgets QTabBar (für die Tab-Leiste) und QStackedWidget (für den Seitenbereich) verwenden.
Bevor Sie das Widget in der Praxis kennenlernen, wollen wir zunächst wieder die gängigsten relevanten Methoden näher beschreiben (Tabelle 4.31).
159
4.5
1542.book Seite 160 Montag, 4. Januar 2010 1:02 13
4
Dialoge, Layout und Qt-Widgets
Methode
Beschreibung
QTabWidget ( QWidget * parent = 0 );
Erzeugt eine neue Tab-Leiste mit dem parent als Eltern-Widget.
~QTabWidget ();
Zerstört eine Tab-Leiste.
int addTab ( QWidget* child, const QString &text);
Fügt ein neues Tab zur Leiste mit dem Label text und dem Seitenbereich child am Ende hinzu. Zurückgegeben wird der Index (angefangen bei 0) der neuen Tab-Leiste.
int addTab ( QWidget* child, const QIcon & icon, const QString & text );
Fügt ein neues Tab zur Leiste mit dem Label text, dem Icon icon und dem Seitenbereich child am Ende hinzu. Zurückgegeben wird der Index (angefangen bei 0) der neuen Tab-Leiste.
int count () const;
Gibt die Anzahl der Tabulatoren der Tab-Leiste zurück.
int currentIndex () const;
Gibt den Index der aktuell sichtbaren Tab in der Leiste (mit Seitenbereich) zurück.
QWidget * currentWidget () const; Gibt einen Zeiger auf den aktuellen Seitenbereich
zurück, der im Augenblick sichtbar ist. Qt::TextElideMode elideMode () const ;
Damit wird ermittelt, wie der ausgelassene Text in den Tabs angezeigt wird (siehe setElideMode()).
int indexOf (QWidget * w) const;
Gibt den Index (angefangen bei 0) der Seite mit dem Widget w zurück oder –1, wenn das Widget nicht gefunden werden konnte.
QSize iconSize () const;
Gibt die Größe für das Icon in der Tab-Leiste zurück.
int insertTab ( int index, QWidget * widget, const QString & text );
Fügt ein neues Tab in die Leiste mit dem Label text mit dem Seitenbereich widget an der Position index hinzu. Zurückgegeben wird der Index (angefangen bei 0) der neuen Tab-Leiste. Sollte sich die angegebene Position außerhalb des erlaubten Bereichs befinden, wird die neue TabLeiste ans Ende (wie addTab()) hinzugefügt.
int insertTab ( int index, QWidget * widget, const QIcon & icon, const QString & text );
Fügt ein neues Tab in die Leiste mit dem Label text, dem Icon icon und dem Seitenbereich widget an der Position index hinzu. Zurückgegeben wird der Index (angefangen bei 0) der neuen Tab-Leiste. Sollte sich die angegebene Position außerhalb des erlaubten Bereichs befinden, wird die neue Tab-Leiste ans Ende (wie addTab()) hinzugefügt.
Tabelle 4.31
160
Gängige Methoden der Klasse QTabWidget
1542.book Seite 161 Montag, 4. Januar 2010 1:02 13
Qt-Widgets
Methode
Beschreibung
bool isTabEnabled ( int index ) const;
Gibt true zurück, wenn der Tab mit dem Index index aktiviert ist (siehe setTabEnable()). Ansonsten wird false zurückgegeben.
void removeTab ( int index )
Entfernt das Tab mit dem Index index aus der Leiste (mitsamt Seitenbereich).
void setElideMode ( Qt::TextElideMode ) ;
Damit können Sie angeben, wie der ausgelassene Text im Tab der Leiste angezeigt wird. Dies kann bspw. verwendet werden, wenn nicht genügend Platz vorhanden ist, alle Tabs in der Leiste anzuzeigen. Der Text wird hierbei mit Ellipsen (...) abgekürzt. Mögliche Werte siehe Tabelle 4.32.
void setIconSize ( const QSize & size );
Damit lässt sich die Größe eines Icons der TabLeiste auf size setzen.
void setTabShape ( Shape shape );
Damit können Sie die Form der Tabs in der Tab-Leiste setzen. Standardmäßig ist dies QTabWidget::Rounded. Alternativ kann hierbei auch QTabWidget::Triangular verwendet werden. Wesentlich mehr Optionen bietet Ihnen QTabBar an.
void setTabEnabled ( int index, bool enabled );
Wenn enabled auf true gesetzt wird, können der Tab und der Seitenbereich mit dem Index index verwendet werden. Mit false deaktivieren Sie den Tab und den Seitenbereich (ausgrauen), so dass darauf nicht mehr zugegriffen werden kann.
void setTabIcon ( int index, const QIcon & icon );
Setzt an der Position index das Icon icon in Tab.
void setTabText ( int index, const QString & text );
Setzt für den Tab mit der Position index den Text text.
void setTabPosition ( TabPosition );
Damit können Sie die Position der Tab-Leiste setzen. Standardmäßig wird die Tab-Leiste oben (QTabWidget::North) angezeigt. Es ist aber auch rechts (QTabWidget::East), links (QTabWidget:: West) und unten (QTabWidget::South) möglich, die Tab-Leiste anzuzeigen.
void setTabToolTip ( int index, const QString & tip );
Damit lässt sich für den Tab mit dem Index index der Tooltip tip setzen. Dieser wird angezeigt, wenn sich bspw. der Mauszeiger über dem entsprechenden Tab in der Leiste befindet.
Tabelle 4.31
Gängige Methoden der Klasse QTabWidget (Forts.)
161
4.5
1542.book Seite 162 Montag, 4. Januar 2010 1:02 13
4
Dialoge, Layout und Qt-Widgets
Methode
Beschreibung
void setUsesScrollButtons ( bool useButtons );
Wenn die Tab-Leiste viele Einträge hat, können Sie hier einen Scroll-Button aktivieren (true) bzw. deaktivieren. Damit können Sie sich praktisch durch die einzelnen Tabs scrollen. Meistens ist dieser Wert mit true vorbelegt, doch handelt es sich hier um keinen Standard.
Shape tabShape () const;
Gibt die Form der Tabs in der Leiste zurück (siehe setTabShape()).
QIcon tabIcon ( int index ) const;
Gibt das gesetzte Icon des Tabs mit dem Index index zurück.
QString tabText ( int index ) const;
Gibt den gesetzten Text des Tabs mit dem Index index zurück.
TabPosition tabPosition () const; Ermittelt die Position der Tab-Leiste (siehe setTabPosition()). QString tabToolTip ( int index ) const;
Gibt den gesetzten Tooltip für das Tab mit dem Index index zurück.
bool usesScrollButtons () const;
Prüft, ob die Tab-Leiste ggf. bei zu vielen Einträgen scrollbar ist. Bei true trifft dies zu, bei false nicht.
Tabelle 4.31
Gängige Methoden der Klasse QTabWidget (Forts.)
Nun noch die möglichen enum-Werte, die Sie mit setElideMode() setzen bzw. mit elideMode() abfragen können. Damit geben Sie an, wo die Ellipse (...) angezeigt werden soll, wenn der Text mal zu lang ist. Konstante
Beschreibung
Qt::ElideLeft
Die Ellipsen werden am Anfang des Texts angezeigt (bspw. beim Text »langer Text« ist dies »... xt«).
Qt::ElideRight
Die Ellipsen werden am Ende des Texts angezeigt (bspw. beim Text »langer Text« ist diese »la ...«).
Qt::ElideMiddle
Die Ellipsen werden in der Mitte des Texts angezeigt (bspw. beim Text »langer Text« ist dies »la ... xt«).
Qt::ElideNone
Die Ellipsen sollten nicht im Text erscheinen.
Tabelle 4.32
Überlange Texte mit Ellipsen (...) abkürzen
Mit currentChanged( int index ) haben Sie das einzige Signal von QTabWidget. Dieses Signal wird ausgelöst, wenn sich der aktuelle Seitenindex verändert hat. Der Parameter enthält den neuen sichtbaren Seitenindex.
162
1542.book Seite 163 Montag, 4. Januar 2010 1:02 13
Qt-Widgets
Zwei Slots werden angeboten. Zum einen der Slot setCurrentIndex(int index) um mit dem Parameter index zum neuen Seitenindex zu wechseln. Selbiges leistet auch der Slot setCurrentWidget (QWidget *widget), nur dass hier zum Seitenbereich widget gewechselt wird. Hierzu nun ein Beispiel, welches die Klasse QTabWidget spielerisch in der Praxis demonstrieren soll. Zunächst das Grundgerüst: 00 01 02 03 04 05 06 07 08 09 10 11 12
// beispiele/qtabwidget/mywidget.h #ifndef MYWIDGET_H #define MYWIDGET_H #include #include #include #include #include #include #include #include #include #include
13 class MyWidget : public QWidget { 14 Q_OBJECT 15 public: 16 MyWidget( QWidget *parent = 0); 17 private: 18 QRadioButton *rbutton1, *rbutton2; 19 QCheckBox *cbutton1, *cbutton2, *cbutton3; 20 QPushButton *pbutton1,*pbutton2,*pbutton3,*pbutton4; 21 QVBoxLayout* vBox1, *vBox2, *vBox3; 22 QGroupBox* groupBox1, *groupBox2, *groupBox3; 23 QTabWidget* tab; 24 QButtonGroup *group1, *group2, *group3; 25 public slots: 26 void changeTabStyle(bool status); 27 void setTabAttribute(int attribut); 28 void setPosition(bool b); 29 void getCurrentPosition( int pos ); 30 }; 31 #endif
Und nun zur eigentlichen Implementierung des Codes, der reichlich kommentiert wird. Zugegeben, der Quellcode wirkt auf den ersten Blick recht umfang-
163
4.5
1542.book Seite 164 Montag, 4. Januar 2010 1:02 13
4
Dialoge, Layout und Qt-Widgets
reich, doch in keinem Verhältnis zu kleineren Projekten, die locker die 10K-Zeilengrenze überschreiten. 00 01 02 03
// beispiele/qtabwidget/mywidget.cpp #include "mywidget.h" #include #include
04 // neue Widget-Klasse vom eigentlichen Widget ableiten 05 MyWidget::MyWidget( QWidget *parent): QWidget(parent) { 06 // zwei Radio-Buttons erzeugen 07 rbutton1=new QRadioButton("Style: Rounded (default)"); 08 rbutton2 = new QRadioButton ("Style: Triangular"); 09 // Button vorbelegen 10 rbutton1->setChecked(true); 11 // drei Check-Button erzeugen 12 cbutton1 = new QCheckBox ("Icons setzen"); 13 cbutton2 = new QCheckBox ("Erstes Tab deaktivieren"); 14 cbutton3 = new QCheckBox ("Tooltips verwenden"); 15 // vier normale Buttons erzeugen ... 16 pbutton1 = new QPushButton("Norden"); 17 pbutton2 = new QPushButton("Süden"); 18 pbutton3 = new QPushButton("Westen"); 19 pbutton4 = new QPushButton("Osten"); 20 // ... und daraus toggled Buttons machen 21 pbutton1->setCheckable(true); 22 pbutton2->setCheckable(true); 23 pbutton3->setCheckable(true); 24 pbutton4->setCheckable(true); 25 // ... nur einer der vier Buttons darf 26 // niedergerückt sein 27 pbutton1->setAutoExclusive(true); 28 pbutton2->setAutoExclusive(true); 29 pbutton3->setAutoExclusive(true); 30 pbutton4->setAutoExclusive(true); 31 // drei Group-Boxen mit Label erzeugen 32 groupBox1 = new QGroupBox("Look && Feel"); 33 groupBox2 = new QGroupBox("Attribute"); 34 groupBox3 = new QGroupBox("Position"); 35 // drei vertikale Layout-Boxen erzeugen 36 vBox1 = new QVBoxLayout; 37 vBox2 = new QVBoxLayout; 38 vBox3 = new QVBoxLayout; 39 // Radio-Buttons in Layoutbox vBox1 packen 40 vBox1->addWidget(rbutton1);
164
1542.book Seite 165 Montag, 4. Januar 2010 1:02 13
Qt-Widgets
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
vBox1->addWidget(rbutton2); // Check-Buttons in Layoutbox vBox2 packen vBox2->addWidget(cbutton1); vBox2->addWidget(cbutton2); vBox2->addWidget(cbutton3); // Toggled Buttons in Layoutbox vBox3 packen vBox3->addWidget(pbutton1); vBox3->addWidget(pbutton2); vBox3->addWidget(pbutton3); vBox3->addWidget(pbutton4); // Stretch (1) für alle Layoutboxen hinzufügen vBox1->addStretch(1); vBox2->addStretch(1); vBox3->addStretch(1); // Group-Boxen ohne Rahmen darstellen groupBox1->setFlat(true); groupBox2->setFlat(true); groupBox3->setFlat(true); // Layout der einzelnen Group-Boxen setzen groupBox1->setLayout(vBox1); groupBox2->setLayout(vBox2); groupBox3->setLayout(vBox3); // neue Tab-Leiste erzeugen tab = new QTabWidget; // Groupbox mit Radio-Buttons als // Seitenbereich hinzufügen tab->addTab(groupBox1, "Look && Feel" ); // Groupbox mit Check-Buttons als // Seitenbereich hinzufügen tab->addTab(groupBox2, "Attribute" ); //Groupbox mit toggled-Buttons hinzufügen tab->addTab(groupBox3, "Position" ); // Ausrichtung der Tab-Leiste überprüfen, um // entsprechenden toogled "niederzudrücken" if( tab->tabPosition() == QTabWidget::North ) pbutton1->setChecked(true); else if( tab->tabPosition() == QTabWidget::South ) pbutton2->setChecked(true); else if( tab->tabPosition() == QTabWidget::West ) pbutton3->setChecked(true); else if( tab->tabPosition() == QTabWidget::East ) pbutton4->setChecked(true); // Signal-Slot-Verbindung für Radio-Buttons connect( rbutton1, SIGNAL( toggled(bool) ), this, SLOT( changeTabStyle(bool) ) );
165
4.5
1542.book Seite 166 Montag, 4. Januar 2010 1:02 13
4
Dialoge, Layout und Qt-Widgets
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 }
connect( rbutton2, SIGNAL( toggled(bool) ), this, SLOT( changeTabStyle(bool) ) ); // Signal-Slot-Verbindung für Check-Buttons connect( cbutton1, SIGNAL( stateChanged(int) ), this, SLOT( setTabAttribute(int) ) ); connect( cbutton2, SIGNAL( stateChanged(int) ), this, SLOT( setTabAttribute(int) ) ); connect( cbutton3, SIGNAL( stateChanged(int) ), this, SLOT( setTabAttribute(int) ) ); // Signal-Slot-Verbindung für toogled-Buttons connect( pbutton1, SIGNAL( toggled(bool) ), this, SLOT( setPosition(bool) ) ); connect( pbutton2, SIGNAL( toggled(bool) ), this, SLOT( setPosition(bool) ) ); connect( pbutton3, SIGNAL( toggled(bool) ), this, SLOT( setPosition(bool) ) ); connect( pbutton4, SIGNAL( toggled(bool) ), this, SLOT( setPosition(bool) ) ); // Signal-Slot-Verbindung für die Tab-Leiste connect( tab, SIGNAL( currentChanged( int ) ), this, SLOT( getCurrentPosition( int ) ) ); // Ein Layout-Widget für das Tab-Widget // brauchen wir noch. QVBoxLayout* layout = new QVBoxLayout; layout->addWidget(tab); setLayout(layout); setWindowTitle("QTabWidget- Demo");
103 // Slot: Radio-Buttons auswerten 104 void MyWidget::changeTabStyle(bool b) { 105 if( rbutton1->isChecked()) 106 tab->setTabShape(QTabWidget::Rounded); 107 if( rbutton2->isChecked()) 108 tab->setTabShape(QTabWidget::Triangular); 109 } 110 // Slot: Check-Buttons auswerten 111 void MyWidget::setTabAttribute(int a) { 112 if( cbutton1->isChecked()) { 113 int i; 114 for(i=0; i < tab->count(); ++i) { 115 QString Icon = 116 QString("/images/icon%1.png").arg(i+1); 117 tab->setTabIcon( i, QIcon(QString("%1 %2")
166
1542.book Seite 167 Montag, 4. Januar 2010 1:02 13
Qt-Widgets
.arg(QCoreApplication::applicationDirPath()) .arg(Icon) ) ); 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 }
} } if( cbutton2->isChecked() ) { tab->setTabEnabled(0, false ); } else if(! cbutton2->isChecked()) { tab->setTabEnabled(0, true ); } if( cbutton3->isChecked() ) { tab->setTabToolTip( 0, "Das Look & Feel verändern" ); tab->setTabToolTip( 1, "Gängige Attribute hinzufügen" ); tab->setTabToolTip( 2, "Position vom Tab verändern" ); } else if (!cbutton3->isChecked()){ tab->setTabToolTip ( 0, "" ); tab->setTabToolTip ( 1, "" ); tab->setTabToolTip ( 2, "" ); }
137 // Slot: toogled Buttons auswerten 138 void MyWidget::setPosition(bool b) { 139 if( pbutton1->isChecked() ) 140 tab->setTabPosition(QTabWidget::North); 141 else if( pbutton2->isChecked() ) 142 tab->setTabPosition(QTabWidget::South); 143 else if( pbutton3->isChecked() ) 144 tab->setTabPosition(QTabWidget::West); 145 else if( pbutton4->isChecked() ) 146 tab->setTabPosition(QTabWidget::East); 147 } 148 // Slot: Tab wurde gewechselt 149 void MyWidget::getCurrentPosition(int pos) { 150 QString msg = 151 QString("Zu Tab %1 gewechselt").arg( tab->tabText(pos) ); 152 QMessageBox::information( 152 this,"Tab gewechsel",msg,QMessageBox::Ok); 153 }
167
4.5
1542.book Seite 168 Montag, 4. Januar 2010 1:02 13
4
Dialoge, Layout und Qt-Widgets
Icons hinzufügen Wem das Hinzufügen des Icons hier mit der Ermittlung des absoluten Pfades zum Arbeitsverzeichnis und dem Anhängen des Verzeichnisses mit der Grafik in der Zeile 117 zu umständlich ist, der kann auf das Ressourcen-System von Qt zurückgreifen. Wir gehen darauf in Abschnitt 12.7 näher ein. Warum hier dieser Weg eingeschlagen wurde, erläuterten wir kurz in Abschnitt 1.4.3.
Nun noch ein Hauptprogramm: 00 // beispiele/qtabwidget/main.cpp 01 #include 02 #include "mywidget.h" 03 int main(int argc, char *argv[]) { 04 QApplication app(argc, argv); 05 MyWidget* window = new MyWidget; 06 window->show(); 07 return app.exec(); 08 }
Das Programm bei seiner Ausführung:
Abbildung 4.60
Das Programm gleich nach dem Start
Betätigen Sie jetzt bspw. in der Tab-Leiste den Tab »Look & Feel«, im Seitenbereich den Radio-Button »Style: Triangular« und wählen in der Tab-Leiste »Position« den Toggled-Button »Süden« im Seitenbereich aus, dann erhalten Sie Abbildung 4.61. Setzen Sie bspw. im Tab Attribute im Seitenbereich ein Häkchen vor Icon setzen und Tooltips verwenden, so erhalten Sie folgendes Bild (wobei hier der Mauszeiger über der Tab-Leiste Attribute verweilte) (siehe Abbildung 4.62).
168
1542.book Seite 169 Montag, 4. Januar 2010 1:02 13
Qt-Widgets
Abbildung 4.61
Style und Position der Tab-Leiste verändert
Abbildung 4.62
Tab-Leiste mit Tooltip und Icon
Da zuvor auf alle Methoden näher eingegangen wurde und das eigentliche Prinzip eines Qt-Programms (bspw. Signal-Slot-Verbindungen) »sitzen« sollte, erspare ich mir weitere Erläuterungen. QFrame Die von QWidget abgeleitete Klasse QFrame ist eine Basisklasse für Widgets mit einem Rahmen. Der Rahmen um die Widgets kann dabei verschiedene Formen annehmen. QFrame kann (und wird) auch als reiner Platzhalter ohne Inhalt verwendet. Folgende Methoden stellt QFrame hierbei zur Verfügung: Methode
Beschreibung
QFrame ( QWidget * parent = 0, Qt::WindowFlags f = 0 );
Konstruktor. Erzeugt einen Frame mit dem Stil NoFrame und 1-Pixel Rahmen-Stärke.
~QFrame ();
Destruktor. Zerstört einen Frame.
Tabelle 4.33
Methoden von QFrame
169
4.5
1542.book Seite 170 Montag, 4. Januar 2010 1:02 13
4
Dialoge, Layout und Qt-Widgets
Methode
Beschreibung
QRect frameRect () const;
Damit erhalten Sie die Größe des rechteckigen Framebereichs. Per Standard ist dies die Größe des darin enthaltenen Widgets. Der rechteckige Bereich des Frames wird automatisch vergrößert bzw. verkleinert, wenn das Widget verändert wurde.
Shadow frameShadow () const;
Damit können Sie den Stil des »Schattens« vom Frame abfragen (siehe Tabelle 4.34).
Shape frameShape () const;
Damit können Sie die Form vom Frame abfragen (siehe Tabelle 4.35).
int frameStyle () const;
Gibt den Stil vom Frame zurück (siehe Tabelle 4.34 und 4.35).
int frameWidth () const;
Damit wird die aktuelle Rahmenbreite des Frames zurückgegeben.
int lineWidth () const;
Damit erhalten Sie die Linienstärke zurück. Diese wird gewöhnlich für Trennlinien (Stil: HLine und VLine) verwendet. Standardwert ist hierbei 1.
int midLineWidth () const;
Damit erhalten Sie die Linienstärke der mittleren Linie zurück. Standardwert ist hierbei 0.
void setFrameRect ( const QRect & );
Damit setzen Sie die Größe des rechteckigen Framebereichs. Per Standard ist dies die Größe des darin enthaltenen Widgets. Setzen Sie den Wert auf 0 (bspw. QRect(0,0,0,0)), ist der rechteckige Bereich immer so groß wie das darin enthaltene Widget.
void setFrameShadow ( Shadow );
Damit setzen Sie den Stil des »Schattens« im Frame auf Shadow (siehe Tabelle 4.34).
void setFrameShape ( Shape );
Damit setzen Sie die Form des Frames auf Shape (siehe Tabelle 4.35).
void setFrameStyle ( int style );
Damit können Sie mit dem bitweisen ODER den »Schatten« und/oder die Form des Frames in einem Rutsch setzen (siehe Tabelle 4.34 und 4.35).
void setLineWidth ( int );
Damit setzen Sie die Linienstärke. Diese wird gewöhnlich für Trennlinien (Stil: HLine und VLine) verwendet.
void setMidLineWidth ( int );
Damit setzen Sie die Linienstärke der mittleren Linie.
Tabelle 4.33
170
Methoden von QFrame (Forts.)
1542.book Seite 171 Montag, 4. Januar 2010 1:02 13
Qt-Widgets
Hinweis Der Raum zwischen dem Frame und dem Inhalt des Frames kann mit der Methode QWidget::setContentsMartins() angepasst werden.
Die Methoden setFrameShadow() und frameShadow() verwenden den Wert Shadow, womit Sie dem Rahmen einen 3D-Effekt verleihen können. Mit dem enum-Wert QFrame::Shadow stehen Ihnen folgende Konstanten zur Verfügung: Konstante
Beschreibung
QFrame::Plain
Der Rahmen wird mit der Vordergrundfarbe ohne jeglichen 3D-Effekt gezeichnet.
QFrame::Raised
Der Rahmen wird angehoben gezeichnet.
QFrame::Sunken
Gegenstück zu Raised. Der Rahmen wird versunken gezeichnet.
Tabelle 4.34
Mögliche »Schatten«-Effekte für den Rahmen (QFrame::Shadow)
Die Form des Rahmens legen Sie mit der Methode setFrameShape() fest (Abfragen mit frameShape()). Auch hierfür ist mit QFrame::Shape ein enum-Typ definiert, womit folgende Konstanten verwendet werden können: Konstante
Beschreibung
QFrame::NoFrame
Es wird kein Rahmen gezeichnet.
QFrame::Box
Zeichnet eine Box um den Inhalt.
QFrame::Panel
Zeichnet ein versunkenes bzw. angehobenes Fach um den Inhalt (abhängig vom Wert Shadow).
QFrame::StyledPanel
Dito (wie QFrame::Panel), nur wird das Fach enstprechend dem aktuellen GUI-Stil gezeichnet. Dieser Wert ist für plattformunabhängige Anwendungen QFrame::Panel vorzuziehen.
QFrame::HLine
QFrame zeichnet eine horizontale Linie, die sich prima als Trenn-
QFrame::VLine
Dito (wie QHLine), nur wird eine vertikale Linie gezeichnet.
QFrame::WinPanel
Damit wird ein rechteckiger Rahmen wie in Windows 95 gezeichnet. Diese Form ist nur wegen der Kompatibilität vorhanden. Für einen unabhängigen GUI-Stil wird StyledPempfohlen.
linie verwenden lässt.
Tabelle 4.35
Verschiedene Formen für QFrame
Hierzu soll jetzt ein Beispiel erstellt werden, womit Sie sich die einzelnen Formen und Schatten der Rahmen selbst in der Praxis ansehen und sie testen können. Die
171
4.5
1542.book Seite 172 Montag, 4. Januar 2010 1:02 13
4
Dialoge, Layout und Qt-Widgets
Linienweite wurde hierbei mit 2 auf einen festen Wert gesetzt. Sie können aber gerne mit den Methoden setLineWidth(int) und setMidLineWidth(int) selbst herumexperimentieren. Zunächst wieder das Grundgerüst: 00 01 02 03 04 05 06 07 08 09 10 11 12 13
// beispiele/qframe/mywidget.h #ifndef MYWIDGET_H #define MYWIDGET_H #include #include #include #include #include #include #include #include #include #include #include
14 class MyWidget : public QWidget { 15 Q_OBJECT 16 public: 17 MyWidget( QWidget *parent = 0); 18 QLabel* framelabel; 19 QRadioButton *rb1, *rb2, *rb3, *rb4, *rb5, *rb6, *rb7, *rb8, *rb9, *rb10; 20 public slots: 21 void changeFrame(bool b); 22 }; 23 #endif
Jetzt zur Implementierung der Demonstration verschiedener Frames: 00 01 02 03
// beispiele/qframe/mywidget.cpp #include "mywidget.h" #include #include
04 // neue Widget-Klasse vom eigentlichen Widget ableiten 05 MyWidget::MyWidget( QWidget *parent): QWidget(parent) { 06 // Radio-Buttons für die Form (Shape) 07 QVBoxLayout *vbox1 = new QVBoxLayout; 08 rb1 = new QRadioButton("QFrame::NoFrame"); 09 rb2 = new QRadioButton("QFrame::Box"); 10 rb3 = new QRadioButton("QFrame::Panel");
172
1542.book Seite 173 Montag, 4. Januar 2010 1:02 13
Qt-Widgets
11 12 13 14 15 16 17 18 19 20 21 22 23
rb4 = new QRadioButton("QFrame::StyledPanel"); rb5 = new QRadioButton("QFrame::HLine"); rb6 = new QRadioButton("QFrame::VLine"); rb7 = new QRadioButton("QFrame::WinPanel"); rb1->setChecked(true); vbox1->addWidget(rb1); vbox1->addWidget(rb2); vbox1->addWidget(rb3); vbox1->addWidget(rb4); vbox1->addWidget(rb5); vbox1->addWidget(rb6); vbox1->addWidget(rb7); vbox1->addStretch(1);
24 25 26 27 28 29
// Radio-Buttons für den Schatten (Shadow) QVBoxLayout *vbox2 = new QVBoxLayout; rb8 = new QRadioButton("QFrame::Plain"); rb9 = new QRadioButton("QFrame::Raised"); rb10 = new QRadioButton("QFrame::Sunken"); rb8->setChecked(true);
30 31 32 33
vbox2->addWidget(rb8); vbox2->addWidget(rb9); vbox2->addWidget(rb10); vbox2->addStretch(1);
34 35 36 37 38 39
// das Frame-Demo framelabel = new QLabel("Frame"); framelabel->setMargin( 25 ); framelabel->setLineWidth(2); framelabel->setAlignment( Qt::AlignCenter ); framelabel->setFrameStyle( QFrame::NoFrame | QFrame::Plain );
40 41 42 43 44
// alles in die Group-Box verpacken QGroupBox *groupBox1 = new QGroupBox("Shape"); groupBox1->setLayout(vbox1); QGroupBox *groupBox2 = new QGroupBox("Shadow"); groupBox2->setLayout(vbox2);
46 47
// Signal-Slot-Verbindungen für die Radio-Buttons connect( rb1, SIGNAL( toggled(bool) ), this, SLOT( changeFrame (bool) ) ); connect( rb2, SIGNAL( toggled(bool) ), this, SLOT( changeFrame (bool) ) );
48
173
4.5
1542.book Seite 174 Montag, 4. Januar 2010 1:02 13
4
Dialoge, Layout und Qt-Widgets
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 }
connect( rb3, SIGNAL( toggled(bool) ), this, SLOT( changeFrame (bool) connect( rb4, SIGNAL( toggled(bool) ), this, SLOT( changeFrame (bool) connect( rb5, SIGNAL( toggled(bool) ), this, SLOT( changeFrame (bool) connect( rb6, SIGNAL( toggled(bool) ), this, SLOT( changeFrame (bool) connect( rb7, SIGNAL( toggled(bool) ), this, SLOT( changeFrame (bool) connect( rb8, SIGNAL( toggled(bool) ), this, SLOT( changeFrame (bool) connect( rb9, SIGNAL( toggled(bool) ), this, SLOT( changeFrame (bool) connect( rb10, SIGNAL( toggled(bool) ), this, SLOT( changeFrame (bool) // das Layout erstellen QHBoxLayout *hbox = new QHBoxLayout; hbox->addWidget(groupBox1); hbox->addWidget(groupBox2); hbox->addWidget(framelabel); setLayout(hbox); setWindowTitle("QFrame – Demo");
) ); ) ); ) ); ) ); ) ); ) ); ) ); ) );
65 // Slot: Radio-Buttons auswerten und Form/Schatten 66 // von QFrame neu einstellen 67 void MyWidget::changeFrame(bool b) { 68 if( rb1->isChecked() ) 69 framelabel->setFrameShape(QFrame::NoFrame); 70 else if( rb2->isChecked() ) 71 framelabel->setFrameShape(QFrame::Box); 72 else if( rb3->isChecked() ) 73 framelabel->setFrameShape(QFrame::Panel); 74 else if( rb4->isChecked() ) 75 framelabel->setFrameShape(QFrame::StyledPanel); 76 else if( rb5->isChecked() ) 77 framelabel->setFrameShape(QFrame::HLine); 78 else if( rb6->isChecked() ) 79 framelabel->setFrameShape(QFrame::VLine); 80 else if( rb7->isChecked() ) 81 framelabel->setFrameShape(QFrame::WinPanel); 82 83
174
if( rb8->isChecked() ) framelabel->setFrameShadow(QFrame::Plain);
1542.book Seite 175 Montag, 4. Januar 2010 1:02 13
Qt-Widgets
84 85 86 87 88 }
else if( rb9->isChecked() ) framelabel->setFrameShadow(QFrame::Raised); else if( rb10->isChecked() ) framelabel->setFrameShadow(QFrame::Sunken);
Das Beispiel ist recht einfach aufgebaut. Zunächst packen wir mehrere Radio-Buttons in zwei Group-Boxen. Je eine Group-Box für die Form (Shape) und eine für den Schatten (Shadow). Jeder Radio-Button bekommt natürlich ein entsprechendes Shape- bzw. Shadow-Label. In den Zeilen 34 bis 39 erzeugen wir ein neues QFrame und vergeben entsprechende Attribute. In den Zeilen 46 bis 56 richten wir die Signal-Slot-Verbindungen für die Radio-Buttons ein. Wenn ein Radio-Button betätigt wurde, wird immer der eigene Slot changeFrame() ausgeführt. Darin werden dann den Radio-Buttons entsprechend die Form und der Schatten für das Frame neu gesetzt. Nun noch eine Hauptfunktion: 00 // beispiele/qframe/main.cpp 01 #include 02 #include "mywidget.h" 03 int main(int argc, char *argv[]) { 04 QApplication app(argc, argv); 05 MyWidget* window = new MyWidget; 06 window->show(); 07 return app.exec(); 08 }
Jetzt können Sie die Formen und Schatten der Frames nach Belieben kombinieren und testen:
Abbildung 4.63
Ein Frame mit QFrame::Box und QFrame::Raised
175
4.5
1542.book Seite 176 Montag, 4. Januar 2010 1:02 13
4
Dialoge, Layout und Qt-Widgets
QToolBox Die von QFrame abgeleitete Klasse ist der Klasse QTabWidget recht ähnlich. Diese Klasse ist eine Spalte mit Tab-Widgets-Elementen, wo auch immer nur der Seitenbereich eines Tab-Widgets auf einmal angezeigt wird. Da diese Klasse von QFrame abgeleitet wurde, kann sie praktisch überall mit eingebaut werden. Auch die Methoden sind denen von QTabWidget recht ähnlich, nur dass hierbei statt der Teilbezeichnung »Tab« die Teilbezeichnung »Item« verwendet wird. Außerdem können Sie auf alle Methoden von QFrame zurückgreifen. Hier ein Überblick zu den Methoden von QToolBox: Methode
Beschreibung
QToolBox ( QWidget * parent = 0, Qt::WindowFlags f = 0 );
Konstruktor. Erzeugt eine neue Toolbox.
~QToolBox ();
Destruktor. Zerstört eine vorhandene Toolbox.
int addItem ( QWidget * widget, const QIcon & iconSet, const QString & text );
Fügt das Widget widget in einem neuen Tab unten der Toolbox hinzu. Mit text setzen Sie den Text und mit iconSet das Icon für den Tab.
int addItem ( QWidget * w, const QString & text );
Dito (wie eben), nur ohne Icon.
int count () const;
Damit erhalten Sie die Anzahl der in der Toolbox befindlichen Elemente.
int currentIndex () const;
Gibt die Indexnummer (angefangen bei 0) des aktuellen Tabs in der Toolbar zurück.
QWidget * currentWidget () const;
Dito (wie eben), nur wird hier ein Zeiger auf das aktuelle Widget zurückgegeben. Gibt es hierbei kein Element, wird 0 zurückgegeben.
int indexOf ( QWidget * widget ) const;
Gibt die Indexnummer von widget zurück (angefangen mit 0). Gibt es dieses widget nicht, wird –1 zurückgegeben.
int insertItem ( int index, QWidget * widget, const QIcon & icon, const QString & text );
Fügt das Widget widget an der Position index (angefangen bei 0) in einem neuen Tab zur Toolbox hinzu. Mit text setzen Sie den Text und mit iconSet das Icon für den Tab. Wird für index ein ungültiger Wert verwendet, wird wie mit addItem() verfahren.
Tabelle 4.36
176
Gängige Methoden von QToolBox
1542.book Seite 177 Montag, 4. Januar 2010 1:02 13
Qt-Widgets
Methode
Beschreibung
int insertItem ( int index, QWidget * widget, const QString & text );
Dito (wie eben), nur ohne Icon
bool isItemEnabled ( int index ) const;
Ist das Element an Position index aktiviert, wird true zurückgegeben. Ansonsten false.
QIcon itemIcon ( int index ) const;
Gibt das Icon des Tabs mit der Position index zurück. Gibt es kein Icon oder ist index außerhalb des Bereichs wird NULL zurückgegeben.
QString itemText ( int index ) const;
Dito, allerdings wird der Text des Tabs zurückgegeben.
QString itemToolTip ( int index ) const;
Dito, nur wird der Text des Tooltips vom Tab zurückgegeben.
void removeItem ( int index );
Entfernt das Tab mit Inhalt an Position index von der Toolbar. Beachten Sie, dass das Element nicht gelöscht wird.
void setItemEnabled ( int index, bool enabled );
Damit aktivieren (true) bzw. deaktivieren (false; ausgrauen) Sie den Tab (und somit den Inhalt) mit der Position index.
void setItemIcon ( int index, const QIcon & icon );
Damit übergeben Sie dem Tab an der Position index das Icon icon.
void setItemText ( int index, const QString & text );
Setzt den Text des Tabs an der Position index mit text.
void setItemToolTip ( int index, const QString & toolTip );
Setzt den Tooltip-Text des Tabs an der Position index mit toolTip.
QWidget * widget ( int index ) const;
Gibt das Widget an der Position index zurück. Ansonsten bei einem Fehler NULL, wenn keines vorhanden ist.
Tabelle 4.36
Gängige Methoden von QToolBox (Forts.)
Wie schon bei der Klasse QTabWidget finden Sie mit currentChanged(int index) das einzige Signal von QToolBox. Dieses Signal wird ausgelöst, wenn der Tab gewechselt wurde. Der Parameter enthält den neuen, sichtbaren Tab-Inhalt. Dieselben Slots wie bei QTabWidget werden verwendet; der Slot setCurrentIndex(int index) wo mit dem Parameter index zum neuen Seitenindex gewech-
selt wird; Selbiges macht auch der Slot setCurrentWidget(QWidget *widget), nur dass man hierbei zum Seitenbereich des Widgets widget gewechselt.
177
4.5
1542.book Seite 178 Montag, 4. Januar 2010 1:02 13
4
Dialoge, Layout und Qt-Widgets
Hierzu nun ein Beispiel, das die Klasse QToolBox in der Praxis demonstrieren soll. Hierbei wurde – bis auf eine Signal-Slot-Verbindung, dass die Toolbox gewechselt wurde – auf jegliche Aktion verzichtet. Einfach ein Beispiel zu Anschauungszwecken. Zunächst wieder das Grundgerüst: 00 01 02 03 04 05 06 07 08 09 10 11 12
// beispiele/qtoolbox/mywidget.h #ifndef MYWIDGET_H #define MYWIDGET_H #include #include #include #include #include #include #include #include #include #include
13 class MyWidget : public QWidget { 14 Q_OBJECT 15 public: 16 MyWidget( QWidget *parent = 0); 17 private: 18 QRadioButton *rbutton1, *rbutton2, *rbutton3; 19 QCheckBox *cbutton1, *cbutton2, *cbutton3; 20 QPushButton *pbutton1,*pbutton2,*pbutton3; 21 QVBoxLayout* vBox1, *vBox2, *vBox3; 22 QGroupBox* groupBox1, *groupBox2, *groupBox3; 23 QToolBox* toolB; 24 QButtonGroup *group1, *group2, *group3; 25 public slots: 26 void getCurrentPosition( int pos ); 27 }; 28 #endif
Jetzt zur Implementierung des Codes: 00 01 02 03
// beispiele/qtoolbox/mywidget.cpp #include "mywidget.h" #include #include
04 // neue Widget-Klasse vom eigentlichen Widget ableiten 05 MyWidget::MyWidget( QWidget *parent): QWidget(parent) {
178
1542.book Seite 179 Montag, 4. Januar 2010 1:02 13
Qt-Widgets
06 07 08 09 10 11 12 13 14 15 16 17 18 19
// Radio-Buttons erzeugen rbutton1 = new QRadioButton("Radio 1"); rbutton2 = new QRadioButton("Radio 2"); rbutton3 = new QRadioButton("Radio 3"); // Button vorbelegen rbutton1->setChecked(true); // Check-Button erzeugen cbutton1 = new QCheckBox ("Check 1"); cbutton2 = new QCheckBox ("Check 2"); cbutton3 = new QCheckBox ("Check 3"); // normale Buttons erzeugen ... pbutton1 = new QPushButton("Button 1"); pbutton2 = new QPushButton("Button 2"); pbutton3 = new QPushButton("Button 3");
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
// drei Group-Boxen mit Label erzeugen groupBox1 = new QGroupBox("Bitte auswählen"); groupBox2 = new QGroupBox("Bitte ankreuzen"); groupBox3 = new QGroupBox("Bitte drücken"); // drei vertikale Layout-Boxen erzeugen vBox1 = new QVBoxLayout; vBox2 = new QVBoxLayout; vBox3 = new QVBoxLayout; // Radio-Buttons in Layoutbox vBox1 packen vBox1->addWidget(rbutton1); vBox1->addWidget(rbutton2); vBox1->addWidget(rbutton3); // Check-Buttons in Layoutbox vBox2 packen vBox2->addWidget(cbutton1); vBox2->addWidget(cbutton2); vBox2->addWidget(cbutton3); // Toggled Buttons in Layoutbox vBox3 packen vBox3->addWidget(pbutton1); vBox3->addWidget(pbutton2); vBox3->addWidget(pbutton3); // Stretch (1) für alle Layoutboxen hinzufügen vBox1->addStretch(1); vBox2->addStretch(1); vBox3->addStretch(1); // Layout der einzelnen Group-Boxen setzen groupBox1->setLayout(vBox1); groupBox2->setLayout(vBox2); groupBox3->setLayout(vBox3); // neue Toolbox erzeugen toolB = new QToolBox;
179
4.5
1542.book Seite 180 Montag, 4. Januar 2010 1:02 13
4
Dialoge, Layout und Qt-Widgets
50 51 52 53 54 55 56 57 58 59
60
61
62 63 64 65 66 67 68 69 70 71 72 73 }
// Groupbox mit Radio-Buttons als // Seitenbereich hinzufügen toolB->addItem(groupBox1, "Radio-Buttons" ); // Groupbox mit Check-Buttons als // Seitenbereich hinzufügen toolB->addItem(groupBox2, "Check-Buttons" ); //Groupbox mit toggled-Buttons hinzufügen toolB->addItem(groupBox3, "Push-Buttons" ); // Icons zu den Tabs hinzufügen toolB->setItemIcon( 0, QIcon(QString("%1 %2") .arg(QCoreApplication::applicationDirPath()) .arg("/images/icon1.png") ) ); toolB->setItemIcon(1, QIcon(QString("%1 %2") .arg(QCoreApplication::applicationDirPath()) .arg("/images/icon2.png"))); toolB->setItemIcon(2, QIcon(QString("%1 %2") .arg(QCoreApplication::applicationDirPath()) .arg("/images/icon3.png"))); // Tooltips zu den Tabs hinzufügen toolB->setItemToolTip(0, "Toolbox mit Radio-Buttons"); toolB->setItemToolTip(1, "Toolbox mit Check-Buttons"); toolB->setItemToolTip(2, "Toolbox mit Push-Buttons"); // Signal-Slot-Verbindung für die Toolbox connect( toolB, SIGNAL( currentChanged( int ) ), this, SLOT( getCurrentPosition( int ) ) ); // ein Layout-Widget für die Toolbox QVBoxLayout* layout = new QVBoxLayout; layout->addWidget(toolB); setLayout(layout); setWindowTitle("QToolBox – Demo");
74 // Slot: Toolbox wurde gewechselt 75 void MyWidget::getCurrentPosition(int pos) { 76 QString msg = QString("Zu Tab %1 gewechselt").arg( toolB->itemText(pos) ); 77 QMessageBox::information( this,"Tab gewechsel",msg,QMessageBox::Ok); 78 }
180
1542.book Seite 181 Montag, 4. Januar 2010 1:02 13
Qt-Widgets
Ich denke, das Beispiel bedarf keiner Worte mehr. Nun noch, wie immer, ein Hauptprogramm: // beispiele/qtoolbox/main.cpp #include #include "mywidget.h" int main(int argc, char *argv[]) { QApplication app(argc, argv); MyWidget* window = new MyWidget; window->show(); return app.exec(); }
Das Programm bei der Ausführung:
Abbildung 4.64
QToolBox im Einsatz
Natürlich können Sie den Rahmen der Toolbox jetzt noch mit Methoden von QFrame verzieren, da ja QToolBox ein Abkömmling von QFrame ist. Bspw.: toolB->setFrameStyle(QFrame::Box | QFrame::Sunken);
4.5.3
Widgets zur Zustandsanzeige
Hierzu gehören alle Widgets, die einen bestimmten Vorgang bzw. Zustand auf dem Bildschirm anzeigen können, allerdings selbst keinerlei Interaktion anbieten. QProgressBar (QProgressDialog) Die Klasse QProgressBar (abgeleitet von QWidget) repräsentiert einen horizontalen bzw. vertikalen Fortschrittsbalken. Ein solcher Fortschrittsbalken zeigt dem Anwender an, wie lange bzw. wie weit eine Operation (bspw. Kopieren oder Löschen von Dateien) fortgeschritten ist oder wie lange diese noch benötigt wird. Die Zustandsleiste wird durch eine minimale und maximale Anzahl von Schritten defi-
181
4.5
1542.book Seite 182 Montag, 4. Januar 2010 1:02 13
4
Dialoge, Layout und Qt-Widgets
niert. Damit lässt sich immer der aktuelle Schritt sowohl in Zahlen als auch in Prozent angeben. Folgende Methoden stehen Ihnen für QProgressBar zur Verfügung: Methode
Beschreibung
QProgressBar ( QWidget * parent = 0 );
Konstruktor. Erzeugt eine neue leere Zustandsleiste.
Qt::Alignment alignment () const; Gibt die Ausrichtung der Zustandsleiste zurück.
Mögliche Werte siehe Tabelle 4.8, 4.9 und 4.10. QString format () const;
Gibt das Format zurück, wie die Werte der Zustandsleiste angezeigt werden (siehe dazu. setFormat()).
bool invertedAppearance ();
Gibt true zurück, wenn die Zustandsleiste umgekehrt ausgeführt wird (also von rechts nach links). Standard ist false (von links nach rechts).
bool isTextVisible () const;
Gibt true zurück, wenn die Werte der Zustandsleiste angezeigt werden. Ansonsten wird false zurückgegeben.
int maximum () const;
Gibt den maximalen Wert der Zustandsleiste zurück.
int minimum () const;
Gibt den minimalen Wert der Zustandsleiste zurück.
Qt::Orientation orientation () const;
Gibt die Orientierung der Zustandsleiste zurück. Die Werte können Qt::Horizontal (Standard) oder Qt::Vertical sein.
void setAlignment ( Qt::Alignment alignment );
Setzt die Ausrichtung der Zustandsleiste auf alignment. Mögliche Werte siehe Tabelle 4.8, 4.9 und 4.10.
void setFormat ( const QString & format );
Damit setzen Sie den String, der für die Anzeige der Werte der Zustandsleiste verwendet wird. Folgende Formate können dabei verwendet werden: 왘
%p – wird durch komplette Prozentanzeige
왘
%v – wird durch den aktuellen Wert der Schritte
왘
%m – wird durch den gesamten Wert der Schritte
ersetzt. ersetzt. ersetzt. Der Standardwert lautet: "%p%". void setInvertedAppearance ( bool invert );
Tabelle 4.37
182
Mit true als Parameter wird die Zustandsleiste umgekehrt (von rechts nach links) ausgeführt. Standard (von links nach rechts) ist false eingestellt.
Methoden von QProgressBar
1542.book Seite 183 Montag, 4. Januar 2010 1:02 13
Qt-Widgets
Methode
Beschreibung
void setRange ( int minimum, int maximum );
Setzt die minimale und maximale Anzahl von Schritten der Zustandsleiste. Ist maximum kleiner als minimum, bekommt minimum einen gültigen Wert.
void setTextDirection ( QProgressBar::Direction textDirection );
Richtet den lesbaren Text einer vertikalen Zustandsleiste aus. Mögliche Werte hierfür sind (Standard) QProgressBar::TopToBottom und QProgressBar::BottomToTop. Diese Eigenschaft hat keinen Einfluss, wenn eine horizontale Zustandsleiste verwendet wird.
void setTextVisible ( bool visible );
Mit true (Standard) wird der Wert in der Zustandsleiste angezeigt. Mit false schalten Sie das ab.
virtual QString text () const;
Gibt den kompletten Text, wie viel Prozent bzw. und/oder Schritte (abhängig von setFormat()) ausgeführt wurden, zurück.
int value () const;
Gibt den aktuellen Wert der Zustandsleiste zurück.
Tabelle 4.37
Methoden von QProgressBar (Forts.)
Mit dem Signal valueChanged(int) von QProgressBar können Sie reagieren, wenn der angezeigte Wert in der Zustandsleiste verändert wurde. Der Parameter enthält dann den neuen Wert, der in der Zustandsleiste angezeigt wird. Die Klasse QProgressBar hingegen enthält mehrere Slots, die in der folgenden Tabelle (4.38) aufgelistet und erläutert sind. Slot
Beschreibung
void reset ();
Setzt den Fortschrittsbalken zurück. Der Fortschrittsbalken wird dabei »zurückgespult« und zeigt keinen Zustand an.
void setMaximum ( int maximum );
Setzt den maximal möglichen Wert des Fortschrittsbalkens auf maximum.
void setMinimum ( int minimum );
Setzt den mininmalen möglichen Wert des Fortschrittsbalkens auf minimum.
void setValue ( int value );
Setzt den aktuellen Wert der Zustandsleiste auf value.
void setOrientation ( Qt::Orientation ) ;
Setzt die Orientierung der Zustandsleiste auf Qt::Horizontal (Standard) oder Qt::Vertical.
Tabelle 4.38
Slots von QProgressBar
183
4.5
1542.book Seite 184 Montag, 4. Januar 2010 1:02 13
4
Dialoge, Layout und Qt-Widgets
Nun zu einem einfachen Beispiel, das die Klasse QProgressBar in der Praxis zeigen soll. Als »Aufwand«, der eben die Zustandsleiste simulieren soll, haben wir eine einfache for-Schleife verwendet, die nichts anders macht, als bis 12345678 hochzuzählen, und zwar 500 mal. Zunächst wieder das Grundgerüst: 00 01 02 03 04 05 06 07
// beispiele/qprogressbar/mywidget.h #ifndef MYWIDGET_H #define MYWIDGET_H #include #include #include #include #include
08 class MyWidget : public QWidget { 09 Q_OBJECT 10 public: 11 MyWidget( QWidget *parent = 0); 12 QProgressBar *pbar; 13 public slots: 14 void startProgress(); 15 }; 16 #endif
Jetzt zur Implementierung des Codes: 00 01 02 03
// beispiele/qprogressbar/mywidget.cpp #include "mywidget.h" #include #include
04 // neue Widget-Klasse vom eigentlichen Widget ableiten 05 MyWidget::MyWidget( QWidget *parent): QWidget(parent) { 06 // neue Zustandsleiste erzeugen 07 pbar = new QProgressBar; 08 // min. und max. Werte festlegen 09 pbar->setRange( 0, 500 ); 10 // Anzeige-Format festlegen 11 pbar->setFormat("%v von %m (%p%)"); 12 // Button zum Starten des Dialogs 13 QPushButton* but1 = new QPushButton("Starten"); 14 // ein Layout-Widget 15 QVBoxLayout *vBox = new QVBoxLayout; 16 vBox->addWidget(pbar); 17 vBox->addWidget(but1); 18 // eine Group-Box
184
1542.book Seite 185 Montag, 4. Januar 2010 1:02 13
Qt-Widgets
19 20 21 22 23 24 25 26 27 28 29 }
QGroupBox *groupBox1 = new QGroupBox( "Fortschrittszustand"); groupBox1->setLayout(vBox); // Signal-Slot-Verbindung einrichten connect( but1, SIGNAL( clicked() ), this, SLOT( startProgress() ) ); // alles in ein Layout verpacken QVBoxLayout* layout = new QVBoxLayout; layout->addWidget(groupBox1); setLayout(layout); setMinimumSize( 300, 150 ); setWindowTitle("QProgressBar – Demo");
30 // Slot: startet die Zustandsleiste und ändert den Wert 31 void MyWidget::startProgress() { 32 for (int i = 0; i < 500; i++) { 33 pbar->setValue(i); 34 for( int j=0; j < 12345678; ++j); 35 //... copy one file 36 } 37 pbar->setValue(500); 38 }
Nun noch ein Hauptprogramm dazu: 00 // beispiele/qprogressbar/main.cpp 01 #include 02 #include "mywidget.h" 03 int main(int argc, char *argv[]) { 04 QApplication app(argc, argv); 05 MyWidget* window = new MyWidget; 06 window->show(); 07 return app.exec(); 08 }
Die Zustandsleiste bei der Ausführung:
Abbildung 4.65
QProgressBar bei der Ausführung
185
4.5
1542.book Seite 186 Montag, 4. Januar 2010 1:02 13
4
Dialoge, Layout und Qt-Widgets
QProgressBar und Threads Wenn das Programm den Rechner zu stark beanspruchen sollte, können Sie Threads verwenden wie in Abschnitt 6.9 besprochen.
QProgressDialog Mit QProgressBar haben Sie eine völlig freie Gestaltungs- und Anwendungsmöglichkeit. Aber Qt bietet natürlich auch hierzu einen fertigen Dialog mit der Klasse QProgressDialog an. Die Klasse QProgressDialog bietet ebenfalls eine Menge Methoden und Slots (sowie ein Signal) an. Hierauf gehen wir jetzt allerdings nicht genauer ein, da dies eine Wiederholung der Klasse QProgressBar wäre mit z. T. anderen Bezeichnern. Für mehr Informationen sollten Sie den Assistant von Qt konsultieren. Hierzu ein einfaches Beispiel mit QProgressDialog: 00 // beispiele/qprogressdialog/main.cpp 01 #include 02 #include 03 int main(int argc, char *argv[]) { 04 QApplication app(argc, argv); 05 QProgressDialog progress( 06 "Zustandsanzeige ...", "Abbrechen", 0, 2000); 07 progress.setWindowTitle("QProgressDialog"); 08 progress.show(); 09 10 11 12 13 14 15 16 17 18 }
for (int i = 0; i < 2000; i++) { progress.setValue(i); if (progress.wasCanceled()) break; for(int i=0; i<5000000; ++i); //... simulieren } progress.setValue(2000); return 0;
Das Programm bei der Ausführung:
Abbildung 4.66
186
QProgressDialog bei der Ausführung
1542.book Seite 187 Montag, 4. Januar 2010 1:02 13
Qt-Widgets
Ein weiterer Vorteil: Man muss sich nicht wie bei QProgressBar um die einzelnen Buttons kümmern, hier vor allem der Button zum Abbrechen des Dialogs. Gleich beim Anlegen wird eine QProgressDialog mit eingebaut und es muss nur noch in der Zustandsschleife mit der Methode wasChanceled() (Zeile 11) überprüft werden, ob der Anwender abgebrochen hat. QLCDNumber Mit der Klasse QLCDNumber (abgeleitet von QFrame) können Sie LCD-ähnliche Ziffern anzeigen lassen. Diese Nummern können sowohl dezimal, hexadezimal und oktal als auch in binärer Form ausgegeben werden. QLCDNumber besitzt folgende Methoden: Methode
Beschreibung
QLCDNumber ( QWidget * parent = 0 );
Erzeugt eine LCD-Ziffer, setzt die Anzahl der Ziffern auf 5 und die Basis auf Dezimal (10). Die Dezimalpunkte sind auf small gestellt und der Stil des Rahmens ist eine hervorgehobene Box (raised Box). Der Stil der LCD-Nummern ist Outline.
QLCDNumber ( uint numDigits, QWidget * parent = 0 );
Dito, nur können Sie zusätzlich die Anzahl der LCD-Ziffern beim Start angeben.
~QLCDNumber ();
Destruktor. Zerstört ein LCD-Ziffern-Objekt.
bool checkOverflow ( double num ) const;
Gibt true zurück, wenn num zu groß ist, um richtig angezeigt werden zu können. Ansonsten wird false zurückgegeben.
bool checkOverflow ( int num ) const;
Dito, nur für Integer
int intValue () const;
Diese Eigenschaft gibt den angezeigten Wert zurück (ggf. wird auf den nächsten Integer auf- bzw. abgerundet). Ist der angezeigte Wert keine Nummer, wird 0 zurückgegeben.
Mode mode () const ;
Diese Eigenschaft enthält den aktuellen Anzeigemodus (Nummern Basis) (siehe Tabelle 4.40).
int numDigits () const ;
Diese Eigenschaft gibt die aktuelle Anzahl der Ziffern zurück, die angezeigt werden können.
SegmentStyle segmentStyle () const ;
Damit erhalten Sie den Stil der LCD-Ziffern zurück. Mögliche Werte siehe Tabelle 4.41.
void setMode ( Mode ) ;
Mit dieser Methode setzen Sie den Anzeigemodus der LCDZiffern (Basis) (siehe Tabelle 4.40).
void setNumDigits ( int nDigits );
Damit setzen Sie die Anzahl der LCD-Ziffern, die angezeigt werden können.
Tabelle 4.39
Methoden von QLCDNumber
187
4.5
1542.book Seite 188 Montag, 4. Januar 2010 1:02 13
4
Dialoge, Layout und Qt-Widgets
Methode
Beschreibung
void setSegmentStyle ( SegmentStyle );
Damit setzen Sie den Stil der LCD-Ziffern. Siehe Tabelle 4.41.
bool smallDecimalPoint () Gibt true zurück, wenn der dezimale Punkt auf small const; gesetzt ist. Ansonsten wird false zurückgegeben. double value () const;
Tabelle 4.39
Diese Eigenschaft gibt den angezeigten Wert zurück. Ist der angezeigte Wert keine Nummer, wird 0 zurückgegeben.
Methoden von QLCDNumber (Forts.)
Folgende Werte (bzw. genauer: Zahlenbasis) können Sie mit dem enum-Wert QLCDNumber::Mode und den Methoden setMode() bzw. mode() verwenden: Konstante
Bedeutung
QLCDNumber::Hex
Hexadezimale Darstellung
QLCDNumber::Dec
Dezimale Darstellung
QLCDNumber::Oct
Oktale Darstellung
QLCDNumber::Bin
Binäre Darstellung
Tabelle 4.40
Folgende
Zahlenbasis von QLCDNumber
Stile
der
LCD-Ziffer
können
Sie
über
die
enum-Konstante
QLCDNumber::SegmentStyle und die Methoden setSegementStyle() bzw. segmentStyle() setzen bzw. abfragen: Konstante
Bedeutung
QLCDNumber::Outline
Die Ziffern werden hervorgehoben und mit der Hintergrundfarbe des Frames gefüllt.
QLCDNumber::Filled
Die Ziffern werden hervorgehoben und mit der Fenstertextfarbe gefüllt.
QLCDNumber::Flat
Die Ziffern erhalten einen flachen Stil und werden mit der Fenstertextfarbe gefüllt.
Tabelle 4.41
Stile für die LCD-Ziffern
Signale besitzt QLCDNumber mit overflow() nur eines. Dieses Signal wird ausgelöst, wenn eine LCD-Ziffer angezeigt werden soll, die zu groß ist. Öffentliche Slots hingegen gibt es gleich mehrere, die in der folgenden Tabelle aufgelistet sind:
188
1542.book Seite 189 Montag, 4. Januar 2010 1:02 13
Qt-Widgets
Slot
Beschreibung
void display (const QString & s); void display ( double num ); void display ( int num );
Damit zeigen Sie die LCD-Ziffern an. Ist der Wert kein gültiger, wird 0 angezeigt.
void setBinMode ()
Ruft intern die Methode setMode(Bin) auf.
void setDecMode ()
Ruft intern die Methode setMode(Dec) auf.
void setHexMode ()
Ruft intern die Methode setMode(Hex) auf.
void setOctMode ()
Ruft intern die Methode setMode(Oct) auf.
void setSmallDecimalPoint (bool); Damit wird der Stil des Dezimalpunktes gesetzt.
Tabelle 4.42
Slots von QLCDNumber
Jetzt wieder ein einfaches Beispiel zur Klasse QLCDNumber in der Praxis. Hierzu wollen wir zwei Zeilen mit LCD-Ziffern verwenden. In einer Zeile wird das Datum mitsamt Uhrzeit (und laufenden Sekunden) angezeigt und in der zweiten Zeile der aktuelle Unix-Timestamp (Zeitstempel). Unix-Timestamp Diese Zeit zählt die vergangenen Sekunden seit dem 1. Januar 1970 um 00:00 Uhr (UTC), wobei Schaltsekunden nicht mitgezählt werden.
Das Programm verwendet auch die Klassen QTimer und QTimeDate, die beide, wie man dem Namen entnehmen kann, etwas mit der Zeit zu tun haben. Zunächst also wieder das Grundgerüst: 00 01 02 03 04 05 06 07 08 09
// beispiele/qlcdnumber/mywidget.h #ifndef MYWIDGET_H #define MYWIDGET_H #include #include #include #include #include #include #include
10 class MyWidget : public QWidget { 11 Q_OBJECT 12 public: 13 MyWidget(QWidget *parent = 0); 14 QLCDNumber* lcd_date_time;
189
4.5
1542.book Seite 190 Montag, 4. Januar 2010 1:02 13
4
Dialoge, Layout und Qt-Widgets
15 QLCDNumber* lcd_time_stamp; 16 private slots: 17 void showTime(); 18 }; 19 #endif
Jetzt zur eigentlichen Implementation: 00 // beispiele/qlcdnumber/mywidget.cpp 01 #include "mywidget.h" 02 #include 03 // neue Widget-Klasse vom eigentlichen Widget ableiten 04 MyWidget::MyWidget( QWidget *parent): QWidget(parent) { 05 // zwei LCD-Ziffern erzeugen 06 lcd_date_time = new QLCDNumber; 07 lcd_time_stamp = new QLCDNumber; 08 // Stil von LCD und Rahmen setzen 09 lcd_date_time->setSegmentStyle(QLCDNumber::Flat); 10 lcd_time_stamp->setSegmentStyle(QLCDNumber::Flat); 11 lcd_date_time->setFrameStyle(QFrame::NoFrame); 12 lcd_time_stamp->setFrameStyle(QFrame::NoFrame); 13 lcd_time_stamp->setSmallDecimalPoint(true); 14 // Timmer starten,der alle 1000ms das Signal timeout 15 // auslöst und den eigenen Slot showTime() aufruft 16 QTimer *timer = new QTimer(this); 17 connect( timer, SIGNAL(timeout()), this, SLOT(showTime()) ); 18 timer->start(1000); 19 // die üblichen Boxen 20 QVBoxLayout *vBox1 = new QVBoxLayout; 21 vBox1->addWidget(lcd_date_time); 22 QVBoxLayout *vBox2 = new QVBoxLayout; 23 vBox2->addWidget(lcd_time_stamp); 24 // Verpacken 25 QGroupBox *groupBox1 = new QGroupBox( "Datum und Uhrzeit"); 26 groupBox1->setLayout(vBox1); 27 QGroupBox *groupBox2 = new QGroupBox( "Unix-Timestamp"); 28 groupBox2->setLayout(vBox2); 29 // ... und das Layout setzen 30 QVBoxLayout* layout = new QVBoxLayout; 31 layout->addWidget(groupBox1); 32 layout->addWidget(groupBox2); 33 setLayout(layout);
190
1542.book Seite 191 Montag, 4. Januar 2010 1:02 13
Qt-Widgets
34 35 36 37 }
// damit die Größe passt, den Slot einmal aufrufen showTime(); setWindowTitle(tr("LCD Demo"));
38 void MyWidget::showTime() { 39 // aktuelles Datum und Uhrzeit 40 QDateTime time = QDateTime::currentDateTime(); 41 // formatieren 42 QString text=time.toString("ddd dd.MM.yyyy hh:mm:ss"); 43 // der Unix-Timestamp 44 unsigned int i_stamp = time.toTime_t(); 45 // auch Formatieren 46 QString s_stamp = QString("%1").arg(i_stamp); 47 // Anzahl der Ziffern setzen 48 lcd_date_time->setNumDigits(text.size()+1); 49 lcd_time_stamp->setNumDigits(s_stamp.size()+1); 50 // und alles anzeigen 51 lcd_date_time->display(text); 52 lcd_time_stamp->display(s_stamp); 53 }
Jetzt nur noch eine Hauptfunktion: 00 // beispiele/qlcdnumber/main.cpp 01 #include 02 #include "mywidget.h" 03 int main(int argc, char *argv[]) { 04 QApplication app(argc, argv); 05 MyWidget* window = new MyWidget; 06 window->show(); 07 return app.exec(); 08 }
Bei der Ausführung erhalten Sie folgendes Bild:
Abbildung 4.67
Die Klasse QLCDNumber bei der Ausführung
191
4.5
1542.book Seite 192 Montag, 4. Januar 2010 1:02 13
4
Dialoge, Layout und Qt-Widgets
QLabel Die von QFrame abgeleitete Klasse QLabel wurde ja schon bei den ersten Qt-Programmen verwendet und dient dazu, einen Text oder ein Bild anzuzeigen ohne jede Anwender-Interaktionen. In dieser Klasse können Sie folgende Typen »stecken«: 왘
Einfacher Text
왘
Rich-Text (RTF)
왘
Pixmap
왘
Movie (Film)
왘
Eine Zahl
Sie sehen: In QLabel steckt also ein wenig mehr als nur ein langweiliges TextWidget. Fangen wir daher am besten wieder mit den Methoden von QLabel an: Methode
Beschreibung
QLabel ( QWidget * parent = 0, Qt::WindowFlags f = 0 );
Erzeugt ein leeres Label.
QLabel ( const QString & text, QWidget * parent = 0, Qt::WindowFlags f = 0 );
Erzeugt ein Label mit dem Text text.
~QLabel ();
Destruktor. Zerstört ein Label.
Qt::Alignment alignment () const; Damit erhalten Sie die Ausrichtung des Labels
zurück. Mögliche Werte wurden bereits in Tabelle 4.8, 4.9 und 4.10 beschrieben. QWidget * buddy () const;
Gibt den Buddy des Labels zurück (siehe setBuddy()).
bool hasScaledContents () const;
Gibt true zurück, wenn das Label skalierbar ist, der Inhalt also den vorhandenen Raum ausfüllt. Wenn diese Option aktiviert ist und das Label ein Pixmap ist, wird Letzterer immer auf dem vorhandenen Raum skaliert.
int margin () const;
Damit erhalten Sie die Breite des Randbegrenzers in Pixel. Dieser Rand ist der Abstand zwischen dem innersten Pixel des Frames und dem äußersten Pixel des Inhalts. Der Standardwert ist 0.
QMovie * movie () const;
Gibt einen Zeiger auf dem Film des Labels zurück. Ist hier keiner, wird 0 zurückgegeben.
Tabelle 4.43
192
Methoden von QLabel
1542.book Seite 193 Montag, 4. Januar 2010 1:02 13
Qt-Widgets
Methode
Beschreibung
bool openExternalLinks () const;
Wird true zurückgegeben, wird der Link automatisch mit QDesktopServices::openUrl() aufgerufen und nicht das Signal anchorClicked() ausgelöst. Ansonsten wird false zurückgegeben.
const QPicture * picture () const;
Gibt einen Zeiger auf ein QPicture-Objekt zurück. Existiert kein solches, wird 0 zurückgegeben.
const QPixmap * pixmap () const;
Damit wird ein Zeiger auf das Pixmap des Labels zurückgegeben. Existiert kein solches Pixmap, wird ein »ungültiges« Pixmap zurückgegeben.
void setAlignment ( Qt::Alignment ) ;
Damit setzen Sie die Ausrichtung des Labels. Mögliche Werte wurden bereits in Tabelle 4.8, 4.9 und 4.10 beschrieben.
void setBuddy ( QWidget * buddy );
Damit setzen Sie den »Kumpel« von einem Label. Dieser Mechanismus ist nur dem Label mit normalem Text vorbehalten. Damit können Sie ein Tastaturkürzel für ein Label einrichten und den Fokus dazu auf das Buddy-Widget richten. Der Text im Label benötigt ein Ampersandzeichen vor einem Buchstaben. Dieser Buchstabe ist dann das Tasturkürzel für den Buddy des Labels. Hierzu sollten Sie sich anschließend das Programmbeispiel ansehen.
void setMargin ( int );
Damit setzen Sie die Breite des Randbegrenzers in Pixel. Dieser Rand ist der Abstand zwischen dem innersten Pixel des Frames und dem äußersten Pixel des Inhalts. Der Stanardwert ist 0.
void setOpenExternalLinks ( bool open );
Mit true wird bei einem Link automatisch DesktopServices::openUrl() aufgerufen und nicht das Signal anchorClicked() ausgelöst.
void setScaledContents ( bool );
Mit true legen Sie fest dass das Label (wenn es sich um eine Grafik oder einen Film handelt) skaliert werden kann, damit es den vorhandenen Platz auffüllen kann. Mit false schalten Sie es ab. Beachten Sie allerdings, dass größer skalierte Grafiken immer gröberkörniger werden.
void setTextFormat ( Qt::TextFormat ) ;
Setzt das Format eines Text-Labels. Mögliche Formate siehe Tabelle 4.44.
void setTextInteractionFlags ( Qt::TextInteractionFlags flags ) ;
Legt fest, wie der Anwender mit einem Text-Label umgehen kann (bspw. Kopieren, ...). Mögliche Werte siehe Tabelle 4.45.
Tabelle 4.43
Methoden von QLabel (Forts.)
193
4.5
1542.book Seite 194 Montag, 4. Januar 2010 1:02 13
4
Dialoge, Layout und Qt-Widgets
Methode
Beschreibung
void setWordWrap ( bool on );
Damit können Sie den Umbruch einer Zeile nach einem Wort einschalten (true). Ist die Zeile bspw. länger als der Frame, in dem sich das Label befindet, wird der Text umbrochen und nicht (Standard) der Frame in die Länge gezogen.
QString text () const;
Gibt den Text des Labels zurück.
Qt::TextFormat textFormat () const ;
Gibt das Format des Text-Labels zurück (mögliche Werte siehe Tabelle 4.44).
Qt::TextInteractionFlags textInteractionFlags () const;
Gibt zurück, wie der Anwender mit einem TextLabel umgehen kann (bspw. Kopieren, ...). Mögliche Werte siehe Tabelle 4.45.
bool wordWrap () const;
Ermittelt, ob der Umbruch von einer überlangen Zeile nach einem Wort eingeschalten (true) ist. Ansonsten wird false zurückgegeben.
Tabelle 4.43
Methoden von QLabel (Forts.)
Hierzu nun die möglichen Werte für das Text-Format eines Labels, die mit der Methode setTextFormat() bzw. textFormat() gesetzt bzw. abgefragt werden können. Folgende enum-Konstanten sind mit Qt::TextFormat definiert: Konstante
Beschreibung
Qt::PlainText
Der Text wird als normaler Text interpretiert.
Qt::RichText
Der Text wird als Rich-Text (RTF) interpretiert.
Qt::AutoText
Der Text wird als Rich-Text angezeigt, wenn Qt::mightBeRichtText() true zurückgibt. Ansonsten wird er als normaler Text interpretiert.
Qt::LogText
Ein spezielles Text-Format, welches normalerweise nur von QTextEdit verwendet wird.
Tabelle 4.44
Mögliche Formate für QLabel
Jetzt noch die möglichen Werte für die enum-Konstante Qt::TextInteractionFlag, mit der Sie festlegen können, wie der Anwender mit dem Text-Label umgehen kann. Alle Werte können mit der Methode setTextInteractionFlags() bzw. textInteractionFlags() gesetzt bzw. abgefragt werden.
194
1542.book Seite 195 Montag, 4. Januar 2010 1:02 13
Qt-Widgets
Konstante
Beschreibung
Qt::NoTextInteraction
Keine Aktionen mit dem Text-Label möglich.
Qt::TextSelectableByMouse
Der Text kann mit der Maus selektiert und via Kontextmenü in die Zwischenablage kopiert werden.
Qt::TextSelectableByKeyboard
Der Text kann mit den Pfeiltasten der Tastatur selektiert werden. Ein Text-Cursor wird ebenfalls angezeigt.
Qt::LinksAccessibleByMouse
Links können hervorgehoben und mit der Maus aktiviert werden.
Qt::LinksAccessibleByKeyboard
Links können den Fokus mit der (ÿ)-Taste erhalten und mit (¢) aktiviert werden.
Qt::TextEditable
Der Text kann verändert werden.
Qt::TextEditorInteraction
Eine Kombination aus: TextSelectableByMouse | TextSelectableByKeyboard | TextEditable
Qt::TextBrowserInteraction
Eine Kombination aus: TextSelectableByMouse | LinksAccessibleByMouse | LinksAccessibleByKeyboard
Tabelle 4.45
Mögliche Aktionen auf einem Text-Label
Hierzu wieder ein Beispiel, das die Klasse QLabel mit möglichst vielen Methoden in der Praxis demonstrieren soll. Für das QMovie-Objekt wurde ein neueres Datenformat mit MNG verwendet. MNG-Format MNG (kurz für Multiple-image Network Graphics) ist ein öffentliches Dateiformat zur Beschreibung animierter Grafikdateien. MNG ist eng verwandt mit dem PNG-Grafikformat. Die Entwickler des MNG-Formates hoffen, dass MNG beginnen wird, GIF für animierte Bilder im World Wide Web zu ersetzen, wie es PNG teilweise bereits für nicht animierte Bilder getan hat.
Wichtig ist auf jeden Fall, dass Sie die Grafikdatei in ein Verzeichnis imageformats legen, welches sich im selben Verzeichnis befindet wie die auszuführende Datei (siehe auch das Beispiel auf der Buch-DVD). In unserem Beispiel wurde eine sich drehende Büroklammer verwendet.
195
4.5
1542.book Seite 196 Montag, 4. Januar 2010 1:02 13
4
Dialoge, Layout und Qt-Widgets
Unterstützte Datenformate für QLabel Welche Formate unterstützt werden, müssen Sie im Verzeichnis plugins/imageformats der Qt-Distribution entnehmen. Darin finden Sie die Librarys bzw. die dazu benötigten DLLs. Hierbei können natürlich jederzeit weitere Formate nachinstalliert werden. Standardmäßig findet man hier gewöhnlich SVG, JPEG und MNG (mitsamt PNG).
Zunächst wieder das Grundgerüst: 00 01 02 03 04 05 06 07 08 09 10
// beispiele/qlabel/mywidget.h #ifndef MYWIDGET_H #define MYWIDGET_H #include #include #include #include #include #include #include #include
11 class MyWidget : public QWidget { 12 Q_OBJECT 13 public: 14 MyWidget(QWidget *parent = 0); 15 QLabel *lab_movie; 16 QLabel *text; 17 QMovie *movie; 18 }; 19 #endif
Jetzt der eigentliche Code: 00 // beispiele/qlabel/mywidget.cpp 01 #include "mywidget.h" 02 #include 03 // neue Widget-Klasse vom eigentlichen Widget ableiten 04 MyWidget::MyWidget( QWidget *parent): QWidget(parent) { 05 // zwei Label erzeugen 06 lab_movie = new QLabel; 07 // Animation laden 08 movie = new QMovie(QString("%1 %2") .arg(QCoreApplication::applicationDirPath()) .arg("/imageformats/bild1.mng"));
196
1542.book Seite 197 Montag, 4. Januar 2010 1:02 13
Qt-Widgets
09 10 11 12 14 15 16
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
// Animation setzen lab_movie->setMovie(movie); // Animation starten (QMovie::start()) movie->start(); lab_movie->setAlignment(Qt::AlignCenter); lab_movie->setScaledContents ( true ); text = new QLabel( "Ein simpler Text, der auch kopiert werden " "kann. Außerdem wurde der Umbruch von Worten " "aktierviert" ); text->setOpenExternalLinks ( true ); text->setAlignment(Qt::AlignCenter); text->setOpenExternalLinks ( true ); text->setTextInteractionFlags( Qt::TextEditorInteraction ); text->setWordWrap(true); text->setMargin(10); // ein Editierfeld der Klasse QLineEdit QLineEdit *name = new QLineEdit(this); // ein Textlabel mit Tastaturkürzel ALT+N QLabel *nameLabel = new QLabel( "[&Name – ALT+N drücken]", this ); // das Editierfeld zum Kumpel von nameLabel erklären // damit wird beim Drücken von ALT+N in das // Editierfeld gesprungen nameLabel->setBuddy(name); // dasselbe mit einem zweiten Editiertfeld und Label // und hier der Tastenkombination ALT+V QLineEdit *vname = new QLineEdit(this); QLabel *vnameLabel = new QLabel( "[&Vorname – ALT+V drücken]:", this ); vnameLabel->setBuddy(vname); // die üblichen Boxen QVBoxLayout *vBox1 = new QVBoxLayout; vBox1->addWidget(lab_movie); QVBoxLayout *vBox2 = new QVBoxLayout; vBox2->addWidget(text); QVBoxLayout *vBox3 = new QVBoxLayout; vBox3->addWidget(name); vBox3->addWidget(nameLabel); vBox3->addWidget(vname); vBox3->addWidget(vnameLabel); // Verpacken QGroupBox *groupBox1 = new QGroupBox( "A MNG-Movie");
197
4.5
1542.book Seite 198 Montag, 4. Januar 2010 1:02 13
4
Dialoge, Layout und Qt-Widgets
48 49 50 51 52 53 54 55 56 57 58 59 60 }
groupBox1->setLayout(vBox1); QGroupBox *groupBox2 = new QGroupBox( "Simple Text"); groupBox2->setLayout(vBox2); QGroupBox *groupBox3 = new QGroupBox( "My Buddy"); groupBox3->setLayout(vBox3); // ... und das Layout setzen QVBoxLayout* layout = new QVBoxLayout; layout->addWidget(groupBox1); layout->addWidget(groupBox2); layout->addWidget(groupBox3); setLayout(layout); setWindowTitle(tr("QLabel"));
Die hier für unser »Buddy«-Beispiel verwendete Klasse QLineEdit erläutern wir auf den nächsten Seiten näher. Nun noch eine Hauptfunktion: 00 // beispiele/qlabel/main.cpp 01 #include 02 #include "mywidget.h" 03 int main(int argc, char *argv[]) { 04 QApplication app(argc, argv); 05 MyWidget* window = new MyWidget; 06 window->show(); 07 return app.exec(); 08 }
Das Programm bei der Ausführung:
Abbildung 4.68
198
QLabel bei der Ausführung
1542.book Seite 199 Montag, 4. Januar 2010 1:02 13
Qt-Widgets
4.5.4
Widgets für die Eingabe
Bei den in Kürze folgenden Klassen kommen wir zu den Widgets, die bei jeder Anwender-Interaktion erforderlich sind, ohne die keine grafische Oberfläche auskommt. QAbstractSlider Die (Basis-)Klasse QAbstractSlider unterstützt einen Integerwert der mit einem Schieberegler in einem bestimmten Bereich ausgewählt werden kann. QAbstractSlider ist die Basisklasse für QSlider, QDial und QScrollBar (siehe Abbildung 4.69).
QWidget
QAbstractSlider
QSlider
QDial
Abbildung 4.69
QScrollBar
Klassenhierarchie von QAbstractSlider
Alle Methoden, Slots und Signale können also für diese davon abgeleiteten Klassen verwendet werden. Hierzu die anschließend z. T. im Beispiel mit QSlider und QDial eingesetzten öffentlichen Methoden. Methode
Beschreibung
QAbstractSlider ( QWidget * parent = 0 );
Erzeugt einen neuen abstrakten Schieberegler. Standard ist der minimale Wert 0 und der maximale Wert 99.
~QAbstractSlider ();
Destruktor. Zerstört einen abstrakten Schieberegler.
bool hasTracking () const;
Wird true (Standard) zurückgegeben, ist das Tracking eingeschaltet. Ist dies der Fall, wir das Signal valueChanged() ausgelöst, sobald der Schieberegler gezogen wird. Ist das Tracking ausgeschaltet (false), wird das Signal valueChanged() ausgelöst, wenn der Schieberegler nach dem Ziehen losgelassen wurde.
bool invertedAppearance () const;
Wird true zurückgegeben, ist die Position des minimalen und maximalen Wertes vertauscht. Standardmäßig wird false zurückgegeben.
Tabelle 4.46
Methoden von QAbstractSlider
199
4.5
1542.book Seite 200 Montag, 4. Januar 2010 1:02 13
4
Dialoge, Layout und Qt-Widgets
Methode
Beschreibung
bool invertedControls () const;
Wir hier true zurückgegeben, ist die Kontrolle der Mausrad- und Tastatur-Events vertauscht. Wird die Pfeil-nach-oben-Taste betätigt, wird der Schieberegler dekrementiert und nicht standardmäßig inkrementiert. Das Gleiche gilt für das Mausrad. Standardmäßig ist diese Eigenschaft auf false gesetzt.
int maximum () const;
Damit wird der maximal mögliche Wert des Schiebereglers zurückgegeben.
int minimum () const;
Damit wird der minimal mögliche Wert des Schiebereglers zurückgegeben.
Qt::Orientation orientation () const ;
Gibt die Orientierung des Schiebereglers zurück. Mögliche Werte sind Qt::Vertical (Standard) oder Qt::Horizontal.
int pageStep () const;
Gibt die Anzahl der Integerwerte zurück, die inkrementiert bzw. dekrementiert werden, wenn der Anwender nicht den Schieberegler mit der Tastatur bzw. Maus zieht, sondern im Bereich der Linie des Schiebereglers klickt.
void setInvertedAppearance ( bool );
Mit true vertauschen Sie die Position der minimalen und maximalen Werte. false bewirkt das Gegenteil.
void setInvertedControls ( bool );
Mit true vertauschen Sie die Kontrolle der Maus und der Tastatur. Wenn man die Pfeil-nach-oben-Taste betätigt, wird der Schieberegler dekrementiert, anstatt standardmäßig inkrementiert zu werden. Das Gleiche gilt für das Mausrad. false bewirkt das Gegenteil.
void setMaximum ( int );
Setzt den maximal möglichen Wert des Schiebereglers.
void setMinimum ( int );
Setzt den minimal möglichen Wert des Schiebereglers.
void setPageStep ( int );
Setzt den Wert zurück, der inkrementiert bzw. dekrementiert wird, wenn der Anwender nicht den Schieberegler mit der Tastatur bzw. Maus zieht, sondern im Bereich der Linie des Schiebereglers klickt.
void setRange ( int min, int max );
Setzt den minimal und maximal möglichen Wertebereich des Schiebereglers.
void setSingleStep ( int );
Setzt die Anzahl des Werts auf dem Schieberegler, der inkrementiert bzw. dekrementiert wird, wenn der Anwender die Pfeil-Tasten verwendet.
void setSliderPosition( int ); Setzt den Schieberegler zum Programmstart auf einen
bestimmten Wert (bzw. Position). Ist der Wert außerhalb eines gültigen Bereichs, wird eben der Wert auf den minimal bzw. maximal möglichen Wert gesetzt. Tabelle 4.46
200
Methoden von QAbstractSlider (Forts.)
1542.book Seite 201 Montag, 4. Januar 2010 1:02 13
Qt-Widgets
Methode
Beschreibung
void setTracking (bool enable);
Mit false schalten Sie das Tracking ab (siehe hasTracking()). Mit true schalten Sie es wieder ein (Standard).
int singleStep () const;
Gibt einen Wert zurück, den der Schieberegler inkrementiert bzw. dekrementiert, wenn der Anwender die Pfeil-Tasten verwendet.
int sliderPosition () const;
Gibt die aktuelle Position des Schiebereglers zurück.
void triggerAction ( SliderAction action );
Damit können Sie eine bestimmte Aktion auf einem Schieberegler ausführen. Mögliche Werte siehe Tabelle 4.49.
int value () const;
Gibt den aktuellen Integerwert des Schiebereglers zurück.
Tabelle 4.46
Methoden von QAbstractSlider (Forts.)
Nun zu den öffentlichen Slots der Basisklasse QAbstractSlider, die auch den davon abgeleiteten Klassen QSlider, QDial und QScrollBar zur Verfügung stehen: Slot
Beschreibung
void setOrientation ( Qt::Orientation );
Setzt die Orientierung des Schiebereglers auf Qt::Vertical (Standard) oder Qt::Horizontal.
void setValue ( int );
Setzt den Wert des Schiebereglers.
Tabelle 4.47
Öffentliche Slots von QAbstractSlider
Öffentliche Signale der Basisklasse QAbstractSlider: Signal
Beschreibung
void actionTriggered ( int action );
Dieses Signal wird ausgelöst, wenn eine bestimmte Aktion des Schiebereglers ausgelöst wurde. Mögliche Aktionen siehe Tabelle 4.49. Beachten Sie, wenn das Signal ausgelöst wurde, dass dies nichts mit dem Wert des Schiebereglers zu tun hat, sondern nur mit einer Aktion des Schiebereglers (siehe auch triggerAction()).
void rangeChanged ( int min, int max );
Dieses Signal wird ausgelöst, wenn der minimale und/oder maximale Wertebereich des Schiebereglers verändert wurde.
Tabelle 4.48
Öffentliche Slots von QAbstractSlider
201
4.5
1542.book Seite 202 Montag, 4. Januar 2010 1:02 13
4
Dialoge, Layout und Qt-Widgets
Signal
Beschreibung
void sliderPressed ();
Signal wird ausgelöst, wenn der Anwender den Schieberegler mit der Maus niederdrückt.
void sliderReleased ();
Signal wird ausgelöst, wenn der Anwender den Schieberegler mit der Maus loslässt.
void valueChanged ( int value ); Signal wird ausgelöst, wenn der Schieberegler betä-
tigt wurde und der Wert sich verändert hat. Tabelle 4.48
Öffentliche Slots von QAbstractSlider (Forts.)
Jetzt noch die möglichen Werte der enum-Konstante SliderAction, die Sie mit der Methode triggerAction() auslösen bzw. die mit dem Signal actionTriggered() aufgefangen werden können. Konstante QAbstractSlider::SliderNoAction QAbstractSlider::SliderSingleStepAdd QAbstractSlider::SliderSingleStepSub QAbstractSlider::SliderPageStepAdd QAbstractSlider::SliderPageStepSub QAbstractSlider::SliderToMinimum QAbstractSlider::SliderToMaximum QAbstractSlider::SliderMove
Tabelle 4.49
Konstanten für QAbstractSlider::SliderAction
QSlider Die von QAbstractSlider abgeleitete Klasse QSlider wird für einen vertikalen oder horizontalen Schieberegler verwendet. Dies ist im Grunde auch das klassische Widget zum Verändern begrenzter Werte. Zusätzlich zu den Methoden der Basisklasse QAbstractSlider bietet QSlider folgende Methoden an: Methode
Beschreibung
QSlider ( QWidget * parent = 0 );
Konstruktor. Erzeugt ein neues QSlider-Objekt.
QSlider ( Qt::Orientation orientation, QWidget * parent = 0 ) ;
Dito, nur kann hierbei die Ausrichtung (Qt::Vertical oder Qt::Horizontal) mit angegeben werden.
~QSlider ();
Destruktor. Zerstört ein QSlider-Objekt.
Tabelle 4.50
202
Methoden der Klasse QSlider
1542.book Seite 203 Montag, 4. Januar 2010 1:02 13
Qt-Widgets
Methode
Beschreibung
void setTickInterval ( int ti );
Damit legen Sie das Intervall der einzelnen Striche fest. Geben Sie hier bspw. 1 an und haben einen Bereich von 0 bis 100, hätten Sie 100 Striche am Schieberegler. Bei einem Wert von 10 hätten Sie nur noch 10 Striche auf der Leiste.
void setTickPosition ( TickPosition position );
Damit legen Sie fest, wie und wo (und ob) Sie die die einzelnen Striche auf der Schiebeleiste festlegen. Mögliche Werte siehe Tabelle 4.51.
int tickInterval () const;
Gibt das Intervall der einzelnen Striche zurück. Standardmäßig wird hierbei 0 zurückgegeben.
TickPosition tickPosition () const;
Gibt die Position der Striche zurück. Mögliche Werte siehe Tabelle 4.51.
Tabelle 4.50
Methoden der Klasse QSlider (Forts.)
Hierzu nun die Werte, um die Position und die Art der einzelnen Striche auf dem Schieberegler festzulegen (setTickPosition() und tickPosition()). Konstante
Beschreibung
QSlider::NoTicks
Keine Striche werden gezeichnet.
QSlider::TicksBothSides
Zeichnet auf beiden Seiten des Schiebereglers Striche.
QSlider::TicksAbove
Zeichnet Striche über dem (horizontalen) Schieberegler.
QSlider::TicksBelow
Zeichnet Striche unter dem (horizontalen) Schieberegler.
QSlider::TicksLeft
Zeichnet Striche links vom (vertikalen) Schieberegler.
QSlider::TicksRight
Zeichet Striche rechts vom (vertikalen) Schieberegler.
Tabelle 4.51
Position und Art der Striche (Ticks) festlegen
Eigene Signale und Slots sind bei der Klasse QSlider nicht definiert und auch nicht nötig, da die Basisklasse QAbstractSlider alles anbietet, was hierfür erforderlich ist. Ein Beispiel zu QSlider, zusammen mit dem nächsten Widget QDial, finden Sie im Anschluss nach der Beschreibung von QDial. QDial Die Klasse QDial ist ein spezielles – wenig bekanntes – Widget (Dial=Skala). Dabei handelt es sich ebenfalls um eine Art Schieberegler, allerdings um einen runden, der einem Tachometer gleicht. Ansonsten ist das Anwendungsgebiet ähnlich wie bei QSlider, nur dass Sie hier wieder von vorne (oder auch von hinten) anfangen können, wenn Sie den Wertebereich über- bzw. unterschreiten.
203
4.5
1542.book Seite 204 Montag, 4. Januar 2010 1:02 13
4
Dialoge, Layout und Qt-Widgets
Neben den Methoden der Basisklasse QAbstractSlider bietet QDial folgende Methoden an: Methode
Beschreibung
QDial(QWidget * parent=0);
Konstruktor. Erzeugt eine neue Skala.
~QDial ();
Destruktor. Zerstört eine Skala.
int notchSize () const;
Gibt die Anzahl der Kerben an der Skala zurück.
Qreal notchTarget () const;
Gibt den Abstand der einzelnen Kerben in Pixel zurück. Der Standardwert lautet 3.7 Pixel.
bool notchesVisible () const;
Gibt true zurück, wenn die Kerben sichtbar sind.
void setNotchTarget( double target );
Setzt den Abstand in Pixeln zwischen den einzelnen Kerben. Der Standardwert lautet 3.7.
bool wrapping () const;
Gibt false zurück, wenn der Platz unten am Ende der Skala mit einem extra leeren Bereich ausgefüllt wird (Standard). Wird true zurückgegeben, ist kein Bereich an dieser Stelle, und die Skala ist durchgehend.
Tabelle 4.52
Methoden von QDial
Die Klasse QDial definiert keine eigenen Signale und verwendet die von der Basisklasse QAbstractSlider abgeleiteten Signale. Hingegen definiert QDial die beiden folgenden Slots: Slot
Beschreibung
void setNotchesVisible ( bool visible );
Damit können Sie die Kerben der Skala mit false abschalten bzw. mit true (Standard) wieder einschalten.
void setWrapping ( bool on );
Mit true wird der per Standard verwendete leere Bereich unten geschlossen, so dass aus dem Tachometer quasi eine Uhr wird. Standardwert ist false.
Tabelle 4.53
Slots von QDial
Hierzu nun ein einfaches Beispiel, welches die Verwendung von QSlider und QDial mit einigen Methoden in der Praxis zeigen soll. Das Grundgerüst: 00 01 02 03
204
// beispiele/qslider/mywidget.h #ifndef MYWIDGET_H #define MYWIDGET_H #include
1542.book Seite 205 Montag, 4. Januar 2010 1:02 13
Qt-Widgets
04 05 06 07 08 09
#include #include #include #include #include #include
10 11 12 13 14 15 16 17 18 19 20 21 22 23
class MyWidget : public QWidget { Q_OBJECT public: MyWidget(QWidget *parent = 0); QLabel *text; QLabel *text2; QPushButton* but1; QSlider* slider; QDial* dial; public slots: void setLabel( int val ); void setLabel2( int val ); }; #endif
Jetzt wieder der eigentliche Code: 00 // beispiele/qslider/mywidget.cpp 01 #include "mywidget.h" 02 #include 03 // neue Widget-Klasse vom eigentlichen Widget ableiten 04 MyWidget::MyWidget( QWidget *parent): QWidget(parent) { 05 text = new QLabel("Aktueller Wert: --"); 06 text->setMargin(10); 07 text->setAlignment(Qt::AlignCenter); 08 09 10
text2 = new QLabel("Aktueller Wert: --"); text2->setMargin(10); text2->setAlignment(Qt::AlignCenter);
11 12 13 14 15
slider = new QSlider(Qt::Horizontal); slider->setTickInterval( 10 ); slider->setRange ( 0, 100 ); slider->setTickPosition(QSlider::TicksAbove); slider->setSliderPosition( 50 );
16
dial = new QDial;
205
4.5
1542.book Seite 206 Montag, 4. Januar 2010 1:02 13
4
Dialoge, Layout und Qt-Widgets
17 18 19 20
dial->setNotchesVisible(true); dial->setRange ( 0, 100 ); dial->setNotchTarget ( 7.4 ); dial->setWrapping( true );
21 22 23 24
// die üblichen Boxen QVBoxLayout *vBox1 = new QVBoxLayout; vBox1->addWidget(text); vBox1->addWidget(slider);
25 26 27
QVBoxLayout *vBox2 = new QVBoxLayout; vBox2->addWidget(text2); vBox2->addWidget(dial);
28 29
// Verpacken QGroupBox *groupBox1 = new QGroupBox( "Ein einfacher Slider"); groupBox1->setLayout(vBox1); QGroupBox *groupBox2 = new QGroupBox( "Ein einfache Skala"); groupBox2->setLayout(vBox2);
30 31 32 33 34
35 36 37 38 39 40 41 }
connect( slider, SIGNAL( valueChanged( int ) ), this, SLOT( setLabel( int ) ) ); connect( dial, SIGNAL( valueChanged( int ) ), this, SLOT( setLabel2( int ) ) ); // ... und das Layout setzen QVBoxLayout* layout = new QVBoxLayout; layout->addWidget(groupBox1); layout->addWidget(groupBox2); setLayout(layout); setWindowTitle(tr("QAbstractSlider – Demo"));
42 void MyWidget::setLabel( int val ) { 43 text->setText(QString("Aktueller Wert: %1").arg(val)); 44 } 45 void MyWidget::setLabel2( int val ) { 46 text2->setText( QString("Aktueller Wert: %1").arg(val) ); 47 }
206
1542.book Seite 207 Montag, 4. Januar 2010 1:02 13
Qt-Widgets
Nun noch eine Hauptfunktion: 00 // beispiele/qslider/main.cpp 01 #include 02 #include "mywidget.h" 03 int main(int argc, char *argv[]) { 04 QApplication app(argc, argv); 05 MyWidget* window = new MyWidget; 06 window->show(); 07 return app.exec(); 08 }
Das Programm bei der Ausführung:
Abbildung 4.70
QSlider und QDial bei der Ausführung
QScrollBar Die Klasse QScrollBar stellt eine vertikale oder horizontale Scroll-Leiste zur Verfügung. Diese Klasse wird gewöhnlich dort eingesetzt, wo ein Widget länger bzw. breiter ist, als dies auf dem Bildschirm dargestellt werden kann. Damit kann sich der Anwender dann zum entsprechenden Bereich navigieren. Sollten Sie ein Scrolling auf ein anderes Widget benötigen, ist es besser, wenn Sie hierfür die (hier nur der Vollständigkeit halber erwähnte) Klasse QScrollArea verwenden. QLineEdit Die von QWidget abgeleitete Klasse QLineEdit ist ein einzeiliges Texteingabefeld. Verglichen mit anderen Bibliotheken für grafische Oberflächen, ist QLineEdit schon eher ein einzeiliger Texteditor.
207
4.5
1542.book Seite 208 Montag, 4. Januar 2010 1:02 13
4
Dialoge, Layout und Qt-Widgets
QLineEdit bietet natürlich auch alle grundlegenden Aktionen, wie das Kopieren,
Ausschneiden, Einfügen und Drag & Drop von normalem Text. Hierzu zunächst eine Zusammenstellung der vielen Methoden, die die Klasse QLineEdit anbietet. Methode
Beschreibung
QLineEdit (QWidget * parent = 0);
Erzeugt ein neues einzeiliges Textfeld.
QLineEdit ( const QString & contents, QWidget * parent = 0 );
Erzeugt ein neues einzeiliges Textfeld, das mit contents vorbelegt ist.
~QLineEdit ()
Destruktor. Zerstört ein Textfeld.
Qt::Alignment alignment () const;
Gibt die aktuelle Ausrichtung des Texts zurück (mögliche Werte wurden bereits in Tabelle 4.8, 4.9 und 4.10 erläutert).
void backspace ();
Wenn kein Text markiert wurde, wird das Zeichen links vom Text-Cursor gelöscht. Sollte ein Text markiert werden, wird alles bis zum Anfang der Markierung (der markierte Text also) gelöscht.
QCompleter* completer () const
Gibt, falls vorhanden, ein Objekt zurück, welches einen oder mehrere Strings enthält, was zur Autovervollständigung des Texts verwendet wird. Siehe setCompleter().
QMenu * createStandardContextMenu ();
Damit können Sie ein eigenes Standard-Kontextmenü erzeugen, das erscheint, wenn der Anwender mit der rechten Maustaste im Textfeld klickt. Siehe auch Klasse QMenu (Abschnitt 5.2.2).
void cursorBackward ( bool mark, int steps = 1 );
Bewegt den Cursor um steps Schritte von der aktuellen Position zurück. Ist mark=true, wird jedes Zeichen außerdem markiert. Mit false kann eine Markierung zurückgenommen werden.
void cursorForward ( bool mark, int steps = 1 );
Bewegt den Cursor um steps Schritte von der aktuellen Position nach vorne. Ist mark=true, wird jedes Zeichen zudem noch markiert. Mit false kann eine Markierung zurückgenommen werden.
int cursorPosition () const;
Gibt die aktuelle Position des Cursors zurück.
int cursorPositionAt ( const QPoint & pos );
Gibt die Cursor-Position vom Punkt pos zurück.
void cursorWordBackward ( bool mark );
Bewegt den Cursor ein Wort zurück. Ist mark zudem true, wird das Wort markiert.
Tabelle 4.54
208
Methoden der Klasse QLineEdit
1542.book Seite 209 Montag, 4. Januar 2010 1:02 13
Qt-Widgets
Methode
Beschreibung
void cursorWordForward ( bool mark );
Bewegt den Cursor ein Wort nach vorne. Ist mark zudem true, wird das Wort markiert.
void del ();
Ist kein Text markiert, wird das Zeichen rechts vom Cursor gelöscht. Ist hingegen ein Text markiert, wird der Text bis zum Anfang des markierten Texts gelöscht.
void deselect ();
Hebt die Markierung eines markierten Texts auf.
QString displayText () const;
Damit wird der angezeigte Text des Textfeldes zurückgegeben.
bool dragEnabled () const;
Gibt diese Methode true zurück, ist das Ziehen (Drag) eines Texts aktiviert. Standardwert ist false.
EchoMode echoMode () const;
Gibt den aktuellen Ausgabemodus des Eingabefeldes zurück. Mögliche Rückgabewerte siehe Tabelle 4.55.
void end ( bool mark );
Bewegt den Text-Cursor ans Ende der Zeile. Ist mark gleich true, wird außerdem der Text von der aktuellen Position bis zum Ende markiert.
bool hasAcceptableInput () const;
Hier wird true zurückgegeben, wenn die Eingabe einer Maske bzw. einem Validator entspricht. Ansonsten wird false zurückgegeben.
bool hasFrame () const;
true wird zurückgegeben (Standardwert), wenn
bool hasSelectedText () const;
Ist ein Text im Textfeld markiert, wird true zurückgegeben. Ansonsten, wenn kein Text markiert ist, wird false zurückgegeben.
void home ( bool mark );
Setzt den Cursor an den Anfang des Textfeldes. Ist mark gleich true, wird zusätzlich der Text von der aktuellen Position bis zum Anfang markiert.
QString inputMask () const;
Hiermit erhalten Sie die gesetzte Eingabemaske zurück. Ist keine solche Maske gesetzt, wird ein leerer String zurückgegeben. Siehe Methode setInputMask().
void insert ( const QString & newText );
Löscht einen markierten Text und fügt einen neuen Text ein.
bool isModified () const;
Gibt true zurück, wenn der Anwender das Textfeld verändert hat. Ansonsten wird false zurückgegeben.
der Text um das Textfeld einen Rahmen besitzt. Ansonsten wird false zurückgegeben.
Tabelle 4.54
Methoden der Klasse QLineEdit (Forts.)
209
4.5
1542.book Seite 210 Montag, 4. Januar 2010 1:02 13
4
Dialoge, Layout und Qt-Widgets
Methode
Beschreibung
bool isReadOnly () const;
Gibt true zurück, wenn das Textfeld nur lesbar ist. Ansonsten wird false zurückgegeben.
bool isRedoAvailable () const;
Gibt true zurück, wenn ein Schritt im Eingabefeld wiederholt werden kann. Ansonsten wird false zurückgegeben.
bool isUndoAvailable () const;
Gibt true zurück, wenn ein zuvor gemachter Schritt im Eingabefeld Rückgängig gemacht werden kann. Ansonsten wird false zurückgegeben.
int maxLength () const;
Gibt die maximal mögliche Länge des Eingabefeldes zurück.
virtual Qsize minimumSizeHint () const;
Gibt die minmale Größe des Textfeldes zurück.
QString selectedText () const;
Gibt den markierten Text zurück. Würde kein Text markiert, wird ein leerer String zurückgegeben.
int selectionStart () const;
Gibt die Anfangsposition des markierten Texts zurück.
void setAlignment ( Qt::Alignment flag );
Setzt die Ausrichtung des Texts auf flag. Hierbei sind allerdings nur horizontale Ausrichtungen möglich. Mögliche Werte wurden bereits in Tabelle 4.8, 4.9 und 4.10 erläutert.
void setCompleter ( QCompleter * c );
Setzt einen Autovervollständigungstext c für das Eingabefeld. Enthält das Objekt QCompleter bspw. Strings wie Auto und Asche, werden beide Wörter angezeigt, sobald der Anwender ein A eingibt (siehe hierzu auch das Programmbeispiel).
void setCursorPosition ( int );
Setzt die Position des Cursors im Textfeld.
void setDragEnabled ( bool b );
Schaltet das Ziehen (Drag) des Texts mit true ein. Mit false wird es wieder ausgeschalten.
void setEchoMode ( EchoMode );
Setzt den Ausgabemodus auf EchoMode. Mögliche Werte siehe Tabelle 4.55.
void setFrame ( bool );
Hiermit kann der Rahmen um das Textfeld mit false ausgeschaltet werden. Standardmäßig ist der Rahmen eingeschaltet (true).
void setInputMask ( const QString & inputMask );
Setzt eine Eingabemaske für das Textfeld. Hierzu können mehrere Maskenzeichen (siehe Tabelle 4.56) verwendet werden. Um eine Maske wieder zu deaktivieren, müssen Sie diese Operation wieder mit einem leeren String aufrufen.
Tabelle 4.54
210
Methoden der Klasse QLineEdit (Forts.)
1542.book Seite 211 Montag, 4. Januar 2010 1:02 13
Qt-Widgets
Methode
Beschreibung
void setMaxLength ( int );
Setzt die maximale Länge des Eingabefeldes.
void setModified ( bool );
Mit true markieren Sie das Feld als verändert. Mit false bewirken Sie das Gegenteil.
void setReadOnly ( bool );
Mit true setzen Sie das Eingabefeld auf nur lesbar. Mit false heben Sie dies wieder auf.
void setSelection ( int start, int length );
Damit markieren Sie einen Text ab der Position start mit length Zeichen.
void setValidator ( const QValidator * v );
Damit legen Sie fest, was in der Zeile eingegeben werden darf bzw. was von dem Eingabefeld akzeptiert wird. Hierzu können Sie bspw. mit QIntValidator, QDoubleValidator, und QRegExpValidator oder gar mit QValidator eigene Validatoren verwenden (siehe hierzu auch das Programmbeispiel).
QString text () const;
Gibt den im Eingabefeld enthaltenen Text zurück.
const QValidator* validator() const;
Gibt einen Zeiger auf den aktuellen Validator zurück. Existiert keiner, wird NULL zurückgegeben.
Tabelle 4.54
Methoden der Klasse QLineEdit (Forts.)
Jetzt zu den möglichen Werten für den enum-Type EchoMode, womit Sie angeben bzw. ermitteln können, wie die Eingabe des Feldes angezeigt wird. Konstante
Beschreibung
QLineEdit::Normal
Der eingegebene Text wird angezeigt. Der Standardwert.
QLineEdit::NoEcho
Die Eingabe wird gar nicht angezeigt. Dies ist bspw. sinnvoll, damit die Länge das Passwortes nicht sichtbar ist.
QLineEdit::Password
Zeigt Sternchen »*« anstatt Zeichen im Eingabefeld.
QLineEdit::PasswordEchoOnEdit
Zeigt Zeichen an, wenn das Eingabefeld editiert wird, ansonsten werden Sternchen angezeigt.
Tabelle 4.55
Mögliche Werte für EchoMode
Jetzt noch zu den möglichen Zeichen der Eingabemaske und wie diese interpretiert werden.
211
4.5
1542.book Seite 212 Montag, 4. Januar 2010 1:02 13
4
Dialoge, Layout und Qt-Widgets
Zeichen
Bedeutung
A
Alphabetische ASCII-Zeichen benötigt (A–Z, a–z).
a
Alphabetische ASCII-Zeichen erlaubt, aber nicht unbedingt erforderlich (A–Z, a–z).
N
Alphabetische ASCII-Zeichen- und Nummern erforderlich (A–Z, a–z, 0–9).
n
Alphabetische ASCII-Zeichen- und Nummern erlaubt (A–Z, a–z, 0–9) aber nicht unbedingt erforderlich.
X
Es werden Zeichen benötigt.
x
Zeichen sind erlaubt, werden aber nicht unbedingt benötigt.
9
ASCII-Nummer (0–9) werden benötigt.
0
ASCII-Nummer (0–9) werden erlaubt, aber nicht unbedingt benötigt.
D
ASCII-Nummer (1–9) werden benötigt.
d
ASCII-Nummer (1–9) sind erlaubt, werden aber nicht unbedingt benötigt.
#
ASCII-Nummer oder Plus/Minus sind erlaubt, werden aber nicht unbedingt benötigt.
H
Hexadezimale Zeichen werden benötigt (A–F, a–f, 0–9).
h
Hexadezimale Zeichen sind erlaubt (A–F, a–f, 0–9), werden aber nicht unbedingt benötigt.
B
Binäre Zeichen werden benötigt (0–1).
b
Binäre Zeichen sind erlaubt (0–1), werden aber nicht unbedingt benötigt.
>
Alle folgenden Buchstaben sind Großbuchstaben.
<
Tabelle 4.56
Alle folgenden Buchstaben sind Kleinbuchstaben. Zeichen, die von der Eingabemaske verstanden werden
Mit folgenden Signalen können Sie mit der Klasse QLineEdit eine Verbindung zu einem Slot aufbauen: Signal
Beschreibung
void cursorPositionChanged ( int old, int new );
Dieses Signal wird ausgelöst, wenn die Cursor-Position im Textfeld verändert wurde. In old finden man die alte und in new die neue Position des Cursors.
void editingFinished ();
Dieses Signal wird ausgelöst, wenn der Anwender die (¢)-Taste gedrückt hat oder das Textfeld den Fokus verliert.
void returnPressed ();
Dieses Signal wird ausgelöst, wenn der Anwender die (¢)-Taste gedrückt hat.
Tabelle 4.57
212
Signale von QLineEdit
1542.book Seite 213 Montag, 4. Januar 2010 1:02 13
Qt-Widgets
Signal
Beschreibung
void selectionChanged ();
Dieses Signal wird aktiv, wenn die Markierung verändert wurde.
void textChanged ( const QString & text )
Wird ausgelöst, wenn der Text verändert wurde. In text befindet sich der neue Text.
void textEdited ( const QString & text );
Dieses Signal wird ausgelöst, wenn der Anwender das Textfeld editiert hat. In text befindet sich der nächste Text.
Tabelle 4.57
Signale von QLineEdit (Forts.)
Folgende öffentliche Slots werden von der Klasse QLineEdit zur Verfügung gestellt: Slot
Beschreibung
void clear ();
Löscht den kompletten Inhalt des Textfeldes.
void copy () const;
Ist ein Text markiert, wird dieser in die Zwischenablage kopiert.
void cut ();
Ist ein Text markiert, wird dieser ausgeschnitten und in die Zwischenablage kopiert.
void paste ();
Befindet sich in der Zwischenablage ein Text, wird dieser in das Textfeld an der aktuellen Cursor-Position eingefügt.
void redo ();
Wiederholt den zuletzt vollzogenen Schritt.
void selectAll ();
Markiert den gesamten Text im Feld.
void setText ( const QString & );
Setzt einen neuen Text im Feld.
void undo ();
Macht den zuvor vollzogenen Schritt rückgängig.
Tabelle 4.58
Slots von QLineEdit
Hierzu wieder ein kurzes Beispiel, welches viele dieser Methoden in der Praxis demonstrieren soll. Natürlich habe ich hierbei auch Masken und Validatoren verwenden. Auch die Autovervollständigung wurde implementiert. Zunächst das Grundgerüst: 00 01 02 03 04 05
// beispiele/qlineedit/mywidget.h #ifndef MYWIDGET_H #define MYWIDGET_H #include #include #include
213
4.5
1542.book Seite 214 Montag, 4. Januar 2010 1:02 13
4
Dialoge, Layout und Qt-Widgets
06 07 08 09
#include #include #include #include
10 class MyWidget : public QWidget { 11 Q_OBJECT 12 public: 13 MyWidget(QWidget *parent = 0); 14 QLabel *text; 15 QLabel *text2; 16 QPushButton* but1; 17 QPushButton* but2; 18 QLineEdit *edit1; 19 QLineEdit *edit2; 20 QRadioButton* radio01; 21 QRadioButton* radio02; 22 QRadioButton* radio03; 23 QRadioButton* radio04; 24 QRadioButton* radio05; 25 QRadioButton* radio06; 26 QRadioButton* radio07; 27 QRadioButton* radio08; 28 QRadioButton* radio09; 29 QRadioButton* radio10; 30 QRadioButton* radio11; 31 QRadioButton* radio12; 32 public slots: 33 void changeEcho( bool ); 34 void readText( ); 35 void setValidMask( bool ); 36 }; 37 #endif
Jetzt die Implementierung des eigentlichen Codes: 00 01 02 03 04 05 06
214
// beispiele/qlineedit/mywidget.cpp #include "mywidget.h" #include #include #include #include #include
1542.book Seite 215 Montag, 4. Januar 2010 1:02 13
Qt-Widgets
07 // neue Widget-Klasse vom eigentlichen Widget ableiten 08 MyWidget::MyWidget( QWidget *parent): QWidget(parent) { 09 edit1 = new QLineEdit; 10 // Autovervollständigung verwenden 11 QStringList wordList; 12 wordList << "Trolltech" << "Wolf" << "Auto" << "Automobil"; 13 QCompleter *completer = new QCompleter(wordList,this); 14 completer->setCaseSensitivity(Qt::CaseInsensitive); 15 edit1->setCompleter(completer); 16 17 18 19 20 21
radio01 = new QRadioButton("Normal"); radio01->setChecked(true); radio02 = new QRadioButton("Password"); radio03 = new QRadioButton("PasswordEchoOnEdit"); radio04 = new QRadioButton("No Echo"); but1 = new QPushButton("Auswerten");
22 23 24 25 26 27 28 29
// die üblichen Boxen QVBoxLayout *vBox1 = new QVBoxLayout; vBox1->addWidget(edit1); vBox1->addWidget(radio01); vBox1->addWidget(radio02); vBox1->addWidget(radio03); vBox1->addWidget(radio04); vBox1->addWidget(but1);
30 31 32 33 34
edit2 = new QLineEdit; edit2->setAlignment(Qt::AlignHCenter); radio05 = new QRadioButton("Integer (Validator)"); // Integer-Validator verwenden edit2->setValidator(new QIntValidator(edit2));
35 36 37
40
radio05->setChecked(true); radio06 = new QRadioButton("Double (Validator)"); radio07 = new QRadioButton( "Regex (Validator) (-9999 bis 9999)" ); radio08 = new QRadioButton( "Datum (Maske) (dd.mm.yyyy)" ); radio09 = new QRadioButton( "IP-Adresse (Maske) (000.000.000.000)" ); radio10 = new QRadioButton("Read-Only");
41 42
QVBoxLayout *vBox2 = new QVBoxLayout; vBox2->addWidget(edit2);
38 39
215
4.5
1542.book Seite 216 Montag, 4. Januar 2010 1:02 13
4
Dialoge, Layout und Qt-Widgets
43 44 45 46 47 48
vBox2->addWidget(radio05); vBox2->addWidget(radio06); vBox2->addWidget(radio07); vBox2->addWidget(radio08); vBox2->addWidget(radio09); vBox2->addWidget(radio10);
49
connect( radio01, SIGNAL( toggled(bool) this, SLOT( changeEcho(bool) ) connect( radio02, SIGNAL( toggled(bool) this, SLOT( changeEcho(bool) ) connect( radio03, SIGNAL( toggled(bool) this, SLOT( changeEcho(bool) ) connect( radio04, SIGNAL( toggled(bool) this, SLOT( changeEcho(bool) ) connect( radio05, SIGNAL( toggled(bool) this, SLOT( setValidMask(bool) connect( radio06, SIGNAL( toggled(bool) this, SLOT( setValidMask(bool) connect( radio07, SIGNAL( toggled(bool) this, SLOT( setValidMask(bool) connect( radio08, SIGNAL( toggled(bool) this, SLOT( setValidMask(bool) connect( radio09, SIGNAL( toggled(bool) this, SLOT( setValidMask(bool) connect( radio10, SIGNAL( toggled(bool) this, SLOT( setValidMask(bool) connect( but1, SIGNAL( clicked() ), this, SLOT( readText() ) );
50 51 52 53 54 55 56 57 58 59
60 61 62 63 64 65 66 67 68 69 70 71 }
216
), ); ), ); ), ); ), ); ), ) ); ), ) ); ), ) ); ), ) ); ), ) ); ), ) );
// Verpacken QGroupBox *groupBox1 = new QGroupBox( "Echo-Demo"); groupBox1->setLayout(vBox1); QGroupBox *groupBox2 = new QGroupBox( "QLineEdit – Demo"); groupBox2->setLayout(vBox2); // ... und das Layout setzen QVBoxLayout* layout = new QVBoxLayout; layout->addWidget(groupBox1); layout->addWidget(groupBox2); setLayout(layout); setWindowTitle(tr("QLineEdit – Demo"));
1542.book Seite 217 Montag, 4. Januar 2010 1:02 13
Qt-Widgets
72 // Textausgaben setzen 73 void MyWidget::changeEcho( bool b ) { 74 if( radio01->isChecked()) 75 edit1->setEchoMode(QLineEdit::Normal); 76 if( radio02->isChecked()) 77 edit1->setEchoMode(QLineEdit::Password); 78 if( radio03->isChecked()) 79 edit1->setEchoMode(QLineEdit::PasswordEchoOnEdit); 78 if( radio04->isChecked()) 79 edit1->setEchoMode(QLineEdit::NoEcho); 80 } 81 // neue Maske bzw. Validator setzen 82 void MyWidget::setValidMask( bool b ) { 83 edit2->setInputMask(""); 84 edit2->setReadOnly(false); 85 if( radio05->isChecked()) 86 edit2->setValidator(new QIntValidator(edit2)); 87 else if( radio06->isChecked()) 88 edit2->setValidator(new QDoubleValidator(edit2)); 89 else if( radio07->isChecked()) { 90 // Integerwert zwischen –9999 und 9999 91 QRegExp rx("-?[1-9]\\d{0,3}"); 92 QValidator *validator = new QRegExpValidator(rx, this); 93 edit2->setValidator(validator); 94 } 95 else if( radio08->isChecked()) 96 edit2->setInputMask("00-00-0000"); 97 else if( radio09->isChecked()) 98 edit2->setInputMask("000.000.000.000;_"); 99 else if( radio10->isChecked()) 100 edit2->setReadOnly(true); 101 } 102 // eingegebenen Text auswerten 103 void MyWidget::readText( ) { 104 QMessageBox::information( this, "Folgender Text wurde eingegeben", edit1->text(), QMessageBox::Ok ); 105 }
217
4.5
1542.book Seite 218 Montag, 4. Januar 2010 1:02 13
4
Dialoge, Layout und Qt-Widgets
Jetzt noch das Hauptprogramm: 00 // beispiele/qlineedit/main.cpp 01 #include 02 #include "mywidget.h" 03 int main(int argc, char *argv[]) { 04 QApplication app(argc, argv); 05 MyWidget* window = new MyWidget; 06 window->show(); 07 return app.exec(); 08 }
Das Programm bei der Ausführung:
Abbildung 4.71
QLineEdit bei der Ausführung
QComboBox Die Klasse QComboBox (abgeleitet von QWidget) ist eine Kombination aus einem Button und einer Liste. Aus dieser Liste kann der Anwender eine Option auswählen, die dann auch angezeigt wird. Dabei nimmt diese Liste relativ wenig Platz ein, weil unbetätigt immer nur ein Element der Liste angezeigt wird. Klickt der Anwender auf diese Box, wird gewöhnlich eine Liste der möglichen auszuwählenden Elemente angezeigt. Dabei ist auch möglich, dass der Anwender den Inhalt einer solchen Box wie bei einem Textfeld editieren kann. Zunächst wieder die Methoden der Klasse QComboBox.
218
1542.book Seite 219 Montag, 4. Januar 2010 1:02 13
Qt-Widgets
Methode
Beschreibung
QComboBox ( QWidget * parent = 0 );
Konstruktor. Erzeugt eine neue leere Combo-Box.
~QComboBox ()
Destruktor. Zerstört eine Combo-Box.
void addItem ( const QString & text, const QVariant & userData = QVariant() );
Fügt ein neues Element ans Ende der Combo-Box mit dem String text und speziellen Daten (userData) hinzu. QVariant ist ein Klasse, ähnlich wie eine Union für die meisten gängigen QtDatentypen.
void addItem ( const QIcon & icon, const QString & text, const QVariant & userData = QVariant() );
Dito, nur wird außerdem ein Icon hinzugefügt.
void addItems ( const QStringList & texts );
Fügt mit einem Mal eine ganze Stringlsite der Combo-Box hinzu.
QCompleter* completer () const;
Gibt, sofern vorhanden, ein Objekt zurück, welches einen oder mehrere Strings enthält, was zur Autovervollständigung des Texts verwendet wird. Siehe setCompleter().
int count () const;
Gibt die Anzahl der Elemente der Combo-Box zurück.
int currentIndex () const;
Gibt die Indexnummer des aktuell aktiven Elements zurück.
QString currentText () const;
Gibt den String des aktuell aktiven Elements zurück.
bool duplicatesEnabled () const;
Wird true zurückgegeben, sind auch doppelte Elemente in der Liste möglich. Bei false gibt es keine doppelten Elemente.
int findData ( const QVariant & data, int role = Qt::UserRole, Qt::MatchFlags flags = Qt::MatchExactly | Qt::MatchCaseSensitive ) const;
Gibt die Indexnummer des Elements zurück, die den Daten data und der Funktion role entspricht. Gibt es kein solches Element, wird –1 zurückgegeben. Die Flags legen fest, wie nach den Elementen in der Combo-Box gesucht wird.
int findText ( const QString & text, Qt::MatchFlags flags = Qt::MatchExactly | Qt::MatchCaseSensitive ) const;
Gibt die Indexnummer des Elements mit dem String text zurück. Gibt es kein solches Element, wird –1 zurückgegeben. Die Flags legen fest, wie nach den Elementen in der Combo-Box gesucht wird.
Tabelle 4.59
Methoden der Klasse QComboBox
219
4.5
1542.book Seite 220 Montag, 4. Januar 2010 1:02 13
4
Dialoge, Layout und Qt-Widgets
Methode
Beschreibung
bool hasFrame () const;
Gibt true zurück, wenn die Combo-Box einen Rahmen hat (Standard). Ansonsten wird false zurückgegeben.
virtual void hidePopup ();
Versteckt die Liste der Elemente, wenn diese gerade sichtbar sind. Ansonsten hat diese Methode keinen Effekt.
QSize iconSize () const;
Damit wird die Größe des angezeigten Icons in der Combo-Box zurückgegeben.
void insertItem ( int index, const QString & text, const QVariant & userData = QVariant() );
Fügt ein neues Elemement hinter der Position index in der Combo-Box mit dem String text und den Daten (userData) hinzu. QVariant ist eine Klasse, ähnlich wie eine Union für die meisten gängigen Qt-Datentypen.
void insertItem ( int index, const QIcon & icon, const QString & text, const QVariant & userData = QVariant() );
Dito, nur wird noch ein Icon hinzugefügt.
void insertItems ( int index, const QStringList & list );
Fügt der Combo-Box in einem Vorgang eine ganze Stringliste (list) hinter der Position index hinzu.
InsertPolicy insertPolicy () const;
Damit erhalten Sie die Art zurück, wie ein neues Element der Liste hinzugefügt wird. Der Standardwert ist AtBottom (also immer hinter dem letzten). Mögliche Werte siehe Tabelle 4.60.
bool isEditable () const;
Gibt true zurück, wenn der String in der Textbox editierbar ist. Standardwert ist false (für »nicht editierbar«).
QVariant itemData ( int index, int role = Qt::UserRole ) const;
Damit erhalten Sie die zusätzlichen Daten (sofern verwendet) des Elements mit dem Index index zurück.
QIcon itemIcon ( int index ) const;
Damit erhalten Sie das Icon des Elements mit dem Index index zurück.
QString itemText ( int index ) const;
Damit erhalten Sie den Text des Elements mit dem Index index zurück.
QLineEdit * lineEdit () const;
Gibt das Texteingabefeld der Klasse QLineEdit (siehe vorigen Abschnitt) zurück. Gibt es kein Texteingabefeld, wird 0 zurückgegeben. Nur editierbare Combo-Boxen haben ein Texteingabefeld.
Tabelle 4.59
220
Methoden der Klasse QComboBox (Forts.)
1542.book Seite 221 Montag, 4. Januar 2010 1:02 13
Qt-Widgets
Methode
Beschreibung
int maxCount () const;
Gibt die maximal erlaubte Anzahl von Elementen der Liste in der Combo-Box zurück.
int maxVisibleItems () const;
Gibt die maximal erlaubten sichtbaren Elemente der Combo-Box zurück.
int minimumContentsLength () const;
Diese Eigenschaft gibt die minimale Anzahl von Zeichen zurück, die ein Element in der Combo-Box enthalten sollte. Der Standardwert ist 0.
void removeItem ( int index );
Entfernt des Element mit dem Index index aus der Liste.
void setCompleter ( QCompleter * completer );
Damit können Sie bei einer editierbaren ComboBox eine Autovervollständigung setzen. Siehe hierzu das Beispiel der Klasse QLineEdit.
void setDuplicatesEnabled ( bool enable );
Damit können Sie setzen, ob Sie doppelte Einträge in der Liste zulassen wollen (mit true). Mit false verbieten Sie doppelte Einträge.
void setEditable ( bool editable );
Mit true setzen Sie das Eingabefeld als editierbar. Mit false bewirken Sie das Gegenteil.
void setFrame ( bool );
Mit false können Sie den Rahmen der ComboBox deaktivieren. Das Gegenteil bewirken Sie mit true (Standard).
void setIconSize ( const QSize & size );
Setzt die Größe des Icons in der Combo-Box auf size.
void setInsertPolicy ( InsertPolicy policy );
Damit setzen Sie, wie ein neues Element zur Liste hinzugefügt wird. Der Standardwert ist AtBottom (also immer hinter dem letzten). Mögliche Werte siehe Tabelle 4.60.
void setItemData ( int index, const QVariant & value, int role = Qt::UserRole );
Damit übergeben Sie dem Element mit dem Index index die Daten value. QVariant ist eine Klasse, ähnlich wie eine Union für die meisten gängigen Qt-Datentypen.
void setItemIcon ( int index, const QIcon & icon );
Damit setzen Sie bei dem Element mit der Nummer index ein Icon icon.
void setItemText ( int index, const QString & text );
Damit setzen Sie bei dem Element mit der Nummer index den String auf text.
void setLineEdit ( QLineEdit * edit );
Damit setzen Sie ein Textfeld der Klasse QLineEdit für ein Element in der Liste in der Combo-Box.
void setMaxCount ( int max );
Setzt die maximal erlaubte Anzahl von Elementen der Liste in der Combo-Box auf max.
Tabelle 4.59
Methoden der Klasse QComboBox (Forts.)
221
4.5
1542.book Seite 222 Montag, 4. Januar 2010 1:02 13
4
Dialoge, Layout und Qt-Widgets
Methode
Beschreibung
void setMaxVisibleItems ( int maxItems );
Setzt die maximal erlaubten sichtbaren Elemente der Combo-Box.
void setMinimumContentsLength ( int characters );
Setzt die minimale Anzahl von Zeichen auch character, die ein Element in der Combo-Box enthalten muss. Der Standardwert ist 0.
void setSizeAdjustPolicy ( SizeAdjustPolicy policy );
Damit legen Sie fest, wie und wann die Größe (bzw. Breite) der Combo-Box angepasst wird, wenn der Inhalt verändert wird. Mögliche Werte siehe Tabelle 4.61. Standardmäßig wird diese Größe angepasst beim ersten Anzeigen der Combo-Box.
void setValidator ( const QValidator* validator );
Damit legen Sie fest, was in der Zeile eingegeben werden darf bzw. was von dem Eingabefeld akzeptiert wird. Hierzu können Sie bspw. mit QIntValidator, QDoubleValidator, und QRegExpValidator oder gar mit QValidator eigene Validatoren verwenden (ein Beispiel dazu wurde bereits mit der Klasse QLineEdit demonstriert).
virtual void showPopup ();
Zeigt die Liste mit den Elementen der Combo-Box an. Ist die Liste leer, werden keine Elemente angezeigt.
SizeAdjustPolicy sizeAdjustPolicy () const;
Gibt zurück, wie und wann die Größe (bzw. Breite) der Combo-Box angepasst wird, wenn der Inhalt verändert wird. Mögliche Werte siehe Tabelle 4.61. Standardmäßig wird diese Größe beim ersten Anzeigen der Combo-Box angepasst.
const QValidator* validator () const;
Gibt einen Zeiger auf den aktuellen Validator zurück. Existiert keiner, wird NULL zurückgegeben.
Tabelle 4.59
Methoden der Klasse QComboBox (Forts.)
Wie neue Elemente ggf. zur Liste der Combo-Box hinzugefügt werden, wird mit der enum-Konstante QComboBox::InsertPolicy angegeben (und mit der Methode setInsertPolicy() gesetzt. Konstante
Beschreibung
QComboBox::NoInsert
Der String wird nicht zur Combo-Box hinzugefügt.
QComboBox::InsertAtTop
Der String wird an erster Position hinzugefügt.
QComboBox::InsertAtCurrent
Der aktuelle String wird durch den neuen String ersetzt.
Tabelle 4.60
222
Wie man neue Elemente der Liste hinzufügt
1542.book Seite 223 Montag, 4. Januar 2010 1:02 13
Qt-Widgets
Konstante
Beschreibung
QComboBox::InsertAtBottom
Der String wird am Ende der Combo-Box hinzugefügt (Standard).
QComboBox::InsertAfterCurrent
Der String wird hinter dem aktuellen Element hinzugefügt.
QComboBox::InsertBeforeCurrent
Der String wird vor dem aktuellen Element hinzugefügt.
QComboBox::InsertAlphabetically
Der String wird alphabetisch sortiert hinzugefügt.
Tabelle 4.60
Wie man neue Elemente der Liste hinzufügt (Forts.)
Wie die Größe der Combo-Box angepasst wird, wenn sich der Inhalt verändert, wird mit der enum-Konstante QComboBox::SizeAdjustPolicy angegeben. Konstante
Beschreibung
QComboBox::AdjustToContents
Die Combo-Box wird immer an den Inhalt angepasst.
QComboBox::AdjustToContentsOnFirstShow
Die Combo-Box wird beim ersten Anzeigen angepasst.
QComboBox::AdjustToMinimumContentsLength
Es wird empfohlen, zuvor eine der beiden Konstanten zu verwenden.
Tabelle 4.61
Anpassung der Combo-Box
Um eine Signal-Slot-Verbindung mit QComboBox einrichten zu können, sind folgende öffentliche Signale definiert: Signal
Bedeutung
void activated ( int index );
Wenn der Anwender die Combo-Box aktiviert hat, wird dieses Signal gesendet. Der Parameter ist die Nummer des aktivierten Elements.
void activated ( const QString & text );
Dito, nur steht hier im Parameter der String des aktivierten Elements.
void currentIndexChanged ( int index );
Dieses Signal wird gesendet, wenn der Anwender das aktuelle Element der Combo-Box geändert hat. Das Signal wird außerdem auch gesendet, wenn das Element programmtechnisch geändert wird. Im Parameter befindet sich die Nummer des Elements oder –1, wenn die Combo-Box ein leeres Element enthält oder zurückgesetzt (Reset) wurde.
Tabelle 4.62
Signale von QComboBox
223
4.5
1542.book Seite 224 Montag, 4. Januar 2010 1:02 13
4
Dialoge, Layout und Qt-Widgets
Signal
Bedeutung
void currentIndexChanged ( const QString & text );
Dito, nur befindet sich im Parameter der String des Elements.
void editTextChanged ( const QString & text );
Das Signal wird ausgelöst, wenn der Text in der ComboBox geändert wurde. Im Parameter befindet sich der neue Text.
void highlighted ( int index ); Dieses Signal wird gesendet, wenn ein Element in der
Combo-Box vom Anwender hervorgehoben (highlighted) wurde. Im Parameter befindet sich die Nummer des Elements. void highlighted ( const QString & text )
Tabelle 4.62
Dito, nur befindet sich im Parameter der String des hervorgehobenen Elements.
Signale von QComboBox (Forts.)
Folgende öffentliche Slots finden Sie außerdem in Klasse QComboBox definiert: Slot
Beschreibung
void clear ();
Entfernt alle Elemente einer Combo-Box.
void clearEditText ();
Entfernt den Inhalt der Zeile, um den Text in der Combo-Box zu editieren.
void setCurrentIndex ( int index );
Setzt das Element mit der Nummer index als das aktive Element in der Combo-Box.
void setEditText ( const QString & text );
Setzt das Element mit dem String »text« als aktives Element in der Combo-Box.
Tabelle 4.63
Öffentliche Slots von QComboBox
Jetzt zu einem einfachen Beispiel mit der Klasse QComboBox. Hier werden zwei Combo-Boxen verwendet. Das eine enthält Elemente mit Icons, Text und Daten vom Typ QVariant. Beim Auswählen eines Elements in der Liste werden natürlich die Daten von QVariant ausgewertet. Bei der zweiten Combo-Box können Sie neue Elemente hinzufügen. Diese Elemente werden alphabetisch sortiert. Zunächst wieder das Gründgerüst: 00 01 02 03 04 05 06 07
224
// beispiel/qcombobox/mywidget.h #ifndef MYWIDGET_H #define MYWIDGET_H #include #include #include #include #include
1542.book Seite 225 Montag, 4. Januar 2010 1:02 13
Qt-Widgets
08 #include 09 #include 10 class MyWidget : public QWidget { 11 Q_OBJECT 12 public: 13 MyWidget(QWidget *parent = 0); 14 QComboBox *combo1; 15 QComboBox *combo2; 16 QPushButton* but1; 17 public slots: 18 void checkCombo( int ); 19 void addToCombo(); 20 }; 21 #endif
Jetzt zum aktiven Code: 00 01 02 03 04 05 06
// beispiele/qcombobox/mywidget.cpp #include "mywidget.h" #include #include #include #include #include
07 // neue Widget-Klasse vom eigentlichen Widget ableiten 08 MyWidget::MyWidget( QWidget *parent): QWidget(parent) { 09 combo1 = new QComboBox; 10 combo1->setSizeAdjustPolicy( QComboBox::AdjustToContents ); 11 // ein paar Daten 12 QStringList wordList; 13 wordList << "Data 1" << "Data 2" << "Data 3" << "Data 4"; 14 QVariant data(wordList); 15 combo1->addItem( " --- Bitte auswählen --- "); 16 combo1->addItem( QIcon("images/icon1.png"), "String-Liste", data ); 17 combo1->addItem( QIcon("images/icon2.png"), "Programmstart", QVariant(QTime::currentTime()) ); 18 combo1->addItem( QIcon("images/icon3.png"), "Datum", QVariant(QDate::currentDate () ) ); 19 // die üblichen Boxen 20 QVBoxLayout *vBox1 = new QVBoxLayout;
225
4.5
1542.book Seite 226 Montag, 4. Januar 2010 1:02 13
4
Dialoge, Layout und Qt-Widgets
21
vBox1->addWidget(combo1);
22 23 24 25 26 27
combo2 = new QComboBox; // Elemente editierbar combo2->setEditable( true ); // Elemente alphabetisch hinzufügen combo2->setInsertPolicy( QComboBox::InsertAlphabetically ); but1 = new QPushButton("Hinzufügen");
28 29 30
QVBoxLayout *vBox2 = new QVBoxLayout; vBox2->addWidget(combo2); vBox2->addWidget(but1);
31 32 33 34 35 36 37
38 39 40 41 42 43 44 }
// Verpacken QGroupBox *groupBox1 = new QGroupBox( "Combo-Box 1 mit Daten (QVariant)" ); groupBox1->setLayout(vBox1); QGroupBox *groupBox2 = new QGroupBox( "Combo-Box 2"); groupBox2->setLayout(vBox2); connect( combo1, SIGNAL( activated ( int ) ), this, SLOT( checkCombo( int ) ) ); connect( but1, SIGNAL( clicked() ), this, SLOT( addToCombo() ) ); // ... und das Layout setzen QVBoxLayout* layout = new QVBoxLayout; layout->addWidget(groupBox1); layout->addWidget(groupBox2); setLayout(layout); setWindowTitle(tr("QComboBox – Demo"));
45 // Combo-Box auswerten 46 void MyWidget::checkCombo( int var ) { 47 // Daten holen 48 QVariant data = combo1->itemData(var); 49 // Sind Daten vorhanden? 50 if( data != QVariant::Invalid ) { 51 // Datentyp ermitteln 52 switch( data.type() ) { 53 case QVariant::StringList: { 54 QStringList list = data.toStringList();
226
1542.book Seite 227 Montag, 4. Januar 2010 1:02 13
Qt-Widgets
55 56 57
// String-List splitten QString msg = list.join("\n"); QMessageBox::information( this, "Folgende Daten sind enthalten", msg, QMessageBox::Ok );
58 59 60 61 62 63 64
} break; case QVariant::Time: { QTime time = data.toTime(); // String daraus machen QString msg = time.toString("hh:mm:ss"); QMessageBox::information( this, "Uhrzeit des Programmstarts", msg, QMessageBox::Ok ); } break; case QVariant::Date: { QDate date = data.toDate(); // String daraus machen QString msg = date.toString( "Datum: (ddd) dd.MMM.20yy"); QMessageBox::information( this, "Datum des Programmstarts", msg, QMessageBox::Ok ); } break;
65 66 67 68 69 70 71
72 73 74 75 76 }
} }
77 void MyWidget::addToCombo() { 78 // eingegeben Text abholen 79 QString select = combo2->currentText(); 80 // wurde was eingegeben 81 if( ! select.isEmpty() ) 82 // Ist der Text bereits vorhanden 83 if( combo2->findText( select ) == –1 ) { 84 // Text zur Combobox hinzufügen 85 combo2->addItem(select); 86 } 87 }
227
4.5
1542.book Seite 228 Montag, 4. Januar 2010 1:02 13
4
Dialoge, Layout und Qt-Widgets
Jetzt benötigen wir nur noch eine Hauptfunktion: 00 // beispiele/qcombobox/main.cpp 01 #include 02 #include "mywidget.h" 03 int main(int argc, char *argv[]) { 04 QApplication app(argc, argv); 05 MyWidget* window = new MyWidget; 06 window->show(); 07 return app.exec(); 08 }
Das Programm bei der Ausführung:
Abbildung 4.72
Die Klasse QComboBox bei der Ausführung
Abbildung 4.73
Element »Datum« in Combo-Box 1 ausgewählt
QFontComboBox Die von QComboBox abgeleitete Klasse QFontComboBox ist eine weitere ComboBox, in der der Anwender eine Schriftart auswählen kann. Die Schriftartenliste ist alphabetisch sortiert (bspw. Arial, Courier, Helvetica, Times New Roman usw.). Gewöhnlich findet man diese Combobox in einer Werkzeugleiste mit Buttons bei einem Texteditor, dazu meist drei weitere Buttons, um die Schrift zu unterstrichen und fett oder kursiv anzuzeigen. QAbstractSpinBox Die von QWidget abgeleitete Klasse QAbstractSpinBox ist eine Mischung aus einem Textfeld und Button mit Pfeilen (oder Plus/Minus), womit Werte ange-
228
1542.book Seite 229 Montag, 4. Januar 2010 1:02 13
Qt-Widgets
zeigt und verändert werden können. Die Werte können hierbei wie bei einem gewöhnlichen Textfeld, oder eben über die Buttons verändert werden. Die Klasse QAbstractSpinBox ist die Superklasse für die davon abgeleiteten Klassen QSpinBox, QDateTimeEdit und QDoubleSpinBox. Die Klasse QDateTimeEdit wieder besitzt mit QDateEdit und QTimeEdit zwei weitere Unterklassen, die wiederum davon abgeleitet wurden (siehe Abbildung 4.74).
QAbstractSpinBox
QSpinBox
QDateTimeEdit
QDateEdit
Abbildung 4.74
QDoubleSpinBox
QTimeEdit
Klassenhierarchie von QAbstractSpinBox
Bevor es allerdings wieder was fürs Auge gibt, müssen wir zunächst die Methoden, Signale und Slots der Superklasse QAbstractSpinBox näher betrachten, da diese alle für die davon vererbte Klasse verwendet werden können. Somit stehen alle gleich erwähnten Methoden auch für QSpinBox, QDateTimeEdit (inkl. QDateEdit und QTimeEdit) und QDoubleSpinBox zur Verfügung. Hierzu nun die öffentlichen Methoden von QAbstractSpinBox: Methode
Beschreibung
QAbstractSpinBox ( QWidget * parent = 0 );
Konstruktor. Erzeugt eine neue Spinbox mit parent als Eltern-Widget.
~QAbstractSpinBox ();
Destruktor. Zerstört eine Spinbox.
Qt::Alignment alignment () const; Gibt die aktuelle Ausrichtung der Spinbox zurück. Mögliche Werte sind hierbei nur Qt::AlignLeft, Qt::AlignRight und Qt::AlignHCenter (siehe
Tabelle 4.8, 4.9 und 4.10). Der Standardwert lautet Qt::AlignLeft. ButtonSymbols buttonSymbols () const;
Tabelle 4.64
Gibt das gesetzte Symbol der Spin-Buttons zurück. Standardmäßig zeigen die Pfeile nach oben bzw. unten. Mögliche zurückgegebene Werte können Sie Tabelle 4.65 entnehmen.
Öffentliche Methoden der Klasse QAbstractSpinBox
229
4.5
1542.book Seite 230 Montag, 4. Januar 2010 1:02 13
4
Dialoge, Layout und Qt-Widgets
Methode
Beschreibung
CorrectionMode correctionMode () const;
Damit erhalten Sie den Modus zurück, wie der fortlaufende Zwischenwert angepasst wird, wenn das Editieren abgeschlossen ist. Mögliche Werte siehe Tabelle 4.66.
virtual void fixup ( QString & input ) const;
Diese virtuelle Methode wird von QAbstractSpinBox aufgerufen, wenn die Eingabe ungültig ist; QValidator::Acceptable, wenn der Anwender (¢) gedrückt oder die Methode interpretText() aufgerufen hat.
bool hasAcceptableInput () const; Gibt true zurück, wenn die Eingabe mit dem aktu-
ellen Validator übereinstimmt. Ansonsten wird false zurückgegeben. bool hasFrame () const;
Gibt true zurück, wenn die Spinbox einen Rahmen hat (Standard). Ansonsten wird false zurückgegeben.
void interpretText ();
Diese Methode interpretiert den Text der Spinbox. Wurde der Wert seit der letzten Interpretation verändert, wird ein Signal ausgelöst.
bool isAccelerated () const;
Diese Methode gibt true zurück, wenn die Frequenz der einzelnen Schritte beschleunigt (bzw. inkrementiert, dekrementiert), wenn die Buttons der Spinbox länger gedrückt gehalten werden; standardmäßig ist sie nicht aktiviert (false).
bool isReadOnly () const;
Diese Methode gibt true zurück, wenn der Text in der Spinbox nur lesbar ist. Es wird hierbei kein Cursor im Textfeld von QAbstractSpinBox angezeigt. Außerdem funktioniert das Kopieren und Drag & Drop von Text.
void setAccelerated ( bool on );
Mit true beschleunigen (bzw. inkrementieren, dekrementieren) Sie die Frequenz der einzelnen Schritte, wenn die Buttons der Spinbox länger gedrückt werden; standardmäßig nicht aktiviert (false).
void setAlignment ( Qt::Alignment flag );
Setzt die aktuelle Ausrichtung der Spinbox auf flag. Mögliche Werte sind hierbei nur Qt::AlignLeft, Qt::AlignRight und Qt::AlignHCenter (siehe Tabelle 4.8, 4.9 und 4.10). Werden ungültige Werte verwendet bzw. kombiniert, hat dies überhaupt keinen Effekt.
void setButtonSymbols ( ButtonSymbols bs );
Setzt das Symbol der Spin-Buttons auf bs. Mögliche Werte siehe Tabelle 4.65.
Tabelle 4.64
230
Öffentliche Methoden der Klasse QAbstractSpinBox (Forts.)
1542.book Seite 231 Montag, 4. Januar 2010 1:02 13
Qt-Widgets
Methode
Beschreibung
void setCorrectionMode ( CorrectionMode cm );
Damit setzen Sie den Modus, wie der fortlaufende Zwischenwert angepasst wird, wenn der Editiervorgang abgeschlossen ist, auf cm. Mögliche Werte siehe Tabelle 4.66.
void setFrame ( bool );
Mit false können Sie den Rahmen um die Spinbox abschalten. Einschalten lässt sich diese wieder mit true.
void setReadOnly ( bool r );
Mit true ist der Text in der Spinbox nur noch lesund nicht mehr editierbar. Außerdem funktioniert trotzdem das Kopieren und Drag & Drop von Text. Mit false stellen Sie den Standard wieder her.
void setSpecialValueText ( const QString & txt );
Damit können Sie einen Text setzen, der angezeigt wird, wenn der aktuelle Wert gleich minimum() ist.
void setWrapping ( bool w );
Standardmäßig ist dieser Wert false. Verwenden Sie hierbei true, fährt der aktuelle Wert, wenn dieser maximum() erreicht hat, mit minimum() fort. Umgekehrt dasselbe; standardmäßig ist ja bei maximum() bzw. minimum() Schluss.
QString specialValueText () const;
Gibt den Text zurück, der angezeigt, wird, wenn der aktuelle Wert gleich minimum() ist.
QString text () const;
Damit erhalten Sie den kompletten Text in der Spinbox (mitsamt Suffix und Präfix).
bool wrapping () const;
Gibt true zurück, wenn das »wrapping« eingeschaltet ist (siehe setWrapping()). Ansonsten wird false zurückgegeben.
Tabelle 4.64
Öffentliche Methoden der Klasse QAbstractSpinBox (Forts.)
Jetzt zu den möglichen enum-Werten für QAbstractSpinBox::ButtonSymbols, womit Sie die Symbole der Buttons auf der Spinbox festlegen bzw. abfragen können. Konstante
Beschreibung
QAbstractSpinBox::UpDownArrows
Der Standardwert, mit dem die üblichen Pfeile auf den Buttons dargestellt werden.
QAbstractSpinBox::PlusMinus
Damit werden die Plus- und Minus-Symbole auf den Buttons dargestellt.
Tabelle 4.65
Konstanten für die Buttons der Spinbox
231
4.5
1542.book Seite 232 Montag, 4. Januar 2010 1:02 13
4
Dialoge, Layout und Qt-Widgets
Nun zu den enum-Konstanten für QAbstractSpinBox::CorrectionMode, um festzulegen bzw. zu ermitteln, wie der fortlaufende Zwischenwert angepasst wird, wenn man mit dem Editieren fertig ist. Konstante
Beschreibung
QAbstractSpinBox:: CorrectToPreviousValue
Die Spinbox kehrt zum letzten gültigen Wert zurück.
QAbstractSpinBox:: CorrectToNearestValue
Die Spinbox kehrt zum nächsten gültigen Wert zurück.
Tabelle 4.66
Mögliche Werte für QAbstractSpinBox::CorrectionMode
Folgende öffentliche Slots bietet die Klasse QAbstractSpinBox für ihre Unterklassen an: Slot
Beschreibung
void selectAll ();
Markiert den kompletten Text in der Spinbox; ausgenommen das Präfix und Suffix.
void stepDown ();
Dekrementiert den Wert der Spinbox um –1. Dieser Aufruf ist analog zu stepBy(-1).
void stepUp ();
Inkrementiert den Wert der Spinbox um 1. Dieser Aufruf entspricht stepBy(1).
Tabelle 4.67
Öffentliche Slots von QAbstractSpinBox
An Signalen bietet die Basisklasse QAbstractSpinBox mit editingFinished() nur eines an. Es wird ausgelöst, wenn die Spinbox den Fokus verliert oder die (¢)-Taste gedrückt wurde. QSpinBox Die Klasse QSpinBox ist die klassische Integerversion der Spinbox. Sollten Sie einen Gleitpunktwert benötigen, können Sie stattdessen die Klasse QDoubleSpinBox verwenden. Die Klasse QSpinBox bietet neben den von QAbstractSpinBox vererbten Methoden noch folgende Methoden an: Methode
Beschreibung
QSpinBox ( QWidget * parent = 0 );
Erzeugt eine neue Spinbox mit parent als ElternWidget.
Tabelle 4.68
232
Methoden der Klasse QSpinBox
1542.book Seite 233 Montag, 4. Januar 2010 1:02 13
Qt-Widgets
Methode
Beschreibung
QString cleanText () const;
Damit erhalten Sie den reinen Wert als String der Spinbox. Ohne Präfix, Suffix oder irgendwelche Whitespace-Zeichen.
int maximum () const;
Gibt den maximal möglichen Wert der Spinbox zurück.
int minimum () const;
Gibt den minimal möglichen Wert der Spinbox zurück.
QString prefix () const;
Gibt das Präfix der Spinbox zurück (falls gesetzt).
void setMaximum ( int max );
Setzt den maximal möglichen Wert der Spinbox auf max.
void setMinimum ( int min );
Setzt den minimal möglichen Wert der Spinbox auf min.
void setPrefix ( const QString & prefix );
Setzt das Präfix prefix vor dem aktuellen Wert der Spinbox. Dieses Präfix wird nicht angezeigt, wenn value() == minimum() und specialValueText() gesetzt sind.
void setRange ( int minimum, int maximum );
Damit setzen Sie den minimal und maximal möglichen Wert der Spinbox.
void setSingleStep ( int val ); Mit val setzen Sie den Wert, der inkrementiert bzw.
dekrementiert wird, wenn die Spinbuttons betätigt werden. Standardmäßig ist dieser Wert 1. void setSuffix ( const QString & suffix );
Damit setzen Sie ein Suffix suffix, welches hinter dem aktuellen Wert der Spinbox steht. Das Suffix wird nicht angezeigt, wenn der Wert minimal() ist und specialValueText() gesetzt ist.
int singleStep () const;
Gibt den Wert zurück, der inkrementiert bzw. dekrementiert wird, wenn die Spinbuttons betätigt werden.
QString suffix () const;
Gibt das Suffix zurück (sofern gesetzt).
int value () const;
Gibt den aktuellen Integerwert der Spinbox zurück.
Tabelle 4.68
Methoden der Klasse QSpinBox (Forts.)
Folgende öffentliche Signale bietet QSpinBox: Signal
Beschreibung
void valueChanged ( int i );
Das Signal wird ausgelöst, wenn der Wert der Spinbox verändert wurde. Der neue Wert befindet sich im Integer i.
void valueChanged ( const QString & text );
Dito, nur befindet sich der Wert im String text.
Tabelle 4.69
Signal von QSpinBox
233
4.5
1542.book Seite 234 Montag, 4. Januar 2010 1:02 13
4
Dialoge, Layout und Qt-Widgets
Die Klasse QSpinBox bietet mit setValue(int) nur einen eigenen Slot an, mit dem der aktuelle Wert der Spinbox gesetzt wird. Dieser aufgerufene Slot löst außerdem das Signal valueChanged() aus, wenn der neue Wert sich vom alten unterscheidet. Ein Beispiel zur Klasse QSpinBox finden Sie am Ende des Kapitels zu den Spinboxen (S. 239 ff.). QDoubleSpinBox Die Klasse QDoubleSpinBox entspricht von der Funktion her exakt der Klasse QSpinBox. Selbst die Methoden, Signale und Slots sind die gleichen, wie soeben
am Beispiel der Klasse QSpinBox beschrieben wurde. Einzig, wo eben ein Integer als Wert gesetzt bzw. zurückgegeben wurde, müssen Sie jetzt den Typ double verwenden. Daher erspare ich mir einige Seiten Text und verweise Sie auf den Abschnitt über die Klasse QSpinBox (nur eben mit double als Datentyp). Zwei weitere Methoden, die für Gleitpunktzahlen nötig sind (Angabe der Genauigkeit nach dem Komma), gibt es dann doch (Tabelle 4.70): Methode
Beschreibung
int decimals () const;
Damit erhalten Sie die Genauigkeit der DoubleSpinbox. Standardmäßig sind zwei dezimale Zahlen hinter dem Komma gesetzt.
void setDecimals ( int prec );
Damit setzen Sie die Genauigkeit der DoubleSpinbox auf prec. Der gültige Bereich ist hier von 0 bis 13.
Tabelle 4.70
In QSpinBox nicht vorhandene Methoden
QDateTimeEdit (mit QTimeEdit und QDateEdit) Die Klasse QDateTimeEdit ist die Spinbox-Version, mit der Sie Datum und Uhrzeit auswählen können. Hiermit ist auch alles möglich, was man von Spinboxen her kennt. Datum und Uhrzeit lassen sich kopieren, ziehen (Drag) und natürlich ggf. auch editieren. Selbstverständlich können Sie herbei auch das gewünschte Format von Datum und/oder Uhrzeit vorgeben. Die Spinbox von Datum und Uhrzeit gibt es in zwei Versionen: in der klassischen (siehe Abbildung 4.75) und in der Popup-Version (siehe Abbildung 4.76).
Abbildung 4.75
234
Klassische Auswahl von Datum und Uhrzeit
1542.book Seite 235 Montag, 4. Januar 2010 1:02 13
Qt-Widgets
Abbildung 4.76
Auswahl per Popup-Fenster von Datum und Uhrzeit
Jetzt zu den Methoden der Klasse QDateTimeEdit, wovon entsprechende auch in der davon abgeleiteten Klasse QTimeEdit und QDateEdit verwendet werden können. Methoden
Beschreibung
QDateTimeEdit ( QWidget * parent = 0 );
Erzeugt eine leere Datums-Uhrzeit-Spinbox mit parent als Eltern-Widget.
QDateTimeEdit ( const QDateTime & datetime, QWidget * parent = 0 );
Erzeugt eine Datums-Uhrzeit-Spinbox mit parent als Eltern-Widget. Der Wert wird auf datetime gesetzt.
QDateTimeEdit ( const QDate & date, QWidget * parent = 0 );
Erzeugt eine Datums-Uhrzeit-Spinbox mit parent als Eltern-Widget. Der Wert wird auf date gesetzt.
QDateTimeEdit ( const QTime & time, QWidget * parent = 0 );
Erzeugt eine Datums-Uhrzeit-Spinbox mit parent als Eltern-Widget. Der Wert wird auf time gesetzt.
bool calendarPopup () const;
Gibt diese Methode true zurück; anstelle der standardmäßig klassischen Datums-Uhrzeit-Spinbox (Abbildung 4.75) wird ein Popup-Fenster (siehe Abbildung 4.76) angezeigt, wo das Datum ausgewählt werden kann.
void clearMaximumDate ();
Damit löschen Sie (falls gesetzt) das maximal mögliche Datum. Sollte das Datum kein gültiges QDate-Objekt sein, hat diese Methode keinen Effekt.
void clearMaximumTime ();
Damit löschen Sie (falls gesetzt) die maximal mögliche Uhrzeit. Sollte diese Zeit kein gültiges QTime-Objekt sein, hat diese Methode keinen Effekt.
void clearMinimumDate ();
Damit löschen Sie (falls gesetzt) das minimal mögliche Datum. Sollte das Datum kein gültiges QDate-Objekt sein, hat diese Methode keinen Effekt.
void clearMinimumTime ();
Damit löschen Sie (falls gesetzt) die minimal mögliche Uhrzeit. Sollte diese Zeit kein gültiges QTime-Objekt sein, hat diese Methode keinen Effekt.
Tabelle 4.71
Öffentliche Methoden der Klasse QDateTimeEdit
235
4.5
1542.book Seite 236 Montag, 4. Januar 2010 1:02 13
4
Dialoge, Layout und Qt-Widgets
Methoden
Beschreibung
Section currentSection () const;
Damit erhalten Sie den aktuell aktiven Teil des Datums bzw. der Uhrzeit der Spinbox. Mögliche aktive Werte finden Sie in Tabelle 4.72.
QDate date () const;
Gibt das Datum (QDate-Objekt) zurück, welches in QDateTimeEdit gesetzt ist.
QDateTime dateTime () const;
Gibt das Datum und die Uhrzeit (QDateTime-Objekt) zurück, welches in QDateTimeEdit gesetzt ist.
QString displayFormat () const; Damit erhalten Sie das aktuelle Format, wie Datum/
Uhrzeit in der Spinbox angezeigt werden. Sections displayedSections () const;
Damit erhalten Sie das oder die aktuell angezeigte(n) Feld(er) im Datum bzw. der Uhrzeit zurück. Mögliche Werte siehe Tabelle 4.72.
QDate maximumDate () const;
Damit erhalten Sie (falls gesetzt) das maximal mögliche Datum.
QTime maximumTime () const;
Damit erhalten Sie (falls gesetzt) die maximal mögliche Uhrzeit.
QDate minimumDate () const;
Damit erhalten Sie (falls gesetzt) das minimal mögliche Datum.
QTime minimumTime () const;
Damit erhalten Sie (falls gesetzt) die minimal mögliche Uhrzeit.
QString sectionText ( Section section ) const;
Damit erhalten Sie vom Feld section den aktuellen Wert als String zurück. Mögliche Sektionen siehe Tabelle 4.72.
void setCalendarPopup ( bool enable );
Damit schalten Sie mit true das Popup-Fenster für die Auswahl des Datums ein. Für die klassische Ansicht wird false (Standardwert) verwendet.
void setCurrentSection ( Section section );
Damit setzen Sie das Feld section als aktives Feld in der Spinbox. Dieses Feld wird dann inkrementiert bzw. dekrementiert, wenn die Spinbuttons bzw. die Pfeil-nach-oben- bzw. Pfeil-nach-unten-Tasten betätigt wurden. Mögliche Werte für section siehe Tabelle 4.72.
void setDateRange ( const QDate & min, const QDate & max );
Damit setzen Sie den minimalen und maximalen Bereich des Datums.
void setDisplayFormat ( const QString & format );
Damit setzen Sie ein Anzeigformat, wie das Datum und/oder die Uhrzeit in der Spinbox angezeigt werden sollen.
Tabelle 4.71
236
Öffentliche Methoden der Klasse QDateTimeEdit (Forts.)
1542.book Seite 237 Montag, 4. Januar 2010 1:02 13
Qt-Widgets
Methoden
Beschreibung
void setMaximumDate ( const QDate & max );
Damit setzen Sie das maximal mögliche Datum. Ist QDate kein gültiges Objekt, hat diese Methode keinen Effekt.
void setMaximumTime ( const QTime & max )
Damit setzen Sie die maximal mögliche Uhrzeit. Ist QTime kein gültiges Objekt, hat diese Methode keinen Effekt.
void setMinimumDate ( const QDate & min );
Damit setzen Sie das minimal mögliche Datum. Ist QDate kein gültiges Objekt, hat diese Methode keinen Effekt.
void setMinimumTime ( const QTime & min );
Damit setzen Sie die minimal mögliche Uhrzeit. Ist QTime kein gültiges Objekt, hat diese Methode keinen Effekt.
void setSelectedSection ( Section section );
Wie setCurrentSection(), nur wird das Feld zusätzlich markiert.
QTime time () const;
Gibt die in QDateTimeEdit gesetzte Uhrzeit (QTimeObjekt) zurück.
void setTimeRange ( const QTime & min, const QTime & max );
Damit setzen Sie den minimalen und maximalen Bereich der Uhrzeit.
Tabelle 4.71
Öffentliche Methoden der Klasse QDateTimeEdit (Forts.)
Hinweis Mehr zu den Klassen QDate, QTime und QDateTime finden Sie in Abschnitt 6.12.
Jetzt zu den möglichen Werten, womit Sie die einzelnen Felder des Datums und der Uhrzeit abfragen bzw. aktivieren können. Alle Konstanten sind in der enumVariablen QDateTimeEdit::Section definiert: Konstante
Beschreibung
QDateTimeEdit::NoSection
kein Feld
QDateTimeEdit::AmPmSection
AM/PM-Feld
QDateTimeEdit::MSecSection
Millisekunden-Feld
QDateTimeEdit::SecondSection
Sekunden-Feld
QDateTimeEdit::MinuteSection
Minuten-Feld
QDateTimeEdit::HourSection
Stunden-Feld
QDateTimeEdit::DaySection
Tages-Feld
Tabelle 4.72
Konstanten der enum-Variablen QDateTimeEdit::Section
237
4.5
1542.book Seite 238 Montag, 4. Januar 2010 1:02 13
4
Dialoge, Layout und Qt-Widgets
Konstante
Beschreibung
QDateTimeEdit::MonthSection
Monats-Feld
QDateTimeEdit::YearSection
Jahres-Feld
Tabelle 4.72
Konstanten der enum-Variablen QDateTimeEdit::Section (Forts.)
Folgende Signale können von der Klasse QDateTimeEdit ausgelöst werden: Signal
Beschreibung
void dateChanged ( const QDate & date );
Dieses Signal wird ausgelöst, wenn das Datum verändert wurde. Das neue Datum befindet sich dann in date.
void dateTimeChanged ( const QDateTime & datetime );
Das Signal wird ausgelöst, wenn das Datum oder die Uhrzeit verändert wurde. Das aktuelle Datum/Uhrzeit befindet sich in datetime.
void timeChanged ( const QTime & time );
Das Signal wird ausgelöst, wenn die Uhrzeit verändert wurde. Die neue Zeit befindet sich dann in time.
Tabelle 4.73
Signale der Klasse QDateTimeEdit
Neben drei Signalen verfügt die Klasse QDateTimeEdit über drei öffentliche Slots: Slot
Beschreibung
void setDate ( const QDate & date );
Damit setzen Sie das aktuelle Datum auf date.
void setDateTime ( const QDateTime & dateTime );
Damit setzen Sie das aktuelle Datum und Uhrzeit auf dateTime.
void setTime ( const QTime & time );
Damit setzen Sie aktuelle Uhrzeit auf time.
Tabelle 4.74
Slots der Klasse QDateTimeEdit
Die von QDateTimeEdit abgeleiteten Klassen QTimeEdit und QDateEdit haben keine eigenen Methoden, Signale und Slots – diese sind im Grunde gar nicht erforderlich, weil ja die Basisklasse QDateTimeEdit alles zur Verfügung stellt. Im Grunde werden diese beiden Klassen eigentlich nicht benötigt, da alles mit QDateTimeEdit machbar ist (was diese beiden Klassen intern ja beweisen). Die Klasse QDateEdit wird nur für das Datum verwendet und kann somit auch nur mit den entsprechenden Methoden der Basisklasse QDateTimeEdit etwas anfangen. Die Klasse QTimeEdit wiederum ist nur für das Editieren der Zeit verantwortlich und kann somit auch nur mit den entsprechenden Methoden, Slots und Signalen der Basisklasse arbeiten.
238
1542.book Seite 239 Montag, 4. Januar 2010 1:02 13
Qt-Widgets
Programmbeispiel Nun zu einem Programmbeispiel, das die hier vorgestellten Spinbox-Klassen in der Praxis zeigen soll. Zunächst das Grundgerüst: 00 01 02 03 04 05 06 07 08 09
// beispiele/spinbox/mywidget.h #ifndef MYWIDGET_H #define MYWIDGET_H #include #include #include #include #include #include #include
10 class MyWidget : public QWidget { 11 Q_OBJECT 12 public: 13 MyWidget(QWidget *parent = 0); 14 QSpinBox *spin1; 15 QSpinBox *spin2; 16 QDoubleSpinBox *dspin; 17 QDateTimeEdit *dtspin; 18 public slots: 19 void checkSpin( const QString ); 20 void changeDecimal( int decimals ); 21 void setFormatString(const QString &formatString); 22 }; 23 #endif
Es folgt der Hauptteil des Programms: 00 01 02 03 04
// beispiele/spinbox/mywidget.cpp #include "mywidget.h" #include #include #include
05 // neue Widget-Klasse vom eigentlichen Widget ableiten 06 MyWidget::MyWidget( QWidget *parent): QWidget(parent) { 07 // QSpinBox 08 QLabel *spinLabel = new QLabel( "Wert von –100 % bis 100 % auswählen\n" "Bestätigen mit Enter" ); 09 spin1 = new QSpinBox;
239
4.5
1542.book Seite 240 Montag, 4. Januar 2010 1:02 13
4
Dialoge, Layout und Qt-Widgets
10 11 12 13 14 15 16 17
spin1->setRange(-100, 100); spin1->setSingleStep(10); spin1->setValue(0); spin1->setSuffix("%"); spin1->setSpecialValueText("Minimum"); spin1->setButtonSymbols(QAbstractSpinBox::PlusMinus); spin1->setWrapping(true); spin1->setAccelerated ( true );
18
QLabel *spinLabel2 = new QLabel( "Genauigkeit nach dem Komma des" " double-Wertes auswählen" ); spin2 = new QSpinBox; spin2->setRange(0, 13); spin2->setValue(2); // QDoubleSpinBox QLabel *doubleLabel1 = new QLabel( "Einen Wert von 0.0 bis 10.0 auswählen" ); dspin = new QDoubleSpinBox; dspin->setRange(0.0, 10.0); dspin->setSingleStep(0.25); dspin->setValue(5.0); dspin->setSpecialValueText("Kein Wert"); dspin->setSuffix("°"); dspin->setAccelerated ( true );
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
// QDateTimeEdit QLabel *dtlabel = new QLabel( "Bitte ein Datum und eine Zeit auswählen:" ); dtspin = new QDateTimeEdit( QDateTime::currentDateTime( ) ); dtspin->setCalendarPopup ( true );
36 37 38 39 40
QLabel *formatLabel = new QLabel( "Bitte den Formatstring auswählen:" ); QComboBox *formatComboBox = new QComboBox; formatComboBox->addItem("yyyy-MM-dd / hh:mm:ss"); formatComboBox->addItem("hh:mm:ss MM/dd/yyyy"); formatComboBox->addItem("hh:mm:ss dd/MM/yyyy"); formatComboBox->addItem("dd.MMM.yyyy – hh:mm:ss");
41 42 43 44
// die üblichen Boxen QVBoxLayout *vBox1 = new QVBoxLayout; vBox1->addWidget(spinLabel); vBox1->addWidget(spin1);
240
1542.book Seite 241 Montag, 4. Januar 2010 1:02 13
Qt-Widgets
45 46 47 48 49
QVBoxLayout *vBox2 = new QVBoxLayout; vBox2->addWidget(spinLabel2); vBox2->addWidget(spin2); vBox2->addWidget(doubleLabel1); vBox2->addWidget(dspin);
50 51 52 53 54
QVBoxLayout *vBox3 = new QVBoxLayout; vBox3->addWidget(dtlabel); vBox3->addWidget(dtspin); vBox3->addWidget(formatLabel); vBox3->addWidget(formatComboBox);
55 56
// Verpacken QGroupBox *groupBox1 = new QGroupBox( "QSpinBox-Demo" ); groupBox1->setLayout(vBox1); QGroupBox *groupBox2 = new QGroupBox( "QDoubleSpinBox-Demo" ); groupBox2->setLayout(vBox2); QGroupBox *groupBox3 = new QGroupBox( "QDateTimeEdit-Demo" ); groupBox3->setLayout(vBox3);
57 58 59 60 61 62 63 64
connect( spin1, SIGNAL(valueChanged(const QString) ), this, SLOT( checkSpin( const QString ) ) ); connect( spin2, SIGNAL(valueChanged(int)), this, SLOT(changeDecimal(int))); connect( formatComboBox, SIGNAL(activated(const QString&)), this,SLOT(setFormatString(const QString&)));
65 // ... und das Layout setzen 66 QVBoxLayout* layout = new QVBoxLayout; 67 layout->addWidget(groupBox1); 68 layout->addWidget(groupBox2); 69 layout->addWidget(groupBox3); 70 setLayout(layout); 71 setWindowTitle(tr("QAbstractSpinBox – Demo")); 72 } 73 void MyWidget::checkSpin( const QString strVal ) { 72 QString svalue = QString( "Wert in QSpinBox: %1").arg(strVal); 74 QMessageBox::information( this, "Auswertung",
241
4.5
1542.book Seite 242 Montag, 4. Januar 2010 1:02 13
4
Dialoge, Layout und Qt-Widgets
svalue, QMessageBox::Ok ); 75 } 76 void MyWidget::changeDecimal(int decimals) { 77 dspin->setDecimals(decimals); 78 } 79 void MyWidget::setFormatString( const QString &formatString ){ 80 dtspin->setDisplayFormat(formatString); 81 }
Nun nur noch eine Hauptfunktion: 00 // beispiele/spinbox/main.cpp 01 #include 02 #include "mywidget.h" 03 int main(int argc, char *argv[]) { 04 QApplication app(argc, argv); 05 MyWidget* window = new MyWidget; 06 window->show(); 07 return app.exec(); 08 }
Das Programm bei der Ausführung:
Abbildung 4.77
242
QSpinBox, QDoubleSpinBox und QDateTimeEdit
1542.book Seite 243 Montag, 4. Januar 2010 1:02 13
Qt-Widgets
QTextEdit Die Klasse QTextEdit ist ein ziemlich fortgeschrittener Text-Betrachter bzw. -Editor, der das Rich-Text-Format (kurz RFT) mit HTML-Formatierung unterstützt. Auch umfangreiche Texte werden unterstützt (was bei den Text-Widgets anderer GUIs nicht immer selbstverständlich ist). Die HTML-Unterstützung macht die Formatierung des Texts einfach. Hierzu können einfache HTML-Tags verwendet werden. Setzen Sie bspw. einen Text zwischen Text, wird dieser fett dargestellt. Es werden eine Menge Tags wie Bilder, Listen, Tabellen usw. unterstützt. Natürlich können Sie sowohl HTML-Dateien als auch normalen Text laden. Beachten Sie allerdings, dass nur Tags von HTML 3.2 und 4 unterstützt werden. Man sollte die Klasse QTextEdit allerdings nicht als Webbrowser missverstehen. Das Widget will auch kein Ersatz für die klassischen Office-Editoren sein, sondern einfach ein kleiner, schneller und portabler Editor, um Hilfen und kleine Dokumente anzusehen bzw. zu editieren. Des Weiteren wurden in diesen Texteditor auch die üblichen Tasten-Kombinationen zum Editieren des Texts eingebaut. Bspw. kopieren Sie mit (Strg)+(C) einen markierten Text in die Zwischenablage, und mit (Strg)+(V) fügen Sie einen Text aus der Zwischenablage wieder in den Editor ein. Mehr zu QTextEdit Die Klasse QTextEdit werden wir hier wohl etwas unzureichend beschreiben müssen. Der Umfang mitsamt der damit zusammenhängenden Klassen ist einfach zu groß. Hier sollte man sich wieder mithilfe des Qt Assistant herantasten.
Jetzt zu den Methoden der Klasse QTextEdit, wovon eine beachtliche Menge zur Verfügung stehen. Methode
Beschreibung
QTextEdit ( QWidget * parent = 0 );
Erzeugt ein QTextEdit-Objekt mit parent als Eltern-Widget.
QTextEdit ( const QString & text, QWidget * parent = 0 );
Erzeugt ein QTextEdit-Objekt mit parent als Eltern-Widget mit text als Inhalt.
virtual ~QTextEdit ();
Destruktor. Zerstört ein QTextEdit-Objekt.
bool acceptRichText () const;
Wird true (Standardwert) zurückgegeben, kann der Anwender einen Text im RTF-Format (Rich Text Format) einfügen. Bei false ist nur normaler (Plain-)Text erlaubt.
Tabelle 4.75
Methoden der Klasse QTextEdit
243
4.5
1542.book Seite 244 Montag, 4. Januar 2010 1:02 13
4
Dialoge, Layout und Qt-Widgets
Methode
Beschreibung
Qt::Alignment alignment () const ;
Gibt die Text-Ausrichtung des aktuellen Absatzes zurück. Mögliche Werte siehe Tabelle 4.8, 4.9 und 4.10.
QString anchorAt ( const QPoint & pos ) const;
Gibt die Referenz des Ankers an der Position pos zurück oder einen leeren String, wenn kein Anker existiert.
AutoFormatting autoFormatting () const;
Damit erhalten Sie den eingeschalteten Wert, der für das automatische Formatieren gesetzt ist. Standardmäßig ist hierbei kein automatischer Wert (AutoNone) gesetzt. Aktuell wird mit AutoBulletList nur ein Wert unterstützt. Ist dieser Wert gesetzt, wird bei einem Sternchen (*) am Anfang der Zeile und (¢) beim Abschließen der Zeile ein Aufzählungspunkt gesetzt.
bool canPaste () const;
Gibt true zurück, wenn ein Text aus der Zwischenablage in das Textfeld kopiert werden kann. Ansonsten wird false zurückgegeben.
QMenu * createStandardContextMenu ();
Diese Methode erzeugt ein Standard-Kontextmenü, das angezeigt wird, wenn der Anwender mit der rechten Maustaste im Textfeld klickt. Aufgerufen wird es über den Standardhandler contextMenuEvent().
QTextCharFormat currentCharFormat () const;
Gibt Formatierungsinformationen der Klasse QTextCharFormat zurück, die als neuer Text eingefügt werden. Siehe setCurrentCharFormat().
QFont currentFont () const ;
Gibt die aktuell gesetzte Schriftart zurück.
QTextCursor cursorForPosition ( const QPoint & pos ) const;
Gibt einen QTextCursor an der Position pos zurück.
QRect cursorRect ( const QTextCursor & cursor ) const;
Gibt einen rechteckigen Bereich zurück, der den Cursor enthält.
QRect cursorRect () const;
Dito, nur die überladene Version.
int cursorWidth () const;
Gibt die Breite des Cursors in Pixel zurück. Der Standardwert ist 1.
QTextDocument * document () const ;
Gibt einen Zeiger auf das grundlegende Dokument zurück.
QString documentTitle () const;
Damit erhalten Sie den Titel des Dokuments zurück.
void ensureCursorVisible ();
Garantiert, dass der Cursor auch beim Scrollen des Texts beim Editieren sichtbar ist.
Tabelle 4.75
244
Methoden der Klasse QTextEdit (Forts.)
1542.book Seite 245 Montag, 4. Januar 2010 1:02 13
Qt-Widgets
Methode
Beschreibung
QList<ExtraSelection> extraSelections () const;
Gibt die voraussichtlich gesetzten Extra-Markierungen zurück (siehe setExtraSelections()).
bool find ( const QString & exp, QTextDocument::FindFlags options = 0 );
Findet den nächsten String exp mit entsprechender Option option. Wurde der String gefunden wird, true zurückgegeben, der Cursor an entsprechender Position platziert und der gefundene Text markiert. Ansonsten wird false zurückgegeben. Mögliche Flags für die Optionen finden Sie in Tabelle 4.76.
QString fontFamily () const;
Gibt die Schriftart des aktuellen Formates zurück.
bool fontItalic () const;
Gibt true zurück, wenn das aktuelle Format der Schrift eine Kursivschrift ist. Ansonsten wird false zurückgegeben.
qreal fontPointSize () const;
Gibt die Punktgröße des aktuellen Formats der Schrift zurück.
bool fontUnderline () const;
Gibt true zurück, wenn das aktuelle Format der Schrift eine unterstrichene ist. Ansonsten wird false zurückgegeben.
int fontWeight () const;
Gibt die Schriftgröße des aktuellen Formats zurück.
bool isReadOnly () const;
Gibt true zurück, wenn der Text im Feld nur lesbar ist. Ansonsten wird false zurückgegeben.
bool isUndoRedoEnabled () const;
Gibt true zurück, wenn dem Anwender Wiederherstellen und Rückgängig zur Verfügung stehen. Ansonsten wird false zurückgegeben.
int lineWrapColumnOrWidth () const;
Damit erhalten Sie die Position zurück wo der Text am Zeilenende umbrochen wird. Ob es sich hierbei um Pixel oder um Spalten handelt, hängt vom gesetzten Modus (LineWrapMode) ab.
LineWrapMode lineWrapMode () const;
Gibt den Modus zurück, wie die Zeile am Ende umbrochen wird. Mögliche Werte siehe Tabelle 4.77. Der Standardwert ist WidgetWidth, womit also immer am Ende des Widgets, hier der rechten Seiten vom Textfeld, umbrochen wird.
void moveCursor ( QTextCursor::MoveOperation operation, QTextCursor::MoveMode mode = QTextCursor::MoveAnchor );
Bewegt den Cursor mit der übergebenen Operation. Mögliche Werte für operation siehe Tabelle 4.78. Wenn der Modus QTextCursor:: KeepAnchor sein sollte, hat diese etwa denselben Effekt, wenn der Anwender an der aktuellen Position die (ª)-Taste gedrückt hält und mit den Pfeiltasten den Text markiert.
Tabelle 4.75
Methoden der Klasse QTextEdit (Forts.)
245
4.5
1542.book Seite 246 Montag, 4. Januar 2010 1:02 13
4
Dialoge, Layout und Qt-Widgets
Methode
Beschreibung
bool overwriteMode () const;
Gibt true zurück, wenn der Überschreibmodus eingeschaltet ist. Der Text hinter dem Cursor wird dann nicht weitergeschoben, sondern überschrieben. Ansonsten wird false zurückgegeben.
void setAcceptRichText ( bool accept );
Mit false schalten Sie ab, dass der Anwender einen Text im RTF-Format einfügen kann. Dann kann nur noch normaler (Plain-)Text eingefügt werden. Mit true (Standard) wird dies wieder eingeschaltet.
void setAutoFormatting ( AutoFormatting features );
Damit können Sie automatische Formatierungen setzen. Im Augenblick existiert hierbei allerdings nur AutoBulletList, womit ein Sternchen am Anfang einer Zeile durch einen Aufzählungspunkt ersetzt wird.
void setCurrentCharFormat ( const QTextCharFormat & format );
Setzt eine Formatierung für die Zeichen des Textfeldes, wenn der Anwender einen Text einfügt und die Methode setCharFormat() aufruft. Wird ein Text im Editor markiert, wird diese Auswahl gleich in die aktuelle Zeichenformatierung gesetzt.
void setCursorWidth (int width);
Damit setzen Sie die Breite des Cursors auf width Pixel.
void setDocument ( QTextDocument * document );
Macht document zum neuen Dokument des Texteditors.
void setDocumentTitle ( const QString & title );
Setzt den Titel des Dokuments auf title.
void setExtraSelections ( const QList<ExtraSelection> & selections );
Damit können Sie verschiedene Stellen im Text mit einer Farbe markieren;wird gerne in Editoren verwendet, die einzelne Zeilen mit einer bestimmten Hintergrundfarbe markieren.
void setLineWrapColumnOrWidth ( int w );
Abhängig vom gesetzten Modus (LineWrapMode) setzen Sie hier mit w, ab welcher Spalte oder ab welchem Pixel die Textzeile umbrochen wird.
void setLineWrapMode ( LineWrapMode mode );
Damit setzen Sie den Modus, wie die Zeile am Ende umbrochen wird. Mögliche Werte für mode siehe Tabelle 4.77.
void setOverwriteMode ( bool overwrite );
Mit true schalten Sie hier den Überschreibmodus ein. Damit wird jedes Zeichen hinter dem Cursor durch ein neues Zeichen überschrieben. Mit false schalten Sie diesen Modus wieder ab.
Tabelle 4.75
246
Methoden der Klasse QTextEdit (Forts.)
1542.book Seite 247 Montag, 4. Januar 2010 1:02 13
Qt-Widgets
Methode
Beschreibung
void setReadOnly ( bool ro );
Damit setzen Sie den Text im Editor auf nur lesbar. Es sind keinerlei Änderungen am Text mehr möglich. Kopieren und Ziehen des Texts funktionieren allerdings nach wie vor.
void setTabChangesFocus ( bool b );
Wird im Editor die Tabulator-Taste gedrückt, erfolgt eine Einrückung. Wollen Sie, dass stattdessen der Fokus vom Textfeld zu einem anderen Widget übergeht – was ja bei den meisten Widgets der Fall ist –, müssen Sie b auf true setzen. Der Standardwert ist false.
void setTabStopWidth ( int width );
Damit setzen Sie die Weite (Einrückung) des Tabulator-Zeichens in Pixel.
void setTextCursor ( const QTextCursor & cursor );
Damit setzen Sie den Textcursor auf cursor.
void setUndoRedoEnabled ( bool enable );
Damit können Sie mit false dem Anwender das Wiederherstellen und Rückgängigmachen der letzten Aktion verbieten. Mit true erreichen Sie das Gegenteil.
void setWordWrapMode ( QTextOption::WrapMode policy );
Damit setzen Sie den Modus, wie ein Wort am Zeilenende umbrochen wird. Mögliche Werte finden Sie in der Tabelle 4.79.
bool tabChangesFocus () const;
Wird false zurückgegeben, erfolgt bei Betätigung der (ÿ)-Taste eine Einrückung. Ist der Rückgabewert true, wird der Fokus vom Textwidget zum nächsten Widget übergeben.
int tabStopWidth () const;
Damit erhalten Sie die Weite (Einrückung) des Tabulatoren-Zeichens in Pixel.
QColor textColor () const;
Gibt die Textfarbe der aktuellen Formatierung zurück.
QTextCursor textCursor () const;
Gibt eine Kopie von QTextCursor zurück, die dem aktuell sichtbaren Cursor entspricht.
QString toHtml () const;
Gibt den Text in Editor als HTML-Text zurück.
QString toPlainText () const;
Gibt den Text im Editor als normalen (Plain-)Text zurück.
QTextOption::WrapMode wordWrapMode () const;
Damit erhalten Sie den Modus, wie ein Wort am Zeilenende umbrochen wird. Mögliche Werte finden Sie in Tabelle 4.79.
Tabelle 4.75
Methoden der Klasse QTextEdit (Forts.)
247
4.5
1542.book Seite 248 Montag, 4. Januar 2010 1:02 13
4
Dialoge, Layout und Qt-Widgets
Als Nächstes zur enum-Konstante QTextDocument::FindFlag, mit der Sie die Option für die Suche mit der Methode find() setzen können. Mehrere Optionen können mit dem ODER-Operator verknüpft werden. Konstante
Beschreibung
QTextDocument::FindBackward
Sucht rückwärts anstatt vorwärts.
QTextDocument::FindCaseSensitively
Per Standard ist die Suche case intensive (Großund Kleinschreibung werden nicht beachtet). Mit dieser Option kann die Suche case sensitive erfolgen (Groß- und Kleinschreibung werden beachtet).
QTextDocument::FindWholeWords
Nur noch ganze Wörter werden gesucht.
Tabelle 4.76
Konstanten für Optionen bei der Suche
Jetzt zu den enum-Konstanten von QTextEdit::LineWrapMode, womit Sie den Modus setzen bzw. abfragen können, wie der Text am Zeilenende umbrochen wird. Konstante
Beschreibung
QTextEdit::NoWrap
Der Text wird gar nicht umbrochen.
QTextEdit::WidgetWidth
Der Text wird am Ende der rechten Seite des Texteditors umbrochen, also abhängig von der Breite des Texteditors; das ist der Standardwert.
QTextEdit::FixedPixelWidth
Der Text wird ab einer bestimmten Pixel-Breite umbrochen; Wert kann mit der Methode setLineWrapColumnOrWidth() gesetzt werden.
QTextEdit::FixedColumnWidth
Der Text wird ab einer bestimmten Spaltenanzahl umbrochen; Wert kann mit der Methode setLineWrapColumnOrWidth() gesetzt werden.
Tabelle 4.77
Konstanten, um den Text am Zeilenende zu umbrechen
Mögliche Operationen, wo Sie den Cursor mit der Methode moveCursor() setzen können, sind in der enum-Konstante QTextCursor::MoveOperation gesetzt:
248
1542.book Seite 249 Montag, 4. Januar 2010 1:02 13
Qt-Widgets
Konstante
Beschreibung
QTextCursor::NoMove
Der Cursor bleibt, wo er ist.
QTextCursor::Start
Der Cursor wird an den Anfang des Dokuments gesetzt.
QTextCursor::StartOfLine
Der Cursor wird an den Anfang der aktuellen Zeile gesetzt.
QTextCursor::StartOfBlock
Der Cursor wird an den Anfang des aktuellen Blocks (Absatz) gesetzt.
QTextCursor::StartOfWord
Der Cursor wird an den Anfang des aktuellen Wortes gesetzt.
QTextCursor::PreviousBlock
Der Cursor wird vor den vorhergehenden Block gesetzt.
QTextCursor::PreviousCharacter
Der Cursor wird eine Position vor dem aktuellen Zeichen gesetzt.
QTextCursor::PreviousWord
Der Cursor wird eine Position vor dem aktuellen Wort gesetzt.
QTextCursor::Up
Der Cursor wird um eine Zeile nach oben versetzt.
QTextCursor::Left
Der Cursor wird um ein Zeichen nach links versetzt.
QTextCursor::WordLeft
Der Cursor wird links vom aktuellen Wort gesetzt.
QTextCursor::End
Der Cursor wird an das Ende des Dokuments gesetzt.
QTextCursor::EndOfLine
Der Cursor wird an das Ende der aktuellen Zeile gesetzt.
QTextCursor::EndOfWord
Der Cursor wird an das Ende des aktuellen Wortes gesetzt.
QTextCursor::EndOfBlock
Der Cursor wird an das Ende des aktuellen Blocks (Absatz) gesetzt.
QTextCursor::NextBlock
Der Cursor wird an den Anfang des nächsten Blocks gesetzt.
QTextCursor::NextCharacter
Der Cursor wird zum nächsten Zeichen gesetzt.
QTextCursor::NextWord
Der Cursor wird zum nächsten Wort gesetzt.
QTextCursor::Down
Der Cursor wird eine Zeile tiefer gesetzt.
QTextCursor::Right
Der Cursor wird rechts vom Zeichen gesetzt.
QTextCursor::WordRight
Der Cursor wird rechts vom aktuellen Wort gesetzt.
Tabelle 4.78
Mögliche Operationen für den Cursor
249
4.5
1542.book Seite 250 Montag, 4. Januar 2010 1:02 13
4
Dialoge, Layout und Qt-Widgets
Wie Sie den Text in einem Dokument umbrechen, gibt die enum-Konstante QTextOption::WrapMode an: Konstante
Beschreibung
QTextOption::NoWrap
Der Text wird niemals umbrochen.
QTextOption::WordWrap
Der Text wird zwischen zwei Worten umbrochen.
QTextOption::ManualWrap
Der Text wird manuell umbrochen. Bspw. durch (¢) bei der Eingabe vom Anwender.
QTextOption::WrapAnywhere
Der Text kann an jedem Punkt der Linie umbrochen werden. Auch mitten in einem Wort.
QTextOption:: Wenn möglich, wird die Zeile zwischen zwei Worten WrapAtWordBoundaryOrAnywhere umbrochen. Ansonsten kann der Text an einer belie-
bigen Position manuell umbrochen werden. Tabelle 4.79
Text in einem Dokument umbrechen
Jetzt zu den öffentlichen Signalen, die unmittelbar bei der Klasse QTextEdit auftreten können. Signal
Beschreibung
void copyAvailable ( bool yes );
Dieses Signal wird ausgelöst, wenn ein Text markiert bzw. de-markiert wurde. Wurde ein Text markiert, wird das Signal mit dem Parameter true ausgelöst. Bei einer De-markierung wird das Signal mit dem Parameter false aufgerufen. Wenn ein Text markiert wurde und das Signal mit true ausgelöst wurde, kann die Markierung bspw. in die Zwischenablage kopiert werden.
void currentCharFormatChanged ( Das Signal wird ausgelöst, wenn die aktuelle Formaconst QTextCharFormat & f ); tierung geändert wurde. Die neue Formatierung befindet sich in f. void cursorPositionChanged ();
Dieses Signal wird ausgelöst, wenn die Position des Cursors verändert wurde.
void redoAvailable ( bool available );
Dieses Signal wird ausgelöst, wenn eine Operation zum Wiederholen vorhanden ist. Ist eine Operation vorhanden, wird der Parameter auf true, ansonsten auf false gesetzt.
void selectionChanged ();
Dieses Signal wird ausgelöst, wenn eine Markierung verändert wurde.
void textChanged ();
Das Signal wird ausgelöst, wenn der Text verändert wurde.
Tabelle 4.80
250
Signale der Klasse QTextEdit
1542.book Seite 251 Montag, 4. Januar 2010 1:02 13
Qt-Widgets
Signal
Beschreibung
void undoAvailable ( bool available );
Dieses Signal wird ausgelöst, wenn eine Operation zu Rückgängig machen vorhanden ist. Ist eine Operation vorhanden, wird der Parameter auf true, ansonsten auf false gesetzt.
Tabelle 4.80
Signale der Klasse QTextEdit (Forts.)
Jetzt noch zu den öffentlichen Slots von QTextEdit: Slot
Beschreibung
void append ( const QString & text );
Fügt einen neuen Absatz mit dem Text text ans Ende des Editors.
void clear ();
Löscht den kompletten Text von Editor. Beachten Sie, dass Sie damit auch den Undo/Redo-Verlauf löschen.
void copy ();
Kopiert einen markierten Text in die Zwischenablage.
void cut ();
Schneidet einen markierten Text aus und legt ihn in die Zwischenablage.
void insertHtml ( const QString & text );
Fügt den Text text, der eine HTML-Formatierung besitzt (bspw. von einer Webseite kopiert), an der aktuellen Cursorposition ein.
void insertPlainText ( const QString & text );
Fügt den (Plain-)Text text an der aktuellen Cursorposition ein.
void paste ();
Fügt einen Text aus der Zwischenablage an der aktuellen Cursorposition ein. Befindet sich in der Zwischenablage kein Text, geschieht nichts.
void redo ();
Wiederholt die vorhergehende Operation. Gibt es keine, geschieht nichts.
void scrollToAnchor ( const QString & name );
Scrollt den Texteditor zum angegebenen String name. Gibt es diesen String nicht, geschieht nichts.
void selectAll ();
Markiert den kompletten Text im Editor.
void setAlignment ( Qt::Alignment a );
Setzt die Ausrichtung des Texts auf a. Mögliche Werte wurden bereits in Tabelle 4.8, 4.9 und 4.10 beschrieben.
void setCurrentFont ( const QFont & f );
Setzt die Schriftart f zur aktuellen Formatierung.
void setFontFamily ( const QString & fontFamily );
Setzt die Schriftart fontFamily zur aktuellen Formatierung.
void setFontItalic ( bool italic );
Mit true als Parameter setzen Sie die Schrift als Kursivschrift. Mit false heben Sie dies auf.
Tabelle 4.81
Slots der Klasse QTextEdit
251
4.5
1542.book Seite 252 Montag, 4. Januar 2010 1:02 13
4
Dialoge, Layout und Qt-Widgets
Slot
Beschreibung
void setFontPointSize ( qreal s );
Setzt die Punktgröße der aktuellen Formatierung auf s.
void setFontUnderline ( bool underline );
Mit true als Parameter wird die Schrift unterstrichen. false bewirkt das Gegenteil.
void setFontWeight ( int weight );
Setzt die Schrift- »Stärke« der aktuellen Formatierung auf weight. Hier sollte ein Wert von 0 bis 99 angegeben werden. Es gibt aber auch vordefinierte Konstanten mit enum-Konstanten von QFont::Weight.
void setHtml ( const QString & text );
Damit fügen Sie den String text in den Editor ein. Der Text wird als Rich-Text in HTML-Formatierung interpretiert. Anderer Text, der sich zuvor im Editor befand, wird gelöscht. ebenso der Verlauf von Undo/ Redo.
void setPlainText ( const QString & text );
Damit wird der (Plain-)Text text in den Editor eingefügt. Der Text wird als reiner (Plain-)Text interpretiert. Anderer Text, der sich zuvor im Editor befand, wird gelöscht, ebenso der Verlauf von Undo/Redo.
void setText ( const QString & text );
Damit wird der String text in den Editor eingefügt. Das Format kann hierbei sowohl im HTML- als auch im (Plain-)Textformat vorliegen. Der Texteditor versucht, den Text im richtigen Format einzufügen. Es wird allerdings empfohlen, entweder setHtml() oder setPlainText() zu verwenden, um Probleme bei der Textdarstellung zu vermeiden.
void setTextColor ( const QColor & c );
Setzt die Textfarbe der aktuellen Formatierung auf c.
void undo ();
Macht die zuletzt gemachte Operation rückgängig. Gibt es keine solche Operation, geschieht nichts.
void zoomIn ( int range = 1 );
Zoomt in den Text, vergrößert die Basisschriftart um range Punkte und berechnet alle Schriftgrößen neu. Bilder werden allerdings nicht vergrößert.
void zoomOut ( int range = 1 );
Zoomt aus dem Text. verkleinert die Basisschriftart um range Punkte und berechnet alle Schriftgrößen neu. Bilder werden allerdings nicht verkleinert.
Tabelle 4.81
Slots der Klasse QTextEdit (Forts.)
Hierzu nun ein ganz einfaches Beispiel eines Texteditors. Es ist wenig sinnvoll, ein umfangreicheres Beispiel zu verwenden, da wir sonst auf zu viele Themen vorgreifen müssen. Unser Texteditor kann lediglich einen Text zum Ansehen laden und demonstriert die Darstellung mit HTML-formatiertem Text, außerdem wurde eine kleine Suche implementiert, die Schriftart und -farbe können verän-
252
1542.book Seite 253 Montag, 4. Januar 2010 1:02 13
Qt-Widgets
dert werden. Die Schriftart bezieht sich auf den kompletten Text. Um die Schriftfarbe zu ändern, markieren Sie den entsprechenden Text. Sie können für jeden Buchstaben eine Farbe auswählen. Im Beispiel wurde notgedrungen auf Menüs zurückgegriffen, um ein wenig Interaktion zu gewährleisten. Mehr zu den Menüs erfahren Sie in Abschnitt 5.2.2. Außerdem wurde kein Dialog mehr verwendet, sondern ein Hauptfenster (QMainWindow) (siehe Kapitel 5). Das Grundgerüst der Anwendung: 00 01 02 03 04 05
// beispiele/qtextedit/mywidget.h #ifndef MYWIDGET_H #define MYWIDGET_H #include #include #include
06 07 08 09 10 11 12 13 14 15 16 17 18
class MyWidget : public QMainWindow { Q_OBJECT public: MyWidget(QMainWindow *parent = 0); QTextEdit* editor; public slots: void openFile(); void newFile(); void changeFont( ); void changeColor(); void search(); }; #endif
Jetzt die Implementierung des Codes: 00 01 02 03 04 05 06 07 08 09 10 11
// beispiele/qtextedit/mywidget.cpp #include "mywidget.h" #include #include #include #include #include #include #include #include #include