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

Der Dokument-Viewer veranschaulicht die Verwendung eines „ QMainWindow “ mit statischen und dynamischen Symbolleisten, Menüs und Aktionen. Darüber hinaus demonstriert er die folgenden Funktionen in Widget-basierten Anwendungen:
- Verwendung von „ QSettings “ zum Abfragen und Speichern von Benutzereinstellungen sowie zum Verwalten des Verlaufs zuvor geöffneter Dateien.
- Steuerung des Cursorverhaltens beim Bewegen des Mauszeigers über Widgets.
- Erstellen dynamisch geladener Plugins.
- Lokalisierung der Benutzeroberfläche in verschiedene Sprachen.
Ausführen des Beispiels
Sie können das Beispiel wie folgt ausführen:
- Qt Creator
Öffnen Sie den Modus „ Welcome “ und wählen Sie das Beispiel unter „ Examples “ aus. Weitere Informationen finden Sie unter Qt Creator: Tutorial: Erstellen und Ausführen.
- Qt Extension for Visual Studio Code
Führen Sie den Befehl „ Qt: Open Qt examples “ über Command Palette aus und wählen Sie das Beispiel aus der Liste aus. Weitere Informationen finden Sie unter Qt Extension for Visual Studio Code: Tutorial: Erstellen und Ausführen.
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 positionsabhängiges Argument, „file“. Wenn der Benutzer beim Starten der Anwendung einen Pfad zu einer Datei angegeben hat, öffnet das Hauptfenster diese Datei:
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QCoreApplication::setOrganizationName("QtProject"_L1);
QCoreApplication::setApplicationName("DocumentViewer"_L1);
QCoreApplication::setApplicationVersion("1.0"_L1);
Translator mainTranslator;
mainTranslator.setBaseName("docviewer"_L1);
mainTranslator.install();
QCommandLineParser parser;
parser.setApplicationDescription(Tr::tr("A viewer for JSON, PDF and text files"));
parser.addHelpOption();
parser.addVersionOption();
parser.addPositionalArgument("File"_L1, Tr::tr("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(mainTranslator);
// Start application only if plugins are available
if (!w.hasPlugins()) {
QMessageBox::critical(nullptr,
Tr::tr("No viewer plugins found"),
Tr::tr("Unable to load viewer plugins. Exiting application."));
return 1;
}
w.show();
if (!fileName.isEmpty())
w.openFile(fileName);
return app.exec();
}Klasse „MainWindow“
Die Klasse „ MainWindow “ stellt eine Anwendungsoberfläche mit Menüs, Aktionen und einer Symbolleiste bereit. Sie kann eine Datei öffnen und dabei deren Inhaltstyp automatisch erkennen. Außerdem verwaltet sie eine Liste zuvor geöffneter Dateien und nutzt dabei „ QSettings “, um Einstellungen zu speichern und beim Start wieder zu laden. Das „MainWindow“ erstellt je nach Inhaltstyp einen geeigneten Viewer für die geöffnete Datei und bietet Unterstützung für den Druck eines Dokuments.
Der Konstruktor von `MainWindow` initialisiert die in ` Qt Designer` erstellte Benutzeroberfläche. Die Datei ` mainwindow.ui ` enthält links einen ` QTabWidget `, der Lesezeichen und Miniaturansichten anzeigt. Rechts befindet sich ein ` QScrollArea ` zum Anzeigen des Dateiinhalts.
Klasse „ViewerFactory“
Die Klasse „ ViewerFactory “ verwaltet Viewer für bekannte Dateitypen. Diese Viewer sind als Plugins implementiert. Beim Erstellen einer Instanz von „ViewerFactory“ werden Zeiger auf den Anzeigebereich und das Hauptfenster an den Konstruktor übergeben:
m_factory.reset(new ViewerFactory(ui->viewArea, this));„ViewerFactory“ lädt bei der Erstellung alle verfügbaren Plugins. Sie stellt eine öffentliche API zur Abfrage der geladenen Plugins, ihrer Namen und der unterstützten MIME-Typen bereit:
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);
}Anschließend werden die Standard-UI-Elemente an den Viewer übergeben und der Haupt-Scrollbereich 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;
}Klasse `AbstractViewer`
AbstractViewer bietet eine verallgemeinerte API zum Anzeigen, Speichern und Drucken eines Dokuments. Eigenschaften sowohl des Dokuments als auch des Viewers können abgefragt werden:
- Enthält das Dokument Inhalt?
- Wurde es geändert?
- Wird eine Übersicht (Miniaturansichten oder Lesezeichen) unterstützt?
„AbstractViewer“ stellt geschützte Methoden für abgeleitete Klassen bereit, um Aktionen und Menüs im Hauptfenster zu erstellen. Um diese Elemente im Hauptfenster anzuzeigen, werden sie diesem als untergeordnete Elemente zugeordnet. „AbstractViewer“ ist dafür verantwortlich, die von ihm erstellten UI-Elemente zu entfernen und zu vernichten. Er erbt von „ QObject “, um Signale und Slots zu implementieren.
Signale
void uiInitialized();
Dieses Signal wird ausgelöst, nachdem ein Viewer alle erforderlichen Informationen über UI-Elemente im Hauptfenster erhalten hat.
void printingEnabledChanged(bool enabled);
Dieses Signal wird ausgelöst, wenn das Drucken von Dokumenten entweder aktiviert oder deaktiviert wird. Dies geschieht, nachdem ein neues Dokument erfolgreich geladen wurde oder beispielsweise der gesamte Inhalt entfernt wurde.
void showMessage(const QString &message, int timeout = 8000);
Dieses Signal wird ausgelöst, um dem Benutzer eine Statusmeldung anzuzeigen.
void documentLoaded(const QString &fileName);
Dieses Signal benachrichtigt die Anwendung, dass ein Dokument erfolgreich geladen wurde.
Die Klasse „TxtViewer“
TxtViewer ist ein einfacher Text-Viewer, der von `AbstractViewer` abgeleitet ist. Er unterstützt die Bearbeitung von Textdateien, das Kopieren, Ausschneiden und Einfügen, das Drucken sowie das Speichern von Änderungen.
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 verwaltet. Darauf folgen die Makros „ Q_PLUGIN_METADATA “ und „ Q_INTERFACES “, die für die Registrierung des Plugins erforderlich sind.
Die Klasse erbt von „ ViewerInterface “, die wiederum von „ AbstractViewer “ erbt. Die Klasse „ ViewerInterface “ dient dazu, eine Schnittstelle zwischen der Hauptfenster-Anwendung und dem Plugin bereitzustellen.
QPluginLoader Außerdem wird die Datei „txtviewer.json“ benötigt, die den Schlüssel des Plugins enthalten muss:
{ "Keys": [ "txtviewer" ] }Die Klasse definiert keinen Konstruktor, was bedeutet, dass nur ein Standardkonstruktor ohne Argumente verfügbar ist. Alle anderen Funktionen, einschließlich des Destruktors, implementieren virtuelle Funktionen von ` ViewerInterface` neu. Sie dienen dem Austausch von Daten, Informationen und Anweisungen mit der Hauptanwendung.
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 DOCUMENTVIEWER_PRINTSUPPORT
protected:
void printDocument(QPrinter *printer) const override;
#endif // DOCUMENTVIEWER_PRINTSUPPORT
private slots:
void setupTxtUi();
private:
void retranslate() override;
void openFile();
bool saveFile (QFile *file);
QPlainTextEdit *m_textEdit;
QAction *m_cutAct = nullptr;
QAction *m_copyAct = nullptr;
QAction *m_pasteAct = nullptr;
};Implementierung der Klasse „TxtViewer“
#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 DOCUMENTVIEWER_PRINTSUPPORT
#include <QPrinter>
#include <QPrintDialog>
#endif
using namespace Qt::StringLiterals;
TxtViewer::TxtViewer()
: m_cutAct(new QAction(this)),
m_copyAct(new QAction(this)),
m_pasteAct(new QAction(this))
{
connect(this, &AbstractViewer::uiInitialized, this, &TxtViewer::setupTxtUi);
const QIcon cutIcon = QIcon::fromTheme(QIcon::ThemeIcon::EditCut,
QIcon(":/demos/documentviewer/images/cut.png"_L1));
m_cutAct->setIcon(cutIcon);
m_cutAct->setShortcuts(QKeySequence::Cut);
const QIcon copyIcon = QIcon::fromTheme(QIcon::ThemeIcon::EditCopy,
QIcon(":/demos/documentviewer/images/copy.png"_L1));
m_copyAct->setIcon(copyIcon);
m_copyAct->setShortcuts(QKeySequence::Copy);
const QIcon pasteIcon = QIcon::fromTheme(QIcon::ThemeIcon::EditPaste,
QIcon(":/demos/documentviewer/images/paste.png"_L1));
m_pasteAct->setIcon(pasteIcon);
m_pasteAct->setShortcuts(QKeySequence::Paste);
}
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());
setTranslationBaseName("txtviewer"_L1);
}
QStringList TxtViewer::supportedMimeTypes() const
{
return {"text/plain"_L1};
}
void TxtViewer::setupTxtUi()
{
QMenu *editMenu = addMenu();
QToolBar *editToolBar = addToolBar();
#if QT_CONFIG(clipboard)
connect(m_cutAct, &QAction::triggered, m_textEdit, &QPlainTextEdit::cut);
editMenu->addAction(m_cutAct);
editToolBar->addAction(m_cutAct);
connect(m_copyAct, &QAction::triggered, m_textEdit, &QPlainTextEdit::copy);
editMenu->addAction(m_copyAct);
editToolBar->addAction(m_copyAct);
connect(m_pasteAct, &QAction::triggered, m_textEdit, &QPlainTextEdit::paste);
editMenu->addAction(m_pasteAct);
editToolBar->addAction(m_pasteAct);
menuBar()->addSeparator();
m_cutAct->setEnabled(false);
m_copyAct->setEnabled(false);
connect(m_textEdit, &QPlainTextEdit::copyAvailable, m_cutAct, &QAction::setEnabled);
connect(m_textEdit, &QPlainTextEdit::copyAvailable, m_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);
});
retranslate();
}Zunächst binden wir die Header-Dateien ein, die für den Zugriff auf alle von TxtViewer verwendeten Klassen erforderlich sind. Außerdem binden wir txtviewer.h ein.
QPrinter und QPrintDialog werden nur eingebunden, wenn die Druckunterstützung auf dem Kompilierungssystem aktiviert ist.
Beachten Sie, dass diese Header nicht direkt in mainwindow.h eingebunden werden. Das Einbinden umfangreicher Header in andere Header-Dateien kann die Build-Leistung beeinträchtigen. In diesem Fall würde dies zwar keine Probleme verursachen, es gilt jedoch als bewährte Vorgehensweise, nur die notwendigen Header einzubinden, um die Abhängigkeiten auf ein Minimum zu beschränken.
Die Implementierung beginnt mit einem leeren Destruktor. Dieser könnte vollständig weggelassen werden. Es ist jedoch empfehlenswert, ihn leer zu implementieren, um den Lesern des Codes deutlich zu machen, dass im Destruktor keine Aktionen erforderlich sind.
Auf den Destruktor folgt eine Initialisierungsfunktion, die drei Argumente entgegennimmt:
file, den Zeiger auf die zu öffnende und anzuzeigende Datei.parent, der auf das „QWidget“ verweist, in dem der Editor platziert werden soll.mainWindow, der auf das Hauptfenster der Anwendung verweist, in dem Menüs und Menüleisten verwaltet werden.
Die Funktion ruft die Basis-Initialisierungsfunktion von ` AbstractViewer` auf. Es wird ein neues ` QPlainTextEdit `-Widget erstellt, das den Inhalt der Datei anzeigt. Anschließend wird die Setup-Funktion von ` TxtViewer` mit dem Signal ` uiInitialized ` der Basisklasse verknüpft.
Die nächste Funktion gibt die Liste der MIME-Typen zurück, die der Text-Viewer unterstützt. Es wird nur reiner Text unterstützt.
Die letzte Initialisierungsfunktion fügt visorspezifische UI-Komponenten wie Menüs, Symbole, Schaltflächen und Tooltips hinzu. Sie nutzt die von AbstractViewer bereitgestellten Funktionen, 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());
#if QT_CONFIG(cursor)
QGuiApplication::setOverrideCursor(Qt::WaitCursor);
#endif
if (!m_textEdit->toPlainText().isEmpty()) {
m_textEdit->clear();
disablePrinting();
}
m_textEdit->setPlainText(in.readAll());
#if QT_CONFIG(cursor)
QGuiApplication::restoreOverrideCursor();
#endif
statusMessage(tr("File %1 loaded.")
.arg(QDir::toNativeSeparators(m_file->fileName())), type);
maybeEnablePrinting();
}openFile öffnet eine Datei, überträgt deren Inhalt in das „ QPlainTextEdit “ und gibt je nachdem, ob das Öffnen erfolgreich war oder nicht, eine Statusmeldung für den Benutzer aus.
bool TxtViewer::hasContent() const
{
return (!m_textEdit->toPlainText().isEmpty());
}
#ifdef DOCUMENTVIEWER_PRINTSUPPORT
void TxtViewer::printDocument(QPrinter *printer) const
{
if (!hasContent())
return;
m_textEdit->print(printer);
}
#endif // 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();
}
void TxtViewer::retranslate()
{
if (toolBars().isEmpty())
return;
menus().at(0)->setTitle(tr("&Edit"));
toolBars().at(0)->setWindowTitle(tr("Edit"));
m_cutAct->setText(tr("Cu&t"));
m_cutAct->setStatusTip(tr("Cut the current selection's contents to the clipboard"));
m_copyAct->setText(tr("&Copy"));
m_copyAct->setStatusTip(tr("Copy the current selection's contents to the clipboard"));
m_pasteAct->setText(tr("&Paste"));
m_pasteAct->setStatusTip(tr("Paste the clipboard's contents into the current selection"));
}Die nächste neu implementierte Funktion teilt der Hauptanwendung mit, ob das Viewer-Plugin tatsächlich Inhalte anzeigt.
Sofern das Drucken auf dem Kompilierungssystem 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.
Klasse „ImageViewer“
ImageViewer zeigt Bilder gemäß der von „ QImageReader “ unterstützten Formate an und verwendet dabei einen „ QLabel “.
Im Konstruktor erhöhen wir das Zuweisungslimit von ` QImageReader `, um größere Fotos zu ermöglichen:
ImageViewer::ImageViewer()
: m_zoomInAct(new QAction(this)),
m_zoomOutAct(new QAction(this)),
m_resetZoomAct(new QAction(this)),
m_formats(imageFormats())
{
connect(this, &AbstractViewer::uiInitialized, this, &ImageViewer::setupImageUi);
QImageReader::setAllocationLimit(1024); // MB
m_zoomInAct->setIcon(QIcon::fromTheme(QIcon::ThemeIcon::ZoomIn));
m_zoomInAct->setShortcut(QKeySequence::ZoomIn);
connect(m_zoomInAct, &QAction::triggered, this, &ImageViewer::zoomIn);
m_zoomOutAct->setIcon(QIcon::fromTheme(QIcon::ThemeIcon::ZoomOut));
m_zoomOutAct->setShortcut(QKeySequence::ZoomOut);
connect(m_zoomOutAct, &QAction::triggered, this, &ImageViewer::zoomOut);
m_resetZoomAct->setIcon(QIcon::fromTheme(QIcon::ThemeIcon::ZoomFitBest));
m_resetZoomAct->setShortcut(QKeySequence(Qt::ControlModifier | Qt::Key_0));
connect(m_resetZoomAct, &QAction::triggered, this, &ImageViewer::resetZoom);
}In der Funktion `openFile()` laden wir das Bild und ermitteln seine Größe. Ist es größer als der Bildschirm, skalieren wir es unter Beibehaltung des Seitenverhältnisses auf Bildschirmgröße herunter. Diese Berechnung muss in nativen Pixeln erfolgen, und das Geräte-Pixelverhältnis muss für die resultierende Pixmap festgelegt werden, damit sie scharf dargestellt wird:
void ImageViewer::openFile()
{
#if QT_CONFIG(cursor)
QGuiApplication::setOverrideCursor(Qt::WaitCursor);
#endif
const QString name = m_file->fileName();
QImageReader reader(name);
const QImage origImage = reader.read();
if (origImage.isNull()) {
statusMessage(tr("Cannot read file %1:\n%2.")
.arg(QDir::toNativeSeparators(name),
reader.errorString()), tr("open"));
disablePrinting();
#if QT_CONFIG(cursor)
QGuiApplication::restoreOverrideCursor();
#endif
return;
}
clear();
QImage image = origImage.colorSpace().isValid()
? origImage.convertedToColorSpace(QColorSpace::SRgb)
: origImage;
const auto devicePixelRatio = m_imageLabel->devicePixelRatioF();
m_imageSize = QSizeF(image.size()) / devicePixelRatio;
QPixmap pixmap = QPixmap::fromImage(image);
pixmap.setDevicePixelRatio(devicePixelRatio);
m_imageLabel->setPixmap(pixmap);
const QSizeF targetSize = m_imageLabel->parentWidget()->size();
if (m_imageSize.width() > targetSize.width()
|| m_imageSize.height() > targetSize.height()) {
m_initialScaleFactor = qMin(targetSize.width() / m_imageSize.width(),
targetSize.height() / m_imageSize.height());
}
m_maxScaleFactor = 3 * m_initialScaleFactor;
m_minScaleFactor = m_initialScaleFactor / 3;
doSetScaleFactor(m_initialScaleFactor);
statusMessage(msgOpen(name, origImage));
#if QT_CONFIG(cursor)
QGuiApplication::restoreOverrideCursor();
#endif
maybeEnablePrinting();
}Klasse „JsonViewer“
JsonViewer zeigt eine JSON-Datei in einem „ QTreeView “ an. Intern lädt sie den Inhalt einer Datei in ein „ QJsonDocument “ und füllt damit mithilfe von „ JsonItemModel “ ein benutzerdefiniertes Baummodell.
Das JSON-Viewer-Plugin veranschaulicht, wie ein benutzerdefiniertes Elementmodell implementiert wird, das von ` QAbstractItemModel` abgeleitet ist. Die Klasse ` JsonTreeItem ` stellt eine grundlegende API zur Bearbeitung von JSON-Daten und zur Rückübertragung dieser Daten an das zugrunde liegende ` QJsonDocument` bereit.
JsonViewer verwendet die Objekte der obersten Ebene 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.
Klasse „PdfViewer“
Die Klasse „ PdfViewer “ (und das dazugehörige Plugin) ist ein Fork des PDF-Viewer-Widget-Beispiels. Sie demonstriert die Verwendung von „ QScroller “, um nahtlos durch ein Dokument zu blättern.
Weitere relevante Klassen
Klasse „HoverWatcher“
Die Klasse „ HoverWatcher “ setzt einen eigenen Cursor, wenn der Mauszeiger über ein Widget bewegt wird, und stellt den ursprünglichen Cursor wieder her, sobald der Mauszeiger das Widget verlässt. 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 von ihm überwachte „ QWidget “ als übergeordnete Instanz an. Es installiert einen Ereignisfilter, um Hover-Ereignisse abzufangen, ohne sie zu verarbeiten:
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);
}Die Enumeration „ HoverAction “ listet die Aktionen auf, auf die HoverWatcher reagiert:
enum HoverAction {
Entered,
MousePress,
MouseRelease,
Left,
Ignore
};Statische Funktionen erstellen Watcher, prüfen deren Vorhandensein für eine bestimmte „ QWidget “ oder heben einen Watcher auf:
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 festgelegt oder aufgehoben werden. Ist keine Cursorform zugeordnet, wird beim Auslösen der Aktion der von der Anwendung überschriebene Cursor wiederhergestellt.
public slots:
void setCursorShape(HoverAction type, Qt::CursorShape shape);
void unSetCursorShape(HoverAction type);Die Eigenschaft „ mouseButtons “ enthält die Maustasten, die für eine „ MousePress “-Aktion berücksichtigt werden sollen:
void setMouseButtons(Qt::MouseButtons buttons);
void setMouseButton(Qt::MouseButton button, bool enable);Nach der Verarbeitung einer Aktion werden aktionsspezifische Signale ausgesendet:
signals:
void entered();
void mousePressed();
void mouseReleased();
void left();Es wird ein allgemeines Signal ausgesendet, das die verarbeitete Aktion als Argument übergibt:
void hoverAction(HoverAction action);Klasse „RecentFiles“
RecentFiles ist ein „ QStringList “, der speziell für die Verwaltung einer Liste zuletzt geöffneter Dateien ausgelegt ist.
„RecentFiles“ verfügt über Slots, um entweder eine einzelne Datei oder mehrere Dateien auf einmal hinzuzufügen. Ein Eintrag wird zur Liste der zuletzt geöffneten Dateien hinzugefügt, wenn der Pfad auf eine Datei verweist, 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 aus „ QSettings “ implementieren:
void saveSettings(QSettings &settings, const QString &key) const;
bool restoreFromSettings(QSettings &settings, const QString &key);Beim Wiederherstellen der Einstellungen werden nicht vorhandene Dateien ignoriert. Die Eigenschaft „ maxFiles “ gibt die maximale Anzahl der zu speichernden zuletzt verwendeten Dateien an (Standardwert ist 10).
qsizetype maxFiles();
void setMaxFiles(qsizetype maxFiles);RecentFiles überprüft, ob eine Datei gelesen werden kann, bevor sie angenommen wird.
Klasse „RecentFileMenu“
RecentFileMenu ist ein „ QMenu “, das speziell dafür ausgelegt ist, ein „RecentFiles “-Objekt als Untermenü anzuzeigen.
Ihr Konstruktor nimmt einen Zeiger auf ein übergeordnetes ` QObject ` sowie einen Zeiger auf ein `RecentFiles`-Objekt entgegen, dessen Inhalt sie visualisiert. Ihr Signal ` fileOpened() `, das ausgelöst wird, wenn der Benutzer eine kürzlich geöffnete Datei aus der Liste auswählt, übergibt den absoluten Pfad zur Datei als Argument.
Hinweis: „ RecentFileMenu “ wird entweder durch sein übergeordnetes Widget oder durch das an seinen Konstruktor übergebene „ RecentFiles “-Objekt zerstört.
class RecentFileMenu : public QMenu
{
Q_OBJECT
public:
explicit RecentFileMenu(QWidget *parent, RecentFiles *recent);
signals:
void fileOpened(const QString &fileName);
...
};Übersetzungen
Die Benutzeroberfläche der Anwendung ist auf Englisch und Deutsch verfügbar. Die Standardsprache wird von Qt automatisch ausgewählt: Deutsch, wenn die Systemsprache Deutsch ist; andernfalls Englisch. Außerdem kann der Benutzer die Sprache im Menü „ Help > Language “ umschalten. Jedes Plugin sowie die Hauptanwendung ist eigenständig dafür verantwortlich, seine eigenen Übersetzungen zur Laufzeit zu laden.
CMake-Integration
In der obersten Ebene der Datei „CMakeLists.txt“ werden die mitgelieferten Sprachen deklariert.
qt_standard_project_setup(REQUIRES 6.8
I18N_SOURCE_LANGUAGE en
I18N_TRANSLATED_LANGUAGES de
)Das Ziel „ documentviewer “ definiert die Hauptanwendung. Es speichert und lädt die lokalisierten Zeichenfolgen für das Ziel in den Dateien „docviewer_de.ts“ und „docviewer_en.ts“. Darüber hinaus fügt es die entsprechenden, von Qt bereitgestellten „ qtbase translations “ in die generierten Übersetzungsdateien ein, sodass auch Qt-Dialoge wie der Druckdialog korrekt übersetzt werden:
qt_add_translations(documentviewer
SOURCE_TARGETS documentviewer abstractviewer
TS_FILE_BASE docviewer
MERGE_QT_TRANSLATIONS
QT_TRANSLATION_CATALOGS qtbase
)Jedes „ CMakeLists.txt “-Ziel auf Plugin-Ebene ruft „ qt_add_translations “ nur für die Quelldateien dieses Plugins auf (SOURCE_TARGETS). Durch die Beschränkung der Übersetzungsdateien auf das Plugin-Ziel wird verhindert, dass die Quelldateien der Hauptanwendung und anderer Plugins erneut gescannt und übersetzt werden:
qt_add_translations(txtviewer
SOURCE_TARGETS txtviewer
TS_FILE_BASE txtviewer
)Klasse „Translator“
Die Klasse ` Translator ` ist ein Wrapper um Qt’s ` QTranslator `, der die Internationalisierung sowohl für die Hauptanwendung als auch für jedes Plugin verwaltet. Jede Komponente (Hauptanwendung und Plugins) verfügt über eine eigene `Translator`-Instanz, was einen koordinierten Sprachwechsel über die gesamte Anwendung hinweg ermöglicht. Beim Start oder wenn der Benutzer eine neue Sprache auswählt, wird die Methode „ Translator::install() “ aufgerufen. Diese Methode verwendet „ QTranslator::load()“, um Übersetzungsdateien basierend auf „ QLocale::uiLanguages()“ und dem Basisnamen im Qt-Ressourcensystem zu laden. Wird keine passende Übersetzung gefunden, wird auf Englisch zurückgegriffen.
void Translator::install()
{
if (m_baseName.isEmpty()) {
qWarning() << "The basename of the translation is not set. Ignoring.";
return;
}
if (!m_translator.isEmpty())
qApp->removeTranslator(&m_translator);
if (m_translator.load(m_trLocale, m_baseName, "_"_L1, ":/i18n/"_L1)
&& qApp->installTranslator(&m_translator)) {
qInfo() << "Loaded translation" << m_translator.filePath();
} else {
if (m_trLocale.language() != QLocale::English) {
qWarning() << "Failed to load translation" << m_baseName <<
"für die Spracheinstellung" << m_trLocale.name() << ". Ausweichend wird die englische Übersetzung verwendet";
setLanguage(QLocale::English);
}
}
}Plugin-Unterstützung
Die Basisklasse „ AbstractViewer “ bietet Übersetzungsunterstützung, sodass jedes Plugin seine eigenen Übersetzungen autonom verwalten kann:
AbstractViewer::setTranslationBaseName(): initialisiert ein `Translator`-Objekt, legt dessen Basisnamen fest und installiert es, um die Standardübersetzungen zu laden.void AbstractViewer::setTranslationBaseName(const QString &baseName) { m_translator.reset(new Translator); m_translator->setBaseName(baseName); m_translator->install(); }AbstractViewer::eventFilter(): Diese Methode wird im Hauptfenster von „init()“ installiert und überwacht Ereignisse vom Typ „ QEvent::LanguageChange “. Wenn sich die Sprachumgebung geändert hat, installiert sie den Übersetzer des Plugins für die neue Sprache neu und ruft „retranslate()“ auf, um den gesamten Text zu aktualisieren.bool AbstractViewer::eventFilter(QObject *, QEvent *event) { if (event->type() != QEvent::LanguageChange) return false; const QLocale locale; if (locale != m_currentLocale) { m_currentLocale = locale; if (m_translator) { m_translator->setLanguage(locale.language()); m_translator->install(); } retranslate(); } return false; }AbstractViewer::retranslate(): Eine virtuelle Methode, die jedes Plugin implementiert, um seine eigenen UI-Texte neu zu übersetzen. Beispielsweise wie in ImageViewer neu implementiert:retranslate(); }
Widgets, die zu einem Plugin gehören und übersetzbare Zeichenfolgen enthalten, können ` QEvent::LanguageChange ` direkt verarbeiten. Beispielsweise überschreibt ` ZoomSelector ` die Methode ` changeEvent() `, um die Einträge seiner Kombinationsfeld-Elemente neu zu übersetzen:
void ZoomSelector::changeEvent(QEvent *event)
{
if (event->type() == QEvent::LanguageChange)
retranslate();
QComboBox::changeEvent(event);
}Anwendungsstart
- Hauptanwendung: In „
main.cpp“ laden wir die Übersetzung der Anwendung, bevor das Fenster angezeigt wird:Translator mainTranslator; mainTranslator.setBaseName("docviewer"_L1); mainTranslator.install(); - Plugins: Jedes Plugin ruft in seiner Funktion „
init()“ die Methode „AbstractViewer::setTranslationBaseName()“ auf, um einen „Translator“ mit dem Namen seiner Übersetzungsdatei zu initialisieren und die Übersetzungen der aktuellen Sprache zu installieren.void ImageViewer::init(QFile *file, QWidget *parent, QMainWindow *mainWindow) ... setTranslationBaseName("imgviewer"_L1); ...
Sprachumschaltung zur Laufzeit
Die Sprachumschaltung zur Laufzeit kann auf zwei Arten ausgelöst werden:
- Wechsel der Sprache des gesamten Systems zur Laufzeit: Das Betriebssystem sendet ein „ QEvent::LocaleChange “-Ereignis an die Anwendung.
- Über das Menü „ Help “ > „ Language “: Ein Klick auf den Eintrag „
QMenu“ löst „MainWindow::onActionSwitchLanguage()“ aus, wodurch die Standard-Locale festgelegt und ein „ QEvent::LocaleChange “-Ereignis gesendet wird, um denselben Ablauf wie bei einer Änderung der System-Locale zu durchlaufen:
In beiden Fällen verarbeitet MainWindow::changeEvent() die Ereignisse. QEvent::LocaleChange installiert den neuen Übersetzer, und das daraus resultierende QEvent::LanguageChange übersetzt die Benutzeroberfläche des Hauptfensters neu. Plugins reagieren über ihre Ereignisfilter.
void MainWindow::changeEvent(QEvent *event)
{
switch (event->type()) {
case QEvent::LanguageChange:
ui->retranslateUi(this);
statusBar()->clearMessage();
break;
case QEvent::LocaleChange:
m_translator.setLanguage(QLocale().language());
m_translator.install();
break;
default:
break;
}
QMainWindow::changeEvent(event);
}Quelldateien
Siehe auch Alle Qt-Beispiele.
© 2026 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.