QT创建代码编辑器(高亮显示)

0.前言

  接上一篇博客,上一篇博客讲到在QT里面调用Lua,还有Lua里面调用QT里面的函数两部分。由于需要在QT里面写Lua脚本或者通过文件导入Lua脚本。为了方便查看代码,就需要进行简单的高亮。

1. 效果图

2. 代码部分

  本次代码分成两部分,一部分是自定义代码高亮,一部分是自定义编辑器区域。

  CodeHighLighter(自定义代码高亮)【以Lua语法为例】

  CodeHighLighter.h

 1 #ifndef CODEHIGHLIGHTER_H
 2 #define CODEHIGHLIGHTER_H
 3 
 4 #include <QSyntaxHighlighter>
 5 #include <QTextCharFormat>
 6 #include <QTextDocument>
 7 
 8 class CodeHighLighter: public QSyntaxHighlighter
 9 {
10     Q_OBJECT
11 
12 public:
13     CodeHighLighter(QTextDocument *parent = 0);
14 
15 protected:
16     void highlightBlock(const QString &text) override;
17 
18 
19 private:
20     struct HighlightingRule
21     {
22         QRegExp pattern;
23         QTextCharFormat format;
24     };
25     QVector<HighlightingRule> highlightingRules;
26 
27     QRegExp commentStartExpression;         //多行注释开始标识符
28     QRegExp commentEndExpression;           //多行注释结束标识符
29 
30     QTextCharFormat keywordFormat;          //关键字
31     QTextCharFormat classFormat;            //
32     QTextCharFormat singleLineKey;          //单行关键字
33     QTextCharFormat singleLineValue;        //单行值
34     QTextCharFormat singleLineCommentFormat;//单行注释
35     QTextCharFormat multiLineCommentFormat; //多行注释
36     QTextCharFormat quotationFormat;        //字符串标识符
37     QTextCharFormat functionFormat;         //方法标识符
38 };
39 
40 #endif // CODEHIGHLIGHTER_H

  CodeHighLighter.cpp

  1 #include "codehighlighter.h"
  2 #include <QtDebug>
  3 
  4 CodeHighLighter::CodeHighLighter(QTextDocument *parent): QSyntaxHighlighter(parent)
  5 {
  6     HighlightingRule rule;
  7 
  8     //对于下面正则表达式,标记为紫色,类名称
  9     classFormat.setFontWeight(QFont::Bold);
 10     classFormat.setForeground(Qt::darkMagenta);
 11     rule.pattern = QRegExp("\b[A-Za-z]+:\b");
 12     rule.format = classFormat;
 13     highlightingRules.append(rule);
 14     rule.pattern = QRegExp("\b[A-Za-z]+\.\b");
 15     rule.format = classFormat;
 16     highlightingRules.append(rule);
 17 
 18     //字符串,标记深红色
 19     quotationFormat.setForeground(Qt::darkRed);
 20     rule.pattern = QRegExp("".*"");
 21     rule.format = quotationFormat;
 22     highlightingRules.append(rule);
 23     rule.pattern = QRegExp("'.*'");
 24     rule.format = quotationFormat;
 25     highlightingRules.append(rule);
 26 
 27     //函数标记为斜体蓝色
 28     functionFormat.setFontItalic(true);
 29     functionFormat.setForeground(Qt::blue);
 30     rule.pattern = QRegExp("\b[A-Za-z0-9_]+(?=\()");
 31     rule.format = functionFormat;
 32     highlightingRules.append(rule);
 33 
 34     //关键字
 35     QStringList keywords = {
 36         "and", "break", "do", "else", "elseif", "end", "false",
 37         "for", "function", "if", "in", "local", "nil", "not", "or",
 38         "repeat", "return", "then", "true", "unitl", "while", "goto"
 39     };
 40     //对于下面关键字部分,标记为深蓝色
 41     keywordFormat.setForeground(Qt::darkBlue);
 42     keywordFormat.setFontWeight(QFont::Bold);
 43     QStringList keywordPatterns;
 44     for(int i=0; i<keywords.length(); i++)
 45     {
 46         QString pattern = "\b" + keywords[i] + "\b";
 47         rule.pattern = QRegExp(pattern);
 48         rule.format = keywordFormat;
 49         highlightingRules.append(rule);
 50     }
 51 
 52     //对于下面正则表达式,单行注释标记为绿色
 53     singleLineCommentFormat.setForeground(Qt::darkGreen);
 54     singleLineCommentFormat.setFontItalic(true);
 55     //rule.pattern = QRegExp("//[^
]*");
 56     rule.pattern = QRegExp("--[^
]*");
 57     rule.format = singleLineCommentFormat;
 58     highlightingRules.append(rule);
 59 
 60     //对于多行注释
 61     multiLineCommentFormat.setForeground(Qt::darkGreen);
 62     multiLineCommentFormat.setFontItalic(true);
 63     //commentStartExpression = QRegExp("/\*");
 64     //commentEndExpression = QRegExp("\*/");
 65     //Lua 多行注释 --[[xx]]
 66     commentStartExpression = QRegExp("--\[\[");
 67     commentEndExpression = QRegExp("\]\]");
 68 }
 69 
 70 void CodeHighLighter::highlightBlock(const QString &text)
 71 {
 72     foreach (const HighlightingRule &rule, highlightingRules) {
 73         QRegExp expression(rule.pattern);
 74         int index = expression.indexIn(text);
 75         while (index >= 0) {
 76             int length = expression.matchedLength();
 77             setFormat(index, length, rule.format);
 78             index = expression.indexIn(text, index + length);
 79         }
 80     }
 81 
 82     setCurrentBlockState(0);
 83 
 84     int startIndex = 0;
 85     if (previousBlockState() != 1)
 86         startIndex = commentStartExpression.indexIn(text);
 87 
 88 
 89     while (startIndex >= 0) {
 90         int endIndex = commentEndExpression.indexIn(text, startIndex);
 91         int commentLength;
 92         if (endIndex == -1) {
 93             setCurrentBlockState(1);
 94             commentLength = text.length() - startIndex;
 95         } else {
 96             commentLength = endIndex - startIndex
 97                     + commentEndExpression.matchedLength();
 98         }
 99         setFormat(startIndex, commentLength, multiLineCommentFormat);
100         startIndex = commentStartExpression.indexIn(text, startIndex + commentLength);
101     }
102 }

  CodeEditor(自定义编辑器)

  CodeEditor.h

 1 #ifndef CODEEDITOR_H
 2 #define CODEEDITOR_H
 3 
 4 #include "CodeTypeDef.h"
 5 #include <QPlainTextEdit>
 6 #include <QObject>
 7 #include <QPaintEvent>
 8 #include <QResizeEvent>
 9 #include <QSize>
10 #include <QWidget>
11 #include <QSyntaxHighlighter>
12 #include <QPainter>
13 
14 typedef enum{
15     BROWSE,
16     EDIT,
17 }EditorMode;
18 
19 class CodeEditor : public QPlainTextEdit
20 {
21     Q_OBJECT
22 
23 public:
24     CodeEditor(QWidget *parent=0);
25     void setMode(EditorMode mode);
26     void lineNumberAreaPaintEvent(QPaintEvent *event);
27     int lineNumberAreaWidth();
28 
29 protected:
30     void resizeEvent(QResizeEvent *e) override;
31 
32 private slots:
33     void updateLineNumberAreaWidth(int newBlockCount);
34     void highlightCurrentLine();
35     void updateLineNumberArea(const QRect &rect, int dy);
36 
37 private:
38     QWidget *lineNumberArea;
39 };
40 
41 class LineNumberArea: public QWidget
42 {
43 public:
44     LineNumberArea(CodeEditor *editor): QWidget(editor){
45         codeEditor = editor;
46     }
47     QSize sizeHint() const override{
48         return QSize(codeEditor->lineNumberAreaWidth(), 0);
49     }
50 protected:
51     void paintEvent(QPaintEvent *event) override {
52         codeEditor->lineNumberAreaPaintEvent(event);
53     }
54 private:
55     CodeEditor *codeEditor;
56 };
57 
58 #endif // CODEEDITOR_H

  CodeEditor.cpp 

  1 #include "codeeditor.h"
  2 #include <QDebug>
  3 
  4 CodeEditor::CodeEditor(QWidget *parent): QPlainTextEdit(parent)
  5 {
  6     lineNumberArea = new LineNumberArea(this);
  7 
  8     //事件绑定
  9     connect(this, SIGNAL(blockCountChanged(int)), this, SLOT(updateLineNumberAreaWidth(int)));
 10     connect(this, SIGNAL(updateRequest(QRect,int)), this, SLOT(updateLineNumberArea(QRect,int)));
 11     connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(highlightCurrentLine()));
 12 
 13     updateLineNumberAreaWidth(0);
 14     setMode(EditorMode::BROWSE);
 15     setTabStopDistance(32);
 16     setFont(QFont(QString::fromUtf8("Source Code Pro"), 12));
 17 }
 18 
 19 int CodeEditor::lineNumberAreaWidth()
 20 {
 21     int digits = 1;
 22     int max = qMax(1, blockCount());
 23     while (max >= 10) {
 24         max /= 10;
 25         ++digits;
 26     }
 27     QFontMetrics metrics(QFont(QString::fromUtf8("Source Code Pro"), 12, QFont::Weight::Bold));
 28     int space = 10 + metrics.horizontalAdvance(QChar('9')) * digits;
 29     return space;
 30 }
 31 
 32 void CodeEditor::updateLineNumberAreaWidth(int)
 33 {
 34     setViewportMargins(lineNumberAreaWidth(), 0, 0, 0);
 35 }
 36 
 37 void CodeEditor::updateLineNumberArea(const QRect &rect, int dy)
 38 {
 39     if (dy)
 40         lineNumberArea->scroll(0, dy);
 41     else
 42         lineNumberArea->update(0, rect.y(), lineNumberArea->width(), rect.height());
 43 
 44     if (rect.contains(viewport()->rect()))
 45         updateLineNumberAreaWidth(0);
 46 }
 47 
 48 void CodeEditor::resizeEvent(QResizeEvent *e)
 49 {
 50     QPlainTextEdit::resizeEvent(e);
 51 
 52     QRect cr = contentsRect();
 53     lineNumberArea->setGeometry(QRect(cr.left(), cr.top(), lineNumberAreaWidth(), cr.height()));
 54 }
 55 
 56 void CodeEditor::highlightCurrentLine()
 57 {
 58     QList<QTextEdit::ExtraSelection> extraSelections;
 59 
 60     if (!isReadOnly()) {
 61         QTextEdit::ExtraSelection selection;
 62         QColor lineColor = QColor("whitesmoke");
 63         selection.format.setBackground(lineColor);
 64         selection.format.setProperty(QTextFormat::FullWidthSelection, true);
 65         selection.cursor = textCursor();
 66         selection.cursor.clearSelection();
 67         extraSelections.append(selection);
 68     }
 69     setExtraSelections(extraSelections);
 70 }
 71 
 72 void CodeEditor::lineNumberAreaPaintEvent(QPaintEvent *event)
 73 {
 74     QPainter painter(lineNumberArea);
 75     painter.fillRect(event->rect(), Qt::lightGray);
 76 
 77 
 78     QTextBlock block = firstVisibleBlock();
 79     int blockNumber = block.blockNumber();
 80     int top = (int) blockBoundingGeometry(block).translated(contentOffset()).top();
 81     int bottom = top + (int) blockBoundingRect(block).height();
 82 
 83     while (block.isValid() && top <= event->rect().bottom()) {
 84         if (block.isVisible() && bottom >= event->rect().top()) {
 85             QString number = QString::number(blockNumber + 1);
 86             painter.setPen(Qt::black);
 87             painter.setFont(QFont(QString::fromUtf8("Source Code Pro"), 12, QFont::Weight::Bold));
 88             painter.drawText(-5, top, lineNumberArea->width(), fontMetrics().height(),
 89                              Qt::AlignRight, number);
 90         }
 91 
 92         block = block.next();
 93         top = bottom;
 94         bottom = top + (int) blockBoundingRect(block).height();
 95         ++blockNumber;
 96     }
 97 }
 98 
 99 void CodeEditor::setMode(EditorMode mode)
100 {
101     if(mode == EditorMode::BROWSE) //预览
102     {
103         this->setReadOnly(true);
104         this->setStyleSheet("background: #f0f0f0;");
105         highlightCurrentLine();
106     }
107     else if(mode == EditorMode::EDIT) //编辑
108     {
109         this->setReadOnly(false);
110         this->setStyleSheet("background: #fcfcfc;");
111         highlightCurrentLine();
112     }
113 }

  MainWidow 调用

 1 MainWindow::MainWindow(QWidget *parent)
 2     : QMainWindow(parent)
 3     , ui(new Ui::MainWindow)
 4 {
 5     ui->setupUi(this);
 6 
 7     codeEditor = new CodeEditor();
 8     codeEditor->setMode(EditorMode::EDIT);
 9     codeEditor->setPlainText("int function()
{
--[[te
st]]
int a = a + b;
	return 0;
}");
10 
11     ui->gridLayout->addWidget(codeEditor);
12     CodeHighLighter *highlighter = new CodeHighLighter();
13     highlighter->setDocument(codeEditor->document());
14 }
15 
16 MainWindow::~MainWindow()
17 {
18     delete ui;
19 }
20 
21 void MainWindow::on_btnHighLightData_clicked()
22 {
23     qDebug() << codeEditor->toPlainText();
24 }

参考资料:

  https://github.com/tianzhihen/SyntaxHighlighterEditor

本文地址:https://www.cnblogs.com/wunaozai/p/14097927.html
个人主页:https://www.wunaozai.com/

原文地址:https://www.cnblogs.com/wunaozai/p/14097927.html