Структура документа с форматированным текстом
Текстовые документы представлены классом QTextDocument, который содержит информацию об внутренней структуре документа, его структуре и отслеживает изменения, чтобы обеспечить возможности отмены/повторного выполнения.
Структурированное представление текстового документа представляет его содержимое в виде иерархии блоков текста, фреймов, таблиц и других объектов. Они обеспечивают логическую структуру документа и описывают, как будет отображаться их содержимое. В основном, фреймы и таблицы используются для группирования других структур, в то время как блоки текста содержат фактическую текстовую информацию.
Новые элементы создаются и вставляются в документ программно с помощью QTextCursor или с помощью виджета редактора, такого как QTextEdit. Элементам можно задать определенный формат при их создании; в противном случае они принимают текущий формат курсора для элемента.
|
Основная структура «Верхний уровень» документа может быть заполнен так, как показано. Каждый документ всегда содержит корневой фрейм, и он всегда содержит по меньшей мере один блок текста. Для документов с некоторым текстовым содержимым корневой фрейм обычно содержит последовательность блоков и других элементов. Последовательности фреймов и таблиц всегда разделяются блоками текста в документе, даже если блоки текста не содержат информации. Это гарантирует, что новые элементы всегда можно вставлять между существующими структурами. |
В этой главе мы рассмотрим каждый из структурных элементов, используемых в документе с форматированным текстом, опишем их особенности и применение, а также покажем, как просматривать их содержимое. Редактирование документов описано в Интерфейсе QTextCursor.
Документы с форматированным текстом
Объекты QTextDocument содержат всю необходимую информацию для создания документов с форматированным текстом. Доступ к текстовым документам можно получить двумя взаимодополняющими способами: в виде линейного буфера для использования редакторами и в виде иерархической структуры объектов, которая полезна для механизмов вывода.
В иерархической модели документа объекты обычно соответствуют визуальным элементам, таким как фреймы, таблицы и списки. На более низком уровне эти элементы описывают такие свойства, как стиль текста и выравнивание. Линейное представление документа используется для редактирования и манипулирования содержимым документа.
Хотя QTextEdit упрощает отображение и редактирование форматированного текста, документы также можно использовать независимо от любого виджета редактора, например:
QTextDocument *newDocument = new QTextDocument;
В качестве альтернативы, их можно извлечь из существующего редактора:
QTextEdit *editor = new QTextEdit; QTextDocument *editorDocument = editor->document();
Эта гибкость позволяет приложениям обрабатывать несколько документов с форматированным текстом без накладных расходов на несколько виджетов редактора или необходимости сохранять документы в каком-либо промежуточном формате.
Пустой документ содержит корневой фрейм, который сам содержит один пустой блок текста. Фреймы обеспечивают логическое разделение частей документа, но также имеют свойства, определяющие, как они будут отображаться при рендеринге. Таблица является специализированным типом фрейма, состоящим из ряда ячеек, организованных в строки и столбцы, каждая из которых может содержать дополнительную структуру и текст. Таблицы обеспечивают функции управления и вывода, которые позволяют создавать гибкие конфигурации ячеек.
Блоки текста содержат фрагменты текста, каждый из которых определяет информацию о тексте и формате символов. Текстовые свойства определяются как на уровне символов, так и на уровне блоков. На уровне символов можно указать такие свойства, как семейство шрифтов, цвет текста и начертание шрифта. Свойства на уровне блока контролируют внешний вид и поведение текста на более высоком уровне, такие как направление потока текста, выравнивание и цвет фона.
Структура документа не изменяется напрямую. Редактирование выполняется через интерфейс на основе курсора. Интерфейс курсора текста автоматически вставляет новые элементы документа в корневой фрейм и гарантирует, что он заполняется пустыми блоками при необходимости.
Мы получаем корневой фрейм следующим образом:
QTextDocument *textDocument;
QTextFrame *root = textDocument->rootFrame();
При навигации по структуре документа полезно начинать с корневого фрейма, потому что он предоставляет доступ ко всей структуре документа.
Элементы документа
Документы с форматированным текстом обычно состоят из общих элементов, таких как абзацы, фреймы, таблицы и списки. Они представлены в QTextDocument классами QTextBlock, QTextFrame, QTextTable и QTextList. В отличие от других элементов документа, изображения представлены специальными форматированными фрагментами текста. Это позволяет размещать их в формате inline вместе с окружающим текстом.
Основными структурными блоками в документах являются QTextBlock и QTextFrame. Сами блоки содержат фрагменты форматированного текста (QTextFragment), но они напрямую не влияют на общую структуру документа.
Элементы, которые могут группировать вместе другие элементы документа, обычно являются подклассами QTextObject и делятся на две категории: элементы, которые группируют блоки текста, являются подклассами QTextBlockGroup, а те, которые группируют фреймы и другие элементы, являются подклассами QTextFrame.
Блоки текста
Блоки текста предоставляются классом QTextBlock.
Блоки текста группируют вместе фрагменты текста с разными форматами символов и используются для представления абзацев в документе. Каждый блок обычно содержит ряд фрагментов текста с разными стилями. Фрагменты создаются при вставке текста в документ, и больше фрагментов добавляется при редактировании документа. Документ разделяет, объединяет и удаляет фрагменты, чтобы эффективно представлять различные стили текста в блоке.
Фрагменты внутри данного блока можно просмотреть, используя QTextBlock::iterator для обхода внутренней структуры блока:
QTextBlock::iterator it;
for (it = currentBlock.begin(); !(it.atEnd()); ++it) {
QTextFragment currentFragment = it.fragment();
if (currentFragment.isValid())
processFragment(currentFragment);
}
Блоки также используются для представления элементов списка. В результате блоки могут определять свои собственные форматы символов, которые содержат информацию об оформлении блока, например, о типе маркеров, используемых для элементов списка. Форматирование самого блока описывается классом QTextBlockFormat и описывает такие свойства, как выравнивание текста, отступы и цвет фона.
Хотя данный документ может содержать сложные структуры, как только у нас есть ссылка на допустимый блок в документе, мы можем перемещаться между каждым из блоков текста в порядке их написания:
QTextBlock currentBlock = textDocument->begin();
while (currentBlock.isValid()) {
processBlock(currentBlock);
currentBlock = currentBlock.next();
}
Этот метод полезен, когда вы хотите извлечь только форматированный текст из документа, потому что он игнорирует фреймы, таблицы и другие типы структуры.
QTextBlock предоставляет операторы сравнения, которые облегчают манипуляции с блоками: operator==() и operator!=() используются для проверки того, являются ли два блока одинаковыми, а operator<() используется для определения того, какой из них встречается первым в документе.
Фреймы
Фреймы предоставляются классом QTextFrame.
Текстовые фреймы группируют блоки текста и дочерние фреймы, создавая структуры документа, которые больше, чем абзацы. Формат фрейма определяет, как он отображается и позиционируется на странице. Фреймы либо вставляются в поток текста, либо ссылаются слева или справа от страницы. Каждый документ содержит корневой фрейм, который содержит все другие элементы документа. В результате все фреймы, кроме корневого, имеют родительский фрейм.
Поскольку блоки текста используются для разделения других элементов документа, каждый фрейм всегда будет содержать по меньшей мере один блок текста и ноль или более дочерних фреймов. Мы можем проверить содержимое фрейма, используя QTextFrame::iterator для обхода дочерних элементов фрейма:
QDomElement frameElement = ...
QTextFrame::iterator it;
for (it = frame->begin(); !(it.atEnd()); ++it) {
QTextFrame *childFrame = it.currentFrame();
QTextBlock childBlock = it.currentBlock();
if (childFrame)
processFrame(frameElement, childFrame);
else if (childBlock.isValid())
processBlock(frameElement, childBlock);
}
Обратите внимание, что итератор выбирает как фреймы, так и блоки, поэтому необходимо проверить, к чему он относится. Это позволяет нам перемещаться по структуре документа фрейм за фреймом, но при необходимости получать доступ к блокам текста. Классы QTextBlock::iterator и QTextFrame::iterator могут использоваться взаимодополняющим образом для извлечения необходимой структуры из документа.
Таблицы
Таблицы предоставляются классом QTextTable.
Таблицы представляют собой коллекции ячеек, организованных в строки и столбцы. Каждая ячейка таблицы — это элемент документа со своим собственным форматом символов, но она также может содержать другие элементы, такие как фреймы и блоки текста. Ячейки таблицы автоматически создаются при построении таблицы или при добавлении дополнительных строк или столбцов. Их также можно перемещать между таблицами.
QTextTable является подклассом QTextFrame, поэтому таблицы обрабатываются как фреймы в структуре документа. Для каждого фрейма, который мы встречаем в документе, мы можем проверить, представляет ли он таблицу, и обработать его по-другому:
QDomElement frameElement = ...
QTextFrame::iterator it;
for (it = frame->begin(); !(it.atEnd()); ++it) {
QTextFrame *childFrame = it.currentFrame();
QTextBlock childBlock = it.currentBlock();
if (childFrame) {
QTextTable *childTable = qobject_cast<QTextTable*>(childFrame);
if (childTable)
processTable(frameElement, childTable);
else
processFrame(frameElement, childFrame);
} else if (childBlock.isValid())
processBlock(frameElement, childBlock);
}
Ячейки внутри существующей таблицы можно просмотреть, перебирая строки и столбцы.
for (int row = 0; row < table->rows(); ++row) {
for (int column = 0; column < table->columns(); ++column) {
QTextTableCell tableCell = table->cellAt(row, column);
processTableCell(tableCell);
}
}
Списки
Списки предоставляются классом QTextList.
Списки представляют собой последовательности блоков текста, отформатированных обычным способом, но также содержат стандартные декорации списков, такие как маркеры и нумерованные элементы. Списки могут быть вложенными и будут иметь отступы, если формат списка определяет ненулевой отступ.
Мы можем сослаться на каждый элемент списка по его индексу в списке:
for (int index = 0; index < list->count(); ++index) {
QTextBlock listItem = list->item(index);
processListItem(listItem);
}
Поскольку QTextList является подклассом QTextBlockGroup, он не группирует элементы списка как дочерние элементы, а вместо этого предоставляет различные функции для их управления. Это означает, что любой блок текста, который мы найдем при просмотре документа, может фактически быть элементом списка. Мы можем гарантировать, что элементы списка будут правильно идентифицированы, используя следующий код:
QTextFrame::iterator it;
for (it = frame->begin(); !(it.atEnd()); ++it) {
QTextBlock block = it.currentBlock();
if (block.isValid()) {
QTextList *list = block.textList();
if (list) {
int index = list->itemNumber(block);
processListItem(list, index);
}
}
}
Изображения
Изображения в QTextDocument представлены фрагментами текста, которые ссылаются на внешние изображения через механизм ресурсов. Изображения создаются с помощью интерфейса курсора и могут быть изменены позже путем изменения формата символов фрагмента текста изображения:
if (fragment.isValid()) {
QTextImageFormat newImageFormat = fragment.charFormat().toImageFormat();
if (newImageFormat.isValid()) {
newImageFormat.setName(":/images/newimage.png");
QTextCursor helper = cursor;
helper.setPosition(fragment.position());
helper.setPosition(fragment.position() + fragment.length(),
QTextCursor::KeepAnchor);
helper.setCharFormat(newImageFormat);
}
}
Фрагмент, представляющий изображение, можно найти, перебирая фрагменты в блоке текста, который содержит изображение.
© The Qt Company Ltd
Licensed under the GNU Free Documentation License, Version 1.3.
https://doc.qt.io/qt-5.9/richtext-structure.html