Различные статьи по Qt и c++

Создание .qch-файла с нуля: структура проекта справки и qhelpgenerator

Введение

В предыдущей статье мы разобрали архитектуру в целом — теперь пройдём полный практический путь от пустой директории до готового, протестированного .qch-файла, который можно открыть в Qt Assistant. В статье построим минимальную, но полностью работающую справку для гипотетического приложения, включая правильную настройку путей и проверку результата.

Концепция

Процесс сборки .qch состоит из трёх шагов: подготовка HTML-контента (обычные статические HTML-файлы, без специфичных для Qt тегов — это просто веб-страницы), описание структуры в .qhp-файле (TOC, индекс, список файлов — как в предыдущей статье), и компиляция через консольный инструмент qhelpgenerator, поставляемый вместе с Qt SDK. Результат — единый файл .qch, который полностью самодостаточен и может быть открыт независимо, без доступа к исходным HTML-файлам, поскольку весь контент уже встроен внутрь скомпилированного файла.

Пример кода: Создание .qch-файла с нуля

<!-- docs/source/index.html -->
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>MyApp — Справка</title>
    <link rel="stylesheet" href="../style.css">
</head>
<body>
    <h1>Добро пожаловать в справку MyApp</h1>
    <p>Эта справка поможет вам начать работу с приложением.</p>
    <ul>
        <li><a href="getting_started.html">Начало работы</a></li>
        <li><a href="api_reference.html">Справочник API</a></li>
    </ul>
</body>
</html>
<!-- docs/source/getting_started.html -->
<!DOCTYPE html>
<html>
<head><meta charset="utf-8"><title>Начало работы</title></head>
<body>
    <h1>Начало работы</h1>
    <h2 id="install">Установка</h2>
    <p>Скачайте установщик с официального сайта и запустите его.</p>
    <h2 id="first-launch">Первый запуск</h2>
    <p>При первом запуске будет предложено настроить базовые параметры.</p>
</body>
</html>
<!-- docs/myapp.qhp -->
<?xml version="1.0" encoding="UTF-8"?>
<QtHelpProject version="1.0">
    <namespace>com.mycompany.myapp.1.0</namespace>
    <virtualFolder>doc</virtualFolder>
    <customFilter name="MyApp 1.0">
        <filterAttribute>myapp</filterAttribute>
        <filterAttribute>1.0</filterAttribute>
    </customFilter>
    <filterSection>
        <filterAttribute>myapp</filterAttribute>
        <filterAttribute>1.0</filterAttribute>
        <toc>
            <section title="MyApp — Справка" ref="source/index.html">
                <section title="Начало работы" ref="source/getting_started.html">
                    <section title="Установка" ref="source/getting_started.html#install" />
                    <section title="Первый запуск" ref="source/getting_started.html#first-launch" />
                </section>
            </section>
        </toc>
        <keywords>
            <keyword name="установка" ref="source/getting_started.html#install" />
            <keyword name="первый запуск" ref="source/getting_started.html#first-launch" />
        </keywords>
        <files>
            <file>source/index.html</file>
            <file>source/getting_started.html</file>
            <file>style.css</file>
        </files>
    </filterSection>
</QtHelpProject>
# Сборка и проверка результата
cd docs
qhelpgenerator myapp.qhp -o myapp.qch

# Быстрая проверка содержимого скомпилированного файла без приложения
assistant -collectionFile myapp.qhc   # либо открыть .qch напрямую через Assistant

Пояснения к коду

<customFilter> и парные <filterAttribute> внутри <filterSection> — механизм фильтрации контента: если в одном .qhp-проекте описано несколько наборов страниц для разных версий или конфигураций продукта, фильтры позволяют скомпилировать или отобрать для отображения только нужное подмножество; для простого случая (как в примере) фильтр просто помечает весь контент единым набором атрибутов.

Привязка к конкретным заголовкам внутри страницы через id (<h2 id="install">) и ссылка на них в .qhp через #install в ref позволяет TOC и индексу указывать не просто на страницу целиком, а на конкретный раздел внутри неё — открытие такой ссылки в справочном браузере сразу прокручивает страницу к нужному месту, что особенно важно для длинных страниц с множеством подразделов.

Подводные камни

  • Относительные пути в HTML, не совпадающие с реальной структурой после компиляции. Ссылки между HTML-страницами (href="getting_started.html") должны быть относительными путями, соответствующими структуре, описанной в .qhp — абсолютные пути или пути, выходящие за пределы директории проекта справки, не будут корректно разрешены внутри скомпилированного .qch.
  • Отсутствие проверки на пустые или «битые» якоря (#install). Если HTML-файл не содержит элемента с указанным id, ссылка не выдаст ошибку при компиляции .qhp, но при открытии в браузере справки просто не произойдёт прокрутки к ожидаемому месту — этот класс ошибок обнаруживается только при ручном тестировании каждой ссылки.
  • Запуск qhelpgenerator без указания -o или с неверным относительным путём к выходному файлу может привести к созданию .qch в неожиданном месте файловой системы — особенно при автоматизации сборки через CI или CMake, где рабочая директория может отличаться от ожидаемой при ручном запуске из терминала.
  • Несинхронизированное обновление .qch после изменения HTML-исходников. Поскольку .qch — это скомпилированный, самодостаточный артефакт, изменение исходных HTML-файлов не отражается в уже работающем приложении автоматически — нужен явный шаг пересборки .qch и его повторного развёртывания вместе с приложением, который легко забыть включить в общий пайплайн сборки релиза.