Структура документа с форматированным текстом
Текстовые документы представлены классом 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. В отличие от других элементов документа, изображения представлены специально отформатированными фрагментами текста. Это позволяет размещать их в формате совместно с окружающим текстом.
Основными структурными строительными блоками в документах являются 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/archives/qt-5.11/richtext-structure.html