文档查看器
一款用于显示和打印 JSON、文本和 PDF 文件的 Widgets 应用程序。
文档查看器演示了如何使用带有静态和动态工具栏、菜单和操作的QMainWindow 。此外,它还演示了基于 widget 的应用程序的以下功能:
- 使用QSettings 查询和保存用户首选项,并管理之前打开的文件历史记录。
- 控制鼠标悬停在 widget 上时的光标行为。
- 创建动态加载的插件。
创建应用程序和主窗口
应用程序及其主窗口在main.cpp
中构建。main()函数使用QCommandLineParser 处理命令行参数--help、version 和一个可选的位置参数file。如果用户在启动应用程序时提供了文件路径,主窗口就会打开该文件:
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
类提供了一个带有菜单、操作和工具栏的应用程序屏幕。它可以打开文件,并自动检测其内容类型。它还能维护以前打开过的文件列表,使用QSettings 来存储并在启动时重新加载设置。MainWindow 会根据打开文件的内容类型为其创建合适的阅读器,并支持打印文档。
MainWindow 的构造函数初始化在Qt Designer 中创建的用户界面。mainwindow.ui
文件左侧提供了一个QTabWidget ,显示书签和缩略图。右侧是QScrollArea ,用于查看文件内容。
查看器工厂类
ViewerFactory
类管理已知文件类型的查看器。这些查看器是作为插件实现的。创建 ViewerFactory 实例时,会将视图区域和主窗口的指针传递给构造函数:
m_factory.reset(new ViewerFactory(ui->viewArea, this));
ViewerFactory 会在构建时加载所有可用的插件。它提供了一个公共 API 来查询已加载的插件、它们的名称和支持的 MIME 类型:
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;
viewer()
函数返回一个指针,指向适合打开作为参数传递的QFile 的插件:
m_viewer = m_factory->viewer(file);
如果应用程序设置中包含查看器的部分,则将其传递给查看器的虚拟restoreState()
函数:
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); }
然后,标准用户界面资产被传递给查看器,主滚动区域被设置为显示查看器的显示部件:
m_viewer->initViewer(ui->actionBack, ui->actionForward, ui->menuHelp->menuAction(), ui->tabWidget); restoreViewerSettings(); ui->scrollArea->setWidget(m_viewer->widget()); return true; }
抽象查看器类
AbstractViewer
提供了查看、保存和打印文档的通用应用程序接口。文档和查看器的属性都可以查询:
- 文档有内容吗?
- 是否被修改过?
- 是否支持概览(缩略图或书签)?
AbstractViewer 为派生类提供了受保护的方法,以便在主窗口上创建操作和菜单。要在主窗口上显示这些资产,它们必须是主窗口的父类。AbstractViewer 负责移除和销毁其创建的用户界面资产。它继承自QObject 以实现信号和槽。
信号
void uiInitialized();
该信号在查看器接收到有关主窗口上用户界面资产的所有必要信息后发出。
void printingEnabledChanged(bool enabled);
启用或禁用文档打印时发出该信号。这发生在成功加载新文档或删除所有内容之后。
void printStatusChanged(AbstractViewer::PrintStatus status);
开始打印后,该信号会通知打印进度的变化。
void documentLoaded(const QString &fileName);
该信号通知应用程序已成功加载文档。
TxtViewer 类
TxtViewer
是一个简单的文本查看器,继承自 AbstractViewer。它支持编辑文本文件、复制/剪切和粘贴、打印以及保存更改。
ImageViewer 类
ImageViewer
使用 显示 支持的图像。QLabel QImageReader
在构造函数中,我们增加了QImageReader 的分配限制,以容纳更大的照片:
ImageViewer::ImageViewer() : m_formats(imageFormats()) { connect(this, &AbstractViewer::uiInitialized, this, &ImageViewer::setupImageUi); QImageReader::setAllocationLimit(1024); // MB }
在 openFile() 函数中,我们加载图片并确定其大小。如果图片大小大于屏幕尺寸,我们就将其缩小到屏幕尺寸,同时保持宽高比。这一计算必须以原始像素进行,而且需要在生成的像素图上设置设备像素比,以使其看起来清晰:
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(); }
JsonViewer 类
JsonViewer
在 中显示一个 JSON 文件。在内部,它将文件内容加载到 中,并用 填充自定义树模型。QTreeView QJsonDocument JsonItemModel
JSON viewer 插件演示了如何实现从QAbstractItemModel 继承的自定义项目模型。JsonTreeItem
类提供了一个基本的 API,用于操作 JSON 数据并将其传播回底层的QJsonDocument 。
JsonViewer 将文档的顶层对象用作导航书签。其他节点(键和值)可作为附加书签添加或从书签列表中删除。QLineEdit 用作搜索字段,以便在 JSON 树中导航。
PdfViewer 类
PdfViewer
类(和插件)是PDF Viewer Widget 示例的分叉。它演示了如何使用QScroller 来流畅地浏览文档。
其他相关类
HoverWatcher 类
当鼠标悬停在部件上时,HoverWatcher
类会设置一个覆盖光标,并在离开时恢复光标。为防止为同一个窗口小部件创建多个 HoverWatcher 实例,该类以每个窗口小部件单例的形式实现。
HoverWatcher 继承自QObject ,并将其监控的QWidget 作为实例的父节点。它安装了一个事件过滤器,以拦截悬停事件而不消耗它们:
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); }
HoverAction
枚举列出了 HoverWatcher 所反应的操作:
enum HoverAction { Entered, MousePress, MouseRelease, Left, Ignore };
静态函数可创建观察者、为特定QWidget 检查观察者是否存在或解除观察者:
static HoverWatcher *watcher(QWidget *watched); static const HoverWatcher *watcher(const QWidget *watched); static bool hasWatcher(QWidget *widget); static void dismiss(QWidget *watched);
可以为每个 HoverAction 设置或取消设置光标形状。如果没有相关的光标形状,则会在触发操作时恢复应用程序的覆盖光标。
public slots: void setCursorShape(HoverAction type, Qt::CursorShape shape); void unSetCursorShape(HoverAction type);
mouseButtons
属性包含MousePress
动作要考虑的鼠标按钮:
void setMouseButtons(Qt::MouseButtons buttons); void setMouseButton(Qt::MouseButton button, bool enable);
处理动作后会发出特定于动作的信号:
signals: void entered(); void mousePressed(); void mouseReleased(); void left();
一般信号会将已处理的动作作为参数传递出去:
void hoverAction(HoverAction action);
RecentFiles 类
RecentFiles
是一个 ,专门用于管理最近打开的文件列表。QStringList
RecentFiles 具有一次性添加单个文件或多个文件的插槽。如果路径指向一个已存在且可打开的文件,条目就会被添加到最近文件列表中。如果文件已经在列表中,则会从原来的位置移除,并添加到顶部。
public slots: void addFile(const QString &fileName) { addFile(fileName, EmitPolicy::EmitWhenChanged); } void addFiles(const QStringList &fileNames);
文件可按名称或索引从列表中移除:
void removeFile(const QString &fileName) { removeFile(m_files.indexOf(fileName)); } void removeFile(qsizetype index) {removeFile(index, RemoveReason::Other); }
实现从QSettings 保存和恢复的插槽:
void saveSettings(QSettings &settings, const QString &key) const; bool restoreFromSettings(QSettings &settings, const QString &key);
恢复设置时,不存在的文件将被忽略。maxFiles
属性用于保存最近文件的最大数量(默认为 10)。
qsizetype maxFiles(); void setMaxFiles(qsizetype maxFiles);
RecentFiles
在接受文件前,将验证该文件是否可读取。
RecentFileMenu 类
RecentFileMenu
是 ,专门用于将QMenuRecentFiles对象显示为子菜单。
它的构造函数接收一个指向父QObject 的指针和一个指向 RecentFiles 对象的指针,它将显示 RecentFiles 对象的内容。fileOpened()
信号会在用户从列表中选择一个最近文件时触发,并将文件的绝对路径作为参数传递给用户。
注: RecentFileMenu
会被其父部件或传给其构造函数的RecentFiles
对象销毁。
© 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.