• Qt富文本处理


    一、富文本文档结构

    文本文档由 QTextDocument 类表示,该类包含有关文档内部表示、结构的信息,并跟踪修改以提供撤消/回撤功能。

    1.1、基本结构

    每个文档始终包含一个根框架,并且始终包含至少一个文本块。

    框架/表格总是由文档中的文本块分隔,即使文本块不包含任何信息。这确保了新元素始终可以插入现有结构之间。

    1.2、富文档元素

    富文本文档通常由文本块(也叫段落)QTextBlock)、框架QTextFrame)、表格QTextTable)、列表QTextList)等常见元素组成。

    文档中的基本结构构建元素是 QTextBlock QTextFrameQTextBlock 本身包含富文本片段QTextFragment),但这些片段不会直接影响文档的高级结构。

    可以使用 QTextObject 的子类将其他文档元素分组:

    • QTextBlockGroup:将文本块分组。
    • QTextFrame:将框架和其他元素分组。

    1.2.1、文本块

    文本块将具有不同字符格式的文本片段组合在一起,用于表示文档中的段落。每个块通常包含多个具有不同样式的文本片段

    可以通过使用 QTextBlock::iterator 遍历块的内部结构来检查给定块中的片段:

    1. QTextDocument * docment = ui->textEdit->document();
    2. QTextBlock block = docment->findBlock(0);
    3. for (QTextBlock::iterator it = block.begin(); !(it.atEnd()); ++it)
    4. {
    5. QTextFragment currentFragment = it.fragment();
    6. if (currentFragment.isValid())
    7. {
    8. qDebug()<text();
    9. }
    10. }

    按顺序遍历文本块:

    1. QTextBlock currentBlock = docment->begin();
    2. while (currentBlock.isValid())
    3. {
    4. //...
    5. currentBlock = currentBlock.next();
    6. }

    1.2.2、框架

    文本框架将文本块和子框架组合在一起,创建大于段落范围的文档结构。

    每个框架含至少一个文本块和大于等于零个子框架。

    每个文档都有一个包含所有其他文档元素的根框架。因此,除根框架外的所有框架都具有父框架。

    可使用 QTextFrame::iterator 遍历框架的子元素:

    1. QTextDocument * docment = ui->textEdit->document();
    2. QTextFrame * rootFrame = docment->rootFrame();
    3. for (QTextFrame::iterator it = rootFrame->begin(); !(it.atEnd()); ++it)
    4. {
    5. QTextFrame *childFrame = it.currentFrame();
    6. QTextBlock childBlock = it.currentBlock();
    7. if (childFrame)
    8. {
    9. //...
    10. }
    11. else if (childBlock.isValid())
    12. {
    13. //...
    14. }
    15. }

    1.2.3、表格

    表格是按行和列排列的单元格集合。每个单元格都是一个具有字符格式的文档元素,但它也可以包含其他元素,如框架和文本块。

    QTextTable QTextFrame 的子类,因此表格在文档结构中被视为框架。

    对于文档中遇到的每个框架,可以测试它是否表示一个表格,并以不同的方式处理它: 

    1. QTextDocument * docment = ui->textEdit->document();
    2. QTextFrame * rootFrame = docment->rootFrame();
    3. for (QTextFrame::iterator it = rootFrame->begin(); !(it.atEnd()); ++it)
    4. {
    5. QTextFrame *childFrame = it.currentFrame();
    6. QTextBlock childBlock = it.currentBlock();
    7. if (childFrame)
    8. {
    9. if (QTextTable *childTable = qobject_cast(childFrame))
    10. {
    11. //...
    12. }
    13. else
    14. {
    15. //...
    16. }
    17. }
    18. else if (childBlock.isValid())
    19. {
    20. //...
    21. }
    22. }

    可以通过遍历行和列来检查现有表中的单元格。

    1. for (int row = 0; row < table->rows(); ++row)
    2. {
    3. for (int column = 0; column < table->columns(); ++column)
    4. {
    5. QTextTableCell tableCell = table->cellAt(row, column);
    6. //...
    7. }
    8. }

     1.2.4、列表

    列表通常以格式化方式展示文本块序列。

    列表可以嵌套和设置缩进。

    可以通过列表中的索引来引用每个列表项:

    1. QTextDocument * docment = ui->textEdit->document();
    2. QTextBlock block = docment->findBlock(0);
    3. if (QTextList * list = block.textList())
    4. {
    5. for (int index = 0; index < list->count(); ++index)
    6. {
    7. QTextBlock listItem = list->item(index);
    8. //...
    9. }
    10. }

    QTextList QTextBlockGroup(可管理文本块分组) 的子类,QTextList 仅为它的列表项提供管理功能,而没有把列表项作为它的子项。 

    即在遍历文档时发现的任何文本块实际上都可能是列表项。可以通过以下代码确保正确识别列表项: 

    1. QTextDocument * docment = ui->textEdit->document();
    2. QTextFrame * rootFrame = docment->rootFrame();
    3. for (QTextFrame::iterator it = rootFrame->begin(); !(it.atEnd()); ++it)
    4. {
    5. QTextBlock block = it.currentBlock();
    6. if (block.isValid())
    7. {
    8. if (QTextList *list = block.textList())
    9. {
    10. int index = list->itemNumber(block);
    11. //processListItem(list, index);
    12. }
    13. }
    14. }

    二、QTextCursor 接口

    2.1、基于光标的文本编辑

    QTextCursor 提供了一个基于光标的编辑方式,允许在字符级别操纵 QTextDocument 的内容。

    光标会跟踪其在父文档中的位置,并可以报告有关周围结构的信息,例如包围的文本块、框架、表格或列表。

    2.1.1、分组光标操作

    一系列编辑操作可以打包在一起,以便可以在一个动作中一起回放或撤消。选择包含光标的内容:

    1. cursor.beginEditBlock();
    2. cursor.movePosition(QTextCursor::StartOfWord);
    3. cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
    4. cursor.endEditBlock();

    如果编辑操作未分组,文档将自动记录各个操作,以便以后可以撤消这些操作。

    2.1.2、多个光标

    可以使用多个光标同时编辑同一个文档,尽管用户在编辑小部件中只能看到一个光标。

    QTextDocument 确保每个光标正确写入文本,并且不会干扰其他光标。

    三、文档布局

    文档的布局仅在要在设备上显示时相关。

    每个文档的布局都由 QAbstractTextDocumentLayout 类的子类管理。

    QTextLayout 用法示例:

    1. #include "widget.h"
    2. #include
    3. #include
    4. Widget::Widget(QWidget *parent)
    5. : QWidget(parent)
    6. {
    7. auto font = this->font();
    8. font.setPixelSize(18);
    9. setFont(font);
    10. }
    11. Widget::~Widget()
    12. {
    13. }
    14. void Widget::paintEvent(QPaintEvent *event)
    15. {
    16. auto rect = event->rect();
    17. QTextLayout textLayout("黄河之水天上来,奔流到海不复回", this->font());
    18. qreal margin = 40;
    19. qreal radius = qMin(rect.width()/2.0, rect.height()/2.0) - margin;
    20. QPainter painter(this);
    21. painter.setRenderHint(QPainter::Antialiasing);
    22. painter.fillRect(rect, Qt::white);
    23. painter.setBrush(QBrush(Qt::black));
    24. painter.setPen(QPen(Qt::black));
    25. painter.setBrush(QBrush(QColor("#a6ce39")));
    26. painter.setPen(QPen(Qt::black));
    27. painter.translate(rect.center());
    28. painter.drawEllipse(QPointF(0,0),radius,radius);
    29. painter.setPen(QPen(Qt::red,5));
    30. for(int i = 0;i < 14;++i)
    31. {
    32. auto angle = 25.0 * i;
    33. auto x = cos(qDegreesToRadians(angle)) * (radius + 20);
    34. auto y = -sin(qDegreesToRadians(angle)) * (radius + 20);
    35. painter.drawPoint(x,y);
    36. }
    37. textLayout.beginLayout();
    38. auto angle{0.0f};
    39. while (true)
    40. {
    41. QTextLine line = textLayout.createLine();
    42. if (!line.isValid())
    43. break;
    44. auto x = cos(qDegreesToRadians(angle)) * (radius + 20);
    45. auto y = -sin(qDegreesToRadians(angle)) * (radius + 20);
    46. line.setLineWidth(20);
    47. line.setPosition(QPointF(x,y));
    48. angle += 25;
    49. }
    50. textLayout.endLayout();
    51. textLayout.draw(&painter, QPoint(0,0));
    52. }

    四、常见富文本编辑操作

    4.1、使用 QTextEdit

    QTextEdit 可以通过以下方式构造并用于显示HTML:

    1. QTextEdit *editor = new QTextEdit(parent);
    2. editor->setHtml(aStringContainingHTMLtext);
    3. editor->show();

    默认 QTextEdit 包含一个带有根框架的文档,其中包含一个空文本块。可以获取此文档:

    QTextDocument *document = editor->document();

    QTextEdit 的光标也可用于编辑文档:

    QTextCursor cursor = editor->textCursor();

    虽然可以同时使用多个光标编辑文档,但 QTextEdit 一次只显示一个光标。因此,如果想更新编辑器以显示特定的光标或其选择,则需要在修改文档后设置编辑器的光标:

    editor->setTextCursor(cursor);

    4.2、选择文本

    要在文档中的两个点之间选择文本,需要将光标定位在第一个点,然后设置移动模式(QTextCursor::MoveMode)和移动操作(QTextCursor::MoveOperation)移动它。

    选择文本时,选择的锚点保留在原来的光标位置:

    1. cursor.movePosition(QTextCursor::StartOfWord);
    2. cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);

    4.3、查找文本

    QTextDocument 提供了基于光标的搜索功能

    查找文档中所有的特定单词,并更改它们的颜色:

    1. auto document = ui->textEdit->document();
    2. auto searchString{"hello"};
    3. QTextCharFormat colorFormat;
    4. colorFormat.setBackground(Qt::cyan);
    5. QTextCursor newCursor(document);
    6. while (!newCursor.isNull() && !newCursor.atEnd())
    7. {
    8. newCursor = document->find(searchString, newCursor);
    9. if (!newCursor.isNull())
    10. {
    11. newCursor.movePosition(QTextCursor::NoMove,QTextCursor::KeepAnchor);
    12. newCursor.mergeCharFormat(colorFormat);
    13. }
    14. }

    在每次搜索和替换操作后,不必移动光标,它总是位于搜索和被替换的词的末尾。

    五、对HTML的支持

    5.1、支持的标记

    Qt富文本引擎支持的HTML标记:

    a

    锚点或链接。更多

    address

    地址。更多

    b

    加粗。更多

    big比常规的字体大一号的字体。更多
    blockquote

    缩进段落。更多

    body文档的主体。更多
    br换行。更多
    center水平居中。更多
    cite表示它所包含的文本是对某个参考文献的引用。更多
    code表示它引用的文本是一段代码,怎么显示交给浏览器。更多
    dd不实用,略。
    dfn不实用,略。
    div一个块级元素,浏览器通常会在div元素前后放置一个换行符。div标签可以把文档分割为独立的、不同的部分。更多
    dl不实用,略。
    dt不实用,略。
    em突出显示文本。更多
    font字体,略。
    h1~h61~6级的标题。h1是最大的。
    head文档头部。更多
    hr显示为一条水平线。更多
    html是 HTML 文档中最外层的元素。更多
    i倾斜文本。更多
    img图片。更多
    kbd不实用,略。
    meta提供了 HTML 文档的元数据。元数据不会显示在客户端,但是会被浏览器解析。更多
    li列表项,很实用。更多
    ol有序列表。更多
    ul无序列表。更多
    p

    标签定义段落。会自动在标签前后创建一些空白。更多

    pre内容所见即所得。更多
    s删除线。更多
    samp不实用,略。
    small小字体。更多
    span

    被此元素包含的文本,可以使用独立的样式。更多

    strong粗体突出显示文本。
    sub下标文本。更多
    sup上标文本。更多
    tableHTML 表格。更多
    tbody设置表格一部分的样式。更多
    td表格的单元格。更多
    tfoot页脚。更多
    th表头单元格。更多
    thead表头。更多
    title略。
    tr表格中的行。更多
    u

    文本下划线。

    5.2、CSS属性

    使用css属性示例:

    1. ui->textEdit->setHtml(R"(
    2. 我是h1

    3. 我是h2

    4. )");

    Qt富文本引擎支持的CSS属性:

    background-color背景色。更多
    background-image背景图片。更多
    background所有背景属性。更多
    color文字颜色。更多

    font-family

    字体族。更多
    font-style字体样式。更多
    font-weight字体粗细。更多
    font字体所有属性。更多
    text-decoration文本装饰,下划线、上划线、删除线等。更多
    text-indent文本块中首行文本的缩进。更多
    white-space空白的处理方式。更多

    margin-top
    margin-bottom
    margin-leftpixels
    margin-right

    元素的四个边距。更多
    padding-top
    padding-bottom    
    padding-left    
    padding-right    
    padding
    元素填充边距。更多
    vertical-align垂直对齐方式。更多
    border-collapse表格的边框是被合并为一个单一的边框,还是像在标准的 HTML 中那样分开显示。更多
    border-color    
    border-top-color
    border-bottom-color    
    border-left-color    
    border-right-color
    边框颜色。更多
    border-style
    border-top-style
    border-bottom-style
    border-left-style
    border-right-style
    边框样式。更多
    border-width
    border-top-width    
    border-bottom-width
    border-left-width
    border-right-width
    边框宽度。更多
    border    
    border-left
    border-right
    border-top
    border-bottom
    所有边框属性。更多
    float

    指定一个元素的浮动方式。更多

    text-transform文本大小写。更多
    font-variant

    可设置将小写字母替换成缩小过的大写字母。更多1更多2

    line-height以百分比计数的行高。更多

    5.3、Qt特定的CSS属性

    可以使用以下Qt特定属性来设置文本块的样式:

    • -qt-block-indent(数值):按指定的数字空格缩进文本块。

    测试发现设置为1会缩进半个制表符的空间。

    • -qt-listindent:按指定的空格数缩进列表项

    • -qt-list-number-prefix
    • -qt-list-number-suffix
    • -qt-paragraph-type
    • -qt-table-type
    • -qt-user-state
  • 相关阅读:
    xcode swift 单元测试 test
    [思维]Tournament Countdown Codeforces1713D
    Rust图形界面编程:egui平直布局
    企业数据文化建设,为什么会加速商业智能BI在企业的发展?
    了解什么是JDBC
    SpringBoot热部署
    后端开发怎么做得更优秀?记住这15个好习惯
    ansible批量添加巡检服务器
    chatGPT的前世今生
    atcoder ABC 232 B~E题解
  • 原文地址:https://blog.csdn.net/kenfan1647/article/details/127902680