简易文本查看器示例

使用 Qt 助手 作为应用程序的定制帮助查看器。

本示例显示了如何将 Qt 助手 用于自定义应用程序中的帮助查看器。这是通过两个阶段实现的。首先,创建文档并定制 Qt 助手;其次,将启动和控制 Qt 助手 的功能添加到应用程序中。

简易文本查看器应用程序允许用户选择和查看现有文件。应用程序提供其自定义文档,可通过在主窗口菜单栏中的 帮助 菜单或通过单击应用程序查找文件对话框中的 帮助 按钮来访问。

本例由四个类组成

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

注意:我们将只评论实现中与主问题相关的内容,即如何使 Qt 助手作为我们的简易文本查看器应用程序的定制帮助查看器。

创建文档和定制 Qt 助手

如何创建实际的文档(以 HTML 页面形式)并不在本示例的范围内。通常,HTML 页面可以手动编写或使用 qdoc 或 Doxygen 等文档工具生成。为了本示例的目的,我们假设 HTML 文件已经创建。因此,剩下的唯一任务是告诉 Qt 助手 如何组织并显示帮助信息。

组织 Qt 助手 的文档

纯 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文档旁边看到简单的文本查看器文档。这对于测试 purposes 是可以接受的,但是为了最终版本,我们希望只在Qt Assistant中只有简单的文本查看器文档。

自定义Qt Assistant

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

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

<?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帮助集合文件),其中将存储全文搜索的缓存文件或设置文件。

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

<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 的默认帮助集合中。通过以下行我们实现了相同的结果。唯一且重要的区别在于,我们 registering the 压缩的帮助文件,不是在默认集合中,而是在我们自己的集合文件中。

  <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

通过 Assistant 类控制 Qt Assistant

我们将首先探讨如何从远程应用程序启动并操作 Qt Assistant。为此,我们创建一个名为 Assistant 的类。

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

通过简单的创建并启动一个 QProcess 来在 startAssistant() 函数中启动 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 上不同。

可以使用 -collectionFile 命令行参数来在启动 Qt Assistant 时更改显示的文档。如果没有任何选项启动, Qt Assistant 将显示一组默认文档。当 Qt 安装时, Qt Assistant 中的默认文档集包含 Qt 参考文档以及与 Qt 一起提供的工具,例如 Qt Designer 和 qmake

在我们的例子中,我们通过将我们的应用程序特定的集合文件传递给进程的命令行选项来替换默认的文档集。

作为最后一个参数,我们添加 -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的析构函数会终止进程,这意味着应用程序没有保存用户设置的机会,这可能导致设置文件损坏。为了避免这种情况,我们在Assistant类的析构函数中要求Qt Assistant终止。

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 类

MainWindow类提供主应用程序窗口,包含两个菜单:文件菜单允许用户打开和查看现有文件,而帮助菜单提供有关应用程序和Qt的信息,并允许用户打开Qt Assistant以显示应用程序的文档。

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

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

然后,我们为Simple Text Viewer应用程序创建了所有操作。特别需要注意的是,可以通过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槽中,我们调用Assistant类的showDocumentation函数,并传入文档主页的URL。

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

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

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

FindFileDialog 类

Simple Text Viewer应用程序提供查找文件对话框,允许用户使用通配符匹配搜索文件。搜索在指定的目录内进行,并给用户一个选项来浏览现有的文件系统以找到相关的目录。

在构造函数中,我们保存了作为参数传入的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 Assistant的进程,以及一个包含Qt压缩帮助文件的定制帮助集合文件。

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

示例项目 @ code.qt.io

© 2024 The Qt Company Ltd. 此处包含的文档贡献归各自所有者版权所有。此处提供的文档是根据自由软件基金会发布的GNU自由文档许可证版本1.3的条款许可的。Qt及其 respective 标志是芬兰以及/或其他国家/地区的The Qt Company Ltd.的商标。所有其他商标均为各自所有者的财产。