Dokument-Viewer

Eine Widgets-Anwendung zum Anzeigen und Drucken von JSON-, Text- und PDF-Dateien.

Document Viewer demonstriert die Verwendung einer QMainWindow mit statischen und dynamischen Symbolleisten, Menüs und Aktionen. Außerdem werden die folgenden Funktionen in Widget-basierten Anwendungen demonstriert:

  • Verwendung von QSettings zum Abfragen und Speichern von Benutzereinstellungen und zum Verwalten des Verlaufs zuvor geöffneter Dateien.
  • Steuerung des Cursor-Verhaltens, wenn der Mauszeiger über Widgets bewegt wird.
  • Erstellen von dynamisch geladenen Plugins.

Erstellen einer Anwendung und des Hauptfensters

Die Anwendung und ihr Hauptfenster werden in main.cpp erstellt. Die Funktion main() verwendet QCommandLineParser, um Befehlszeilenargumente zu verarbeiten - help, version und ein optionales Positionsargument, file. Wenn der Benutzer beim Starten der Anwendung einen Pfad zu einer Datei angegeben hat, wird diese im Hauptfenster geöffnet:

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    QCoreApplication::setOrganizationName("QtProject"_L1);
    QCoreApplication::setApplicationName("DocumentViewer"_L1);
    QCoreApplication::setApplicationVersion("1.0"_L1);

    QCommandLineParser parser;
    parser.setApplicationDescription(QApplication::translate("main",
                                                     "A viewer for JSON, PDF and text files"));
    parser.addHelpOption();
    parser.addVersionOption();
    parser.addPositionalArgument("File"_L1, QApplication::translate("main",
                                                                    "JSON, PDF or text file to open"));
    parser.process(app);

    const QStringList &positionalArguments = parser.positionalArguments();
    const QString &fileName = (positionalArguments.count() > 0) ? positionalArguments.at(0)
                                                                : QString();

    MainWindow w;

    // Start application only if plugins are available
    if (!w.hasPlugins()) {
        QMessageBox::critical(nullptr,
                              "No viewer plugins found"_L1,
                              "Unable to load viewer plugins. Exiting application."_L1);
        return 1;
    }

    w.show();
    if (!fileName.isEmpty())
        w.openFile(fileName);

    return app.exec();
}

MainWindow-Klasse

Die Klasse MainWindow bietet einen Anwendungsbildschirm mit Menüs, Aktionen und einer Symbolleiste. Sie kann eine Datei öffnen und erkennt automatisch ihren Inhaltstyp. Sie verwaltet außerdem eine Liste der zuvor geöffneten Dateien und verwendet QSettings, um die Einstellungen zu speichern und beim Start neu zu laden. Das MainWindow erstellt einen geeigneten Betrachter für die geöffnete Datei, basierend auf ihrem Inhaltstyp, und bietet Unterstützung für das Drucken eines Dokuments.

Der Konstruktor von MainWindow initialisiert die in Qt Designer erstellte Benutzeroberfläche. Die Datei mainwindow.ui bietet auf der linken Seite eine QTabWidget, die Lesezeichen und Miniaturansichten anzeigt. Auf der rechten Seite befindet sich ein QScrollArea zur Anzeige des Dateiinhalts.

ViewerFactory-Klasse

Die Klasse ViewerFactory verwaltet Viewer für bekannte Dateitypen. Diese Betrachter werden als Plugins implementiert. Wenn eine Instanz einer ViewerFactory erstellt wird, werden Zeiger auf den Ansichtsbereich und das Hauptfenster an den Konstruktor übergeben:

m_factory.reset(new ViewerFactory(ui->viewArea, this));

ViewerFactory lädt alle verfügbaren Plugins bei der Erstellung. Sie bietet eine öffentliche API um die geladenen Plugins, ihre Namen und unterstützten MIME Typen abzufragen:

    using ViewerList = QList<AbstractViewer *>;
    QStringList viewerNames(bool showDefault = false) const;
    ViewerList viewers() const;
    AbstractViewer *findViewer(const QString &viewerName) const;
    AbstractViewer *defaultViewer() const;
    QStringList supportedMimeTypes() const;

Die Funktion viewer() gibt einen Zeiger auf das Plugin zurück, das zum Öffnen des als Argument übergebenen QFile geeignet ist:

m_viewer = m_factory->viewer(file);

Wenn die Anwendungseinstellungen einen Abschnitt für den Viewer enthalten, wird dieser an die virtuelle Funktion restoreState() des Viewers übergeben:

void MainWindow::restoreViewerSettings()
{
    if (!m_viewer)
        return;

    QSettings settings;
    settings.beginGroup(settingsViewers);
    QByteArray viewerSettings = settings.value(m_viewer->viewerName(), QByteArray()).toByteArray();
    settings.endGroup();
    if (!viewerSettings.isEmpty())
        m_viewer->restoreState(viewerSettings);
}

Dann werden die Standard-UI-Assets an den Viewer übergeben und der Hauptbildlaufbereich wird so eingestellt, dass er das Anzeige-Widget des Viewers anzeigt:

    m_viewer->initViewer(ui->actionBack, ui->actionForward, ui->menuHelp->menuAction(), ui->tabWidget);
    restoreViewerSettings();
    ui->scrollArea->setWidget(m_viewer->widget());
    return true;
}

AbstractViewer-Klasse

AbstractViewer bietet eine verallgemeinerte API zum Anzeigen, Speichern und Drucken eines Dokuments. Eigenschaften sowohl des Dokuments als auch des Viewers können abgefragt werden:

  • Hat das Dokument einen Inhalt?
  • Wurde es geändert?
  • Wird eine Übersicht (Miniaturansichten oder Lesezeichen) unterstützt?

AbstractViewer bietet geschützte Methoden für abgeleitete Klassen, um Aktionen und Menüs im Hauptfenster zu erstellen. Damit diese Elemente im Hauptfenster angezeigt werden können, werden sie diesem übergeordnet. AbstractViewer ist für das Entfernen und Zerstören der von ihm erstellten UI-Assets verantwortlich. Er erbt von QObject, um Signale und Slots zu implementieren.

Signale

void uiInitialized();

Dieses Signal wird ausgegeben, nachdem ein Viewer alle notwendigen Informationen über UI-Assets im Hauptfenster erhalten hat.

void printingEnabledChanged(bool enabled);

Dieses Signal wird ausgegeben, wenn der Dokumentendruck entweder aktiviert oder deaktiviert ist. Dies geschieht, nachdem ein neues Dokument erfolgreich geladen wurde oder z. B. der gesamte Inhalt entfernt wurde.

void printStatusChanged(AbstractViewer::PrintStatus status);

Nach dem Start des Druckvorgangs informiert dieses Signal über Änderungen in dessen Verlauf.

void documentLoaded(const QString &fileName);

Dieses Signal teilt der Anwendung mit, dass ein Dokument erfolgreich geladen wurde.

TxtViewer-Klasse

TxtViewer ist ein einfacher Textbetrachter, der von AbstractViewer erbt. Sie unterstützt das Bearbeiten von Textdateien, Kopieren/Ausschneiden und Einfügen, Drucken und Speichern von Änderungen.

JsonViewer-Klasse

JsonViewer zeigt eine JSON-Datei in einem QTreeView an. Intern lädt sie den Inhalt einer Datei in ein QJsonDocument und verwendet ihn, um ein benutzerdefiniertes Baummodell mit JsonItemModel zu füllen.

Das JSON-Viewer-Plugin demonstriert, wie ein von QAbstractItemModel geerbtes benutzerdefiniertes Elementmodell implementiert wird. Die Klasse JsonTreeItem bietet eine grundlegende API für die Bearbeitung von JSON-Daten und deren Rückübertragung an die zugrunde liegende QJsonDocument.

JsonViewer verwendet die Top-Level-Objekte des Dokuments als Lesezeichen für die Navigation. Andere Knoten (Schlüssel und Werte) können als zusätzliche Lesezeichen hinzugefügt oder aus der Lesezeichenliste entfernt werden. Ein QLineEdit wird als Suchfeld verwendet, um durch den JSON-Baum zu navigieren.

PdfViewer-Klasse

Die Klasse PdfViewer (und das Plugin) ist ein Fork des PDF Viewer Widget Beispiels. Sie demonstriert die Verwendung von QScroller, um reibungslos durch ein Dokument zu blättern.

Andere relevante Klassen

HoverWatcher-Klasse

Die Klasse HoverWatcher setzt einen Override-Cursor, wenn die Maus über einem Widget schwebt, und stellt ihn beim Verlassen wieder her. Um zu verhindern, dass mehrere HoverWatcher-Instanzen für dasselbe Widget erstellt werden, ist sie als Singleton pro Widget implementiert.

HoverWatcher erbt von QObject und nimmt das QWidget, das er beobachtet, als Elternteil der Instanz. Er installiert einen Ereignisfilter, um Hover-Ereignisse abzufangen, ohne sie zu konsumieren:

HoverWatcher::HoverWatcher(QWidget *watched)
    : QObject(watched), m_watched(watched)
{
    Q_ASSERT(watched);
    m_cursorShapes[Entered].emplace(Qt::OpenHandCursor);
    m_cursorShapes[MousePress].emplace(Qt::ClosedHandCursor);
    m_cursorShapes[MouseRelease].emplace(Qt::OpenHandCursor);
    // no default for Left => restore override cursor
    m_watched->installEventFilter(this);
}

Das HoverAction enum listet die Aktionen auf, auf die HoverWatcher reagiert:

    enum HoverAction {
        Entered,
        MousePress,
        MouseRelease,
        Left,
        Ignore
    };

Statische Funktionen erstellen Beobachter, prüfen deren Existenz für eine bestimmte QWidget, oder beenden einen Beobachter:

    static HoverWatcher *watcher(QWidget *watched);
    static const HoverWatcher *watcher(const QWidget *watched);
    static bool hasWatcher(QWidget *widget);
    static void dismiss(QWidget *watched);

Für jede HoverAction kann eine Cursorform gesetzt oder nicht gesetzt werden. Wenn es keine zugehörige Cursorform gibt, wird der Override-Cursor der Anwendung wiederhergestellt, wenn die Aktion ausgelöst wird.

public slots:
    void setCursorShape(HoverAction type, Qt::CursorShape shape);
    void unSetCursorShape(HoverAction type);

Die Eigenschaft mouseButtons enthält die zu berücksichtigenden Maustasten für eine MousePress Aktion:

    void setMouseButtons(Qt::MouseButtons buttons);
    void setMouseButton(Qt::MouseButton button, bool enable);

Aktionsspezifische Signale werden nach der Verarbeitung einer Aktion ausgegeben:

signals:
    void entered();
    void mousePressed();
    void mouseReleased();
    void left();

Es wird ein allgemeines Signal ausgegeben, das die verarbeitete Aktion als Argument übergibt:

void hoverAction(HoverAction action);
Die Klasse RecentFiles

RecentFiles ist eine QStringList, die darauf spezialisiert ist, eine Liste von kürzlich geöffneten Dateien zu verwalten.

RecentFiles hat Slots, um entweder eine einzelne Datei oder mehrere Dateien auf einmal hinzuzufügen. Ein Eintrag wird in die Liste der zuletzt geöffneten Dateien aufgenommen, wenn der Pfad auf eine Datei zeigt, die existiert und geöffnet werden kann. Befindet sich eine Datei bereits in der Liste, wird sie von ihrer ursprünglichen Position entfernt und an den Anfang gesetzt.

public slots:
    void addFile(const QString &fileName) { addFile(fileName, EmitPolicy::EmitWhenChanged); }
    void addFiles(const QStringList &fileNames);

Dateien werden entweder nach Namen oder nach Index aus der Liste entfernt:

    void removeFile(const QString &fileName) { removeFile(m_files.indexOf(fileName)); }
    void removeFile(qsizetype index) {removeFile(index, RemoveReason::Other); }

Slots, die das Speichern und Wiederherstellen von QSettings ermöglichen:

    void saveSettings(QSettings &settings, const QString &key) const;
    bool restoreFromSettings(QSettings &settings, const QString &key);

Bei der Wiederherstellung von Einstellungen werden nicht vorhandene Dateien ignoriert. Die Eigenschaft maxFiles enthält die maximale Anzahl der zu speichernden aktuellen Dateien (Standard ist 10).

qsizetype maxFiles();
void setMaxFiles(qsizetype maxFiles);

RecentFiles prüft, ob eine Datei gelesen werden kann, bevor sie akzeptiert wird.

RecentFileMenu-Klasse

RecentFileMenu ist eine QMenu, die darauf spezialisiert ist, ein RecentFiles-Objekt als Untermenü anzuzeigen.

Ihr Konstruktor nimmt einen Zeiger auf ein übergeordnetes QObject und einen Zeiger auf ein RecentFiles-Objekt entgegen, dessen Inhalt sie visualisieren wird. Das Signal fileOpened(), das ausgelöst wird, wenn der Benutzer eine neue Datei aus der Liste auswählt, übergibt den absoluten Pfad zu der Datei als Argument.

Hinweis: RecentFileMenu wird entweder durch sein übergeordnetes Widget oder durch das RecentFiles Objekt, das an seinen Konstruktor übergeben wird, zerstört.

class RecentFileMenu : public QMenu
{
    Q_OBJECT

public:
    explicit RecentFileMenu(QWidget *parent, RecentFiles *recent);

signals:
    void fileOpened(const QString &fileName);
    ...
};

Beispielprojekt @ code.qt.io

© 2025 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.