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


    在这里插入图片描述
    参考qt示例
    关键代码
    CodeEditor.h 文件

    #ifndef CODEEDITOR_H
    #define CODEEDITOR_H
    
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    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
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124

    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.isVisible() && 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;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267
    • 268
    • 269
    • 270
    • 271
    • 272
    • 273
    • 274
    • 275
    • 276
    • 277
    • 278
    • 279
    • 280
    • 281
    • 282
    • 283
    • 284
    • 285
    • 286
    • 287
    • 288
    • 289
    • 290
    • 291
    • 292
    • 293
    • 294
    • 295
    • 296
    • 297
    • 298
    • 299
    • 300
    • 301
    • 302
    • 303
    • 304
    • 305
    • 306
    • 307
    • 308
    • 309
    • 310
    • 311
    • 312
    • 313

    mainwindow.cpp

    #include "mainwindow.h"
    #include "ui_mainwindow.h"
    #include "CodeEditor.h"
    #include 
    
    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()
    {
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
  • 相关阅读:
    海外媒体发稿:彭博社发稿宣传中,5种精准营销方式
    Unity 动画系统基本概念
    【7z密码】7z压缩包密码忘记了,怎么办?i
    python+pytest接口自动化之测试函数、测试类/测试方法的封装
    如何提高表达能力?这五个方法要掌握
    数据结构作业4-图
    YOLO算法(You Only Look Once)系列讲解与实现(待完善)
    ElasticSearch映射与模板介绍
    Code For Better 谷歌开发者之声——盘点大家用过的Google 产品
    写代码的七八九十宗罪,多图、胆小慎入!
  • 原文地址:https://blog.csdn.net/kchmmd/article/details/133846738