Spec-Zone.ru › Qt

Интерфейс 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. QTextDocument гарантирует, что каждый курсор записывает текст правильно и не мешает другим.

Вставка элементов документа

QTextCursor предоставляет несколько функций, которые можно использовать для изменения структуры документа с форматированным текстом. Как правило, эти функции позволяют создавать элементы документа с соответствующей информацией о форматировании, и они вставляются в документ в позиции курсора.

Первая группа функций вставляет элементы уровня блока и обновляет позицию курсора, но не возвращает вставленный элемент:

  • insertBlock() вставляет новый текстовый блок (абзац) в документ в позиции курсора и перемещает курсор в начало нового блока.
  • insertFragment() вставляет существующий фрагмент текста в документ в позиции курсора.
  • insertImage() вставляет изображение в документ в позиции курсора.
  • insertText() вставляет текст в документ в позиции курсора.

Вы можете просмотреть содержимое вставленного элемента через интерфейс курсора.

Вторая группа функций вставляет элементы, которые обеспечивают структуру документа, и возвращает вставленную структуру:

  • 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);

Курсор размещается в начале нового блока.

Фреймы

Фреймы вставляются в документ с помощью курсора и будут размещены внутри текущего фрейма курсора после текущего блока. Следующий код демонстрирует, как можно вставить фрейм между двумя текстовыми блоками в корневом фрейме документа. Сначала мы находим текущий фрейм курсора:

    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.

Примеры

Форматированный текст хранится в текстовых документах, которые могут быть созданы путем импорта 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 предоставляет гораздо более сложные функции компоновки и форматирования.

© The Qt Company Ltd
Licensed under the GNU Free Documentation License, Version 1.3.
https://doc.qt.io/qt-6.2/richtext-cursor.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API