Kapitel 34. Hauptfenster, Erstellung von SDI- und MDI-Anwendungen

Sind Sie auf das Gefühl gestoßen, dass “das Hauptfenster machen” in Qt schnell geht… bis das echte Leben beginnt? Das Menü lebt separat, Toolbar-Buttons separat, die Statusleiste schweigt, und einen Befehl zu deaktivieren muss an zwei Stellen geschehen — und das alles kann leicht aus der Synchronisation geraten.

Dieses Kapitel zeigt, wie professionelle Entwickler das “Skelett” der Anwendung so zusammenbauen, dass die Oberfläche bei der ersten Erweiterung nicht auseinanderfällt. Hier werden Sie einen unerwarteten Weg entdecken, Befehle zu zentralisieren, das Geheimnis sauberer Menü-/Toolbar-Synchronisation erfahren und Geschwindigkeitsvorteil bei der Entwicklung ohne Codechaos erhalten.

Es werden 3 Schlüsselknoten von QMainWindow (Arbeitsbereich, Panels, Statusleiste), Single Source of Truth über QAction, Dock-Panels (QDockWidget) und der “professionelle” Splash-Screen (QSplashScreen) behandelt, der lange Initialisierung maskiert. Plus — praktischer Vergleich von SDI vs MDI und wie man mehrere Dokumente öffnet, ohne das Projekt in einen Signalknäuel zu verwandeln.

Wenn Ihre Anwendung wie eine “echte” aussehen soll — ist dies genau der Übergangsschritt, den man besser nicht überspringen sollte.

Das Kapitel enthält Code-Beispiele, die sofort einsatzbereit sind.

Selbstüberprüfung zum Kapitel

Warum ist die Verwendung von QAction wesentlich effizienter als das Erstellen separater Menübefehle und Toolbar-Buttons?Antwort
Richtige Antwort: QAction zentralisiert alle Oberflächenelemente (Menütext, Icon, Tastenkombinationen, Tooltips) in einem Objekt, beseitigt Code-Duplizierung und synchronisiert automatisch den Zustand der Elemente — z.B. deaktiviert das Deaktivieren der Aktion sowohl den Menübefehl als auch die Toolbar-Schaltfläche.
Worin besteht der grundlegende Unterschied zwischen den Widgets QToolBar und QDockWidget hinsichtlich ihrer Bestimmung?Antwort
Richtige Antwort: QToolBar ist für die Platzierung einfacher Schnellzugriffs-Buttons für Befehle gedacht, während QDockWidget die Platzierung komplexer zusammengesetzter Widgets ermöglicht, die der Benutzer zwischen Fensterkanten verschieben und sogar zu eigenständigen Fenstern abtrennen kann.
Warum muss bei langwierigen Initialisierungsoperationen QApplication::processEvents() aufgerufen werden?Antwort
Richtige Antwort: Ohne Verarbeitung angesammelter Events “friert” die Oberfläche ein, was sich als “nicht antwortende” Anwendung oder sich drehende Warteindikatoren manifestiert. Der Aufruf von processEvents() ermöglicht die Verarbeitung von GUI-Events und Erhaltung der Oberflächenresponsivität.
Warum muss in einer SDI-Anwendung die Methode closeEvent() den Status document()->isModified() vor dem Schließen überprüfen?Antwort
Richtige Antwort: Dies verhindert den Verlust nicht gespeicherter Daten — wenn das Dokument geändert wurde, wird dem Benutzer angeboten, die Änderungen zu speichern. Der Aufruf von pe->ignore() ermöglicht das Abbrechen des Fensterschließens, wenn der Benutzer seine Meinung ändert.
Welches Problem entsteht, wenn in einer MDI-Anwendung m_pma->activeSubWindow()->widget() ohne vorherige Überprüfung aufgerufen wird?Antwort
Richtige Antwort: Wenn es kein aktives Fenster im Arbeitsbereich gibt, gibt activeSubWindow() nullptr zurück, und der Versuch, widget() aufzurufen, führt zu einem Programmabsturz. Es ist notwendig, zuerst das Ergebnis auf nullptr zu überprüfen.
Worin besteht der Vorteil der Verwendung von setAttribute(Qt::WA_DeleteOnClose) für Dokumentfenster in einer MDI-Anwendung?Antwort
Richtige Antwort: Das Attribut garantiert automatisches Löschen des Widgets aus dem Speicher beim Schließen des Fensters, verhindert Speicherlecks bei der Arbeit mit vielen Dokumenten ohne Notwendigkeit manueller Verwaltung der Objektlebensdauer.
Warum muss das Menü Windows in einer MDI-Anwendung vor jeder Anzeige gelöscht und neu gefüllt werden?Antwort
Richtige Antwort: Die Zusammensetzung offener Dokumente ändert sich dynamisch — Fenster werden erstellt und geschlossen. Die Neuerstellung des Menüs vor der Anzeige garantiert Aktualität der Dokumentenliste und korrekte Markierung des aktiven Fensters.
Wofür wird das Objekt QSignalMapper im Kontext des Windows-Menüs einer MDI-Anwendung verwendet?Antwort
Richtige Antwort: QSignalMapper ermöglicht das Senden eines Zeigers auf ein bestimmtes Dokumentfenster zusammen mit dem Signal, da das Standard-Signal triggered() nur einen booleschen Wert übergibt. Dies ist notwendig für die Aktivierung des ausgewählten Fensters.
Wie beeinflusst die Methode setAllowedAreas() in QDockWidget die Benutzererfahrung bei der Arbeit mit der Anwendung?Antwort
Richtige Antwort: Die Methode schränkt mögliche Platzierungsorte des Docks ein, was dem Entwickler ermöglicht, die Oberflächenanordnung zu kontrollieren — z.B. Platzierung des Panels an den Seiten verbieten, wenn es für vertikale Anordnung zu breit ist.
Worin besteht der Unterschied zwischen temporären und permanenten Nachrichten der Statusleiste aus architektonischer Sicht der Anwendung?Antwort
Richtige Antwort: Temporäre Nachrichten (showMessage) verwenden den gemeinsamen Textbereich und werden automatisch gelöscht, während permanente (addPermanentWidget) separate Widgets erfordern und dauerhaft sichtbar bleiben — dies trennt temporäre Benachrichtigungen und stabile Statusinformationen.
Was passiert, wenn in einer MDI-Anwendung das Signal changeWindowTitle() des Dokuments nicht mit dem Slot des Hauptfensters verbunden wird?Antwort
Richtige Antwort: Der Dokumentfenstertitel wird beim Speichern der Datei unter neuem Namen oder beim Laden der Datei nicht aktualisiert — der Benutzer sieht veraltete Informationen, was die Usability der Anwendung reduziert.
Warum wird im Beispiel mit dem Splash-Screen das Objekt QSplashScreen nach QApplication, aber vor dem Aufruf von exec() erstellt?Antwort
Richtige Antwort: QApplication muss für die Arbeit mit GUI existieren, und der Splash-Screen muss vor dem Start der Event-Loop exec() angezeigt werden — um während der Anwendungsinitialisierung sichtbar zu sein und die Ladezeit zu verbergen.
Welches architektonische Problem löst die Delegation von Lade- und Speicheroperationen von MDIProgram an DocWindow?Antwort
Richtige Antwort: Verantwortungstrennung — MDIProgram verwaltet Fenster und Menüs, während DocWindow die Logik der Arbeit mit Dateien kapselt. Dies entspricht dem Prinzip der Einzelverantwortlichkeit und vereinfacht die Wiederverwendung von DocWindow in anderen Anwendungen.

Praktische Aufgaben

Einfaches Niveau

Texteditor mit Toolbar
Erstellen Sie eine SDI-Anwendung mit einem Texteditor (QTextEdit), die eine Toolbar mit drei Buttons hat: Bold (Fett), Italic (Kursiv) und Underline (Unterstrichen). Verwenden Sie QAction für jeden Button und fügen Sie entsprechende Icons hinzu. Die Buttons sollen die Formatierung des markierten Textes ändern.
Hinweise: Erben Sie die Klasse von QMainWindow. Verwenden Sie QTextEdit::setFontWeight(), setFontItalic() und setFontUnderline() zum Ändern der Formatierung. Erstellen Sie QAction-Objekte mit den Methoden setText(), setIcon() und setToolTip(). Verbinden Sie das Signal triggered() jeder Aktion mit Slots, die die Formatierung ändern. Fügen Sie Aktionen zur Toolbar mit addAction() hinzu.
Mittleres Niveau

Anwendung mit anpassbaren Docks
Entwickeln Sie eine Anwendung mit zentralem Widget (QTextEdit) und zwei Dock-Widgets: eines zur Anzeige der Liste offener Dateien (QListWidget), das zweite für Dokumenteigenschaften (Anzahl Zeichen, Zeilen, Wörter in QLabel). Die Docks sollen links und rechts platziert werden, aber der Benutzer kann sie verschieben. Bei Texteingabe sollen die Informationen im rechten Dock in Echtzeit aktualisiert werden.
Hinweise: Erstellen Sie zwei QDockWidget und setzen Sie entsprechende Widgets mit der Methode setWidget() hinein. Verwenden Sie addDockWidget() mit den Parametern Qt::LeftDockWidgetArea und Qt::RightDockWidgetArea. Verbinden Sie das Signal textChanged() des Texteditors mit einem Slot, der die Statistik neu berechnet. Verwenden Sie QString::length(), split(‘\n’).size() und split(QRegularExpression(“\\s+”)).size() zum Zählen von Zeichen, Zeilen und Wörtern.
Schwieriges Niveau

Voll funktionsfähige MDI-Anwendung mit Behandlung nicht gespeicherter Änderungen
Implementieren Sie eine MDI-Anwendung für die Arbeit mit Textdokumenten, die das Erstellen, Öffnen und Speichern von Dateien unterstützt. Die Anwendung soll: nicht gespeicherte Änderungen in jedem Dokument verfolgen (Sternchen im Titel anzeigen), beim Schließen eines Fensters mit nicht gespeicherten Daten warnen, das Schließen der gesamten Anwendung mit Überprüfung aller offenen Dokumente korrekt behandeln, ein Windows-Menü mit Befehlen Cascade und Tile implementieren sowie eine Liste aller offenen Dokumente mit Wechselmöglichkeit zwischen ihnen.
Hinweise: Erben Sie die Dokumentklasse von QTextEdit und fügen Sie Änderungsverfolgung über document()->setModified() hinzu. Überschreiben Sie closeEvent() sowohl im Dokumentfenster als auch im Hauptfenster. In closeEvent() des Hauptfensters verwenden Sie eine Schleife über m_pma->subWindowList() zur Überprüfung von isModified() aller Dokumente. Verwenden Sie QMessageBox::question() mit Buttons Save/Discard/Cancel. Aktualisieren Sie den Fenstertitel durch Hinzufügen von “[*]” und Aufruf von setWindowModified(true). Im Windows-Menü verwenden Sie eine Schleife zum Erstellen von Aktionen mit Dokumentnamen und setCheckable(true) für das aktive Fenster.

💬 Treten Sie der Diskussion bei!

Haben Sie die Architektur von MDI-Anwendungen verstanden? Haben Sie Fragen, wann QDockWidget statt Toolbar verwendet werden soll?

Sind Schwierigkeiten bei der Behandlung nicht gespeicherter Änderungen oder Oberflächensynchronisation über QAction aufgetreten?

Teilen Sie Ihre Erfahrungen bei der Implementierung des Anwendungshauptfensters, diskutieren Sie Best Practices bei der Arbeit mit Dokumenten oder stellen Sie Fragen an die Community!

Leave a Reply

Your email address will not be published. Required fields are marked *