QCodeEditor - 基于Qt的代码编辑器

QCodeEditor - 基于Qt的代码编辑器_第1张图片

一 基本介绍

It’s not a project from a Qt example.

这不是qt的示例项目!!!这是开源项目的介绍

QCodeEditor是一个支持自动完成、语法高亮和行号的代码编辑器。
它面向所有想要支持多种语言的人,从编程语言到标记语言,甚至是自定义脚本语言。在目前的状态下,它可以完美地处理上面提到的所有功能,但是还有很多事情需要实现。QCodeEditor是基于 Qt 的QPlainTextEdit,它已经包含一个用于添加语法突出显示和自动完成的接口。

源代码和示例代码下载链接:https://download.csdn.net/download/weixin_42887343/76428055
在这里插入图片描述

二 代码使用

使用编辑器本身非常简单。可以通过QPlainTextEdit在表单中添加 a 并将其提升到kgl::QCodeEditor(请注意,必须将“ include/KGL/Widgets ”添加到INCLUDEPATH变量中并且global include必须选中“ ”复选框)或以编程方式将其添加到形式:

using namespace kgl;

// ## MainWindow::MainWindow
QCodeEditor *editor = new QCodeEditor;
setCentralWidget(editor); // or: ui->someLayout->addWidget(editor);

通过使用QCodeEditorDesign该类可以更改编辑器的视觉外观。在下面的示例中,我们假设代码编辑器被多个小部件包围,因此为其添加了边框。我们还修改了外观以具有“深色”风格:

using namespace kgl;

// ## MainWindow::MainWindow
QCodeEditorDesign design;
design.setLineColumnVisible(false);
design.setEditorBackColor(0xff333333);
design.setEditorTextColor(0xffdddddd);
design.setEditorBorderColor(0xff999999);
design.setEditorBorder(QMargins(1,1,1,1)); // l t r b
editor->setDesign(design);

但是如何实际添加一些语法高亮规则,如上图所示?有两种方法可以做到这一点:以编程方式添加它们或从 XML 文件中提取它们。

以编程方式:

using namespace kgl;

// ## MainWindow::MainWindow
QList<QSyntaxRule> rules;
QSyntaxRule rule;
rule.setForeColor(QColor(Qt::blue));
rule.setRegex("\\bFoo\\b");
rule.setId("Foo");
rules.push_back(rule);
editor->setRules(rules);

XML 文件:

using namespace kgl;
// ## MainWindow::MainWindow
QCodeEditorDesign design;
// modify design ...
QList<QSyntaxRule> rules = 
QSyntaxRules::loadFromFile(":/rule_cpp.xml", design);
editor->setRules(rules);
// Note: ':/rule_cpp.xml' refers to the path of a QRC resource

下一章将提供如何创建这些 XML 规则的指南。但首先,我们的编辑器需要一些自动完成关键字:

// ## MainWindow::MainWindow
QStringList keywords = { "printf", "scanf" };
editor->setKeywords(keywords);

如果希望添加图标以暗示关键字是函数/成员/宏/… QStandardItemModel,则需要创建自定义并将其传递给“ QCodeEditor::setKeywordModel(model)”。

三 创建 XML 规则文件

XML 规则文件包含由多个子元素组成的最顶层元素。每个子元素都必须包含正则表达式或关键字列表,所有其他属性都是可选的:

<rules>
    <rule>
        <regex>\bFoo\b</regex>
        <keywords>foo bar key</keywords>
    </rule>
</rules>

有关所有可用属性的参考,请转到rules_template.xml的 github 页面。QCodeEditor甚至支持包含不止一行的规则。虽然它们对于实现多行注释很有用,但它们也可以用于其他目的。

四 ID的用处

从rules_template.xml中可以看出,规则甚至可以定义自定义 ID。在本节中,我将说明如何使用 ID 以及它们为何如此有用。人们可能已经注意到,静态添加关键字并不是一种好的做法,特别是如果您的语言允许包含其他文件或定义变量。

“onMatch”信号
QCodeEditorHighlighteronMatch只要 a string- 通过正则表达式找到 - 被突出显示,就会发出一个名为 ’ ’ 的信号。这使我们能够检索有string问题的:

// ## MainWindow.h
using namespace kgl;
class MainWindow : public QMainWindow {
Q_OBJECT
public:
...

private slots:

    void addMacro(const QSyntaxRule &rule, QString seq, QTextBlock block);

private:

    QMap<QTextBlock, QSyntaxRule> m_RuleMap;
    QMap<QTextBlock, QString> m_MacroMap;
    QCodeEditor *m_Editor;
};

// ## MainWindow::MainWindow
QSyntaxRule defineRule;
defineRule.setRegex("(#define\\s+)\\K(\\D\\w*)(?=\s+\S+)");
defineRule.setId("define");
editor->setRules({ defineRule });
connect(m_Editor->highlighter(), SIGNAL(onMatch(QSyntaxRule,QString,QTextBlock)),
                            this, SLOT(addMacro(QSyntaxRule,QString,QTextBlock)));

// ## MainWindow::addMacro
if (rule.id() == "define") {
    foreach (const QTextBlock &b, m_RuleMap.keys()) {
        if (b.userData() != NULL && block.userData() != NULL) {
            auto *d1 = static_cast<QCodeEditorBlockData *>(block.userData());
            auto *d2 = static_cast<QCodeEditorBlockData *>(b.userData());
            if (d1->id == d2->id) {
                return;
            }
        }
    }

    // Not existing yet; add it
    QString def = seq.split(' ').at(0);
    m_RuleMap.insert(block, rule);
    m_MacroMap.insert(block, def);
    m_Editor->addKeyword(def);
}

这样,可以为自定义类、变量和定义提供自动完成功能,或者包含其他文件并从中导入符号。

“onRemove”信号
删除曾经添加的宏可能有点棘手,因为 QTextBlock 的设计使我们几乎不可能跟踪它。QCodeEditorHighlighter提供 ’ onRemove’ 信号,一旦荧光笔检测到先前匹配的规则不再匹配,就会发出该信号:

// ## MainWindow.h
private slots:

    void addMacro(const QSyntaxRule &rule, QString seq, QTextBlock block);
    void removeMacro(QCodeEditorBlockData *data); // Add this to the slots

// ## MainWindow::MainWindow
// Add another connection
connect(m_Editor->highlighter(), SIGNAL(onRemove(QCodeEditorBlockData*)),
                        this, SLOT(removeMacro(QCodeEditorBlockData*)));

// ## MainWindow::removeMacro
foreach (const QTextBlock &b, m_RuleMap.keys()) {
    if (b.userData()) {
        auto *d = static_cast<QCodeEditorBlockData *>(b.userData());
        if (d->id == data->id) {
            // Data is the same; block must be the one from before!
            m_Editor->removeKeyword(m_MacroMap.value(b));
            m_RuleMap.remove(b);
            m_MacroMap.remove(b);
        }
    }
}

实现这样一个使用起来相对简单的信号是一项艰巨的任务。查看“兴趣点”一章了解更多信息。

五 编译说明

为了编译 QCodeEditor,您需要定义 ’ KGL_BUILD’ 以将符号导出到动态库。如果你想构建一个静态库,只需定义’ KGL_STATIC’。还要确保您使用的是 Qt5或更高版本。

六 可关注的点

最大的障碍之一是正确渲染行号。虽然将行列添加为子小部件非常容易,但确定滚动时所有可见的行号却并非如此。在阅读 Qt 文档很长一段时间后,我想我可以跳到迭代中的下一行,并在当前行不再可见时立即停止迭代。

另一个大障碍是在按下制表键时实现多个选定行的缩进。我通过使用真正令人惊叹的 ’ QTextCursor::movePosition’ 方法解决了这个问题,这使得实现这个功能(和后缩进)变得很容易。

QTextBlock
尽管 QTextBlock 具有惊人的功能和可能性,但它仍然有一个弱点:对我们来说,在文本小部件中跟踪 QTextBlock 几乎是不可能的。为了实现一个允许删除关键字的信号,我首先尝试复制有问题的 QTextBlock,然后使用重载的 ‘==’ 运算符检查是否相等。这不起作用,因为行号可能会改变并导致等式失败。追踪 QTextBlock 的唯一可能性是使用 ’ setUserData’ 函数为其分配 QTextBlockUserData。为了实现这一点,我继承了 QTextBlockUserData 并将 auuid和 the存储regex string在其中。uuid(+ 一些 do-while 循环)确保该块在整个应用程序中真正保持唯一。通过这些测量,‘onRemove’ 信号终于可靠且没有错误。


文章参考链接:https://www.codeproject.com/Articles/1139741/QCodeEditor-Widget-for-Qt

你可能感兴趣的:(开源项目,qt,编辑器,codeEdit,高亮,代码编辑器)