Qt Widgets - Text Viewer Plugin Beispiel
Ein Widget-Beispiel mit Menüs, Symbolleisten und einer Statusleiste.
Das Text-Viewer-Beispiel ist ein Text-Editor, der auf QPlainTextEdit aufbaut, und zwar in Form eines Plugins für einen Allzweck-Dokumenten-Viewer.
Der gesamte Code für das Text-Viewer-Beispiel befindet sich in der Klasse TxtViewer
, die von AbstractViewer
erbt. AbstractViewer
bietet den Rahmen für die Interaktion zwischen einem Viewer und dem Hauptfenster. Die Anwendung bietet die Einträge File, Edit und Help in der Menüleiste.
Die Statusleiste am unteren Rand des Hauptfensters zeigt eine Nachricht an, die die Anwendung dem Benutzer übermittelt.
Die zuletzt geöffneten Dateien werden im Menü File angezeigt. Das Beispiel kann immer nur eine Datei gleichzeitig laden.
Klassendefinition
class TxtViewer : public ViewerInterface { Q_OBJECT Q_PLUGIN_METADATA(IID "org.qt-project.Qt.Examples.DocumentViewer.ViewerInterface" FILE "txtviewer.json") Q_INTERFACES(ViewerInterface)
Die Klassendefinition beginnt mit dem Makro Q_OBJECT
, das Signale und Slots verarbeitet. Es folgen die Makros Q_PLUGIN_METADATA
und Q_INTERFACES
, die zur Registrierung des Plugins erforderlich sind.
Die Klasse erbt von ViewerInterface
, die von AbstractViewer
erbt. Die Klasse ViewerInterface
wird verwendet, um eine Schnittstelle zwischen der Hauptanwendung und dem Plugin bereitzustellen.
QPluginLoader
Die Klasse txtviewer benötigt außerdem die Datei txtviewer.json, die den Schlüssel des Plugins enthalten muss:
{ "Keys": [ "txtviewer" ] } public: TxtViewer(); ~TxtViewer() override; void init(QFile *file, QWidget *parent, QMainWindow *mainWindow) override; QString viewerName() const override { return QLatin1StringView(staticMetaObject.className()); }; QStringList supportedMimeTypes() const override; bool saveDocument() override { return saveFile(m_file.get()); }; bool saveDocumentAs() override; bool hasContent() const override; QByteArray saveState() const override { return {}; } bool restoreState(QByteArray &) override { return true; } bool supportsOverview() const override { return false; } #ifdef QT_DOCUMENTVIEWER_PRINTSUPPORT protected: void printDocument(QPrinter *printer) const override; #endif // QT_DOCUMENTVIEWER_PRINTSUPPORT private slots: void setupTxtUi(); private: void openFile(); bool saveFile (QFile *file); QPlainTextEdit *m_textEdit; };
Die Klasse definiert keinen Konstruktor, was bedeutet, dass nur ein Standardkonstruktor ohne Argumente zur Verfügung steht. Alle anderen Funktionen, einschließlich des Destruktors, re-implementieren virtuelle Funktionen von ViewerInterface
. Sie werden verwendet, um Daten, Informationen und Anweisungen mit der Hauptanwendung auszutauschen.
Es ist keine Funktionalität zum Speichern und Wiederherstellen von Einstellungen implementiert. Die Funktion supportsOverview
gibt immer false
zurück, was der Hauptanwendung mitteilt, dass kein Fenster für die Miniaturbildnavigation angezeigt werden muss.
Implementierung der TxtViewer-Klasse
#include "txtviewer.h" #include <QFileDialog> #include <QMainWindow> #include <QMenu> #include <QMenuBar> #include <QPlainTextEdit> #include <QScrollBar> #include <QToolBar> #include <QGuiApplication> #include <QPainter> #include <QTextDocument> #include <QDir> #ifdef QT_DOCUMENTVIEWER_PRINTSUPPORT #include <QPrinter> #include <QPrintDialog> #endif using namespace Qt::StringLiterals; TxtViewer::TxtViewer() { connect(this, &AbstractViewer::uiInitialized, this, &TxtViewer::setupTxtUi); } TxtViewer::~TxtViewer() = default; void TxtViewer::init(QFile *file, QWidget *parent, QMainWindow *mainWindow) { AbstractViewer::init(file, new QPlainTextEdit(parent), mainWindow); m_textEdit = qobject_cast<QPlainTextEdit *>(widget()); } QStringList TxtViewer::supportedMimeTypes() const { return {"text/plain"_L1}; } void TxtViewer::setupTxtUi() { QMenu *editMenu = addMenu(tr("&Edit")); QToolBar *editToolBar = addToolBar(tr("Edit")); #if QT_CONFIG(clipboard) const QIcon cutIcon = QIcon::fromTheme(QIcon::ThemeIcon::EditCut, QIcon(":/demos/documentviewer/images/cut.png"_L1)); QAction *cutAct = new QAction(cutIcon, tr("Cu&t"), this); cutAct->setShortcuts(QKeySequence::Cut); cutAct->setStatusTip(tr("Cut the current selection's contents to the " "clipboard")); connect(cutAct, &QAction::triggered, m_textEdit, &QPlainTextEdit::cut); editMenu->addAction(cutAct); editToolBar->addAction(cutAct); const QIcon copyIcon = QIcon::fromTheme(QIcon::ThemeIcon::EditCopy, QIcon(":/demos/documentviewer/images/copy.png"_L1)); QAction *copyAct = new QAction(copyIcon, tr("&Copy"), this); copyAct->setShortcuts(QKeySequence::Copy); copyAct->setStatusTip(tr("Copy the current selection's contents to the " "clipboard")); connect(copyAct, &QAction::triggered, m_textEdit, &QPlainTextEdit::copy); editMenu->addAction(copyAct); editToolBar->addAction(copyAct); const QIcon pasteIcon = QIcon::fromTheme(QIcon::ThemeIcon::EditPaste, QIcon(":/demos/documentviewer/images/paste.png"_L1)); QAction *pasteAct = new QAction(pasteIcon, tr("&Paste"), this); pasteAct->setShortcuts(QKeySequence::Paste); pasteAct->setStatusTip(tr("Paste the clipboard's contents into the current " "selection")); connect(pasteAct, &QAction::triggered, m_textEdit, &QPlainTextEdit::paste); editMenu->addAction(pasteAct); editToolBar->addAction(pasteAct); menuBar()->addSeparator(); cutAct->setEnabled(false); copyAct->setEnabled(false); connect(m_textEdit, &QPlainTextEdit::copyAvailable, cutAct, &QAction::setEnabled); connect(m_textEdit, &QPlainTextEdit::copyAvailable, copyAct, &QAction::setEnabled); #endif // QT_CONFIG(clipboard) openFile(); connect(m_textEdit, &QPlainTextEdit::textChanged, this, [&](){ maybeSetPrintingEnabled(hasContent()); }); connect(m_uiAssets.back, &QAction::triggered, m_textEdit, [&](){ auto *bar = m_textEdit->verticalScrollBar(); if (bar->value() > bar->minimum()) bar->setValue(bar->value() - 1); }); connect(m_uiAssets.forward, &QAction::triggered, m_textEdit, [&](){ auto *bar = m_textEdit->verticalScrollBar(); if (bar->value() < bar->maximum()) bar->setValue(bar->value() + 1); }); }
Wir beginnen mit der Einbindung der Header-Dateien, die für den Zugriff auf alle von TxtViewer
verwendeten Klassen erforderlich sind. Wir binden auch txtviewer.h
ein.
QPrinter
und QPrintDialog
sind nur enthalten, wenn die Druckunterstützung auf dem Kompilierungssystem aktiviert ist.
Sie fragen sich vielleicht, warum wir diese Header nicht in mainwindow.h
einbinden und damit fertig sind. Der Grund dafür ist, dass das Einbinden mehrerer großer Header aus einer anderen Header-Datei die Leistung schnell beeinträchtigen kann. In diesem Fall würde es nicht schaden, aber es ist im Allgemeinen trotzdem eine gute Idee, nur die Headerdateien aus einer anderen Headerdatei einzubinden, die unbedingt erforderlich sind.
Die Implementierung beginnt mit einem leeren Destruktor. Er könnte komplett weggelassen werden. Es ist eine gute Praxis, ihn leer zu implementieren, um den Lesern des Codes zu zeigen, dass im Destruktor nichts getan werden muss.
Auf den Destruktor folgt eine Initialisierungsfunktion, die drei Argumente entgegennimmt:
file
, den Zeiger auf die zu öffnende und anzuzeigende Datei.parent
, der Zeiger auf dieQWidget
, in der der Editor platziert werden soll.mainWindow
, der auf das Hauptfenster der Anwendung zeigt, in dem die Menüs und Menüleisten verwaltet werden.
Die Funktion ruft die Basisfunktion init von AbstractViwer
auf. Es wird ein neues Widget QPlainTextEdit erstellt, das den Inhalt der Datei anzeigt. Anschließend wird die Setup-Funktion von TxtViewer
mit dem Signal uiInitialized der Basisklasse verbunden.
Die nächste Funktion gibt die Liste der Mime-Typen zurück, die der Text-Viewer unterstützt. Es wird nur einfacher Text unterstützt.
Die letzte Initialisierungsfunktion fügt Viewer-spezifische UI-Komponenten wie Menüs, Icons, Schaltflächen und Tooltips hinzu. Sie verwendet die von AbstractViewer
bereitgestellte Funktionalität, um sicherzustellen, dass diese Komponenten aus dem Hauptfenster der Anwendung entfernt werden, sobald eine andere Datei mit einem anderen Viewer-Plugin angezeigt wird.
void TxtViewer::openFile() { const QString type = tr("open"); if (!m_file->open(QFile::ReadOnly | QFile::Text)) { statusMessage(tr("Cannot read file %1:\n%2.") .arg(QDir::toNativeSeparators(m_file->fileName()), m_file->errorString()), type); return; } QTextStream in(m_file.get()); #ifndef QT_NO_CURSOR QGuiApplication::setOverrideCursor(Qt::WaitCursor); #endif if (!m_textEdit->toPlainText().isEmpty()) { m_textEdit->clear(); disablePrinting(); } m_textEdit->setPlainText(in.readAll()); #ifndef QT_NO_CURSOR QGuiApplication::restoreOverrideCursor(); #endif statusMessage(tr("File %1 loaded.") .arg(QDir::toNativeSeparators(m_file->fileName())), type); maybeEnablePrinting(); }
openFile
öffnet eine Datei, überträgt ihren Inhalt in das QPlainTextEdit und gibt eine Statusmeldung für den Benutzer aus, je nachdem, ob das Öffnen erfolgreich war oder nicht.
bool TxtViewer::hasContent() const { return (!m_textEdit->toPlainText().isEmpty()); } #ifdef QT_DOCUMENTVIEWER_PRINTSUPPORT void TxtViewer::printDocument(QPrinter *printer) const { if (!hasContent()) return; m_textEdit->print(printer); } #endif // QT_DOCUMENTVIEWER_PRINTSUPPORT bool TxtViewer::saveFile(QFile *file) { QString errorMessage; QGuiApplication::setOverrideCursor(Qt::WaitCursor); if (file->open(QFile::WriteOnly | QFile::Text)) { QTextStream out(file); out << m_textEdit->toPlainText(); } else { errorMessage = tr("Cannot open file %1 for writing:\n%2.") .arg(QDir::toNativeSeparators(file->fileName())), file->errorString(); } QGuiApplication::restoreOverrideCursor(); if (!errorMessage.isEmpty()) { statusMessage(errorMessage); return false; } statusMessage(tr("File %1 saved") .arg(QDir::toNativeSeparators(file->fileName()))); return true; } bool TxtViewer::saveDocumentAs() { QFileDialog dialog(mainWindow()); dialog.setWindowModality(Qt::WindowModal); dialog.setAcceptMode(QFileDialog::AcceptSave); if (dialog.exec() != QDialog::Accepted) return false; const QStringList &files = dialog.selectedFiles(); if (files.isEmpty()) return false; //newFile(); m_file->setFileName(files.first()); return saveDocument(); }
Die nächste neu implementierte Funktion teilt der Hauptanwendung mit, ob das Viewer-Plugin den Inhalt tatsächlich anzeigt oder nicht.
Wenn das Drucken auf dem kompilierenden System unterstützt wird, wird es im nächsten Abschnitt implementiert.
Die letzten beiden Neuimplementierungen bieten Funktionen zum Speichern der aktuellen Datei oder zum Speichern unter einem neuen Namen.
© 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.