Qt Widgets - 文本查看器插件示例
带有菜单、工具栏和状态栏的 widget 示例。
文本查看器示例是一个围绕QPlainTextEdit 构建的文本编辑器,采用通用文档查看器插件的形式。
文本查看器示例的所有代码都在TxtViewer
类中,该类继承于AbstractViewer
。AbstractViewer
提供了查看器与主窗口之间的交互框架。应用程序在菜单栏中提供File 、Edit 和Help 条目。
主窗口底部的状态栏显示应用程序向用户提供的信息。
最近打开的文件显示在File 菜单中。该示例一次只能加载一个文件。
类定义
class TxtViewer : public ViewerInterface { Q_OBJECT Q_PLUGIN_METADATA(IID "org.qt-project.Qt.Examples.DocumentViewer.ViewerInterface" FILE "txtviewer.json") Q_INTERFACES(ViewerInterface)
类定义以处理信号和槽的Q_OBJECT
宏开始。随后是Q_PLUGIN_METADATA
和Q_INTERFACES
宏,这是注册插件所必需的。
该类继承于ViewerInterface
,后者继承于AbstractViewer
。ViewerInterface
类用于在主窗口应用程序和插件之间提供接口。
QPluginLoader
该类还需要 txtviewer.json 文件,其中必须包含插件的密钥:
{ "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; };
该类没有定义构造函数,这意味着只有一个不带参数的标准构造函数可用。所有其他函数,包括析构函数,都是重新实现ViewerInterface
的虚拟函数。它们用于与主程序交换数据、信息和指令。
没有实现保存和恢复设置的功能。supportsOverview
函数总是返回false
,告诉主程序无需显示缩略图导航窗口。
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 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); }); }
我们首先包含访问TxtViewer
使用的所有类所需的头文件。我们还包括txtviewer.h
。
QPrinter
只有当编译系统启用了打印支持时,才会包含 TxtViewer 和 。QPrintDialog
您可能会问,为什么不将这些头文件包含在mainwindow.h
中就完事了呢?原因是,包含来自另一个头文件的多个大头文件会迅速降低性能。在这里,这样做不会有任何坏处,但一般来说,只从另一个头文件中包含绝对必要的头文件仍然是个好主意。
该实现以一个空的析构函数开始。析构函数可以完全省略。为了向代码阅读者指出析构函数中不需要做任何事情,空析构函数是一个很好的做法。
析构函数之后是一个初始化函数,需要三个参数:
file
, 打开和显示文件的指针。parent
指向编辑器所在的QWidget
。mainWindow
指向应用程序的主窗口,菜单和菜单栏都在主窗口中处理。
该函数调用AbstractViwer
的基本 init 函数。创建一个新的QPlainTextEdit widget,用于显示文件内容。然后,TxtViewer
的设置函数与基类的 uiInitialized 信号相连。
下一个函数将返回文本查看器支持的 mime 类型列表。只支持纯文本。
最后一个初始化函数将添加查看器特定的用户界面组件,如菜单、图标、按钮和工具提示。它使用AbstractViewer
提供的功能,确保在使用另一个查看器插件显示另一个文件时,这些组件会从应用程序的主窗口中移除。
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
打开一个文件,将其内容传输到 ,并根据打开是否成功为用户打印状态信息。QPlainTextEdit
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(); }
下一个重新实现的函数将告诉主程序,查看器插件是否真的在显示内容。
如果编译系统支持打印,下一节将实现打印功能。
最后两个重新实现提供了保存当前文件或以新名称保存文件的功能。
© 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.