シンプルなテキストビューアの例

Qtアシスタントをアプリケーションのカスタマイズヘルプビューアとして使用する

この例では、Qt Assistant をカスタム・アプリケーションのカスタマイズされたヘルプ・ビューワとして使用する方法を示します。これは2つの段階で行います。まず、ドキュメントを作成して Qt Assistant をカスタマイズします。次に、Qt Assistant を起動して制御する機能をアプリケーションに追加します。

Simple Text Viewerアプリケーションでは、既存のファイルを選択して表示することができます。このアプリケーションは、メイン・ウィンドウのメニュー・バーのヘルプ・メニューから、またはアプリケーションのファイル検索ダイアログのヘルプ・ボタンをクリックすることで利用できる独自のカスタム・ドキュメントを提供します。

このサンプルは4つのクラスで構成されています:

  • Assistant は Qt Assistant を起動する機能を提供します。
  • MainWindow はメイン・アプリケーション・ウィンドウです。
  • FindFileDialog ワイルドカードを使ってファイルを検索できます。
  • TextEdit HTML ドキュメントで参照されている画像が正しく表示されるようにするリッチテキストブラウザです。

注: ここでは、Qt Assistant をシンプル・テキスト・ビューワー・アプリケーション用にカスタマイズされたヘルプ・ビューワーとして動作させるという、主要な問題に関連する実装部分についてのみコメントします。

ドキュメントの作成とQtアシスタントのカスタマイズ

HTMLページの形で実際のドキュメントを作成する方法は、この例の範囲ではありません。一般的に、HTMLページは手で書くか、qdocやDoxygenのようなドキュメントツールの助けを借りて生成することができます。この例では、HTMLファイルはすでに作成されていると仮定します。そのため、あとはQt Assistantにヘルプ情報の構成と表示方法を指示するだけです。

Qt Assistantのドキュメントを整理する

プレーンなHTMLファイルには、特定のトピックに関するテキストやドキュメントが含まれているだけで、通常、複数のHTMLドキュメントが互いにどのように関連しているか、どの順番で読むことになっているかについての情報は含まれていません。不足しているのは、情報の一部を見つけるために多くのドキュメントをブラウズすることなく、特定のヘルプ内容に素早くアクセスするための索引と目次です。

ドキュメントを整理して Qt Assistant で利用できるようにするには、Qt ヘルププロジェクト (.qhp) ファイルを作成する必要があります。プロジェクトファイルの最初の、そして最も重要な部分は、名前空間の定義です。名前空間は一意でなければならず、Qt AssistantのページURLの最初の部分になります。さらに、ドキュメント・セットの共通フォルダとして機能する仮想フォルダを設定する必要があります。これは、2つの異なる名前空間で識別される2つのドキュメント・セットが、HTMLファイルを相互参照できることを意味します。しかし、この例では、利用可能なドキュメント・セットは1つだけなので、仮想フォルダーの名前と機能は重要ではありません。

<?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はドキュメントを非表示にします。

この場合も、ドキュメントは1つしかないので、Qt Assistantのフィルタリング機能は必要ありません。

次に、目次を作成します。テーブルの項目は、section タグによって定義されます。このタグには、項目のタイトルと実際のページへのリンクの属性が含まれています。セクション・タグは無限に入れ子にすることができますが、実際的な理由から、3、4レベルより深く入れ子にすることは推奨されません。この例では、目次に次のようなアウトラインを使いたいと思います:

  • シンプル・テキスト・ビューア
    • ファイル検索
      • ファイルダイアログ
      • ワイルドカード・マッチング
      • ブラウズ
    • ファイルを開く

ヘルプ・プロジェクト・ファイルでは、アウトラインは次のように表されます:

<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アシスタントで見たい場合は、Qt圧縮ヘルプファイルを生成し、Qtアシスタントのデフォルトヘルプコレクションに登録する必要があります。

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

今Qt Assistantを起動すると、Qtドキュメントの横にSimple Text Viewerのドキュメントが表示されます。これはテスト目的であれば問題ありませんが、最終バージョンではQtアシスタントにシンプルテキストビューアのドキュメントだけを表示させたいと思います。

Qtアシスタントのカスタマイズ

Qtアシスタントにシンプルテキストビューアのドキュメントだけを表示させる最も簡単な方法は、独自のヘルプコレクションファイルを作成することです。コレクションファイルは圧縮されたヘルプファイルに似たバイナリ形式で保存され、ヘルプコレクションプロジェクトファイル(*.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 ヘルプ・プロジェクト・ファイルで定義された名前空間と仮想フォルダ、そして実際のページ・ファイル名で構成されます。

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

次に、「About」メニュー項目の名前を「About Simple Text Viewer」に変更します。Aboutダイアログの内容も、about テキストやアイコンの元となるファイルを指定して変更します。

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

Qt Assistant では、環境設定ダイアログを使ってドキュメントを追加したり削除したりすることができます。この機能は、Qt Assistantをより多くのアプリケーションの中心的なヘルプ・ビューワとして使用する場合に便利ですが、私たちのケースでは、ユーザーがドキュメントを削除できないようにしたいのです。そこで、環境設定ダイアログのドキュメントタブを非表示にします。

このような小さなドキュメントでは、アドレスバーはあまり意味がないので、これもオフにします。フィルター属性を持たないフィルターセクションを1つだけ持つことで、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アシスタントを制御する

まず、リモート・アプリケーションから Qt Assistant を起動して操作する方法を説明します。そのために、Assistant というクラスを作成します。

このクラスには、ドキュメントのページを表示するためのパブリック関数と、Qt Assistant が起動していることを確認するためのプライベート・ヘルパー関数があります。

Qtアシスタントの起動は、startAssistant() 関数の中で、QProcessを作成して起動するだけです。プロセスがすでに実行されている場合、関数はすぐに戻ります。そうでない場合は、プロセスをセットアップして開始する必要があります。

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 Designer やqmake といった Qt に付属するツールが含まれます。

この例では、プロセスのコマンドラインオプションにアプリケーション固有のコレクションファイルを渡すことで、デフォルトのドキュメントセットをカスタムドキュメントに置き換えます。

最後の引数として、-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ショートカットやHelp>Help Contentsメニュー項目からアクセスできる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");
}

最後に、アプリケーションを終了する前に、アプリケーションのQt Assistantインスタンスが適切に閉じられるように、保護されたQWidget::closeEvent()イベントハンドラを再実装する必要があります。

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アシスタントをアプリケーションのカスタマイズされたヘルプツールとして動作させるには、Qt圧縮ヘルプファイルを含むカスタムヘルプコレクションファイルに加えて、Qtアシスタントを制御するプロセスをアプリケーションに提供する必要があります。

Qt Assistant をカスタムヘルプビューアとして使用するアプリケーションで使用できるオプションと設定の詳細については、「Qt Assistant をカスタマイズする」を参照してください。

サンプルプロジェクト @ code.qt.io

©2024 The Qt Company Ltd. 本書に含まれるドキュメントの著作権は、それぞれの所有者に帰属します。 本書で提供されるドキュメントは、Free Software Foundation が発行したGNU Free Documentation License version 1.3に基づいてライセンスされています。 Qtおよびそれぞれのロゴは、フィンランドおよびその他の国におけるThe Qt Company Ltd.の 商標です。その他すべての商標は、それぞれの所有者に帰属します。