Qt6.5实战用QPlainTextEdit打造轻量级代码编辑器附语法高亮配置在开发工具和IDE的构建过程中代码编辑器是最基础也最核心的组件之一。Qt框架提供的QPlainTextEdit控件为开发者提供了一个高性能的纯文本编辑解决方案特别适合需要处理大量代码的场景。本文将深入探讨如何利用Qt6.5中的QPlainTextEdit控件从零开始构建一个功能完善的轻量级代码编辑器。1. 为什么选择QPlainTextEdit在Qt的文本编辑控件家族中QPlainTextEdit和QTextEdit是最常用的两个选择。但它们在设计目标和性能特性上有着显著差异性能对比表特性QPlainTextEditQTextEdit文本处理能力优化的大文本处理适合中小文本内存占用更低更高富文本支持仅纯文本支持HTML/RTF滚动性能更流畅相对较慢语法高亮实现更高效实现复杂对于代码编辑器这类需要处理可能非常大的纯文本文件且需要频繁更新显示的应用程序QPlainTextEdit无疑是更好的选择。它针对纯文本编辑进行了专门优化使用了一种称为块状文本布局的技术可以高效处理包含数千行甚至更多代码的文件。2. 基础编辑器搭建让我们从创建一个最基本的代码编辑器开始。首先需要设置开发环境# CMakeLists.txt find_package(Qt6 REQUIRED COMPONENTS Widgets) add_executable(code_editor main.cpp) target_link_libraries(code_editor PRIVATE Qt6::Widgets)然后是主窗口的实现// mainwindow.h #include QMainWindow #include QPlainTextEdit class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent nullptr); private: QPlainTextEdit *editor; void setupEditor(); }; // mainwindow.cpp #include mainwindow.h MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { editor new QPlainTextEdit(this); setCentralWidget(editor); setupEditor(); } void MainWindow::setupEditor() { // 基本编辑器配置 editor-setWordWrapMode(QTextOption::NoWrap); // 代码通常不自动换行 editor-setLineWrapMode(QPlainTextEdit::NoWrap); editor-setTabStopDistance(40); // 设置制表符宽度为40像素 // 使用等宽字体 QFont font(Consolas, 12); editor-setFont(font); // 其他实用设置 editor-setUndoRedoEnabled(true); editor-setCursorWidth(2); // 更显眼的光标 }这个基础实现已经提供了一个可用的代码编辑器支持基本的文本编辑功能如复制、粘贴、撤销/重做等。但作为一个专业的代码编辑器我们还需要添加更多功能。3. 实现语法高亮语法高亮是代码编辑器的核心功能之一。在Qt中我们可以通过继承QSyntaxHighlighter类来实现自定义的高亮规则。以下是一个支持C语法的高亮器实现// syntaxhighlighter.h #include QSyntaxHighlighter #include QTextCharFormat class SyntaxHighlighter : public QSyntaxHighlighter { Q_OBJECT public: SyntaxHighlighter(QTextDocument *parent nullptr); protected: void highlightBlock(const QString text) override; private: struct HighlightingRule { QRegExp pattern; QTextCharFormat format; }; QVectorHighlightingRule highlightingRules; QTextCharFormat keywordFormat; QTextCharFormat classFormat; QTextCharFormat singleLineCommentFormat; QTextCharFormat multiLineCommentFormat; QTextCharFormat quotationFormat; QTextCharFormat functionFormat; }; // syntaxhighlighter.cpp #include syntaxhighlighter.h SyntaxHighlighter::SyntaxHighlighter(QTextDocument *parent) : QSyntaxHighlighter(parent) { // 设置关键字格式 keywordFormat.setForeground(Qt::darkBlue); keywordFormat.setFontWeight(QFont::Bold); QStringList keywordPatterns; keywordPatterns \\bchar\\b \\bclass\\b \\bconst\\b \\bdouble\\b \\benum\\b \\bexplicit\\b \\bfriend\\b \\binline\\b \\bint\\b \\blong\\b \\bnamespace\\b \\boperator\\b \\bprivate\\b \\bprotected\\b \\bpublic\\b \\bshort\\b \\bsignals\\b \\bsigned\\b \\bslots\\b \\bstatic\\b \\bstruct\\b \\btemplate\\b \\btypedef\\b \\btypename\\b \\bunion\\b \\bunsigned\\b \\bvirtual\\b \\bvoid\\b \\bvolatile\\b \\bbool\\b; foreach (const QString pattern, keywordPatterns) { HighlightingRule rule; rule.pattern QRegExp(pattern); rule.format keywordFormat; highlightingRules.append(rule); } // 设置类名格式 classFormat.setFontWeight(QFont::Bold); classFormat.setForeground(Qt::darkMagenta); HighlightingRule classRule; classRule.pattern QRegExp(\\bQ[A-Za-z]\\b); classRule.format classFormat; highlightingRules.append(classRule); // 设置单行注释格式 singleLineCommentFormat.setForeground(Qt::gray); HighlightingRule commentRule; commentRule.pattern QRegExp(//[^\n]*); commentRule.format singleLineCommentFormat; highlightingRules.append(commentRule); // 设置多行注释格式 multiLineCommentFormat.setForeground(Qt::gray); // 设置字符串格式 quotationFormat.setForeground(Qt::darkGreen); HighlightingRule stringRule; stringRule.pattern QRegExp(\.*\); stringRule.format quotationFormat; highlightingRules.append(stringRule); // 设置函数格式 functionFormat.setFontItalic(true); functionFormat.setForeground(Qt::blue); HighlightingRule functionRule; functionRule.pattern QRegExp(\\b[A-Za-z0-9_](?\\()); functionRule.format functionFormat; highlightingRules.append(functionRule); } void SyntaxHighlighter::highlightBlock(const QString text) { foreach (const HighlightingRule rule, highlightingRules) { QRegExp expression(rule.pattern); int index expression.indexIn(text); while (index 0) { int length expression.matchedLength(); setFormat(index, length, rule.format); index expression.indexIn(text, index length); } } // 处理多行注释 setCurrentBlockState(0); int startIndex 0; if (previousBlockState() ! 1) startIndex text.indexOf(/*); while (startIndex 0) { int endIndex text.indexOf(*/, startIndex); int commentLength; if (endIndex -1) { setCurrentBlockState(1); commentLength text.length() - startIndex; } else { commentLength endIndex - startIndex 2; } setFormat(startIndex, commentLength, multiLineCommentFormat); startIndex text.indexOf(/*, startIndex commentLength); } }要在编辑器中使用这个高亮器只需在MainWindow的setupEditor函数中添加new SyntaxHighlighter(editor-document());4. 增强编辑功能一个专业的代码编辑器还需要许多增强功能来提升开发体验。让我们逐步添加这些功能4.1 行号显示行号是代码编辑器的重要辅助功能。我们可以通过创建一个继承自QWidget的LineNumberArea类来实现// linenumberarea.h #include QWidget #include QPlainTextEdit class LineNumberArea : public QWidget { public: LineNumberArea(QPlainTextEdit *editor) : QWidget(editor), codeEditor(editor) {} QSize sizeHint() const override { return QSize(codeEditor-lineNumberAreaWidth(), 0); } protected: void paintEvent(QPaintEvent *event) override { codeEditor-lineNumberAreaPaintEvent(event); } private: QPlainTextEdit *codeEditor; }; // 在MainWindow类中添加以下成员和方法 class MainWindow : public QMainWindow { // ... private slots: void updateLineNumberAreaWidth(int newBlockCount); void highlightCurrentLine(); void updateLineNumberArea(const QRect rect, int dy); private: LineNumberArea *lineNumberArea; void resizeEvent(QResizeEvent *event) override; }; // 实现 void MainWindow::updateLineNumberAreaWidth(int /* newBlockCount */) { editor-setViewportMargins(lineNumberArea-sizeHint().width(), 0, 0, 0); } void MainWindow::highlightCurrentLine() { QListQTextEdit::ExtraSelection extraSelections; if (!editor-isReadOnly()) { QTextEdit::ExtraSelection selection; QColor lineColor QColor(Qt::yellow).lighter(160); selection.format.setBackground(lineColor); selection.format.setProperty(QTextFormat::FullWidthSelection, true); selection.cursor editor-textCursor(); selection.cursor.clearSelection(); extraSelections.append(selection); } editor-setExtraSelections(extraSelections); } void MainWindow::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(editor-viewport()-rect())) updateLineNumberAreaWidth(0); } void MainWindow::resizeEvent(QResizeEvent *event) { QMainWindow::resizeEvent(event); QRect cr editor-contentsRect(); lineNumberArea-setGeometry(QRect(cr.left(), cr.top(), lineNumberArea-sizeHint().width(), cr.height())); } // 在setupEditor中添加 lineNumberArea new LineNumberArea(editor); connect(editor, QPlainTextEdit::blockCountChanged, this, MainWindow::updateLineNumberAreaWidth); connect(editor, QPlainTextEdit::updateRequest, this, MainWindow::updateLineNumberArea); connect(editor, QPlainTextEdit::cursorPositionChanged, this, MainWindow::highlightCurrentLine); updateLineNumberAreaWidth(0); highlightCurrentLine();4.2 代码折叠代码折叠是处理大型代码文件的实用功能。我们可以基于QTextBlock的userState属性来实现简单的折叠功能// 在MainWindow类中添加 private slots: void toggleFold(); private: void foldBlock(QTextBlock block, bool fold); // 实现 void MainWindow::toggleFold() { QTextCursor cursor editor-textCursor(); QTextBlock block cursor.block(); foldBlock(block, !block.isVisible()); } void MainWindow::foldBlock(QTextBlock block, bool fold) { // 简单实现根据缩进级别折叠 int indent block.text().indexOf(QRegExp(\\S)); if (indent -1) return; block.setVisible(!fold); block.setLineCount(fold ? 0 : 1); QTextBlock next block.next(); while (next.isValid()) { int nextIndent next.text().indexOf(QRegExp(\\S)); if (nextIndent -1) { next next.next(); continue; } if (nextIndent indent) break; next.setVisible(!fold); next.setLineCount(fold ? 0 : 1); next next.next(); } editor-document()-markContentsDirty(block.position(), next.isValid() ? next.position() - block.position() : -1); }4.3 自动补全基本的自动补全功能可以通过QCompleter实现// 在setupEditor中添加 QCompleter *completer new QCompleter(this); QStringList words; words int float double char class struct return if else for while do switch case default; completer-setModel(new QStringListModel(words, completer)); completer-setModelSorting(QCompleter::CaseInsensitivelySortedModel); completer-setCaseSensitivity(Qt::CaseInsensitive); completer-setWrapAround(false); editor-setCompleter(completer);5. 高级功能实现5.1 多文档支持一个完整的代码编辑器通常需要支持同时编辑多个文件。我们可以通过添加一个QTabWidget来实现// 修改MainWindow类 class MainWindow : public QMainWindow { // ... private: QTabWidget *tabWidget; QPlainTextEdit *currentEditor() const; void createNewEditorTab(); }; // 实现 MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { tabWidget new QTabWidget(this); tabWidget-setTabsClosable(true); setCentralWidget(tabWidget); connect(tabWidget, QTabWidget::tabCloseRequested, [this](int index) { if (tabWidget-count() 1) { delete tabWidget-widget(index); } else { tabWidget-widget(0)-deleteLater(); createNewEditorTab(); } }); createNewEditorTab(); } QPlainTextEdit *MainWindow::currentEditor() const { return qobject_castQPlainTextEdit*(tabWidget-currentWidget()); } void MainWindow::createNewEditorTab() { QPlainTextEdit *editor new QPlainTextEdit; tabWidget-addTab(editor, Untitled); setupEditor(editor); } // 修改setupEditor以接收参数 void MainWindow::setupEditor(QPlainTextEdit *editor) { // 之前的setupEditor实现但操作传入的editor // ... }5.2 查找/替换功能查找和替换是代码编辑器的基本功能。我们可以创建一个查找替换工具栏// 在MainWindow类中添加 private slots: void showFindDialog(); void findNext(); void findPrevious(); void replace(); void replaceAll(); private: QLineEdit *findLineEdit; QLineEdit *replaceLineEdit; QCheckBox *caseSensitiveCheck; QCheckBox *wholeWordCheck; QToolBar *findToolBar; void find(bool backward false); // 实现 void MainWindow::showFindDialog() { if (!findToolBar) { findToolBar new QToolBar(this); findToolBar-setWindowTitle(tr(Find and Replace)); findLineEdit new QLineEdit; replaceLineEdit new QLineEdit; caseSensitiveCheck new QCheckBox(tr(Case sensitive)); wholeWordCheck new QCheckBox(tr(Whole words)); QPushButton *findNextBtn new QPushButton(tr(Find Next)); QPushButton *findPrevBtn new QPushButton(tr(Find Previous)); QPushButton *replaceBtn new QPushButton(tr(Replace)); QPushButton *replaceAllBtn new QPushButton(tr(Replace All)); connect(findNextBtn, QPushButton::clicked, this, MainWindow::findNext); connect(findPrevBtn, QPushButton::clicked, this, MainWindow::findPrevious); connect(replaceBtn, QPushButton::clicked, this, MainWindow::replace); connect(replaceAllBtn, QPushButton::clicked, this, MainWindow::replaceAll); findToolBar-addWidget(new QLabel(tr(Find:))); findToolBar-addWidget(findLineEdit); findToolBar-addWidget(findNextBtn); findToolBar-addWidget(findPrevBtn); findToolBar-addWidget(new QLabel(tr(Replace:))); findToolBar-addWidget(replaceLineEdit); findToolBar-addWidget(replaceBtn); findToolBar-addWidget(replaceAllBtn); findToolBar-addWidget(caseSensitiveCheck); findToolBar-addWidget(wholeWordCheck); addToolBar(Qt::BottomToolBarArea, findToolBar); } findToolBar-show(); findLineEdit-setFocus(); } void MainWindow::findNext() { find(); } void MainWindow::findPrevious() { find(true); } void MainWindow::find(bool backward) { QPlainTextEdit *editor currentEditor(); if (!editor) return; QString searchString findLineEdit-text(); if (searchString.isEmpty()) return; QTextDocument::FindFlags flags; if (caseSensitiveCheck-isChecked()) flags | QTextDocument::FindCaseSensitively; if (wholeWordCheck-isChecked()) flags | QTextDocument::FindWholeWords; if (backward) flags | QTextDocument::FindBackward; bool found editor-find(searchString, flags); if (!found) { QTextCursor cursor editor-textCursor(); if (backward) cursor.movePosition(QTextCursor::End); else cursor.movePosition(QTextCursor::Start); editor-setTextCursor(cursor); found editor-find(searchString, flags); } if (!found) { QMessageBox::information(this, tr(Find), tr(Cannot find \%1\).arg(searchString)); } } void MainWindow::replace() { QPlainTextEdit *editor currentEditor(); if (!editor) return; QTextCursor cursor editor-textCursor(); if (cursor.hasSelection() cursor.selectedText() findLineEdit-text()) { cursor.insertText(replaceLineEdit-text()); } findNext(); } void MainWindow::replaceAll() { QPlainTextEdit *editor currentEditor(); if (!editor) return; QString searchString findLineEdit-text(); if (searchString.isEmpty()) return; QTextDocument::FindFlags flags; if (caseSensitiveCheck-isChecked()) flags | QTextDocument::FindCaseSensitively; if (wholeWordCheck-isChecked()) flags | QTextDocument::FindWholeWords; QTextCursor cursor(editor-document()); cursor.beginEditBlock(); int count 0; while (!cursor.isNull() !cursor.atEnd()) { cursor editor-document()-find(searchString, cursor, flags); if (!cursor.isNull()) { cursor.insertText(replaceLineEdit-text()); count; } } cursor.endEditBlock(); QMessageBox::information(this, tr(Replace All), tr(Replaced %1 occurrences).arg(count)); }5.3 主题支持现代代码编辑器通常支持多种颜色主题。我们可以通过QPalette和样式表来实现// 在MainWindow类中添加 void setEditorTheme(const QString theme); // 实现 void MainWindow::setEditorTheme(const QString theme) { QPlainTextEdit *editor currentEditor(); if (!editor) return; if (theme Dark) { QPalette p editor-palette(); p.setColor(QPalette::Base, QColor(53, 53, 53)); p.setColor(QPalette::Text, QColor(200, 200, 200)); p.setColor(QPalette::Highlight, QColor(42, 130, 218)); p.setColor(QPalette::HighlightedText, Qt::white); editor-setPalette(p); editor-setStyleSheet(QPlainTextEdit { border: 1px solid #3A3939; border-radius: 2px; padding: 5px; background-color: #353535; color: #C8C8C8; }); } else if (theme Light) { editor-setPalette(QPalette()); editor-setStyleSheet(); } }6. 性能优化技巧当处理大型代码文件时性能变得尤为重要。以下是一些优化QPlainTextEdit性能的技巧延迟高亮对于非常大的文件可以考虑在用户停止输入一段时间后再进行语法高亮视口更新优化使用setViewportUpdateMode()控制更新策略文档分段加载对于超大文件可以实现分段加载机制内存管理定期清理undo/redo栈// 在setupEditor中添加性能优化设置 editor-setViewportUpdateMode(QPlainTextEdit::SmartViewportUpdate); editor-setMaximumBlockCount(100000); // 限制最大行数防止内存耗尽通过以上步骤我们已经构建了一个功能丰富、性能优异的代码编辑器。这个编辑器具备了语法高亮、行号显示、代码折叠、自动补全、查找替换等专业功能同时保持了QPlainTextEdit的高效特性。
Qt6.5实战:用QPlainTextEdit打造轻量级代码编辑器(附语法高亮配置)
Qt6.5实战用QPlainTextEdit打造轻量级代码编辑器附语法高亮配置在开发工具和IDE的构建过程中代码编辑器是最基础也最核心的组件之一。Qt框架提供的QPlainTextEdit控件为开发者提供了一个高性能的纯文本编辑解决方案特别适合需要处理大量代码的场景。本文将深入探讨如何利用Qt6.5中的QPlainTextEdit控件从零开始构建一个功能完善的轻量级代码编辑器。1. 为什么选择QPlainTextEdit在Qt的文本编辑控件家族中QPlainTextEdit和QTextEdit是最常用的两个选择。但它们在设计目标和性能特性上有着显著差异性能对比表特性QPlainTextEditQTextEdit文本处理能力优化的大文本处理适合中小文本内存占用更低更高富文本支持仅纯文本支持HTML/RTF滚动性能更流畅相对较慢语法高亮实现更高效实现复杂对于代码编辑器这类需要处理可能非常大的纯文本文件且需要频繁更新显示的应用程序QPlainTextEdit无疑是更好的选择。它针对纯文本编辑进行了专门优化使用了一种称为块状文本布局的技术可以高效处理包含数千行甚至更多代码的文件。2. 基础编辑器搭建让我们从创建一个最基本的代码编辑器开始。首先需要设置开发环境# CMakeLists.txt find_package(Qt6 REQUIRED COMPONENTS Widgets) add_executable(code_editor main.cpp) target_link_libraries(code_editor PRIVATE Qt6::Widgets)然后是主窗口的实现// mainwindow.h #include QMainWindow #include QPlainTextEdit class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent nullptr); private: QPlainTextEdit *editor; void setupEditor(); }; // mainwindow.cpp #include mainwindow.h MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { editor new QPlainTextEdit(this); setCentralWidget(editor); setupEditor(); } void MainWindow::setupEditor() { // 基本编辑器配置 editor-setWordWrapMode(QTextOption::NoWrap); // 代码通常不自动换行 editor-setLineWrapMode(QPlainTextEdit::NoWrap); editor-setTabStopDistance(40); // 设置制表符宽度为40像素 // 使用等宽字体 QFont font(Consolas, 12); editor-setFont(font); // 其他实用设置 editor-setUndoRedoEnabled(true); editor-setCursorWidth(2); // 更显眼的光标 }这个基础实现已经提供了一个可用的代码编辑器支持基本的文本编辑功能如复制、粘贴、撤销/重做等。但作为一个专业的代码编辑器我们还需要添加更多功能。3. 实现语法高亮语法高亮是代码编辑器的核心功能之一。在Qt中我们可以通过继承QSyntaxHighlighter类来实现自定义的高亮规则。以下是一个支持C语法的高亮器实现// syntaxhighlighter.h #include QSyntaxHighlighter #include QTextCharFormat class SyntaxHighlighter : public QSyntaxHighlighter { Q_OBJECT public: SyntaxHighlighter(QTextDocument *parent nullptr); protected: void highlightBlock(const QString text) override; private: struct HighlightingRule { QRegExp pattern; QTextCharFormat format; }; QVectorHighlightingRule highlightingRules; QTextCharFormat keywordFormat; QTextCharFormat classFormat; QTextCharFormat singleLineCommentFormat; QTextCharFormat multiLineCommentFormat; QTextCharFormat quotationFormat; QTextCharFormat functionFormat; }; // syntaxhighlighter.cpp #include syntaxhighlighter.h SyntaxHighlighter::SyntaxHighlighter(QTextDocument *parent) : QSyntaxHighlighter(parent) { // 设置关键字格式 keywordFormat.setForeground(Qt::darkBlue); keywordFormat.setFontWeight(QFont::Bold); QStringList keywordPatterns; keywordPatterns \\bchar\\b \\bclass\\b \\bconst\\b \\bdouble\\b \\benum\\b \\bexplicit\\b \\bfriend\\b \\binline\\b \\bint\\b \\blong\\b \\bnamespace\\b \\boperator\\b \\bprivate\\b \\bprotected\\b \\bpublic\\b \\bshort\\b \\bsignals\\b \\bsigned\\b \\bslots\\b \\bstatic\\b \\bstruct\\b \\btemplate\\b \\btypedef\\b \\btypename\\b \\bunion\\b \\bunsigned\\b \\bvirtual\\b \\bvoid\\b \\bvolatile\\b \\bbool\\b; foreach (const QString pattern, keywordPatterns) { HighlightingRule rule; rule.pattern QRegExp(pattern); rule.format keywordFormat; highlightingRules.append(rule); } // 设置类名格式 classFormat.setFontWeight(QFont::Bold); classFormat.setForeground(Qt::darkMagenta); HighlightingRule classRule; classRule.pattern QRegExp(\\bQ[A-Za-z]\\b); classRule.format classFormat; highlightingRules.append(classRule); // 设置单行注释格式 singleLineCommentFormat.setForeground(Qt::gray); HighlightingRule commentRule; commentRule.pattern QRegExp(//[^\n]*); commentRule.format singleLineCommentFormat; highlightingRules.append(commentRule); // 设置多行注释格式 multiLineCommentFormat.setForeground(Qt::gray); // 设置字符串格式 quotationFormat.setForeground(Qt::darkGreen); HighlightingRule stringRule; stringRule.pattern QRegExp(\.*\); stringRule.format quotationFormat; highlightingRules.append(stringRule); // 设置函数格式 functionFormat.setFontItalic(true); functionFormat.setForeground(Qt::blue); HighlightingRule functionRule; functionRule.pattern QRegExp(\\b[A-Za-z0-9_](?\\()); functionRule.format functionFormat; highlightingRules.append(functionRule); } void SyntaxHighlighter::highlightBlock(const QString text) { foreach (const HighlightingRule rule, highlightingRules) { QRegExp expression(rule.pattern); int index expression.indexIn(text); while (index 0) { int length expression.matchedLength(); setFormat(index, length, rule.format); index expression.indexIn(text, index length); } } // 处理多行注释 setCurrentBlockState(0); int startIndex 0; if (previousBlockState() ! 1) startIndex text.indexOf(/*); while (startIndex 0) { int endIndex text.indexOf(*/, startIndex); int commentLength; if (endIndex -1) { setCurrentBlockState(1); commentLength text.length() - startIndex; } else { commentLength endIndex - startIndex 2; } setFormat(startIndex, commentLength, multiLineCommentFormat); startIndex text.indexOf(/*, startIndex commentLength); } }要在编辑器中使用这个高亮器只需在MainWindow的setupEditor函数中添加new SyntaxHighlighter(editor-document());4. 增强编辑功能一个专业的代码编辑器还需要许多增强功能来提升开发体验。让我们逐步添加这些功能4.1 行号显示行号是代码编辑器的重要辅助功能。我们可以通过创建一个继承自QWidget的LineNumberArea类来实现// linenumberarea.h #include QWidget #include QPlainTextEdit class LineNumberArea : public QWidget { public: LineNumberArea(QPlainTextEdit *editor) : QWidget(editor), codeEditor(editor) {} QSize sizeHint() const override { return QSize(codeEditor-lineNumberAreaWidth(), 0); } protected: void paintEvent(QPaintEvent *event) override { codeEditor-lineNumberAreaPaintEvent(event); } private: QPlainTextEdit *codeEditor; }; // 在MainWindow类中添加以下成员和方法 class MainWindow : public QMainWindow { // ... private slots: void updateLineNumberAreaWidth(int newBlockCount); void highlightCurrentLine(); void updateLineNumberArea(const QRect rect, int dy); private: LineNumberArea *lineNumberArea; void resizeEvent(QResizeEvent *event) override; }; // 实现 void MainWindow::updateLineNumberAreaWidth(int /* newBlockCount */) { editor-setViewportMargins(lineNumberArea-sizeHint().width(), 0, 0, 0); } void MainWindow::highlightCurrentLine() { QListQTextEdit::ExtraSelection extraSelections; if (!editor-isReadOnly()) { QTextEdit::ExtraSelection selection; QColor lineColor QColor(Qt::yellow).lighter(160); selection.format.setBackground(lineColor); selection.format.setProperty(QTextFormat::FullWidthSelection, true); selection.cursor editor-textCursor(); selection.cursor.clearSelection(); extraSelections.append(selection); } editor-setExtraSelections(extraSelections); } void MainWindow::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(editor-viewport()-rect())) updateLineNumberAreaWidth(0); } void MainWindow::resizeEvent(QResizeEvent *event) { QMainWindow::resizeEvent(event); QRect cr editor-contentsRect(); lineNumberArea-setGeometry(QRect(cr.left(), cr.top(), lineNumberArea-sizeHint().width(), cr.height())); } // 在setupEditor中添加 lineNumberArea new LineNumberArea(editor); connect(editor, QPlainTextEdit::blockCountChanged, this, MainWindow::updateLineNumberAreaWidth); connect(editor, QPlainTextEdit::updateRequest, this, MainWindow::updateLineNumberArea); connect(editor, QPlainTextEdit::cursorPositionChanged, this, MainWindow::highlightCurrentLine); updateLineNumberAreaWidth(0); highlightCurrentLine();4.2 代码折叠代码折叠是处理大型代码文件的实用功能。我们可以基于QTextBlock的userState属性来实现简单的折叠功能// 在MainWindow类中添加 private slots: void toggleFold(); private: void foldBlock(QTextBlock block, bool fold); // 实现 void MainWindow::toggleFold() { QTextCursor cursor editor-textCursor(); QTextBlock block cursor.block(); foldBlock(block, !block.isVisible()); } void MainWindow::foldBlock(QTextBlock block, bool fold) { // 简单实现根据缩进级别折叠 int indent block.text().indexOf(QRegExp(\\S)); if (indent -1) return; block.setVisible(!fold); block.setLineCount(fold ? 0 : 1); QTextBlock next block.next(); while (next.isValid()) { int nextIndent next.text().indexOf(QRegExp(\\S)); if (nextIndent -1) { next next.next(); continue; } if (nextIndent indent) break; next.setVisible(!fold); next.setLineCount(fold ? 0 : 1); next next.next(); } editor-document()-markContentsDirty(block.position(), next.isValid() ? next.position() - block.position() : -1); }4.3 自动补全基本的自动补全功能可以通过QCompleter实现// 在setupEditor中添加 QCompleter *completer new QCompleter(this); QStringList words; words int float double char class struct return if else for while do switch case default; completer-setModel(new QStringListModel(words, completer)); completer-setModelSorting(QCompleter::CaseInsensitivelySortedModel); completer-setCaseSensitivity(Qt::CaseInsensitive); completer-setWrapAround(false); editor-setCompleter(completer);5. 高级功能实现5.1 多文档支持一个完整的代码编辑器通常需要支持同时编辑多个文件。我们可以通过添加一个QTabWidget来实现// 修改MainWindow类 class MainWindow : public QMainWindow { // ... private: QTabWidget *tabWidget; QPlainTextEdit *currentEditor() const; void createNewEditorTab(); }; // 实现 MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { tabWidget new QTabWidget(this); tabWidget-setTabsClosable(true); setCentralWidget(tabWidget); connect(tabWidget, QTabWidget::tabCloseRequested, [this](int index) { if (tabWidget-count() 1) { delete tabWidget-widget(index); } else { tabWidget-widget(0)-deleteLater(); createNewEditorTab(); } }); createNewEditorTab(); } QPlainTextEdit *MainWindow::currentEditor() const { return qobject_castQPlainTextEdit*(tabWidget-currentWidget()); } void MainWindow::createNewEditorTab() { QPlainTextEdit *editor new QPlainTextEdit; tabWidget-addTab(editor, Untitled); setupEditor(editor); } // 修改setupEditor以接收参数 void MainWindow::setupEditor(QPlainTextEdit *editor) { // 之前的setupEditor实现但操作传入的editor // ... }5.2 查找/替换功能查找和替换是代码编辑器的基本功能。我们可以创建一个查找替换工具栏// 在MainWindow类中添加 private slots: void showFindDialog(); void findNext(); void findPrevious(); void replace(); void replaceAll(); private: QLineEdit *findLineEdit; QLineEdit *replaceLineEdit; QCheckBox *caseSensitiveCheck; QCheckBox *wholeWordCheck; QToolBar *findToolBar; void find(bool backward false); // 实现 void MainWindow::showFindDialog() { if (!findToolBar) { findToolBar new QToolBar(this); findToolBar-setWindowTitle(tr(Find and Replace)); findLineEdit new QLineEdit; replaceLineEdit new QLineEdit; caseSensitiveCheck new QCheckBox(tr(Case sensitive)); wholeWordCheck new QCheckBox(tr(Whole words)); QPushButton *findNextBtn new QPushButton(tr(Find Next)); QPushButton *findPrevBtn new QPushButton(tr(Find Previous)); QPushButton *replaceBtn new QPushButton(tr(Replace)); QPushButton *replaceAllBtn new QPushButton(tr(Replace All)); connect(findNextBtn, QPushButton::clicked, this, MainWindow::findNext); connect(findPrevBtn, QPushButton::clicked, this, MainWindow::findPrevious); connect(replaceBtn, QPushButton::clicked, this, MainWindow::replace); connect(replaceAllBtn, QPushButton::clicked, this, MainWindow::replaceAll); findToolBar-addWidget(new QLabel(tr(Find:))); findToolBar-addWidget(findLineEdit); findToolBar-addWidget(findNextBtn); findToolBar-addWidget(findPrevBtn); findToolBar-addWidget(new QLabel(tr(Replace:))); findToolBar-addWidget(replaceLineEdit); findToolBar-addWidget(replaceBtn); findToolBar-addWidget(replaceAllBtn); findToolBar-addWidget(caseSensitiveCheck); findToolBar-addWidget(wholeWordCheck); addToolBar(Qt::BottomToolBarArea, findToolBar); } findToolBar-show(); findLineEdit-setFocus(); } void MainWindow::findNext() { find(); } void MainWindow::findPrevious() { find(true); } void MainWindow::find(bool backward) { QPlainTextEdit *editor currentEditor(); if (!editor) return; QString searchString findLineEdit-text(); if (searchString.isEmpty()) return; QTextDocument::FindFlags flags; if (caseSensitiveCheck-isChecked()) flags | QTextDocument::FindCaseSensitively; if (wholeWordCheck-isChecked()) flags | QTextDocument::FindWholeWords; if (backward) flags | QTextDocument::FindBackward; bool found editor-find(searchString, flags); if (!found) { QTextCursor cursor editor-textCursor(); if (backward) cursor.movePosition(QTextCursor::End); else cursor.movePosition(QTextCursor::Start); editor-setTextCursor(cursor); found editor-find(searchString, flags); } if (!found) { QMessageBox::information(this, tr(Find), tr(Cannot find \%1\).arg(searchString)); } } void MainWindow::replace() { QPlainTextEdit *editor currentEditor(); if (!editor) return; QTextCursor cursor editor-textCursor(); if (cursor.hasSelection() cursor.selectedText() findLineEdit-text()) { cursor.insertText(replaceLineEdit-text()); } findNext(); } void MainWindow::replaceAll() { QPlainTextEdit *editor currentEditor(); if (!editor) return; QString searchString findLineEdit-text(); if (searchString.isEmpty()) return; QTextDocument::FindFlags flags; if (caseSensitiveCheck-isChecked()) flags | QTextDocument::FindCaseSensitively; if (wholeWordCheck-isChecked()) flags | QTextDocument::FindWholeWords; QTextCursor cursor(editor-document()); cursor.beginEditBlock(); int count 0; while (!cursor.isNull() !cursor.atEnd()) { cursor editor-document()-find(searchString, cursor, flags); if (!cursor.isNull()) { cursor.insertText(replaceLineEdit-text()); count; } } cursor.endEditBlock(); QMessageBox::information(this, tr(Replace All), tr(Replaced %1 occurrences).arg(count)); }5.3 主题支持现代代码编辑器通常支持多种颜色主题。我们可以通过QPalette和样式表来实现// 在MainWindow类中添加 void setEditorTheme(const QString theme); // 实现 void MainWindow::setEditorTheme(const QString theme) { QPlainTextEdit *editor currentEditor(); if (!editor) return; if (theme Dark) { QPalette p editor-palette(); p.setColor(QPalette::Base, QColor(53, 53, 53)); p.setColor(QPalette::Text, QColor(200, 200, 200)); p.setColor(QPalette::Highlight, QColor(42, 130, 218)); p.setColor(QPalette::HighlightedText, Qt::white); editor-setPalette(p); editor-setStyleSheet(QPlainTextEdit { border: 1px solid #3A3939; border-radius: 2px; padding: 5px; background-color: #353535; color: #C8C8C8; }); } else if (theme Light) { editor-setPalette(QPalette()); editor-setStyleSheet(); } }6. 性能优化技巧当处理大型代码文件时性能变得尤为重要。以下是一些优化QPlainTextEdit性能的技巧延迟高亮对于非常大的文件可以考虑在用户停止输入一段时间后再进行语法高亮视口更新优化使用setViewportUpdateMode()控制更新策略文档分段加载对于超大文件可以实现分段加载机制内存管理定期清理undo/redo栈// 在setupEditor中添加性能优化设置 editor-setViewportUpdateMode(QPlainTextEdit::SmartViewportUpdate); editor-setMaximumBlockCount(100000); // 限制最大行数防止内存耗尽通过以上步骤我们已经构建了一个功能丰富、性能优异的代码编辑器。这个编辑器具备了语法高亮、行号显示、代码折叠、自动补全、查找替换等专业功能同时保持了QPlainTextEdit的高效特性。