基于QPlainTextEdit带标签行号的文本编辑器

news2024/11/19 6:39:03

在这里插入图片描述

关键代码
CodeEditor.h 文件

#ifndef CODEEDITOR_H
#define CODEEDITOR_H

#include <QPlainTextEdit>
#include <QPaintEvent>
#include <QContextMenuEvent>
#include <QMouseEvent>
#include <QMouseEvent>
#include <QPainter>
#include <QTextBlock>
#include <QFontMetrics>
#include <QTextCursor>
#include <QTextBlockFormat>
#include <QTextDocument>
#include <QTextCodec>
#include <QMenu>
#include <QScrollBar>
QString GetQString(QByteArray byteArray);//防止乱码
enum TagType
{
    NUMBER=0,
    MARKROUND,//圆
    MARKBLOCK //方块
};

class TagLineNumberArea;
class  TagDataItem
{
public:
    TagDataItem(TagType tagTypeTmp, int widthTmp = 20):
       tagType(tagTypeTmp), width(widthTmp)
    {
    }
    bool BLineNumbersMark(int number)
    {
        //return std::find(vecLineNumbersMark.begin(), vecLineNumbersMark.end(),number) != vecLineNumbersMark.end();
        return mapMarkLineColor.find(number) != mapMarkLineColor.end();
    }
    void markerAdd(int line, QColor color=Qt::black)
    {
        mapMarkLineColor[line] = color;
    }
    QColor getLineColor(int line)
    {
        return mapMarkLineColor[line];
    }
    TagType tagType;
    int positionX;
    int width;
private:
    std::map<int, QColor> mapMarkLineColor;
};


class CodeEditor:public QPlainTextEdit
{
    Q_OBJECT
public:
    CodeEditor(QWidget *paren=0);
    ~CodeEditor();

    virtual void PaintTag(QPainter& painter, int line, int y);

    void AddTag(TagDataItem* item);//添加标记或行号

    void UpdateColor(int line, int startIndex, int endIndex, QColor fontColor, QColor backColor);
    void UpdateColor(int startLine, int endLine, int startColumn, int endColumn, QColor fontColor, QColor backColor);//更新行字体或背景色

    void UpdateLineNumberWidth(int lineNumberWidthNew);//更新行号宽度
    void UpdateTagWidth(int lineNumberWidthOld, int lineNumberWidth);//更新行号后面宽度

    void GoToLine(int line);//跳转至行

    int GetLineNumber(QTextCursor &cursor);//获取行号

    void lineNumberAreaPaintEvent(QPaintEvent* event);//绘制
    int lineNumberAreaWidth();//计算行号宽度
protected:
    virtual void resizeEvent(QResizeEvent *event)override;
private slots:
    void updateLineNumberAreaWidth(int newBlockCount);
    void hightlightCurrentLine();
    void updateLineNumberArea(const QRect &rect, int dy);
    void updateLineNumber();
public:
    TagLineNumberArea *tagLineNumberArea;
    int currentLineNumber;
    int currentLinePositionY;

    std::vector<TagDataItem*> vecTagDataItem;
    QColor backgroundColor;
    QColor fontColor;
    QColor fontCurrentColor;
    int lineNumberPositionX;
    int lineNumberWidth;
    int tagCurrentWidth;
    int spacing;
    bool bLineNumber;
    bool bHightCurrentLineNumber;
};

class TagLineNumberArea: public QWidget
{
public:
    TagLineNumberArea(CodeEditor *editor):QWidget(editor), codeEditor(editor)
    {

    }

    QSize sizeHint()const override
    {
        return QSize(codeEditor->lineNumberAreaWidth(), 0);
    }
protected:
    void paintEvent(QPaintEvent* event)override
    {
        codeEditor->lineNumberAreaPaintEvent(event);
    }
public:
    CodeEditor* codeEditor;
};

#endif // CODEEDITOR_H

CodeEditor.cpp 文件

#include "CodeEditor.h"

CodeEditor::CodeEditor(QWidget *parent):QPlainTextEdit(parent)
{
    setMouseTracking(true);//获取鼠标移动
    setWordWrapMode(QTextOption::NoWrap);//自动换行, 会导致获取的行号有问题
    backgroundColor = QColor(240,240,240);
    fontColor = QColor(160,160,160);
    fontCurrentColor = QColor(0,0,0);
    lineNumberPositionX = 0;
    tagCurrentWidth = 0;
    lineNumberWidth = 0;
    spacing = 5;
    bLineNumber = false;
    bHightCurrentLineNumber = true;

    tagLineNumberArea = new TagLineNumberArea(this);

    connect(this, &CodeEditor::blockCountChanged, this, &CodeEditor::updateLineNumberAreaWidth);
    connect(this, &CodeEditor::updateRequest, this, &CodeEditor::updateLineNumberArea);

    //connect(this, &CodeEditor::cursorPositionChanged, this, &CodeEditor::hightlightCurrentLine);

    UpdateLineNumberWidth(0);
}

CodeEditor::~CodeEditor()
{

}

void CodeEditor::PaintTag(QPainter &painter, int line, int y)
{
    for (TagDataItem* item:vecTagDataItem)
    {
        switch (item->tagType)
        {
            case NUMBER:
                break;
            case MARKROUND:
                if(item->BLineNumbersMark(line))
                {
                    painter.setPen(QColor(item->getLineColor(line)));
                    painter.setBrush(QColor(item->getLineColor(line)));
                    int w = item->width < fontMetrics().height() ? item->width : fontMetrics().height();
                    painter.drawEllipse(item->positionX + item->width/2 - w/2, y + fontMetrics().height()/2 - w/2,
                                        w, w);
                }
                break;
            case MARKBLOCK:
                if(item->BLineNumbersMark(line))
                {
                    painter.setPen(QColor(item->getLineColor(line)));
                    painter.setBrush(QColor(item->getLineColor(line)));
                    painter.drawRect(item->positionX, y, item->width, fontMetrics().height());
                }
                break;
            default:
                break;
        }
    }
}


void CodeEditor::AddTag(TagDataItem *item)
{
    item->positionX = tagCurrentWidth + spacing;
    vecTagDataItem.push_back(item);
    tagCurrentWidth = tagCurrentWidth + spacing + item->width;
    if(!bLineNumber && item->tagType == NUMBER)
    {
        lineNumberPositionX = item->positionX;
        lineNumberWidth = item->width;
        bLineNumber = true;
    }
}

void CodeEditor::UpdateColor(int line, int startIndex, int endIndex, QColor fontColor, QColor backColor)
{
    QTextCursor cursor = this->textCursor();

    //移动行
    cursor.movePosition(QTextCursor::Start);
    for(int i=1; i<line; i++)
    {
        cursor.movePosition(QTextCursor::Down);
    }
    //移动列
    cursor.movePosition(QTextCursor::StartOfBlock);
    for(int i=1; i<startIndex; i++)
    {
        cursor.movePosition(QTextCursor::NextCharacter);
    }
    if(endIndex == 0)
    {
        cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
    }
    else
    {
        for(int i=1; i<endIndex; i++)
        {
            cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor);
        }
    }
    QTextCharFormat fmt;
    fmt.setForeground(QBrush(fontColor));//字体色
    fmt.setBackground(QBrush(backColor));//背景色
    cursor.setCharFormat(fmt);
}

void CodeEditor::UpdateColor(int startLine, int endLine, int startColumn, int endColumn, QColor fontColor, QColor backColor)
{
    QTextCursor cursor = this->textCursor();

    //移动行
    cursor.movePosition(QTextCursor::Start);
    for(int i=1; i<startLine; i++)
    {
        cursor.movePosition(QTextCursor::Down);
    }
    //移动列
    cursor.movePosition(QTextCursor::StartOfBlock);
    for(int i=1; i<startColumn; i++)
    {
        cursor.movePosition(QTextCursor::NextCharacter);
    }
    //移动行
    cursor.movePosition(QTextCursor::Start);
    for(int i=startLine; i<endLine; i++)
    {
        cursor.movePosition(QTextCursor::Down, QTextCursor::KeepAnchor);
    }
    //移动列
    cursor.movePosition(QTextCursor::StartOfBlock, QTextCursor::KeepAnchor);
    for(int i=0; i<endColumn; i++)
    {
        cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor);
    }

    QTextCharFormat fmt;
    fmt.setForeground(QBrush(fontColor));//字体色
    fmt.setBackground(QBrush(backColor));//背景色
    cursor.setCharFormat(fmt);
}

void CodeEditor::UpdateLineNumberWidth(int lineNumberWidthNew)
{
    int width = tagCurrentWidth - lineNumberWidth + lineNumberWidthNew;
    tagCurrentWidth = width;
    UpdateTagWidth(lineNumberWidth, lineNumberWidthNew);
    lineNumberWidth = lineNumberWidthNew;
}

void CodeEditor::UpdateTagWidth(int lineNumberWidthOld, int lineNumberWidth)
{
    bool numberAfterTag = false;
    for(TagDataItem* item: vecTagDataItem)
    {
        if(item->tagType == NUMBER)
        {
            numberAfterTag = true;
        }

        if(numberAfterTag)
        {
            item->positionX = item->positionX - lineNumberWidthOld + lineNumberWidth;
        }
    }
}

void CodeEditor::updateLineNumberAreaWidth(int /* newBlockCount */)
{
    setViewportMargins(lineNumberAreaWidth(), 0, 0, 0);
}

void CodeEditor::hightlightCurrentLine()
{
    QList<QTextEdit::ExtraSelection> extraSelections;
    if(!isReadOnly())
    {
        QTextEdit::ExtraSelection selection;
        QColor lineColor = QColor(Qt::yellow).lighter(160);
        selection.format.setBackground(lineColor);
        selection.format.setProperty(QTextFormat::FullWidthSelection, true);
        selection.cursor = textCursor();
        selection.cursor.clearSelection();
        extraSelections.append(selection);
    }
    setExtraSelections(extraSelections);
}

void CodeEditor::updateLineNumberArea(const QRect &rect, int dy)
{
    if (dy)
        tagLineNumberArea->scroll(0, dy);
    else
        tagLineNumberArea->update(0, rect.y(), tagLineNumberArea->width(), rect.height());

    if (rect.contains(viewport()->rect()))
        updateLineNumberAreaWidth(0);
}

void CodeEditor::updateLineNumber()
{
    tagLineNumberArea->update();
}

void CodeEditor::GoToLine(int line)
{
    QTextCursor textCursor = this->textCursor();
    int position = document()->findBlockByNumber(line - 1).position();
    textCursor.setPosition(position, QTextCursor::MoveAnchor);
    setTextCursor(textCursor);
    this->verticalScrollBar()->setValue(line-1);
}

int CodeEditor::GetLineNumber(QTextCursor &cursor)
{
    QTextLayout *layout = cursor.block().layout();
    int pos = cursor.position() - cursor.block().position();
    int line = layout->lineForTextPosition(pos).lineNumber() + cursor.block().firstLineNumber();
    return line;
}

int CodeEditor::lineNumberAreaWidth()
{
    if(!bLineNumber)
        return tagCurrentWidth;
    int digits = 1;
    int max = qMax(1, blockCount());
    while(max >= 10)
    {
        max /=10;
        ++digits;
    }
    int lineNumberWidth = 3 + fontMetrics().horizontalAdvance(QLatin1Char('9'))*digits;
    UpdateLineNumberWidth(lineNumberWidth);
    return tagCurrentWidth;
}

void CodeEditor::lineNumberAreaPaintEvent(QPaintEvent *event)
{
   //当前行
    QTextCursor cursor = textCursor();
    int line = GetLineNumber(cursor);
    currentLineNumber = line;
    currentLinePositionY = -1;
    //背景
    QPainter painter(tagLineNumberArea);
    painter.setRenderHint(QPainter::Antialiasing, true);
    painter.fillRect(event->rect(), backgroundColor);

    //
    QTextBlock block = firstVisibleBlock();
    int blockNumber = block.blockNumber();
    int top = qRound(blockBoundingGeometry(block).translated(contentOffset()).top());
    int bottom = top + qRound(blockBoundingRect(block).height());

    while(block.isValid() && top <= event->rect().bottom())
    {
        if(block.isValid() && bottom >= event->rect().top())
        {
            QString number = QString::number(blockNumber +1);
            if(blockNumber == line)
                currentLinePositionY = top;//记录当前位置
            //记载行号
            if(bLineNumber)
            {
                if(blockNumber == line && bHightCurrentLineNumber)
                {
                    painter.setPen(fontCurrentColor);//当前行号高亮
                    QFont ft = painter.font();
                    ft.setBold(true);
                    painter.setFont(ft);
                }
                else
                {
                    painter.setPen(fontColor);
                }
                painter.drawText(lineNumberPositionX, top, lineNumberWidth, fontMetrics().height(),
                                 Qt::AlignRight, number);
            }
            //绘制tag
            PaintTag(painter, blockNumber, top);
        }
        block = block.next();
        top = bottom;
        bottom = top + qRound(blockBoundingGeometry(block).height());
        ++blockNumber;
    }
}

void CodeEditor::resizeEvent(QResizeEvent *e)
{
    QPlainTextEdit::resizeEvent(e);

    QRect cr = contentsRect();
    tagLineNumberArea->setGeometry(QRect(cr.left(), cr.top(), lineNumberAreaWidth(), cr.height()));
}

QString GetQString(QByteArray byteArray)
{
    QTextCodec::ConverterState state;
    QTextCodec *codec = QTextCodec::codecForName("UTF-8");
    QString qstr = codec->toUnicode(byteArray.constData(), byteArray.size(), &state);
    if(state.invalidChars > 0)
        qstr = QTextCodec::codecForName("GBK")->toUnicode(byteArray);
    else {
        qstr = byteArray;
    }
    return qstr;
}

mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "CodeEditor.h"
#include <QDebug>

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent)
{
    CodeEditor *edit = new CodeEditor;

    TagDataItem* item = new TagDataItem(MARKBLOCK, 10);
    item->markerAdd(2, Qt::blue);
    item->markerAdd(5, Qt::yellow);
    item->markerAdd(526, Qt::red);
    item->markerAdd(527, Qt::black);
    edit->AddTag(item);

    TagDataItem *itemNumber = new TagDataItem(NUMBER, 20);
    edit->AddTag(itemNumber);

    TagDataItem *item2 = new TagDataItem(MARKBLOCK, 10);
    item2->markerAdd(5, Qt::blue);
    item2->markerAdd(6, Qt::yellow);
    item2->markerAdd(7, Qt::red);
    item2->markerAdd(8, Qt::red);
    item2->markerAdd(526, Qt::red);
    item2->markerAdd(527, Qt::red);
    edit->AddTag(item2);

    this->setCentralWidget(edit);
    QFont ft;
    ft.setFamily("微软雅黑");
    ft.setPointSize(10);
    edit->setFont(ft);

    QFile file("C:/Data/test.cpp");
    qDebug() << file.exists();
    if(file.open(QIODevice::ReadOnly))
    {
        edit->appendPlainText(file.readAll());
        file.close();
    }
    edit->GoToLine(520);

    edit->UpdateColor(1,3,2, 5,Qt::red, Qt::yellow);
    edit->UpdateColor(2,3,2, 5,Qt::red, Qt::yellow);

}

MainWindow::~MainWindow()
{
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1095240.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Hadoop 配置 Kerberos 认证

1、安装 Kerberos 服务器和客户端 1.1 规划 服务端&#xff1a; bigdata3 客户端&#xff08;Hadoop集群&#xff09;&#xff1a; bigdata0 bigdata1 bigdata2 192.168.50.7 bigdata0.example.com bigdata0 192.168.50.8 bigdata1.example.com bigdata1 192.168.50.9 b…

vue补充继上一篇

组合式API-reactive和ref函数 1.reactive() 作用&#xff1a;接受对象类型数据的参数传入并返回一个响应式的对象 1.从vue包中导入reactive函数 2.在<script setup>中执行reactive函数并传入类型为对象的初始值&#xff0c;并使用变量接受返回值。 2.ref() 作用&am…

如何做好一个管理者

一、管理的目标 管理的目的是效率和效益。管理的核心是人。管理的本质是协调&#xff0c;协调的中心是人。管理的真谛是聚合企业的各类资源&#xff0c;充分运用管理的功能&#xff0c;以最优的投入获得最佳的回报&#xff0c;以实现企业既定目标。 二、管理中的核心 2.1、核…

前馈型BP神经网络

1.感知机和激活函数 感知机&#xff0c;是构成神经网络的基本单位&#xff0c;一个感知机可以接收n个输入X&#xff08;x1,x2,x3…xn)T&#xff08;每个输入&#xff0c;可以理解为一种特征&#xff09;,n个输入对应n个权值W&#xff08;w1,w2,w3…wn),此外还有一个偏置项b&am…

AI换脸之Faceswap技术原理与实践

目录 1.方法介绍 2.相关资料 3.实践记录 ​4.实验结果 1.方法介绍 Faceswap利用深度学习算法和人脸识别技术&#xff0c;可以将一个人的面部表情、眼睛、嘴巴等特征从一张照片或视频中提取出来&#xff0c;并将其与另一个人的面部特征进行匹配。主要应用在图像/视频换脸&am…

GMM模型与EM算法

GMM模型与EM算法 --> 聚类 -> 无监督机器学习[参考] 一、单个高斯分布GM的估计参数 1.1 高斯分布 结果趋近于正态分布 每次弹珠往下走的时候&#xff0c;碰到钉子会随机往左还是往右走&#xff0c;可以观测到多次随机过程结合的 高斯分布的似然函数 X1-XN 全部发生的…

【2023】redis-stream配合spring的data-redis详细使用(包括广播和组接收)

目录 一、简介1、介绍2、对比 二、整合spring的data-redis实现1、使用依赖2、配置类2.1、配置RedisTemplate bean2.2、异常类 3、实体类3.1、User3.2、Book 4、发送消息4.1、RedisStreamUtil工具类4.2、通过延时队列线程池模拟发送消息4.3、通过http主动发送消息 5、&#x1f3…

电动力学专题研讨:运动电荷之间的相互作用是否满足牛顿第三定律?

电动力学专题研讨&#xff1a;运动电荷之间的相互作用是否满足牛顿第三定律&#xff1f;​​​​​​​ 两个稳恒电流元之间的相互作用不一定服从牛顿第三定律常见的解释是&#xff1a;稳恒电流元是不能孤立存在的&#xff0c;因此不能得出结论 符号约定 两个运动点电荷之间的力…

for循环遍历的`form表单组件`rules规则校验失效问题——下拉框选择之后还是报红---亲测有效

问题: 大概的效果就是这种, for循环选择之后还是还是报红 看文章之前 : 先检查 model rules pops 有没有判定好 解决: 参考了他的 for循环遍历的form表单组件rules规则校验失效问题——输入内容后依然提示必填&#xff0c;亲测有效——基础积累_a-form-model的validat…

java项目之学生综合考评管理系统()

项目简介 学生综合考评管理系统实现了以下功能&#xff1a; 管理员&#xff1a;个人中心、通知公告管理、班级管理、学生管理、教师管理&#xff0c;课程信息管理、作业布置管理、作业提交管理、留言信息管理、课程成绩管理。学生&#xff1a;个人中心、通知公告管理、教师管…

Python赋值运算符:= 等号 以及 复合赋值运算符 += -= *=

赋值运算符 运算符描述实例赋值**将右侧的结果赋值给等号左侧的变量 案例1&#xff1a;把某个值赋值给某个变量 num 10 案例2&#xff1a;多个变量同时进行赋值操作 n 5 f 10.88 s ‘hello world’ 简写为&#xff1a;----------就类似一对一的&#xff0c;一个序号对应…

推荐一个备受好评的电子期刊制作网站

在当今数字化的时代&#xff0c;电子期刊制作已成为许多企业和个人的必备技能。对于想要制作出高质量、专业的电子期刊的人来说&#xff0c;选择一个简单易用、功能强大的网站至关重要。 下面分享一款备受好评的电子期刊制作网站-----FLBOOK在线制作电子杂志平台&#xff0c;在…

【frp实现内网穿透踩坑到成功篇】

【frp实现内网穿透踩坑到成功篇】 背景&需求配置服务器端配置客户端总结 背景&需求 白嫖了一个tencent入门级服务器 ∗ 1 *1 ∗1&#xff0c;学校实验室内网服务器 ∗ 1 *1 ∗1&#xff0c;需要访问内网的服务器。一顿搜寻资料后确定大致的路子&#xff1a; 第一步是…

因为写保护,U盘会“假死”。如何在Windows 10上删除写保护

本文介绍如何从USB驱动器、SD卡或单个文件中删除写保护。说明适用于Windows 10、Windows 8和Windows 7。 如何使用锁定开关解除写保护 如果你的计算机告诉你介质受写保护&#xff0c;请在USB或SD卡上查找写保护开关&#xff08;也称为锁定开关&#xff09;。如果介质有此开关…

文心一言 VS 讯飞星火 VS chatgpt (113)-- 算法导论10.2 5题

五、用go语言&#xff0c;使用单向循环链表实现字典操作 INSERT、DELETE 和 SEARCH&#xff0c;并给出所写过程的运行时间。 文心一言&#xff1a; 在Go语言中&#xff0c;我们首先需要定义一个Node结构体来表示单向循环链表的节点&#xff0c;然后再定义一个LinkedList结构体…

大数据Doris(十一):添加FS_BROKER步骤

文章目录 添加FS_BROKER步骤 一、 ​​​​​​​配置broker节点

笔试面试相关记录(10)

&#xff08;1&#xff09;马走棋盘&#xff0c;只能走‘日’字形&#xff0c;起点在&#xff08;0,0&#xff09;&#xff0c;给定一个点[x,y](-300<x,y<300)&#xff0c;问最少多少步可以到达&#xff1f; 输入&#xff1a;2,1 输出&#xff1a;1 这个题目通过了91.7…

深入理解RNN

[人工智能-深度学习-47]&#xff1a;卷积神经网CNN循环神经网络RNN与组合电路时序电路的比较 [人工智能-深度学习-51]&#xff1a;循环神经网络 - RNN基本原理详解 组合逻辑电路 组合逻辑电路在逻辑功能上的特点是任意时刻的输出仅仅取决于该时刻的输入&#xff0c;与电路原来…

IOday6

主线程和分支线程都可以访问&#xff0c;虚拟地址和物理地址都相同 分支线程不可以访问&#xff0c;使用pthread_create函数的第四个参数传址 不能访问,传递地址过去