QT应用主窗口——富文本处理

QTextEdit支持富文本的处理,什么是富文本呢?富文本或者叫做富文本格式,简单来说就是在文档中可以使用多种格式,比如字体颜色,图片和表格等等。它是与纯文本相对而言的,比如Windows上的记事本就是纯文本编辑器,而Word就是富文本编辑器,Qt中提供了对富文本处理的支持,可以在帮助中通过RichTextProcessing关键字查看,哪里详细讲解了富文本处理的相关内容,如果想进一步了解富文本可以在QT中用帮助去学习。

富文本文档结构

Qt对富文本的处理分为编辑操作和只读操作两种方式。编辑操作使用基于光标的一些接口函数,更好地模拟了用户的编辑操作,更加容易理解,而且不会丢失底层的文档框架;而对于文档结构的概览,则使用了只读的分层次的接口函数,有利于文档的检索和输出。可见,对于文档的读取和编辑要使用不同的两组接口。文档的光标主要基于QTextCursor类,而文档的框架主要基于一个文本编辑QTextDocument类。一个富文本文档的结构分为几种元素来表示,分别是框架、文本块、表格和列表分别用以下来创建QTextFrame、QtextBlock、QTextTable、QTextList。每种元素的格式又使用相应的format类来表示,分别是框架格式QTextFrameFormat、文本格式QtextBlockFormat、表格格式QTextTable、列表格式QTextListFormat,这些格式一般在编辑文档时使用,所以常和QTextCursor类配合使用。QTextEdit类就是一个富文本编辑器,所以在构建QTextEdot类的对象时就已经构建了一个QTextDocument类的对象和一个文本QTextCursor类对象,只须调用它们进行相关的操作即可。

一个空的文档包含了一个根框架,这个根框架又包含了一个空的文本块。框架将一个文档分为多个部分,在根框架里可以再添加文本块、子框架和表格等。

新建一个Qt Widgets应用,项目名称为myrichtext,类名默认为MainWindow基类默认为QMainWindow。建立好项目后,在设计模式向中心拖入一个TextEdit部件。然后到mainwindow.cpp文件中添加头文件#include<QTextFrame>,再在MainWindow类的构造函数中添加如下代码:

 

 

然后进入设计模式拖入一个TextEdit部件:

 

 然后进入mainwindow.cpp文件中,先添加头文件,然后在构造函数中添加如下代码

    QTextDocument *document = ui->textEdit->document();    //获取文件对象
    ui->textEdit->setFixedSize(1500,900);
    QTextFrame *rootFrame = document->rootFrame();  //获得根框架
    QTextFrameFormat format;    //创建框架格式
    format.setBorderBrush(Qt::red); //框架边界格式
    format.setBorder(3);    //框架边界宽度
    rootFrame->setFrameFormat(format);  //框架使用的格式

在构造函数中获取了编辑器中的文本对象,然后获取了文档的根框架,并且重新设置了框架的格式。现在运行程序,则发现只能在红色的边框中进行输入。这里还可以使用setHeight()和setWidth()函数来固定框架的高度。

下面继续添加代码,使用光标类对象,在根框架中再添加一个子框架:

    QTextFrameFormat frameFormat;
    frameFormat.setBackground(Qt::lightGray);   //设置背景颜色
    frameFormat.setMargin(10);  //设置边距
    frameFormat.setPadding(5);  //设置填充
    frameFormat.setBorder(2);   //设置边框边界宽度
    frameFormat.setBorderStyle(QTextFrameFormat::BorderStyle_Dotted);   //设置边框样式
    QTextCursor cursor=ui->textEdit->textCursor();  //获取光标
    cursor.insertFrame(frameFormat);    //在光标处插入框架

这里有建立了一个框架格式,然后获取了编辑器的光标对象,并使用这个框架格式插入了一个新的框架,这里为框架格式设置了边白,它分为边界内与本身内容间的空白,即填衬(Padding),和边界外与其他内容间的空白,即为边距(Margin)。框架边界的样式有实线、点线等。

 文本块

 文本块就是QTextBlock类为文本文档QTextDocument提供的一个文本片段(QTextFragment)的容器。一个文本块可以看做是一个段落,但是不能使用回车换行,因为一个回车换行就表示创建一个新的文本块。QTextBlock提供了只读接口,是前面提到的文档分层次接口的一部分,如果QTextFrame看作一层,那么其中的QTextBlock就是另一层。文本块的格式由QTextBlockFormat类来处理,它主要涉及对齐方式、文本块的四周边距、缩进等内容。而文本块中的文本内容的格式,比如字体大小、加粗、下划线等,则由QTextCharFormat类来设置。下面从例子中去理解这些内容

继续在前面的项目之中加入代码。在mainwindow.h文件中添加私有槽的声明:

private slots:
    void showTextFrame();

然后mainwindow.cpp文件中添加头文件#include<QDebug>,并在构造函数中添加如下代码:

    QAction *action_textFrame= new QAction(tr("框架"),this);      //设置工具栏的按钮动作
    connect(action_textFrame,&QAction::triggered,this,&MainWindow::showTextFrame);
    ui->mainToolBar->addAction(action_textFrame);   //在工具栏添加动作

这里新建了一个动作,然后将它的触发信号和showTextFrame()槽关联,最后将它加入到了工具栏中。下面添加showTextFrame()槽的实现:

void MainWindow::showTextFrame()    //遍历框架
{
    QTextDocument *document = ui->textEdit->document();
    QTextFrame *frame = document->rootFrame();
    QTextFrame::iterator it;    //建立QTextFrame类的迭代器
    for(it=frame->begin();!(it.atEnd());++it)
    {
        QTextFrame *childFrame = it.currentFrame(); //获取当前框架的指针
        QTextBlock childBlock = it.currentBlock();  //获取当前文本块
        if(childFrame)
            qDebug()<<"frame:";
        else if(childBlock.isValid())
            qDebug()<<"block:"<<childBlock.text();
    }
}

在这个函数中获取了文档的根框架,然后使用它的迭代器iterator来遍历根框架中的所有子框架和文本块。在循环语句中先使用QTextFram类的begin()函数使iterator指向根框架最开始的元素,然后使用iterator的atEnd()函数判断是否已经到达了根框架的最后一个元素。这里如果出现子框架,则输出一个框架的提示如果出现文本块,则输出文本块提示和文本块内容。现在运行程序,然后在编辑器中输入一些内容,按下工具栏“框架”动作,查看一下输出栏的信息如下所示:

 可以看到,这里只能输出根框架中的文本块和子框架,子框架中的文本块无法遍历到。起始还可以使用其他方法来编辑文档的所有文本块。下面继续在mainwindow.h文件中继续添加私有槽的声明:

void showTextBlock();

然后到mainwindow.cpp文件中的构造函数里继续添加如下代码:

    QAction *action_textBlock = new QAction(tr("文本块"),this);
    connect(action_textBlock,&QAction::triggered,this,&MainWindow::showTextBlock);
    ui->mainToolBar->addAction(action_textBlock);   //在工具栏中添加动作

下面添加showTextBlock()槽的定义:

void MainWindow::showTextBlock()    //遍历文本块
{
    QTextDocument *document = ui->textEdit->document();
    QTextBlock block = document->firstBlock();  //获取文档的第一个文本块
    for(int i=0;i<document->blockCount();i++)
    {
        qDebug()<<tr("文本块%1,文本块首行行号:%2,长度为:%3,内容为:").arg(i).arg(block.firstLineNumber()).arg(block.length())<<block.text();
        block=block.next(); //获取下一个文本块
    }
}

这里使用了QTextDocument类的firstBlock()函数来获取文档的第一个文本块,而blockCount()函数可以获取文档中所有文本块的个数,这样便可以使用循环遍历所有文本块。每一个文本块都输出了编号、第一行行号、长度和内容,然后使用QTextBlock的next()函数来获取下一个文本块。这里需要说明的是,tr()函数中使用的“%1”等位置标记,然后在后面使用arg()添加变量作为参数 ,这样这些参数就会代替前面字符串中的“%1”显示出来。字符串中有几个“%”号,后面就应该有几个arg()与其对应。arg()是QString类中的函数,因为tr()函数返回的是QString类对象,所以这里可以这样使用。运行程序以后在编辑器中输入一些内容然后按下文本块,观察输出端的输出内容。文本块的长度是从1开始计算的就是说,就算什么都不写,那么文本块的长度也是1,所以长度会比实际字符数多1.

 下面我们在来看看怎么对文本块及其内容进行格式的更改。前面讲到,编辑操作是使用基于光标的函数接口,我们来介绍几个常用的编辑操作。在mainwindow.h文件中添加私有槽private slots声明:

void setTextFont(bool checked)

然后在mainwindow.cpp文件的构造函数中添加代码:

    QAction *action_font = new QAction(tr("字体"),this);
    action_font->setCheckable(true);    //设置动作可以被选中
    connect(action_font,&QAction::toggled,this,&MainWindow::setTextFont);
    ui->mainToolBar->addAction(action_font);    //设置工具栏动作

这里创建了一个动作,并设置它可以被选中,然后关联它的切换信号到自定义的槽上。当动作的选中和取消选中状态切换时会触发信号toggled(bool),当处于选中状态时,参数bool值为true。下面setTextFont()槽的定义:

void MainWindow::setTextFont(bool checked)
{
    if(checked)     //如果处于选中状态
    {
        QTextCursor cursor = ui->textEdit->textCursor();
        QTextBlockFormat blockFormat;   //文本块格式
        blockFormat.setAlignment(Qt::AlignCenter);  //水平居中
        cursor.insertBlock(blockFormat);    //使用文本格式块
        QTextCharFormat charFormat; //字符格式
        charFormat.setBackground(Qt::lightGray);    //背景色
        charFormat.setForeground(Qt::blue);     //字体颜色
        //使用宋体,12号,加粗,倾斜
        charFormat.setFont(QFont(tr("宋体"),12,QFont::Bold,true));
        charFormat.setFontUnderline(true);  //使用下划线
        cursor.setCharFormat(charFormat);   //使用字体格式
        cursor.insertText(tr("测试字体"));  //插入文本
    }
    else{
        /*恢复默认字体*/      //如果处于非选中状态,可以进行其他操作
    }
}

这里先获得了编辑器的光标,然后为其添加了文本块格式和字符格式。文本块格式主要设置对其方式、缩进等格式,字符格式主要设置字体、颜色、下划线等格式。最后使用光标插入一个测试文字。下面运行程序,按下“字体”动作,结果如下图所示:

表格、列表与图片

现在看一下怎么在编辑器里插入表格、列表和图片。在前面的程序中继续添加代码,在mainwindow.h文件中添加私有槽private slots声明:

void insertTable();    //插入表格
void insertList();    //插入列表
void insertImage();    //插入图片

 然后到mainwindow.cpp文件的构造函数中继续添加代码:

    QAction *action_textTable = new QAction(tr("表格"),this);
    QAction *action_textList = new QAction(tr("列表"),this);
    QAction *action_textImage = new QAction(tr("图片"),this);
    connect(action_textTable,&QAction::triggered,this,&MainWindow::insertTable);
    connect(action_textList,&QAction::triggered,this,&MainWindow::insertList);
    connect(action_textImage,&QAction::triggered,this,&MainWindow::insertImage);
    ui->mainToolBar->addAction(action_textTable);
    ui->mainToolBar->addAction(action_textList);
    ui->mainToolBar->addAction(action_textImage);

这里新建了3个动作,并将它们添加到工具栏中,下面是几个槽的定义:

void MainWindow::insertTable()  //插入表格
{
    QTextCursor cursor = ui->textEdit->textCursor();
    QTextTableFormat format;    //表格格式
    format.setCellSpacing(2);   //表格外边白
    format.setCellPadding(10);  //表格内边白
    cursor.insertTable(2,2,format);     //插入2行,2列表格
}

void MainWindow::insertList()   //插入列表
{
    QTextListFormat format; //列表格式
    format.setStyle(QTextListFormat::ListDecimal);  //数字编码
    ui->textEdit->textCursor().insertList(format);
}

void MainWindow::insertImage()  //插入图片
{
    QTextImageFormat format;    //图片格式
    format.setName("../myrichtext/logo.png");   //图片路径
    ui->textEdit->textCursor().insertImage(format);
}

对于表格和列表,也可以使用QTextFrame::iterator来遍历它们,可以在帮助中通过Rich Text Document Structure 关键字查看。表格对应的是QTextTable类,该类还提供了cellAt()函数用来获取指定的单元格;insertColumns()函数用来插入列;insertRows()函数用来插入行;mergeCells()函数用来合并单元格;splitCell()函数用来拆分单元格。对于一个单元格,其对应类是QTextTableCell,其格式对应的类是QTextTableCellFormat类。列表对应的类是QTextList,该类提供了count()函数来获取列表中项目的个数;item()函数获取指定项目的文本块;removeItem()函数来删除一个项目。对于列表编号,这里使用了数字编号,更多的选项可以通过QTextListFormat::Style关键字查看。对于图片,可以使用QTextImageFormat类的setHeight()和setWidth()函数来设置图片的高度和宽度,这可能会使图片进行拉伸或者压缩而变小。

查找功能

其实像字体格式设置等操作完全可以在QTextEdit类中直接进行。QTextEdit类提供了很多方便的函数,比如常用的赋值、粘贴操作,撤销、恢复操作,放大、缩小操作等等。关于这些,这里就不再一一介绍了,因为使用起来很简单,只须调用一个函数即可。下面介绍文本查找的的功能,他是用的是QTextEdit类的find()函数。

在mainwindow.h文件中添加类的前置声明:

class QLineEdit;
class QDialog;

然后,添加私有private对象指针:

QLineEdit *lineEdit;
QDialog *findDialog;

再添加两个私有槽private slots声明:

void textFind();    //查找文本
void findNext();    //查找下一个

然后,到mainwindow.cpp文件中添加头文件:

#include<QLineEdit>
#include<QDialog>
#include<QPushButton>
#include<QVBoxLayout>

再在构造函数中添加如下代码:

    QAction *action_textFind = new QAction(tr("查找"),this);
    connect(action_textFind,&QAction::triggered,this,&MainWindow::textFind);
    ui->mainToolBar->addAction(action_textFind);

    findDialog = new QDialog(this); //创建对话框;
    findDialog->resize(QSize(100,100));
    lineEdit = new QLineEdit(findDialog); //创建行编辑器
    QPushButton *btn = new QPushButton(findDialog); //创建按钮
    btn->setText(tr("查找下一个"));
    connect(btn,&QPushButton::clicked,this,&MainWindow::findNext);
    QVBoxLayout *layout = new QVBoxLayout;  //创建垂直布局管理器
    layout->addWidget(lineEdit);    //添加部件
    layout->addWidget(btn);
    findDialog->setLayout(layout);  //在对话框里使用布局管理器

这里在工具栏中添加了“查找”动作,然后创建了查找对话框。下面添加两个槽的定义:

void MainWindow::textFind() //查找文本
{
    findDialog->show();
}

void MainWindow::findNext() //查找下一个
{
    QString string = lineEdit->text();
    //使用查找函数查找指定字符串,查找方式为向后查找
    bool isfind = ui->textEdit->find(string,QTextDocument::FindBackward);
    if(isfind)
    {
        qDebug()<<tr("行号:%1 列号:%2")
                  .arg(ui->textEdit->textCursor().blockNumber())
                  .arg(ui->textEdit->textCursor().columnNumber());
    }
    else
    {
        qDebug()<<"没有查询到!";
    }
}

这里使用了find()函数进行查找。选项QTextDocument::FindBackward表示向后查找;默认的是向前查找;另外还有QTextDocument::FindCaseSensitively表示不区分大小写,QTextDocument::FindWholeWords表示匹配整个单词。其实,QTextEdit中的find()函数只是为了方便使用而设计的,更多的查找功能可以使用QTextDocument类的find()函数,它有几种形式可以选择,其中还可以使用正则表达式。在查找到相应字符时,这里输出了所在的行号和列号,行号和列号都是从0开始编号的。运行程序查看效果。

 语法高亮与HTML

使用Qt Create编辑代码时可以发现,输入关键字时会显示不同的颜色,这就是所谓的语法高亮。Qt的富文本处理中提供了QSyntaxHighLighter的子类,然后重新实现highlightBlock()函数,使用时直接将QTextDocument类对象指针作为其父部件指针,这样就可以自动调用highlightBlock()函数。

往之前的项目中添加新文件,模板选择C++Class,类名为MySyntaxHighlighter,基类手动设置为QSyntaxHighlighter。完成后将mysyntaxhighlighter.h文件内容更改如下:

#ifndef MYSYNTAXHIGHLIGHTER_H
#define MYSYNTAXHIGHLIGHTER_H
#include<QSyntaxHighlighter>

class MySyntaxHighlighter : public QSyntaxHighlighter
{
    Q_OBJECT
public:
     explicit MySyntaxHighlighter(QTextDocument *parent=0);
protected:
    void highlightBlock(const QString &text);   //必须重新实现该函数
};

#endif // MYSYNTAXHIGHLIGHTER_H

 现在到mysyntaxhighlighter.cpp文件中,先更改构造函数为:

MySyntaxHighlighter::MySyntaxHighlighter(QTextDocument *parent):
    QSyntaxHighlighter(parent)
{
}

然后添加highlightBlock()函数定义:

void MySyntaxHighlighter::highlightBlock(const QString &text)   //高亮文本
{
    QTextCharFormat myFormat;   //字符格式
    myFormat.setFontWeight(QFont::Bold);
    myFormat.setForeground(Qt::green);
    QString pattern = "\\bchar\\b"; //要匹配的字符,这里是char单词
    QRegExp expression(pattern);    //创建正则表达式
    int index = text.indexOf(expression);   //从位置0开始匹配字符串
    //如果匹配成果,那么返回值为字符串的起始位置,它大于或等于0
    while(index>=0)
    {
        int length = expression.matchedLength();    //要匹配字符串的长度
        setFormat(index,length,myFormat);   //对要匹配的字符串设置格式
        index = text.indexOf(expression,index+length);  //继续匹配
    }
}

这里主要使用了正则表达式和QString类的indexOf()函数来进行字符串匹配,如果匹配成功,则使用QSyntaxHighlighter类的setFormat()函数来设置字符格式。下面来使用一个自定义的类。

在mainwindow.h文件中添加类的前置声明:

class MySyntaxHighlighter;

然后再添加私有对象:

MySyntaxHighlighter *highlighter;

到mainwindow.cpp文件中添加头文件:

#incldue"mysyntaxhighlighter.h"

然后在构造函数的最后一行添加一行代码:

 highlighter = new MySyntaxHighlighter(ui->textEdit->document());

这里创建了MySyntaxHighlighter类的对象,并且使用编辑器的文档对象指针作为其参数,这样每当编辑器中的文本改变时都会调用highlightBlock()函数来设置语法高亮。现在可以运行程序看一下效果。

 这篇博客主要讲述QT中一个应用程序主窗口的实例,添加字体,表格,列表,图片,查找内容等功能。感谢大家可以看完这篇博客,希望你们可以从中学到一些关于QT主窗口的一些内容!!

猜你喜欢

转载自blog.csdn.net/m0_61886762/article/details/127110843