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

在这里插入图片描述
参考qt示例
关键代码
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.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;
}

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()
{
}

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,以下是一个基于STL的简易文本编辑器的示例代码: ```c++ #include <iostream> #include <fstream> #include <string> #include <vector> #include <algorithm> using namespace std; // 定义文本行类型 typedef vector<string> TextLines; // 加载文件到文本行中 void loadFile(const string& fileName, TextLines& lines) { ifstream file(fileName); if (file.is_open()) { string line; while (getline(file, line)) { lines.push_back(line); } file.close(); } } // 将文本行保存到文件中 void saveFile(const string& fileName, const TextLines& lines) { ofstream file(fileName); if (file.is_open()) { for (const auto& line : lines) { file << line << endl; } file.close(); } } // 在指定行插入一行 void insertLine(TextLines& lines, int lineNum, const string& newLine) { auto it = lines.begin() + lineNum; lines.insert(it, newLine); } // 删除指定行 void deleteLine(TextLines& lines, int lineNum) { auto it = lines.begin() + lineNum; lines.erase(it); } // 替换指定行的内容 void replaceLine(TextLines& lines, int lineNum, const string& newLine) { auto it = lines.begin() + lineNum; *it = newLine; } int main() { TextLines lines; loadFile("test.txt", lines); while (true) { // 显示菜单 cout << "1. 查看文件" << endl; cout << "2. 插入行" << endl; cout << "3. 删除行" << endl; cout << "4. 替换行" << endl; cout << "5. 保存文件" << endl; cout << "6. 退出" << endl; // 读取用户选择的操作 int choice; cin >> choice; switch (choice) { case 1: // 查看文件 for (const auto& line : lines) { cout << line << endl; } break; case 2: // 插入行 int lineNum; cout << "请输入要插入的行号:"; cin >> lineNum; cin.ignore(); // 忽略之前输入的回车符 string newLine; cout << "请输入要插入的内容:"; getline(cin, newLine); insertLine(lines, lineNum, newLine); break; case 3: // 删除行 cout << "请输入要删除的行号:"; cin >> lineNum; deleteLine(lines, lineNum); break; case 4: // 替换行 cout << "请输入要替换的行号:"; cin >> lineNum; cin.ignore(); // 忽略之前输入的回车符 string replaceLine; cout << "请输入要替换的内容:"; getline(cin, replaceLine); replaceLine(lines, lineNum, replaceLine); break; case 5: // 保存文件 saveFile("test.txt", lines); cout << "文件已保存。" << endl; break; case 6: // 退出 return 0; default: cout << "无效的选择,请重新输入。" << endl; } } } ``` 这个编辑器支持查看文件、插入行、删除行、替换行和保存文件等基本操作。可以根据需要进行扩展。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值