QTextCursorインターフェイス

ドキュメントは、QTextCursor クラスによって提供されるインターフェースを介して編集することができます。カーソルはコンストラクタを使用して作成されるか、エディタウィジェットから取得されます。カーソルはコンストラクタを使用して作成されるか、エディタウィジェットから取得されます。カーソルは、ユーザがエディタで行う編集操作に正確に対応する編集操作を行うために使用されます。その結果、文書構造に関する情報もカーソルを通して利用でき、これによって構造を変更することができます。編集にカーソル指向のインターフェイスを使用することで、編集操作を簡単に視覚化することができるため、開発者にとってカスタムエディタを書くプロセスがよりシンプルになります。

また、QTextCursor クラスは、ドキュメント内で選択されたテキストに関する情報を保持します。この場合も、エディタ内でテキストを選択するためにユーザが行うアクションと概念的に類似したモデルに従っています。

リッチテキスト・ドキュメントは複数のカーソルを持つことができ、それぞれのカーソルはドキュメント内の位置と選択に関する情報を持ちます。このカーソルベースのパラダイムは、テキストの切り取りや貼り付けのような一般的な操作をプログラムで簡単に実装できるようにします。

この章では、テキストやドキュメント要素の基本的な挿入から、ドキュメント構造のより複雑な操作まで、カーソルを使用して実行する必要がある一般的な編集操作のほとんどについて説明します。

カーソルによる編集

最も単純なレベルでは、テキスト文書は文字列で構成され、文書内のテキストのブロック構造を表すために何らかの方法でマークアップされます。QTextCursor は、QTextDocument の内容を文字レベルで操作できるカーソルベースのインターフェイスを提供します。要素(ブロック、フレーム、テーブルなど)も文字ストリームにエンコードされているので、カーソルによって文書構造自体を変更することができます。

カーソルは親文書内の位置を追跡し、テキストブロック、フレーム、テーブル、リストなどの周囲の構造に関する情報を報告することができます。また、カーソルを通して、囲まれている構造の書式を直接得ることもできます。

カーソルの使用

カーソルの主な用途は、ブロック内のテキストを挿入または変更することです。このためにテキストエディタのカーソルを使用することができます:

    QTextEdit *editor = new QTextEdit();
    QTextCursor cursor(editor->textCursor());

また、文書から直接カーソルを取得することもできます:

    QTextDocument *document = new QTextDocument(editor);
    QTextCursor cursor(document);

カーソルはドキュメントの先頭に置かれ、ドキュメントの最初の(空の)ブロックに書き込むことができます。

カーソル操作のグループ化

一連の編集操作をパッケージ化することで、単一の操作で再生したり、元に戻したりすることができます。これは、カーソルを含む単語を選択する次の例のように、beginEditBlock()endEditBlock() 関数を使用することで実現できます:

    cursor.beginEditBlock();
    cursor.movePosition(QTextCursor::StartOfWord);
    cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
    cursor.endEditBlock();

編集操作がグループ化されていない場合、文書は自動的に個々の操作を記録し、後で取り消すことができます。操作を大きなパッケージにグループ化することで、ユーザーにとってもアプリケーションにとっても編集の効率が上がりますが、ユーザーがアンドゥのプロセスを細かく制御したい場合があるため、あまり多くの操作をグループ化しないように注意する必要があります。

複数のカーソル

複数のカーソルを使って、同じドキュメントを同時に編集することができます。ただし、QTextEdit ウィジェットでは、ユーザーに見えるのは1つだけです。QTextDocument は、各カーソルがテキストを正しく書き込み、他のカーソルに干渉しないことを保証します。

文書要素の挿入

QTextCursor には、リッチテキスト文書の構造を変更するために使用できる関数がいくつか用意されています。一般的に、これらの関数を使用すると、関連する書式情報を持つドキュメント要素を作成することができ、それらはカーソルの位置でドキュメントに挿入されます。

最初の関数群は、ブロックレベルの要素を挿入し、カーソル位置を更新しますが、挿入された要素を返しません:

  • insertBlock() は、新しいテキスト・ブロック(段落)をカーソル位置の文書に挿入し、カーソルを新しいブロックの先頭に移動します。
  • insertFragment() は、既存のテキスト断片をカーソルの位置の文書に挿入します。
  • insertImage() はカーソル位置の文書に画像を挿入する。
  • insertText() は、カーソル位置の文書にテキストを挿入します。

挿入された要素の内容は、カーソルインターフェイスを通して調べることができます。

2番目の関数群は、ドキュメントに構造を提供する要素を挿入し、挿入された構造を返します:

  • insertFrame() は、カーソルの現在のブロックの後にフレームをドキュメントに挿入し、カーソルを新しいフレームの空のブロックの先頭に移動します。
  • insertList() は、カーソルの位置にあるドキュメントにリストを挿入し、カーソルをリストの最初の項目の先頭に移動します。
  • insertTable() は、カーソルの現在のブロックの後のドキュメントに表を挿入し、カーソルを表に続くブロックの先頭に移動します。

これらの要素は、文書内の他の要素を含むかグループ化します。

テキストとテキストフラグメント

テキストは、現在の文字書式、またはテキストと一緒に指定されたカスタム書式で現在のブロックに挿入することができます:

    cursor.insertText(tr("Character formats"),
                      headingFormat);

    cursor.insertBlock();

    cursor.insertText(tr("Text can be displayed in a variety of "
                                  "different character formats. "), plainFormat);
    cursor.insertText(tr("We can emphasize text by "));
    cursor.insertText(tr("making it italic"), emphasisFormat);

一旦カーソルで文字書式が使用されると、他の文字書式が指定されるまで、その書式がそのカーソルで挿入されるテキストのデフォルト書式になります。

文字書式を指定せずにカーソルでテキストを挿入した場合、そのテキストは文書内のその位置で使用されている文字書式が適用されます。

ブロック

テ キ ス ト ブ ロ ッ ク は、insertBlock ()関数で文書に挿入 し ます。

    QTextBlockFormat backgroundFormat = blockFormat;
    backgroundFormat.setBackground(QColor("lightGray"));

    cursor.setBlockFormat(backgroundFormat);

カーソルは新しいブロックの開始位置に置かれます。

フレーム

フレームはカーソルを使って文書に挿入され、現在のブロックの後にカーソルの現在のフレーム内に配置されます。次のコードは、ドキュメントのルート・フレーム内の2つのテキスト・ブロックの間にフレームを挿入する方法を示しています。まず、カーソルの現在のフレームを見つけます:

    QTextFrame *mainFrame = cursor.currentFrame();
    cursor.insertText(...);

このフレームにテキストを挿入し、子フレームのフレーム・フォーマットを設定します:

    QTextFrameFormat frameFormat;
    frameFormat.setMargin(32);
    frameFormat.setPadding(8);
    frameFormat.setBorder(4);

このフレーム・フォーマットでは、フレームに32ピクセルの外部マージン、8ピクセルの内部パディング、4ピクセル幅のボーダーが与えられます。フレームフォーマットの詳細については、QTextFrameFormat のドキュメントを参照してください。

フレームは前のテキストの後に文書に挿入されます:

    cursor.insertFrame(frameFormat);
    cursor.insertText(...);

フレームを挿入した直後に、文書にテキストを追加します。フレームが文書に挿入されるとき、テキストカーソルはフレームの内側に置かれているので、このテキストもフレームの内側に挿入されます。

最後に、先に記録したフレーム内で最後に利用可能なカーソル位置を取って、フレームの外側にカーソルを配置します:

    cursor = mainFrame->lastCursorPosition();
    cursor.insertText(...);

最後に追加したテキストは、ドキュメントの子フレームの後に挿入されます。各フレームにはテキストブロックが挿入されるため、カーソルを使って常に多くの要素を挿入することができます。

テーブルはカーソルを使ってドキュメントに挿入され、カーソルの現在のフレーム内で現在のブロックの後に配置されます:

    QTextCursor cursor(editor->textCursor());
    QTextTable *table = cursor.insertTable(rows, columns, tableFormat);

表は、その配置、背景色、使用されるセル間隔など、表の全体的なプロパティを定義する特定のフォーマットで作成することができます。また、各列の制約を決定し、各列の幅を固定にしたり、利用可能なスペースに応じてサイズを変更したりすることもできます。

    QTextTableFormat tableFormat;
    tableFormat.setBackground(QColor("#e0e0e0"));
    QList<QTextLength> constraints;
    constraints << QTextLength(QTextLength::PercentageLength, 16);
    constraints << QTextLength(QTextLength::PercentageLength, 28);
    constraints << QTextLength(QTextLength::PercentageLength, 28);
    constraints << QTextLength(QTextLength::PercentageLength, 28);
    tableFormat.setColumnWidthConstraints(constraints);
    QTextTable *table = cursor.insertTable(rows, columns, tableFormat);

上で作成した表の列は、それぞれ利用可能な幅の一定割合を占めることになります。表の書式は任意であることに注意してください。書式なしで表を挿入した場合、表のプロパティには適切なデフォルト値が使用されます。

セルは他の文書要素を含むことができるので、それらも必要に応じて書式やスタイルを設定することができます。

カーソルで各セルに移動し、テキストを挿入することで、表にテキストを追加することができます。

    cell = table->cellAt(0, 0);
    cellCursor = cell.firstCursorPosition();
    cellCursor.insertText(tr("Week"), charFormat);

この方法で簡単な時刻表を作成することができます:

    for (column = 1; column < columns; ++column) {
        cell = table->cellAt(0, column);
        cellCursor = cell.firstCursorPosition();
        cellCursor.insertText(tr("Team %1").arg(column), charFormat);
    }

    for (row = 1; row < rows; ++row) {
        cell = table->cellAt(row, 0);
        cellCursor = cell.firstCursorPosition();
        cellCursor.insertText(tr("%1").arg(row), charFormat);

        for (column = 1; column < columns; ++column) {
            if ((row-1) % 3 == column-1) {
                cell = table->cellAt(row, column);
                QTextCursor cellCursor = cell.firstCursorPosition();
                cellCursor.insertText(tr("On duty"), charFormat);
            }
        }
    }

リスト

ブロック要素のリストを自動的に作成し、現在のカーソル位置の文書に挿入することができます。この方法で作成される各リストは、リスト・フォーマットを指定する必要があります:

    QTextListFormat listFormat;
    if (list) {
        listFormat = list->format();
        listFormat.setIndent(listFormat.indent() + 1);
    }

    listFormat.setStyle(QTextListFormat::ListDisc);
    cursor.insertList(listFormat);

上記のコードでは、まずカーソルが既存のリスト内にあるかどうかをチェックし、既存のリスト内にある場合は、新しいリストのリスト・フォーマットに適切なインデント・レベルを与えます。これによって、インデントのレベルを増やしてネストしたリストを作成することができます。より洗練された実装では、リストの各レベルの箇条書きに異なる種類のシンボルを使用することもできます。

画像

インライン画像は、通常の方法でカーソルを使って文書に追加されます。他の多くの要素とは異なり、画像のプロパティはすべて画像のフォーマットによって指定されます。これは、画像を挿入する前にQTextImageFormat オブジェクトを作成しなければならないことを意味します:

    QTextImageFormat imageFormat;
    imageFormat.setName(":/images/advert.png");
    cursor.insertImage(imageFormat);

画像名は、アプリケーションのリソースファイル内のエントリを参照します。画像名は、アプリケーションのリソースファイル内のエントリを参照します。この名前を導き出すために使用される方法は、Qt Resource Systemで説明されています。

リッチテキストは、外部ソースから HTML をインポートして作成されるか、QTextCursor を使って生成されるテキストドキュメントに保存されます。

リッチテキストの操作

リッチテキスト・ドキュメントを使う最も簡単な方法は、QTextEdit クラスを使うことです。以下のコードはHTMLをドキュメントにインポートし、テキスト編集ウィジェットを使ってドキュメントを表示します。

QTextEdit *editor = new QTextEdit(parent);
editor->setHtml(aStringContainingHTMLtext);
editor->show();

document()関数を使用して、テキスト編集からドキュメントを取得することができます。その後、QTextCursor クラスを使用してプログラムでドキュメントを編集することができます。このクラスはスクリーン・カーソルをモデルにしており、編集操作は同じセマンティクスに従います。次のコードは、ドキュメントの最初の行を太字フォントに変更し、他のフォント・プロパティはそのままにしています。エディタは自動的に更新され、基礎となるドキュメント・データに加えられた変更が反映されます。

QTextDocument *document = edit->document();
QTextCursor cursor(document);

cursor.movePosition(QTextCursor::Start);
cursor.movePosition(QTextCursor::EndOfLine, QTextCursor::KeepAnchor);

QTextCharFormat format;
format.setFontWeight(QFont::Bold);

cursor.mergeCharFormat(format);

カーソルは最初の行の先頭から末尾に移動されましたが、行頭のアンカーは保持されていることに注意してください。これは、QTextCursor クラスのカーソルベースの選択機能を示しています。

カレンダーの生成

リッチテキストはカーソルベースのアプローチを使って非常に素早く生成することができます。次の例は、QTextEdit ウィジェットに、曜日を太字のヘッダーで表示したシンプルなカレンダーです:

    editor = new QTextEdit(this);

    QTextCursor cursor(editor->textCursor());
    cursor.movePosition(QTextCursor::Start);

    QTextCharFormat format(cursor.charFormat());
    format.setFontFamily("Courier");

    QTextCharFormat boldFormat = format;
    boldFormat.setFontWeight(QFont::Bold);

    cursor.insertBlock();
    cursor.insertText(" ", boldFormat);

    QDate date = QDate::currentDate();
    int year = date.year(), month = date.month();

    for (int weekDay = 1; weekDay <= 7; ++weekDay) {
        cursor.insertText(QString("%1 ").arg(QLocale::system().dayName(weekDay), 3),
            boldFormat);
    }

    cursor.insertBlock();
    cursor.insertText(" ", format);

    for (int column = 1; column < QDate(year, month, 1).dayOfWeek(); ++column) {
        cursor.insertText("    ", format);
    }

    for (int day = 1; day <= date.daysInMonth(); ++day) {
        int weekDay = QDate(year, month, day).dayOfWeek();

        if (QDate(year, month, day) == date)
            cursor.insertText(QString("%1 ").arg(day, 3), boldFormat);
        else
            cursor.insertText(QString("%1 ").arg(day, 3), format);

        if (weekDay == 7) {
            cursor.insertBlock();
            cursor.insertText(" ", format);
        }
    }

上記の例は、最小限のコードで新しいリッチテキスト文書を素早く生成することがいかに簡単かを示しています。多くのコードを引用することを避けるため、粗い固定ピッチのカレンダーを生成しましたが、Scribeはより洗練されたレイアウトおよびフォーマット機能を提供します。

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