基於Qt的CodeEditor

來源:互聯網
上載者:User

首先看最終效果:

主要要實現的地方是行號的顯示,還有選中行的高亮。

項目結構

整個程式只有三個檔案,最主要的只有一個CodeEditor類,它是繼承自QPlainTextEdit,這個類相比於普通的TextEdit更適合於做富 文字編輯器。

標頭檔

//codeeditor.h#ifndef CODEEDITOR_H#define CODEEDITOR_H#include <QPlainTextEdit>#include <QObject>QT_BEGIN_NAMESPACEclass QPaintEvent;class QResizeEvent;class QSize;class QWidget;QT_END_NAMESPACEclass LineNumberArea;//![codeeditordefinition]class CodeEditor : public QPlainTextEdit{    Q_OBJECTpublic:    CodeEditor(QWidget *parent = 0);    void lineNumberAreaPaintEvent(QPaintEvent *event);    int lineNumberAreaWidth();protected:    void resizeEvent(QResizeEvent *event);private slots:    void updateLineNumberAreaWidth(int newBlockCount);    void highlightCurrentLine();    void updateLineNumberArea(const QRect &, int);private:   
    LineNumberArea *lineNumberArea;

};//![codeeditordefinition]//![extraarea]class LineNumberArea : public QWidget{public: LineNumberArea(CodeEditor *editor) : QWidget(editor) { codeEditor = editor; } QSize sizeHint() const { return QSize(codeEditor->lineNumberAreaWidth(),
0); }protected: void paintEvent(QPaintEvent *event) { codeEditor->lineNumberAreaPaintEvent(event); }private:

Code Editor *codeEditor;};//![extraarea]#endif

標頭檔中包含了兩個類的定義:繼承QPlainTextEdit的CodeEditor和繼承QWidget的LineNumberArea。

寫在一起的原因:在顯示行號的時候需要用到QPlainTextEdit的protected方法,為了方便就直接寫死在CodeEditor中。

在編輯器中,當代碼的行數發生改變,或者編輯器的大小發生改變,行號都需要重新繪製,為此而建立了兩個slot:updateLineNumberWidth() 和 updateLineNumberArea()。

cpp檔案

//codeeditor.cpp#include <QtWidgets>#include "codeeditor.h"//![constructor]CodeEditor::CodeEditor(QWidget *parent) : QPlainTextEdit(parent){    lineNumberArea = new LineNumberArea(this);    connect(this, SIGNAL(blockCountChanged(int)), this, SLOT(updateLineNumberAreaWidth(int)));    connect(this, SIGNAL(updateRequest(QRect,int)), this, SLOT(updateLineNumberArea(QRect,int)));    connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(highlightCurrentLine()));    updateLineNumberAreaWidth(0);    highlightCurrentLine();}//![constructor]//![extraAreaWidth]int CodeEditor::lineNumberAreaWidth(){    int digits = 1;    int max = qMax(1, blockCount());    while (max >= 10) {        max /= 10;        ++digits;    }    int space = 3 + fontMetrics().width(QLatin1Char('9')) * digits;    return space;}//![extraAreaWidth]//![slotUpdateExtraAreaWidth]void CodeEditor::updateLineNumberAreaWidth(int /* newBlockCount */){     setViewportMargins(lineNumberAreaWidth(), 0, 0, 0);}//![slotUpdateExtraAreaWidth]//![slotUpdateRequest]void CodeEditor::updateLineNumberArea(const QRect &rect, int dy){    if (dy)        lineNumberArea->scroll(0, dy);    else        lineNumberArea->update(0, rect.y(), lineNumberArea->width(), rect.height());    if (rect.contains(viewport()->rect()))        updateLineNumberAreaWidth(0);}//![slotUpdateRequest]//![resizeEvent]void CodeEditor::resizeEvent(QResizeEvent *e){    QPlainTextEdit::resizeEvent(e);    QRect cr = contentsRect();    lineNumberArea->setGeometry(QRect(cr.left(), cr.top(), lineNumberAreaWidth(), cr.height()));}//![resizeEvent]//![cursorPositionChanged]void CodeEditor::highlightCurrentLine(){    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);}//![cursorPositionChanged]//![extraAreaPaintEvent_0]void CodeEditor::lineNumberAreaPaintEvent(QPaintEvent *event){    QPainter painter(lineNumberArea);    painter.fillRect(event->rect(), Qt::lightGray);//![extraAreaPaintEvent_0]//![extraAreaPaintEvent_1]    QTextBlock block = firstVisibleBlock();    int blockNumber = block.blockNumber();    int top = (int) blockBoundingGeometry(block).translated(contentOffset()).top();    int bottom = top + (int) blockBoundingRect(block).height();//![extraAreaPaintEvent_1]//![extraAreaPaintEvent_2]    while (block.isValid() && top <= event->rect().bottom()) {        if (block.isVisible() && bottom >= event->rect().top()) {            QString number = QString::number(blockNumber + 1);            painter.setPen(Qt::black);            painter.drawText(0, top, lineNumberArea->width(), fontMetrics().height(),                             Qt::AlignRight, number);        }        block = block.next();        top = bottom;        bottom = top + (int) blockBoundingRect(block).height();        ++blockNumber;    }}//![extraAreaPaintEvent_2]

在cpp中,首先是類的建構函式,初始化私人成員,連結相應的signal和slot。計算linenumbeearea的寬度並高亮第一行也是必須的。

lineNumberAreaWidth():用於計算 LineNumberArea的寬度,取得最大行數,將其位元與數字9的寬度相乘,再加上3的間隔就可以了。

updateLineNumberAreaWidth(int):更新LineNumberArea的寬度,主要用到了setViewportMargins(),設定內邊距。當行數不斷增加,只要將左邊的內邊距加大就可以了。

updateLineNumberArea(const QRect &rect, int dy):當有換行出現的時候,對LineNumberArea進行更新

resizeEvent(QResizeEvent *e):當視窗大小變化的時候,LineNumberArea的大小也要進行變化。

highlightCurrentLine():當游標位置發生改變的時候,高亮的位置也發生改變。

lineNumberAreaPaintEvent(QPaintEvent *event):主要負責顯示行號,首先是繪製黑色的背景,然後迴圈繪製行號。在plain text edit中,每一行都只包含一個 QTextBlock。

因此,出現折行的話也不用擔心行號不正確。注意要進行兩次判斷,一次是calid,依次是visible。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.