====== Komplexe Anwendungsfenster erstellen ====== Natürlich kann man sich komplexe Fenster durch das Verschachteln von Widgets selbst erstellen. Die Klasse ''QMainWindow'' bietet jedoch bereits eine Vorlage für übliche Hauptfenster, sodass nur mehr entsprechende Methoden der Klasse aufgerufen werden müssen.\\ Zuerst sehen wir uns mal ein typisches Hauptfenster an:\\ {{:frameworks:qt:gui:mainwindow:mainwindow.png?|}} \\ \\ An diesem Beispiel wollen wir uns nun die Klasse ''QMainWindow'' ansehen. Gleichzeitig soll unsere Todo-Anwendung aus dem Kapitel [[frameworks:qt:gui:basic:predefdialogs#objekt_mit_signal|Dialoge]] erweitert werden. Unser Hauptprogramm sieht während der ganzen Entwicklung so aus: #include #include "MainWindow.h" int main ( int argc, char *argv[] ) { QApplication app( argc, argv ); MainWindow mw; mw.show(); return app.exec(); } Außerdem wird die Klasse ''MessageListWidget'' aus dem genannten Kapitel verwendet. Wir fügen aber noch ein paar Signale hinzu, die uns die Kommunikation erleichtern: #ifndef MESSAGELISTWIDGET_H #define MESSAGELISTWIDGET_H #include class QListWidget; class QPushButton; class QInputDialog; class MessageListWidget : public QWidget { Q_OBJECT public: MessageListWidget(); private: QListWidget *messageList; QPushButton *addButton, *removeButton; QInputDialog *dialog; public slots: void showInputDialog(); void removeSelectedMessage(); signals: void entryAdded( const QString& ); void entryRemoved( const QString& ); void numEntriesChanged( const int ); }; #endif // MESSAGELISTWIDGET_H #include "MessageListWidget.h" #include #include #include #include #include #include MessageListWidget::MessageListWidget() : messageList( new QListWidget() ), addButton( new QPushButton ("+" ) ), removeButton( new QPushButton( "-" ) ), dialog( new QInputDialog() ) { QVBoxLayout *vLayout = new QVBoxLayout(); QHBoxLayout *hLayout = new QHBoxLayout(); messageList->setSelectionMode( QAbstractItemView::SingleSelection ); dialog->setInputMode( QInputDialog::TextInput ); dialog->setModal( true ); dialog->setWindowTitle( "New Entry" ); connect( addButton, &QPushButton::clicked, this, &MessageListWidget::showInputDialog ); connect( removeButton, &QPushButton::clicked, this, &MessageListWidget::removeSelectedMessage ); hLayout->addStretch(); hLayout->addWidget( addButton ); hLayout->addWidget( removeButton ); vLayout->addWidget( messageList ); vLayout->addLayout( hLayout ); setWindowTitle( "Todo" ); setLayout( vLayout ); resize( 400, 400 ); } void MessageListWidget::showInputDialog() { dialog->setTextValue( "" ); dialog->exec(); if( dialog->textValue().trimmed().size() > 0 ) { messageList->addItem( dialog->textValue() ); emit entryAdded( dialog->textValue() ); emit numEntriesChanged( messageList->count() ); } } void MessageListWidget::removeSelectedMessage() { QList items = messageList->selectedItems(); if( items.size() > 0 ) { for( int i = 0; i < messageList->count(); i++ ) { if( messageList->item( i ) == items.first() ) { QListWidgetItem *item = messageList->takeItem( i ); QString text = item->text(); delete item; emit entryRemoved( text ); emit numEntriesChanged( messageList->count() ); return; } } } } ===== Zentrales Widget ===== {{:frameworks:qt:gui:mainwindow:central.png?|}} \\ Wie der Name schon sagt ist das zentrale Widget der Mittelpunkt der Anwendung. Wird die Größe des Hauptfensters verändert, hat das nur Einfluss auf das zentrale Widget, alle anderen Teile bleiben unbeeinflusst. Gesetzt wird das Widget intuitiv über die Methode ''setCentralWidget()'' des ''QMainWindow''-Objektes. Da das zentrale Widget ziemlich wahrscheinlich ein zusammengesetztes Widget ist, muss dafür wie im Kapitel [[frameworks:qt:gui:basic:layouts#zusammengesetzte_widgets_erstellen|Layouts]] beschrieben ein Wrapper-Widget verwendet werden, das alle anderen Widgets enthält.\\ \\ Da das zentrale Widget bereits fertig implementiert ist, fällt unser Code hier entsprechend kurz und einfach aus: #ifndef MAINWINDOW_H #define MAINWINDOW_H #include class MessageListWidget; class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(); private: MessageListWidget *m_listWidget; }; #endif // MAINWINDOW_H #include "MainWindow.h" #include "MessageListWidget.h" MainWindow::MainWindow() : m_listWidget( new MessageListWidget() ) { setCentralWidget( m_listWidget ); setWindowTitle( "Todo" ); resize( 400, 500 ); } \\ {{:frameworks:qt:gui:example_central.png?|}} \\ Optisch sieht man hier noch keinen Unterschied, jedoch befindet sich unser Widget bereits innerhalb des Hauptfensters. ===== Menüleiste ===== {{:frameworks:qt:gui:mainwindow:menubar.png?|}} \\ Die Menüleiste steht in vielen Programmen im Mittelpunkt der Navigation. Über verschachtelte Menüpunkte können komplexe Navigationsstrukturen abgebildet werden. Eine durch ein ''QMenuBar''-Objekt repräsentierte Menüleiste ist nichts weiter als ein Container für ''QMenu''-Objekte, die wiederum selbst ''QMenu''-Objekte oder ''QAction''-Objekte enthalten. ''QAction''-Objekte sind schließlich die endgültigen Menü-Einträge, die mit Slots verbunden werden können und reagieren, wenn auf sie geklickt wird. Außerdem können diese Elemente auch das Verhalten von ''QCheckBox'' oder ''QRadioButton'' nachahmen. Angezeigt wird die Menüleiste über die Methode ''setMenuBar()'' der Hauptfenster-Klasse. \\ \\ Praktisch wäre natürlich, wenn man unsere Anwendung über das Menü beenden könnte. Außerdem sollten die Buttons zum Hinzufügen und Löschen dort ebenfalls vorhanden sein. Der Übersicht wegen erstellen wir eine Methode ''createMenuBar()'' die uns ein passendes ''QMenuBar''-Objekt erstellt und zurückliefert. #ifndef MAINWINDOW_H #define MAINWINDOW_H #include class MessageListWidget; class QMenuBar; class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(); private: MessageListWidget *m_listWidget; QMenuBar *createMenuBar(); }; #endif // MAINWINDOW_H #include "MainWindow.h" #include "MessageListWidget.h" #include #include MainWindow::MainWindow() : m_listWidget( new MessageListWidget() ) { setCentralWidget( m_listWidget ); setMenuBar( createMenuBar() ); setWindowTitle( "Todo" ); resize( 400, 500 ); } QMenuBar *MainWindow::createMenuBar() { // Die eigentliche Menüleiste erstellen QMenuBar *mBar = new QMenuBar(); // 1. Menü mit dem Namen "File" QMenu *fileMenu = new QMenu( "File" ); // Wird der Eintrag "Quit" betätigt, führt das Objekt "qApp" // den Slot "quit()" aus. fileMenu->addAction( "Quit", qApp, &QApplication::quit ); // Menü in die Menüleiste einfügen mBar->addMenu( fileMenu ); // 2. Menü mit dem Namen "Edit" QMenu *editMenu = new QMenu( "Edit" ); // Wird der Eintrag "Add" betätigt, führt das Objekt "m_listWidget" // den Slot "showInputDialog()" aus. editMenu->addAction( "Add", m_listWidget, &MessageListWidget::showInputDialog ); // Wird der Eintrag "Remove" betätigt, führt das Objekt "m_listWidget" // den Slot "removeSelectedMessage()" aus. editMenu->addAction( "Remove", m_listWidget, &MessageListWidget::removeSelectedMessage ); // Menü in die Menüleiste einfügen mBar->addMenu( editMenu ); // Menüleiste zurückliefern return mBar; } In nur wenigen Zeilen haben wir über das Signal/Slot-Konzept ein funktionsfähiges Menü erstellt: \\ {{:frameworks:qt:gui:example_menu.png?|}} ===== Werkzeugleiste ===== {{:frameworks:qt:gui:mainwindow:toolbar.png?|}} \\ Die Werkzeugleiste besteht aus grafischen Symbolen. Oftmals sind diese Symbole oft nur Abkürzungen, um auf oft verwendete Elemente in der Menüleiste (z.B. "Speichern", "Drucken", ...) zugreifen zu können. Da ein Programm auch mehrere Werkzeugleisten haben kann, werden diese über die Methode ''addToolBar()'' hinzugefügt. Ähnlich wie ''QMenuBar'' ein Container für ''QMenu''-Objekte ist, werden in einer ''QToolBar'' Objekte des Typs ''QToolButton'' angezeigt. Diese sind ganz normale Widgets und können über ''connect()'' mit Slots verbunden werden. \\ \\ In unseren Beispiel wollen wir einfach die gleichen Einträge wie in der Menüleiste darstellen. Folgende Icons werden verwendet: \\ {{:frameworks:qt:gui:appointment-new.png?|appointment-new.png}} {{:frameworks:qt:gui:archive-remove.png?|archive-remove.png}} {{:frameworks:qt:gui:application-exit.png?|application-exit.png}}\\ Diese Icons sind Teil der KDE-Desktopumgebung, deshalb solltet ihr euch vor der Verwendung in euren Anwendungen über die [[https://techbase.kde.org/Projects/Oxygen/Licensing#Using_Oxygen_Icons_Outwith_KDE|Lizenzbedingungen]] informieren.\\ Um die Icons darzustellen, werden sie mit dem [[frameworks:qt:build:rc|Resource Compiler]] in unsere Anwendung eingebunden. Dabei wird das Präfix "icons" verwendet. Abermals erstellen wir uns eine eigene Methode ''createToolbar()'' und bauen sie in unser Programm ein: #ifndef MAINWINDOW_H #define MAINWINDOW_H #include class MessageListWidget; class QMenuBar; class QToolBar; class QIcon; class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(); ~MainWindow(); private: MessageListWidget *m_listWidget; QIcon *m_addIcon, *m_removeIcon, *m_quitIcon; QMenuBar *createMenuBar(); QToolBar *createToolBar(); }; #endif // MAINWINDOW_H #include "MainWindow.h" #include "MessageListWidget.h" #include #include #include #include #include MainWindow::MainWindow() : m_listWidget( new MessageListWidget() ), m_addIcon( new QIcon( ":/icons/appointment-new.png" ) ), m_removeIcon( new QIcon( ":/icons/archive-remove.png" ) ), m_quitIcon( new QIcon( ":/icons/application-exit.png" ) ) { setCentralWidget( m_listWidget ); setMenuBar( createMenuBar() ); addToolBar( createToolBar() ); setWindowTitle( "Todo" ); resize( 400, 500 ); } MainWindow::~MainWindow() { delete m_quitIcon; delete m_removeIcon; delete m_addIcon; } QMenuBar *MainWindow::createMenuBar() { // Die eigentliche Menüleiste erstellen QMenuBar *mBar = new QMenuBar(); // 1. Menü mit dem Namen "File" QMenu *fileMenu = new QMenu( "File" ); // Wird der Eintrag "Quit" betätigt, führt das Objekt "qApp" // den Slot "quit()" aus. fileMenu->addAction( "Quit", qApp, &QApplication::quit ); // Menü in die Menüleiste einfügen mBar->addMenu( fileMenu ); // 2. Menü mit dem Namen "Edit" QMenu *editMenu = new QMenu( "Edit" ); // Wird der Eintrag "Add" betätigt, führt das Objekt "m_listWidget" // den Slot "showInputDialog()" aus. editMenu->addAction( "Add", m_listWidget, &MessageListWidget::showInputDialog ); // Wird der Eintrag "Remove" betätigt, führt das Objekt "m_listWidget" // den Slot "removeSelectedMessage()" aus. editMenu->addAction( "Remove", m_listWidget, &MessageListWidget::removeSelectedMessage ); // Menü in die Menüleiste einfügen mBar->addMenu( editMenu ); // Menüleiste zurückliefern return mBar; } QToolBar *MainWindow::createToolBar() { // Die eigentliche Werkzeugleiste erstellen QToolBar *tBar = new QToolBar(); // Werkzeugleisten können normal verschoben werden, das wollen wir aber nicht tBar->setMovable( false ); // Neuen Eintrag zum Hinzufügen von Einträgen anlegen QToolButton *addButton = new QToolButton(); // Icon für den Eintrag setzen addButton->setIcon( *m_addIcon ); // Wird der Eintrag betätigt, führt das Objekt "m_listWidget" // den Slot "showInputDialog()" aus. connect( addButton, &QToolButton::clicked, m_listWidget, &MessageListWidget::showInputDialog ); // Eintrag zur Werkzeugleiste hinzufügen tBar->addWidget( addButton ); // Neuen Eintrag zum Entfernen von Einträgen anlegen QToolButton *removeButton = new QToolButton(); // Icon für den Eintrag setzen removeButton->setIcon( *m_removeIcon ); // Wird der Eintrag betätigt, führt das Objekt "m_listWidget" // den Slot "removeSelectedMessage()" aus. connect( removeButton, &QToolButton::clicked, m_listWidget, &MessageListWidget::removeSelectedMessage ); // Eintrag zur Werkzeugleiste hinzufügen tBar->addWidget( removeButton ); // Ein Trenn-Objekt einfügen, um die Einträge optisch zu trennen tBar->addSeparator(); // Neuen Eintrag zum Beenden der Anwendung anlegen QToolButton *quitButton = new QToolButton(); // Icon für den Eintrag setzen quitButton->setIcon( *m_quitIcon ); // Wird der Eintrag betätigt, führt das Objekt "qApp" // den Slot "quit()" aus. connect( quitButton, &QToolButton::clicked, qApp, &QApplication::quit ); // Eintrag zur Werkzeugleiste hinzufügen tBar->addWidget( quitButton ); // Werkzeugleiste zurückliefern return tBar; } \\ {{:frameworks:qt:gui:example_tool.png?|}} ===== Statusleiste ===== {{:frameworks:qt:gui:mainwindow:statusbar.png?|}} \\ Am unteren Fensterrand befindet sich gewöhnlich die Statusleiste des Programms. Sie enthält meist aktuelle Informationen über den Zustand der Anwendung, kann aber jedes beliebige Widget enthalten. \\ Zusätzlich zur Methode ''setStatusbar()'', muss die Statusleiste explizit über die Methode ''setVisible()'' sichtbar gemacht werden. Über die Methode ''showMessage()'' kann eine Nachricht angezeigt werden, die jedoch nach Ablauf eines ebenfalls übergebenen Timeouts verschwindet. Außerdem können noch Widgets in der Statusleiste angezeigt werden. Hier gibt es jedoch 2 Möglichkeiten: * Widget kann von Nachrichten verdeckt werden: ''addWidget()'' * Widget kann nicht verdeckt werden (permanentes Widget): ''addPermanentWidget()'' \\ \\ Unsere Anwendung soll den aktuellen Status in der Statusleiste darstellen. Dazu gehören Benachrichtigungen, wenn ein Eintrag hinzugefügt bzw. gelöscht wird und die aktuelle Anzahl an Einträgen: #ifndef MAINWINDOW_H #define MAINWINDOW_H #include class MessageListWidget; class QMenuBar; class QToolBar; class QIcon; class QStatusBar; class QLabel; class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(); ~MainWindow(); private: MessageListWidget *m_listWidget; QIcon *m_addIcon, *m_removeIcon, *m_quitIcon; QLabel *m_counter; QMenuBar *createMenuBar(); QToolBar *createToolBar(); QStatusBar *createStatusBar(); private slots: void showAddedText(); void showRemovedText(); void updateCount( const int count ); }; #endif // MAINWINDOW_H #include "MainWindow.h" #include "MessageListWidget.h" #include #include #include #include #include #include #include MainWindow::MainWindow() : m_listWidget( new MessageListWidget() ), m_addIcon( new QIcon( ":/icons/appointment-new.png" ) ), m_removeIcon( new QIcon( ":/icons/archive-remove.png" ) ), m_quitIcon( new QIcon( ":/icons/application-exit.png" ) ), m_counter( new QLabel( "0" ) ) { setCentralWidget( m_listWidget ); setMenuBar( createMenuBar() ); addToolBar( createToolBar() ); setStatusBar( createStatusBar() ); statusBar()->setVisible( true ); setWindowTitle( "Todo" ); resize( 400, 500 ); } MainWindow::~MainWindow() { delete m_quitIcon; delete m_removeIcon; delete m_addIcon; } QMenuBar *MainWindow::createMenuBar() { // Die eigentliche Menüleiste erstellen QMenuBar *mBar = new QMenuBar(); // 1. Menü mit dem Namen "File" QMenu *fileMenu = new QMenu( "File" ); // Wird der Eintrag "Quit" betätigt, führt das Objekt "qApp" // den Slot "quit()" aus. fileMenu->addAction( "Quit", qApp, &QApplication::quit ); // Menü in die Menüleiste einfügen mBar->addMenu( fileMenu ); // 2. Menü mit dem Namen "Edit" QMenu *editMenu = new QMenu( "Edit" ); // Wird der Eintrag "Add" betätigt, führt das Objekt "m_listWidget" // den Slot "showInputDialog()" aus. editMenu->addAction( "Add", m_listWidget, &MessageListWidget::showInputDialog ); // Wird der Eintrag "Remove" betätigt, führt das Objekt "m_listWidget" // den Slot "removeSelectedMessage()" aus. editMenu->addAction( "Remove", m_listWidget, &MessageListWidget::removeSelectedMessage ); // Menü in die Menüleiste einfügen mBar->addMenu( editMenu ); // Menüleiste zurückliefern return mBar; } QToolBar *MainWindow::createToolBar() { // Die eigentliche Werkzeugleiste erstellen QToolBar *tBar = new QToolBar(); // Werkzeugleisten können normal verschoben werden, das wollen wir aber nicht tBar->setMovable( false ); // Neuen Eintrag zum Hinzufügen von Einträgen anlegen QToolButton *addButton = new QToolButton(); // Icon für den Eintrag setzen addButton->setIcon( *m_addIcon ); // Wird der Eintrag betätigt, führt das Objekt "m_listWidget" // den Slot "showInputDialog()" aus. connect( addButton, &QToolButton::clicked, m_listWidget, &MessageListWidget::showInputDialog ); // Eintrag zur Werkzeugleiste hinzufügen tBar->addWidget( addButton ); // Neuen Eintrag zum Entfernen von Einträgen anlegen QToolButton *removeButton = new QToolButton(); // Icon für den Eintrag setzen removeButton->setIcon( *m_removeIcon ); // Wird der Eintrag betätigt, führt das Objekt "m_listWidget" // den Slot "removeSelectedMessage()" aus. connect( removeButton, &QToolButton::clicked, m_listWidget, &MessageListWidget::removeSelectedMessage ); // Eintrag zur Werkzeugleiste hinzufügen tBar->addWidget( removeButton ); // Ein Trenn-Objekt einfügen, um die Einträge optisch zu trennen tBar->addSeparator(); // Neuen Eintrag zum Beenden der Anwendung anlegen QToolButton *quitButton = new QToolButton(); // Icon für den Eintrag setzen quitButton->setIcon( *m_quitIcon ); // Wird der Eintrag betätigt, führt das Objekt "qApp" // den Slot "quit()" aus. connect( quitButton, &QToolButton::clicked, qApp, &QApplication::quit ); // Eintrag zur Werkzeugleiste hinzufügen tBar->addWidget( quitButton ); // Werkzeugleiste zurückliefern return tBar; } QStatusBar *MainWindow::createStatusBar() { // Eigentliche Statusleiste anlegen QStatusBar *sBar = new QStatusBar(); // Zähl-Widget permanent anzeigen sBar->addPermanentWidget( m_counter ); // Benachrichtigung anzeigen, wenn ein Eintrag hinzugefügt wurde connect( m_listWidget, &MessageListWidget::entryAdded, this, &MainWindow::showAddedText ); // Benachrichtigung anzeigen, wenn ein Eintrag entfernt wurde connect( m_listWidget, &MessageListWidget::entryRemoved, this, &MainWindow::showRemovedText ); // Zähler aktualisieren, wenn ein Eintrag hinzgefügt oder entfernt wurde connect( m_listWidget, &MessageListWidget::numEntriesChanged, this, &MainWindow::updateCount ); // Statusleiste zurückliefern return sBar; } void MainWindow::showAddedText() { // Nachricht für 3 Sekunden in der Statusleiste anzeigen statusBar()->showMessage( "Entry added", 3000 ); } void MainWindow::showRemovedText() { // Nachricht für 3 Sekunden in der Statusleiste anzeigen statusBar()->showMessage( "Entry removed", 3000 ); } void MainWindow::updateCount( const int count ) { // Permanentes Zähler-Label in der Statusleiste aktualisieren m_counter->setText( QString::number( count ) ); } \\ {{:frameworks:qt:gui:example_status.png?|}}