Spec-Zone.ru › Qt 5.11

Интерфейс QTextCursor

Документы могут быть отредактированы через интерфейс, предоставляемый классом QTextCursor; курсоры создаются с помощью конструктора или извлекаются из виджета редактора. Курсор используется для выполнения операций редактирования, которые точно соответствуют операциям, которые пользователь может выполнить в редакторе. В результате информация о структуре документа также доступна через курсор, что позволяет изменять структуру. Использование курсорно-ориентированного интерфейса для редактирования упрощает процесс написания пользовательского редактора для разработчиков, поскольку операции редактирования легко визуализируются.

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

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

В этой главе описываются большинство распространенных операций редактирования, которые вам понадобятся для выполнения с помощью курсора, от базовой вставки текста и элементов документа до более сложных манипуляций со структурами документа.

Редактирование с использованием курсора

На самом простом уровне текстовые документы представляют собой строку символов, помеченных каким-либо образом, чтобы отразить структуру блоков текста в документе. QTextCursor предоставляет курсорно-ориентированный интерфейс, который позволяет манипулировать содержимым QTextDocument на уровне символов. Поскольку элементы (блоки, фреймы, таблицы и т. д.) также закодированы в потоке символов, структура документа сама по себе может быть изменена с помощью курсора.

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

Использование курсора

Основное использование курсора — вставка или изменение текста в блоке. Мы можем использовать курсор текстового редактора для этого:

    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"));
    QVector<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(QDate::shortDayName(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/archives/qt-5.11/richtext-cursor.html

Spec-Zone.ru

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