简单文本查看器示例

Qt Assistant 用作应用程序的自定义帮助查看器。

本示例展示了如何在自定义应用程序中将Qt Assistant 用作自定义帮助查看器。操作分为两个阶段。首先,创建文档并定制Qt Assistant ;其次,将启动和控制Qt Assistant 的功能添加到应用程序中。

简单文本查看器应用程序允许用户选择和查看现有文件。该应用程序提供了自己的定制文档,可通过主窗口菜单栏的帮助菜单或点击应用程序查找文件对话框中的帮助按钮获取。

该示例由四个类组成:

  • Assistant 提供启动 的功能。Qt Assistant
  • MainWindow 是应用程序的主窗口。
  • FindFileDialog 允许用户使用通配符匹配搜索文件。
  • TextEdit 提供富文本浏览器,确保正确显示 HTML 文档中引用的图像。

注: 我们将只评论与主要问题相关的实现部分,即让Qt Assistant 成为我们的简单文本查看器应用程序的定制帮助查看器。

创建文档和自定义Qt Assistant

如何以 HTML 页面的形式创建实际文档不在本示例的讨论范围之内。一般来说,HTML 页面既可以手工编写,也可以借助 qdoc 或 Doxygen 等文档工具生成。在本例中,我们假设 HTML 文件已经创建。因此,唯一需要做的就是告诉Qt Assistant 如何组织和显示帮助信息。

组织帮助文档Qt Assistant

普通 HTML 文件只包含有关特定主题的文本或文档,但通常不包含有关多个 HTML 文档如何相互关联或应该以何种顺序阅读的信息。缺少的是目录和索引,这样就可以快速访问某些帮助内容,而不必为了查找某条信息而浏览大量文件。

为了组织文档并将其提供给Qt Assistant ,我们必须创建一个 Qt 帮助项目(.qhp)文件。项目文件的第一个也是最重要的部分是命名空间的定义。命名空间必须是唯一的,并将成为Qt Assistant 中页面 URL 的第一部分。此外,我们还必须设置一个虚拟文件夹,作为文档集的公共文件夹。这意味着,由两个不同命名空间标识的两个文档集可以交叉引用 HTML 文件,因为这些文件都在一个大的虚拟文件夹中。不过,在本例中,我们只有一个文档集可用,所以虚拟文件夹的名称和功能并不重要。

<?xml version="1.0" encoding="UTF-8"?>
<QtHelpProject version="1.0">
  <namespace>org.qt-project.examples.simpletextviewer</namespace>
  <virtualFolder>doc</virtualFolder>

下一步是定义过滤部分。过滤器部分包含目录、索引和所有文档文件的完整列表,并可分配任意数量的过滤器属性。过滤属性是一个普通字符串,可以自由选择。随后在Qt Assistant 中,用户可以参考这些属性定义自定义过滤器。如果过滤器部分的属性与自定义过滤器的属性相匹配,则会显示文档,否则Qt Assistant 将隐藏文档。

同样,由于我们只有一套文档,所以不需要Qt Assistant 的过滤功能,因此可以跳过过滤属性。

现在,我们建立目录。表格中的项目由section 标签定义,该标签包含项目标题属性以及指向实际页面的链接。章节标签可以无限嵌套,但出于实际考虑,不建议嵌套超过三或四层。在我们的示例中,我们希望使用以下大纲来编写目录:

  • 简单文本查看器
    • 查找文件
      • 文件对话框
      • 通配符匹配
      • 浏览
    • 打开文件

在帮助项目文件中,大纲用 表示:

<filterSection>
  <toc>
    <section title="Simple Text Viewer" ref="index.html">
      <section title="Find File" ref="findfile.html">
        <section title="File Dialog" ref="filedialog.html"/>
        <section title="Wildcard Matching" ref="wildcardmatching.html"/>
        <section title="Browse" ref="browse.html"/>
      </section>
      <section title="Open File" ref="openfile.html"/>
    </section>
  </toc>

确定目录后,我们将列出所有索引关键字:

<keywords>
  <keyword name="Display" ref="index.html"/>
  <keyword name="Rich text" ref="index.html"/>
  <keyword name="Plain text" ref="index.html"/>
  <keyword name="Find" ref="findfile.html"/>
  <keyword name="File menu" ref="findfile.html"/>
  <keyword name="File name" ref="filedialog.html"/>
  <keyword name="File dialog" ref="filedialog.html"/>
  <keyword name="File globbing" ref="wildcardmatching.html"/>
  <keyword name="Wildcard matching" ref="wildcardmatching.html"/>
  <keyword name="Wildcard syntax" ref="wildcardmatching.html"/>
  <keyword name="Browse" ref="browse.html"/>
  <keyword name="Directory" ref="browse.html"/>
  <keyword name="Open" ref="openfile.html"/>
  <keyword name="Select" ref="openfile.html"/>
</keywords>

最后一步,我们必须列出组成文档的所有文件。这里需要注意的一点是,所有文件都必须列出,包括图像文件,甚至样式表(如果使用)。

    <files>
      <file>browse.html</file>
      <file>filedialog.html</file>
      <file>findfile.html</file>
      <file>index.html</file>
      <file>intro.html</file>
      <file>openfile.html</file>
      <file>wildcardmatching.html</file>
      <file>images/browse.png</file>
      <file>images/fadedfilemenu.png</file>
      <file>images/filedialog.png</file>
      <file>images/handbook.png</file>
      <file>images/mainwindow.png</file>
      <file>images/open.png</file>
      <file>images/wildcard.png</file>
    </files>
  </filterSection>
</QtHelpProject>

帮助项目文件至此完成。如果您想在Qt Assistant 中看到生成的文档,您必须从中生成一个 Qt 压缩帮助文件,并将其注册到Qt Assistant 的默认帮助集合中。

qhelpgenerator simpletextviewer.qhp -o simpletextviewer.qch
assistant -register simpletextviewer.qch

如果您现在启动Qt Assistant ,您会在 Qt 文档旁边看到 Simple Text Viewer 文档。这对于测试目的来说没有问题,但对于最终版本,我们希望只有 Simple Text Viewer 文档能出现在Qt Assistant 中。

自定义Qt Assistant

Qt Assistant 只显示简单文本查看器文档的最简单方法是创建我们自己的帮助集合文件。帮助集合文件以二进制格式存储,类似于压缩帮助文件,由帮助集合项目文件(*.qhcp)生成。借助帮助文件集,我们可以自定义Qt Assistant 的外观甚至某些功能。

首先,我们要更改窗口标题和图标。它将不再显示 "Qt Assistant",而是显示 "Simple Text Viewer"(简单文本查看器),这样用户就能更清楚地知道帮助查看器实际上属于我们的应用程序。

<?xml version="1.0" encoding="UTF-8"?>
<QHelpCollectionProject version="1.0">
<assistant>
    <title>Simple Text Viewer</title>
    <applicationIcon>images/handbook.png</applicationIcon>
    <cacheDirectory>QtProject/SimpleTextViewer</cacheDirectory>

cacheDirectory 标签指定了用户数据目录的一个子目录(请参阅Qt Help Collection Files),全文搜索的缓存文件或设置文件将存放在该目录下。

之后,我们将Qt Assistant 首次启动时显示的页面设置为新配置。URL 由 Qt help 项目文件中定义的命名空间和虚拟文件夹组成,后面是实际的页面文件名。

<startPage>qthelp://org.qt-project.examples.simpletextviewer/doc/index.html</startPage>

接下来,我们将 "关于 "菜单项的名称改为 "关于简单文本查看器"。关于"对话框的内容也将通过指定一个文件来更改,该文件中的关于文本或图标将取自该文件。

<aboutMenuText>
    <text>About Simple Text Viewer</text>
</aboutMenuText>
<aboutDialog>
    <file>about.txt</file>
    <icon>images/icon.png</icon>
</aboutDialog>

Qt Assistant 通过首选项对话框,可以添加或删除文档。当 作为更多应用程序的中央帮助查看器时,这一功能会很有帮助,但在我们的例子中,我们希望切实防止用户删除文档。因此,我们在Qt Assistant 首选项对话框中隐藏了 "文档"选项卡。

由于地址栏在如此小的文档集中并不重要,因此我们也将其关闭。由于只有一个过滤部分,没有任何过滤属性,我们还可以禁用Qt Assistant 的过滤功能,这意味着过滤页面和过滤工具栏将不可用。

    <enableDocumentationManager>false</enableDocumentationManager>
    <enableAddressBar>false</enableAddressBar>
    <enableFilterFunctionality>false</enableFilterFunctionality>
</assistant>

出于测试目的,我们已经生成了压缩帮助文件,并在Qt Assistant 的默认帮助集中进行了注册。通过以下几行,我们获得了相同的结果。唯一的重要区别是,我们不是在默认帮助文件集中注册压缩帮助文件,而是在我们自己的帮助文件集中注册。

  <docFiles>
    <generate>
        <file>
            <input>simpletextviewer.qhp</input>
            <output>simpletextviewer.qch</output>
            </file>
        </generate>
    <register>
        <file>simpletextviewer.qch</file>
        </register>
    </docFiles>
</QHelpCollectionProject>

最后一步,我们必须从帮助文件集项目文件中生成二进制文件集。这可以通过运行qhelpgenerator 工具来完成。

qhelpgenerator simpletextviewer.qhcp -o simpletextviewer.qhc

为了测试我们对Qt Assistant 所做的所有定制,我们在命令行中添加了集合文件名:

assistant -collectionFile simpletextviewer.qhc

通过助手类控制Qt Assistant

我们首先来看看如何从远程应用程序启动和操作Qt Assistant 。为此,我们创建了一个名为Assistant 的类。

该类提供了一个用于显示文档页面的公共函数,以及一个用于确保Qt Assistant 启动和运行的私有辅助函数。

在函数startAssistant() 中,只需创建并启动一个 QProcess 即可启动Qt Assistant 。如果进程已在运行,函数会立即返回。否则,必须设置并启动该进程。

bool Assistant::startAssistant()
{
    if (m_process.isNull()) {
        m_process.reset(new QProcess);
        QObject::connect(m_process.data(), &QProcess::finished,
                         m_process.data(), [this](int exitCode, QProcess::ExitStatus status) {
            finished(exitCode, status);
        });
    }

    if (m_process->state() != QProcess::Running) {
        QString app = QLibraryInfo::path(QLibraryInfo::BinariesPath);
#ifndef Q_OS_DARWIN
        app += "/assistant"_L1;
#else
        app += "/Assistant.app/Contents/MacOS/Assistant"_L1;
#endif

        const QString collectionDirectory = documentationDirectory();
        if (collectionDirectory.isEmpty()) {
            showError(tr("The documentation directory cannot be found"));
            return false;
        }

        const QStringList args{"-collectionFile"_L1,
                               collectionDirectory + "/simpletextviewer.qhc"_L1,
                               "-enableRemoteControl"_L1};

        m_process->start(app, args);

        if (!m_process->waitForStarted(3000)) {
            showError(tr("Unable to launch Qt Assistant (%1): %2")
                      .arg(QDir::toNativeSeparators(app), m_process->errorString()));
            return false;
        }
    }
    return true;
}

要启动进程,我们需要Qt Assistant 的可执行文件名以及在自定义模式下运行Qt Assistant 的命令行参数。可执行文件名有点麻烦,因为它取决于平台,但幸运的是,它只在 macOS 上有所不同。

在启动Qt Assistant 时,可以使用-collectionFile 命令行参数更改显示的文档。在不带任何选项的情况下启动时,Qt Assistant 会显示一组默认文档。安装 Qt 时,Qt Assistant 中的默认文档集包含 Qt 参考文档以及 Qt 附带的工具,如Qt Designerqmake

在我们的示例中,通过在进程的命令行选项中传递特定于应用程序的集合文件,我们用自定义文档替换了默认文档集。

作为最后一个参数,我们添加了-enableRemoteControl ,这将使Qt Assistant 监听其stdin 频道以获取命令,例如显示文档中某一页的命令。然后,我们启动进程并等待其实际运行。如果由于某种原因Qt Assistant 无法启动,startAssistant() 将返回 false。

showDocumentation() 的实现现在变得简单明了。首先,我们要确保Qt Assistant 正在运行,然后通过进程的stdin 频道发送显示page 的请求。这里非常重要的一点是,命令必须以行尾标记结束,以刷新通道。

void Assistant::showDocumentation(const QString &page)
{
    if (!startAssistant())
        return;

    QByteArray ba("SetSource ");
    ba.append("qthelp://org.qt-project.examples.simpletextviewer/doc/");

    m_process->write(ba + page.toLocal8Bit() + '\n');
}

最后,我们要确保Qt Assistant 在应用程序关闭时被正确终止。QProcess 的析构函数会杀死进程,这意味着应用程序无法执行保存用户设置等操作,从而导致设置文件损坏。为了避免这种情况,我们要求Qt AssistantAssistant 类的析构函数中终止。

Assistant::~Assistant()
{
    if (!m_process.isNull() && m_process->state() == QProcess::Running) {
        QObject::disconnect(m_process.data(), &QProcess::finished, nullptr, nullptr);
        m_process->terminate();
        m_process->waitForFinished(3000);
    }
}

主窗口类

MainWindow 类为应用程序主窗口提供了两个菜单:文件(File)菜单可让用户打开并查看现有文件,而帮助(Help)菜单则提供了有关应用程序和 Qt 的信息,并可让用户打开Qt Assistant 以显示应用程序的文档。

为了能够访问帮助功能,我们在MainWindow 的构造函数中初始化了Assistant 对象。

MainWindow::MainWindow()
    : textViewer(new TextEdit)
    , assistant(new Assistant)
{
    ...
}

然后,我们创建简单文本查看器应用程序的所有操作。特别值得注意的是通过F1快捷键或帮助>帮助内容菜单项访问assistantAct 的操作。该操作连接到MainWindow 类的showDocumentation() 插槽。

void MainWindow::createActions()
{
    assistantAct = new QAction(tr("Help Contents"), this);
    assistantAct->setShortcut(QKeySequence::HelpContents);
    connect(assistantAct, &QAction::triggered, this, &MainWindow::showDocumentation);
    ...
}

showDocumentation() 插槽中,我们使用文档主页的 URL 调用Assistant 类的showDocumentation() 函数。

void MainWindow::showDocumentation()
{
    assistant->showDocumentation("index.html");
}

最后,我们必须重新实现受保护的 QWidget::closeEvent() 事件处理程序,以确保在终止应用程序之前正确关闭应用程序的Qt Assistant 实例。

void MainWindow::closeEvent(QCloseEvent *)
{
    delete assistant;
}

查找文件对话框类

简单文本查看器应用程序提供了一个查找文件对话框,允许用户使用通配符匹配搜索文件。搜索在指定目录内进行,用户可以选择浏览现有文件系统来查找相关目录。

在构造函数中,我们保存了作为参数传递的AssistantQTextEdit 对象的引用。Assistant 对象将用于FindFileDialoghelp() 插槽,我们很快就会看到,而 QTextEdit 将用于对话框的openFile() 插槽,以显示所选文件。

FindFileDialog::FindFileDialog(TextEdit *editor, Assistant *assistant)
    : QDialog(editor)
    , currentEditor(editor)
    , currentAssistant(assistant)
{
    ...
}

FindFileDialog 类中最值得注意的成员是私有的help() 插槽。该槽与对话框的帮助按钮相连,并通过调用AssistantshowDocumentation() 函数,将当前Qt Assistant 实例与对话框的文档一起引入前台。

void FindFileDialog::help()
{
    currentAssistant->showDocumentation("filedialog.html");
}

摘要

为了使Qt Assistant 成为应用程序的定制帮助工具,除了包含 Qt 帮助压缩文件的定制帮助收集文件外,还必须为应用程序提供一个控制Qt Assistant 的进程。

有关使用Qt Assistant 作为自定义帮助查看器的应用程序可用选项和设置的更多信息,请参阅自定义Qt Assistant

示例项目 @ 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.