【Qt实用技巧】将多个文字合成整体插入输入框

713 阅读3分钟

📒博客首页:何名取 的个人主页 - 文章 - 掘金 (juejin.cn)
🎉欢迎关注🔎点赞👍收藏⭐️留言📝
❤️期待一起交流!
🙏作者水平很有限,如果发现错误,求告知,多谢!
🌺有问题可私信交流!!!


QTextObject

前言

之前的文章中实现了群聊@的功能,对比微信的群聊艾特功能发现还是有一些差异,微信的艾特选择群成员后在输入框中插入的是@xxx的一个整体文字块,不能修改,删除时也会将整个文字块进行删除。本节则实现将多个文字合成整体插入输入框中,做出类似下图微信里的效果。

image.png

富文本类简介

在进行代码实现之前,首先来简单了解一下QTextEidt这个控件。QTextEidt是一个富文本编辑器,已经包含了QTextCursor(文字光标,用来操作QTextDocument)和QTextDocument(保存格式文本)。

因此,我们需要先使用QTextDocument类相关的子类来自定义一个文字块对象,然后使用QTextCursor类将这个自定义的文本对象插入到QTextEdit输入框中即可。

image.png

自定义文本对象类

QTextObjectInterface类允许在QTextDocuments中绘制自定义文本对象。

具体操作如下:

  • 选择一个对象类型objectType。objectType是一个大于或等于QTextFormat::UserObject的整数。
  • 创建一个QTextCharFormat对象,并使用setObjectType()函数将对象类型设置为所选类型。
  • 通过继承实现QTextObjectInterface类。
  • 使用QTextObjectInterface子类的实例调用QAbstractTextDocumentLayout::registerHandler()来注册你的对象类型。
  • 将QChar::ObjectReplacementCharacter与前面提到的所选对象类型的QTextCharFormat插入到文档中。正如前面提到的,QTextObjectInterface intrinsicSize()和drawObject()函数将在遇到替换字符时以QTextFormat作为参数调用。

以下是通过继承实现的艾特文本块对象:

#ifndef ATMEMBERTEXTOBJECT_H
#define ATMEMBERTEXTOBJECT_H

#include <QTextObjectInterface>
#include <QFontMetrics>
#include <QPainter>

QT_BEGIN_NAMESPACE
class QTextDocument;
class QTextFormat;
class QPainter;
class QRectF;
class QSizeF;
QT_END_NAMESPACE

enum { AtTextFormat = QTextFormat::UserObject + 1 };
enum AtTextProperties { AtData = 1 };

class AtMemberTextObject : public QObject, public QTextObjectInterface
{
    Q_OBJECT
    Q_INTERFACES(QTextObjectInterface)
public:
    explicit AtMemberTextObject();

    QSizeF intrinsicSize(QTextDocument *doc, int posInDocument,
                         const QTextFormat &format) override;
    void drawObject(QPainter *painter, const QRectF &rect, QTextDocument *doc,
                    int posInDocument, const QTextFormat &format) override;

private:
    QFontMetrics *m_pmf;
    QFont m_font;
};

#endif // ATMEMBERTEXTOBJECT_H

#include "atmembertextobject.h"

AtMemberTextObject::AtMemberTextObject()
{
    m_font.setFamily("微软雅黑");
    m_font.setPointSize(16);
    m_pmf = new QFontMetrics(m_font);
//    qDebug()<<"AtMemberTextObject-height:"<<m_pmf->height();//高度
//    qDebug()<<"AtMemberTextObject-ascent:"<<m_pmf->ascent();//基线以上高度
//    qDebug()<<"AtMemberTextObject-descent:"<<m_pmf->descent();//基线以下高度
//    qDebug()<<"AtMemberTextObject-leading:"<<m_pmf->leading();//行距
//    qDebug()<<"AtMemberTextObject-lineSpacing:"<<m_pmf->lineSpacing();//行高
}

QSizeF AtMemberTextObject::intrinsicSize(QTextDocument *doc, int posInDocument, const QTextFormat &format)
{
    Q_UNUSED(doc)
    Q_UNUSED(posInDocument)
    QString txt = qvariant_cast<QString>(format.property(AtData));
    return QSizeF(m_pmf->size(Qt::AlignCenter,txt).width(),
                  m_pmf->height());
}

void AtMemberTextObject::drawObject(QPainter *painter, const QRectF &rect, QTextDocument *doc, int posInDocument, const QTextFormat &format)
{
    Q_UNUSED(doc)
    Q_UNUSED(posInDocument)
    QString txt = qvariant_cast<QString>(format.property(AtData));

    painter->fillRect(rect,QBrush(QColor("#00ffff")));
    painter->drawText(rect,QTextCharFormat::AlignBaseline,txt);
}

接下来是在输入框QTextEdit包含的QTextDocument中进行注册对象类型:

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QTextDocument>
#include <QAbstractTextDocumentLayout>
#include <QTextFrame>
#include <QTextBlock>
#include "atmembertextobject.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    AtMemberTextObject *atObj = new AtMemberTextObject;
    ui->textEdit->document()->documentLayout()->registerHandler(AtTextFormat,atObj);
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::on_pushButton_clicked()
{
    QTextDocument* doc = ui->textEdit->document();
    QTextFrame *root_frame = doc->rootFrame();
    QTextFrameFormat root_frame_format = root_frame->frameFormat();//创建框架格式

    QTextCharFormat textcharFormat;
    textcharFormat.setVerticalAlignment(QTextCharFormat::AlignBottom);
    textcharFormat.setObjectType(AtTextFormat);
    textcharFormat.setProperty(AtData,QVariant("@小路"));
    ui->textEdit->mergeCurrentCharFormat(textcharFormat);

    QTextCursor cursor = ui->textEdit->textCursor();
    cursor.movePosition(QTextCursor::End);
    cursor.insertText(QString(QChar::ObjectReplacementCharacter),textcharFormat);
    cursor.insertText("关注我");
}

需要注意的是,在插入对象之前可以选择将文本放置于Block框架底部,不然会造成文本对象与其他正常文字不平齐的现象 image.png

textcharFormat.setVerticalAlignment(QTextCharFormat::AlignBottom);

实现效果如下: image.png