qml中的TextArea使用QSyntaxHighlighter显示高亮语法

news2025/5/10 12:43:07

在这里插入图片描述
效果图,左侧显示行号,右侧用TextArea显示文本内容,并且语法高亮。

2025年5月8号更新

1、多行文本注释

多行文本注释跟普通的高亮规则代码不太一样,代码需要修改,这里以JavaScript举例。
先制定多行文本注释规则:

QVector<QPair<QRegularExpression, QTextCharFormat>> getJSMultiLineRules()
{
    QTextCharFormat multiLineCommentFormat;
    multiLineCommentFormat.setForeground(Qt::darkGreen);
    multiLineCommentFormat.setFontItalic(true);

    QVector<QPair<QRegularExpression, QTextCharFormat>> m_rules;
    m_rules << qMakePair(QRegularExpression("/\\*"), multiLineCommentFormat); // start
    m_rules << qMakePair(QRegularExpression("\\*/"), multiLineCommentFormat); // end


    return m_rules;
}

然后我们设置规则的时候,需要先设置完普通的规则,再设置多行文本规则:

void SyntaxHighlighter::highlightBlock(const QString &text)
{
    for (const auto &rule : m_rules) {
        QRegularExpressionMatchIterator it = rule.first.globalMatch(text);
        while (it.hasNext()) {
            QRegularExpressionMatch match = it.next();
            setFormat(match.capturedStart(), match.capturedLength(), rule.second);
        }
    }

    // 再设置多行规则
    setCurrentBlockState(0);

    if (m_language == JavaScript) {
        QVector<QPair<QRegularExpression, QTextCharFormat>> rules = getJSMultiLineRules();
        for (int i = 0; i < rules.size(); i+=2) { // +=2是因为多行的开头和结尾是一个配对,有两条规则
            QPair<QRegularExpression, QTextCharFormat> startRules = rules[i];
            QPair<QRegularExpression, QTextCharFormat> endRules = rules[i+1];

            int startIndex = 0;
            if (previousBlockState() != 1)
                startIndex = text.indexOf(startRules.first);

            while (startIndex >= 0) {
                QRegularExpressionMatch match = endRules.first.match(text, startIndex);
                int endIndex = match.capturedStart();
                int commentLength = 0;
                if (endIndex == -1) {
                    setCurrentBlockState(1);
                    commentLength = text.length() - startIndex;
                } else {
                    commentLength = endIndex - startIndex + match.capturedLength();
                }
                setFormat(startIndex, commentLength, startRules.second);
                startIndex = text.indexOf(startRules.first, startIndex + commentLength);
            }
        }
    }
}

最后实现结果:
在这里插入图片描述

2、单行文本注释

单行文本注释,需要放在所有普通注释的规则之后,以免被覆盖。
否则就会出现这种情况,举个例子:
在这里插入图片描述

以下是正文

需要实现的功能:

1、左侧显示行号
2、右侧TextArea
3、可显示语法高亮

1、左侧显示行号

这里我用了一个ListView,让它跟TextView的行数对应起来,并且可以一起滚动。
简单的做法是,将ListView和TextView都放在一个ScrollView中,这样滚动的时候就可以让TextView和ListView一起滚动了。
我之前就是这么做的,但是后面发现TextView中有过长的内容时,横向滚动会把ListView滚走,这不是我想要的……
所以,我把ListView放在ScrollView的外面,看代码:

import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15

Rectangle {
    id: breakListRec
    x: 5
    width: 50
    height: textArea.height
    anchors.verticalCenter: textArea.verticalCenter
    color: "#F1F1F1"
    clip: true

    ListView {
        id: breakListView
        anchors.fill: parent
        model: textArea.lineCount
        clip: true
        contentY: textArea.contentY
        interactive: false

        delegate: Item {
            width: breakListView.width
            height: index === 0 ?  (textArea.lineHeight + textArea.topPadding/2) : textArea.lineHeight
            Rectangle {
                width: 1
                height: parent.height
                color: "#999999"
                anchors.right: parent.right
            }

            Text {
                text: qsTr(String(index+1))
                anchors.verticalCenter: parent.verticalCenter
                anchors.right: parent.right
                anchors.rightMargin: 8
                font.pixelSize: 20
                color: "#888888"
            }
        }

    }

}

这里的几个重点:
1、ListView的model为textArea的lineCount;
2、ListView的contentY绑定到TextArea的contentY属性上,当然TextArea本身是没有这个属性的,这是我自己自定义算出来的
3、ListView中的delegate的height,如果是第一行的话,需要注意的是TextArea本身有一个topPadding,所以要把这个也带上,然后TextArea本身也是没有lineHeight属性的,这个也是我自定义算出来的;

2、右侧TextArea

再看看右侧的TextArea怎么实现的,首先它肯定是放在一个ScrollView中的,其次我们需要实现行号需要的那几个属性值,看代码:

Item {
    anchors.right: parent.right
    anchors.rightMargin: 5
    anchors.left: breakListRec.right
    anchors.top: header.bottom
    anchors.topMargin: 5
    anchors.bottom: parent.bottom
    anchors.bottomMargin: 5

    property int lineCount: textArea.lineCount
    property int lineHeight: textArea.cursorRectangle.height
    property real contentY: textAreaScroll.contentHeight * textAreaScroll.ScrollBar.vertical.position
    property int topPadding: textArea.topPadding

    ScrollView {
        id: textAreaScroll
        anchors.fill: parent
        clip: true
        background: Rectangle { color: "#F1F1F1" }


        TextArea {
            id: textArea
            background: Rectangle { color: "#F1F1F1" }
            font.pixelSize: 20
            selectByMouse: true
            selectionColor: "#87cefa"
            leftPadding: 0
        }
    }
}

textArea.cursorRectangle.height可以获取到TextArea中一行的真实高度;
contentY需要用到滚动条的position来进行计算;

3、可显示语法高亮

这是本文的重点,这里采用了cpp中的QSyntaxHighlighter类,能更方便地定制高亮规则。
我这里简单定制了JSON, CPP, Python, JavaScript四种规则,可以相互切换;
首先我们先定义一个类SyntaxHighlighter,来继承QSyntaxHighlighter;

class SyntaxHighlighter : public QSyntaxHighlighter {}

其次,我们需要在类SyntaxHighlighter中重新实现函数highlightBlock;highlightBlock函数就是能让TextArea应用高亮的函数,在更改完高亮风格后,都必须要重新调用这个函数,以让TextArea刷新高亮风格;

void highlightBlock(const QString &text) override;

然后我们还需要将属性document和language暴露出来,给到qml使用;

Q_PROPERTY(QQuickTextDocument* document READ document WRITE setDocument NOTIFY documentChanged)
Q_PROPERTY(Language language READ language WRITE setLanguage NOTIFY languageChanged)

public:
    enum Language { JSON, CPP, Python, JavaScript };
    // 设置语法格式
    Language language() const;
    void setLanguage(Language lang);

    // 设置文本内容
    QQuickTextDocument* document() const { return m_quickDocument; }
    void setDocument(QQuickTextDocument* doc);
    
signals:
    void documentChanged();
    void languageChanged();

另外,我们还需要学习两个类,QRegularExpression和QTextCharFormat。
QRegularExpression是用来定制语法识别规则的,比如这样可以识别到单行注释:

QRegularExpression("//[^\n]*")

QTextCharFormat则是用来制定高亮风格的,比如这样可以制定高亮为加粗、蓝色:

QTextCharFormat keywordFormat;
keywordFormat.setForeground(Qt::blue);
keywordFormat.setFontWeight(QFont::Bold);

最后,我们还需要在main中注册这个类,这样qml才能使用:

qmlRegisterType<SyntaxHighlighter>("CustomHighlighter", 1, 0, "SyntaxHighlighter");

我们看看qml怎么使用这个类:

TextArea {
     id: textArea
     background: Rectangle { color: "#F1F1F1" }
     font.pixelSize: 20
     selectByMouse: true
     selectionColor: "#87cefa"
     leftPadding: 0

     SyntaxHighlighter {
         id: highlighter
         document: textArea.textDocument
         language: SyntaxHighlighter.CPP
         onLanguageChanged: {
             var data = textArea.text
             textArea.text = ""
             textArea.text = data
         }
     }
 }

☆☆ 好了,现在来看完整代码

先制定四种语法规则:

CPPRules.h

#ifndef CPPRULES_H
#define CPPRULES_H

#include <QObject>
#include <QTextCharFormat>
#include <QRegularExpression>


QVector<QPair<QRegularExpression, QTextCharFormat>> getCPPRules()
{
    QVector<QPair<QRegularExpression, QTextCharFormat> > m_rules;

    // 1. 关键字(蓝色加粗)
    QTextCharFormat keywordFormat;
    keywordFormat.setForeground(Qt::blue);
    keywordFormat.setFontWeight(QFont::Bold);
    QStringList keywords = {
        "char", "class", "const",
        "double", "enum", "explicit",
        "friend", "inline", "int",
        "long", "namespace", "operator",
        "private", "protected", "public",
        "short", "signals", "signed",
        "slots", "static", "struct",
        "template", "typedef", "typename",
        "union", "unsigned", "virtual",
        "void", "volatile", "bool"
    };
    for (const QString &kw : keywords) {
        m_rules << qMakePair(QRegularExpression("\\b" + kw + "\\b"), keywordFormat);
    }

    // 2.类名
    QTextCharFormat classFormat;
    classFormat.setFontWeight(QFont::Bold);
    classFormat.setForeground(Qt::darkMagenta);
    m_rules << qMakePair(QRegularExpression("\\bQ[A-Za-z]+\\b"), classFormat);

    // 3. 单行注释(绿色)
    QTextCharFormat singleLineCommentFormat;
    singleLineCommentFormat.setForeground(Qt::darkGreen);
    m_rules << qMakePair(QRegularExpression("//[^\n]*"), singleLineCommentFormat);

    // 4. 多行注释(绿色斜体)
    QTextCharFormat multiLineCommentFormat;
    multiLineCommentFormat.setForeground(Qt::darkGreen);
    multiLineCommentFormat.setFontItalic(true);
    m_rules << qMakePair(QRegularExpression("/\\*.*?\\*/"), multiLineCommentFormat);

    // 5. 字符串(橙色)
    QTextCharFormat stringFormat;
    stringFormat.setForeground(QColor(255, 165, 0)); // 橙色
    m_rules << qMakePair(QRegularExpression("\".*\""), stringFormat);

    // 6. 数字(紫色)
    QTextCharFormat numberFormat;
    numberFormat.setForeground(Qt::darkMagenta);
    m_rules << qMakePair(QRegularExpression("\\b\\d+\\b"), numberFormat);

    // 7. 预处理指令(灰色)
    QTextCharFormat preprocessorFormat;
    preprocessorFormat.setForeground(Qt::gray);
    m_rules << qMakePair(QRegularExpression("#.*"), preprocessorFormat);

    // 8.函数名
    QTextCharFormat functionFormat;
    functionFormat.setForeground(Qt::blue);
    m_rules << qMakePair(QRegularExpression("(\\w+)::"), functionFormat);

    // 9.被引用,如A::Test中的Test
    QTextCharFormat functionTwoFormat;
    functionTwoFormat.setForeground(Qt::darkBlue);
    m_rules << qMakePair(QRegularExpression("\\b[A-Za-z0-9_]+(?=\\()"), functionTwoFormat);


    return m_rules;
}


#endif // CPPRULES_H

JavaScriptRules.h

#ifndef JAVASCRIPTRULES_H
#define JAVASCRIPTRULES_H

#include <QObject>
#include <QTextCharFormat>
#include <QRegularExpression>


QVector<QPair<QRegularExpression, QTextCharFormat>> getJavaScriptRules() {
    QVector<QPair<QRegularExpression, QTextCharFormat> > m_rules;

    // 1. 关键字(蓝色加粗)
    QTextCharFormat keywordFormat;
    keywordFormat.setForeground(Qt::blue);
    keywordFormat.setFontWeight(QFont::Bold);
    QStringList keywords = {
        "function", "if", "else", "for", "while", "do", "switch", "case", "break",
        "return", "var", "let", "const", "new", "this", "true", "false", "null",
        "undefined", "try", "catch", "finally", "throw", "class", "extends", "import",
        "export", "async", "await", "yield"
    };
    for (const QString &kw : keywords) {
        m_rules << qMakePair(QRegularExpression("\\b" + kw + "\\b"), keywordFormat);
    }

    // 2. 内置对象和方法(深蓝色)
    QTextCharFormat builtinFormat;
    builtinFormat.setForeground(QColor(0, 0, 139)); // 深蓝色
    QStringList builtins = {
        "console", "Object", "Array", "String", "Number", "Math", "JSON", "Promise",
        "setTimeout", "fetch", "document", "window", "require"
    };
    for (const QString &bn : builtins) {
        m_rules << qMakePair(QRegularExpression("\\b" + bn + "\\b"), builtinFormat);
    }

    // 3. 单行注释(绿色)
    QTextCharFormat singleLineCommentFormat;
    singleLineCommentFormat.setForeground(Qt::darkGreen);
    m_rules << qMakePair(QRegularExpression("//[^\n]*"), singleLineCommentFormat);

    // 4. 多行注释(绿色斜体)
    QTextCharFormat multiLineCommentFormat;
    multiLineCommentFormat.setForeground(Qt::darkGreen);
    multiLineCommentFormat.setFontItalic(true);
    m_rules << qMakePair(QRegularExpression("/\\*.*?\\*/"), multiLineCommentFormat);

    // 5. 字符串(橙色)
    QTextCharFormat stringFormat;
    stringFormat.setForeground(QColor(255, 165, 0)); // 橙色
    // 匹配单引号、双引号、模板字符串
    m_rules << qMakePair(QRegularExpression("\".*?\""), stringFormat);
    m_rules << qMakePair(QRegularExpression("'.*?'"), stringFormat);
    m_rules << qMakePair(QRegularExpression("`.*?`"), stringFormat);

    // 6. 正则表达式(紫色)
    QTextCharFormat regexFormat;
    regexFormat.setForeground(Qt::darkMagenta);
    m_rules << qMakePair(QRegularExpression("/.+?/[gimuy]*"), regexFormat);

    // 7. 数字(紫色)
    QTextCharFormat numberFormat;
    numberFormat.setForeground(Qt::darkMagenta);
    m_rules << qMakePair(QRegularExpression("\\b\\d+\\.?\\d*\\b"), numberFormat);

    // 8. 函数定义(深红色)
    QTextCharFormat functionDefFormat;
    functionDefFormat.setForeground(QColor(139, 0, 0)); // 深红色
    m_rules << qMakePair(QRegularExpression("\\bfunction\\s+(\\w+)"), functionDefFormat);
    m_rules << qMakePair(QRegularExpression("\\b(\\w+)\\s*=\\s*function\\b"), functionDefFormat);

    // 9. 箭头函数(深青色)
    QTextCharFormat arrowFunctionFormat;
    arrowFunctionFormat.setForeground(QColor(0, 139, 139)); // 深青色
    m_rules << qMakePair(QRegularExpression("\\b(\\w+)\\s*=>"), arrowFunctionFormat);

    return m_rules;
}


#endif // JAVASCRIPTRULES_H

JsonRules.h

#ifndef JSONRULES_H
#define JSONRULES_H

#include <QObject>
#include <QTextCharFormat>
#include <QRegularExpression>


QVector<QPair<QRegularExpression, QTextCharFormat>> getJsonRules() {
    QVector<QPair<QRegularExpression, QTextCharFormat> > m_rules;

    // 1. JSON Key(深蓝色加粗)
    QTextCharFormat keyFormat;
    keyFormat.setForeground(Qt::darkBlue);
    keyFormat.setFontWeight(QFont::Bold);
    m_rules << qMakePair(QRegularExpression("\"(\\w+)\"\\s*:"), keyFormat);

    // 2. JSON String Value(绿色)
    QTextCharFormat stringValueFormat;
    stringValueFormat.setForeground(Qt::darkGreen);
    m_rules << qMakePair(QRegularExpression("\".*\""), stringValueFormat);

    // 3. JSON Number(紫色)
    QTextCharFormat numberFormat;
    numberFormat.setForeground(Qt::darkMagenta);
    m_rules << qMakePair(QRegularExpression("\\b\\d+\\b"), numberFormat);

    return m_rules;
}

#endif // JSONRULES_H

PythonRules.h

#ifndef PYTHONRULES_H
#define PYTHONRULES_H

#include <QObject>
#include <QTextCharFormat>
#include <QRegularExpression>


QVector<QPair<QRegularExpression, QTextCharFormat>> getPythonRules() {
    QVector<QPair<QRegularExpression, QTextCharFormat> > m_rules;

    // 1. 关键字(蓝色加粗)
    QTextCharFormat keywordFormat;
    keywordFormat.setForeground(Qt::blue);
    keywordFormat.setFontWeight(QFont::Bold);
    QStringList keywords = {
        "def", "class", "if", "elif", "else", "for", "while",
        "try", "except", "finally", "with", "import", "from",
        "as", "return", "yield", "lambda", "nonlocal", "global"
    };
    for (const QString &kw : keywords) {
        m_rules << qMakePair(QRegularExpression("\\b" + kw + "\\b"), keywordFormat);
    }

    // 2. 内置函数和类型(深蓝色)
    QTextCharFormat builtinFormat;
    builtinFormat.setForeground(QColor(0, 0, 139)); // 深蓝色
    QStringList builtins = {
        "print", "len", "range", "list", "dict", "str", "int",
        "float", "True", "False", "None", "self"
    };
    for (const QString &bn : builtins) {
        m_rules << qMakePair(QRegularExpression("\\b" + bn + "\\b"), builtinFormat);
    }

    // 3. 单行注释(绿色)
    QTextCharFormat commentFormat;
    commentFormat.setForeground(Qt::darkGreen);
    m_rules << qMakePair(QRegularExpression("#[^\n]*"), commentFormat);

    // 4. 字符串(橙色)
    QTextCharFormat stringFormat;
    stringFormat.setForeground(QColor(255, 165, 0)); // 橙色
    // 匹配单引号、双引号、三引号字符串
    m_rules << qMakePair(QRegularExpression("\"\"\".*?\"\"\""), stringFormat);
    m_rules << qMakePair(QRegularExpression("'''.*?'''"), stringFormat);
    m_rules << qMakePair(QRegularExpression("\".*?\""), stringFormat);
    m_rules << qMakePair(QRegularExpression("'.*?'"), stringFormat);

    // 5. 装饰器(紫色)
    QTextCharFormat decoratorFormat;
    decoratorFormat.setForeground(Qt::darkMagenta);
    m_rules << qMakePair(QRegularExpression("@\\w+"), decoratorFormat);

    // 6. 数字(紫色)
    QTextCharFormat numberFormat;
    numberFormat.setForeground(Qt::darkMagenta);
    m_rules << qMakePair(QRegularExpression("\\b\\d+\\.?\\d*\\b"), numberFormat);

    // 7. 函数定义(深红色)
    QTextCharFormat functionDefFormat;
    functionDefFormat.setForeground(QColor(139, 0, 0)); // 深红色
    m_rules << qMakePair(QRegularExpression("\\bdef\\s+(\\w+)"), functionDefFormat);

    return m_rules;
}

#endif // PYTHONRULES_H

再写一个QMLFunction类,用来给qml读取文件等信息

QMLFunction.h

#ifndef QMLFUNCTION_H
#define QMLFUNCTION_H

#include <QUrl>
#include <QFile>
#include <QObject>
#include <QFileInfo>

class QMLFunction : public QObject
{
    Q_OBJECT
public:
    explicit QMLFunction(QObject *parent = nullptr);
    Q_INVOKABLE QString readFile(QUrl filePath);
    Q_INVOKABLE void saveFile(QString data);
    Q_INVOKABLE int fileLanguage();

private:
    QString currentFilePath;

signals:

};

#endif // QMLFUNCTION_H

QMLFunction.cpp

#include "QMLFunction.h"
#include "SyntaxHighlighter.h"

QMLFunction::QMLFunction(QObject *parent)
    : QObject{parent}
{

}

QString QMLFunction::readFile(QUrl filePath)
{
    currentFilePath = "";
    currentFilePath = filePath.path(QUrl::PrettyDecoded);
#ifdef Q_OS_WIN32
    if(currentFilePath.startsWith('/')){
        currentFilePath = currentFilePath.remove(0,1);
    }
#endif

    QString data = "";
    QFile file(currentFilePath);
    if (file.open(QIODevice::ReadOnly)) {
        data = file.readAll();
        file.close();
    }

    return data;
}

void QMLFunction::saveFile(QString data)
{
    QFile file(currentFilePath);
    if (file.open(QIODevice::ReadWrite | QIODevice::Truncate)) {
        file.write(data.toUtf8());
        file.close();
    }
}

int QMLFunction::fileLanguage()
{
    QFileInfo info(currentFilePath);
    QString suffix = info.suffix();
    if (suffix == "CPP") {
        return SyntaxHighlighter::CPP;
    } else if (suffix == "json") {
        return SyntaxHighlighter::JSON;
    } else if (suffix == "h") {
        return SyntaxHighlighter::CPP;
    } else if (suffix == "js") {
        return SyntaxHighlighter::JavaScript;
    } else if (suffix == "py") {
        return SyntaxHighlighter::Python;
    } else {
        return SyntaxHighlighter::CPP;
    }
}

再写高亮的主要类SyntaxHighlighter

SyntaxHighlighter.h

#ifndef SYNTAXHIGHLIGHTER_H
#define SYNTAXHIGHLIGHTER_H

#include <QObject>
#include <QQuickTextDocument>
#include <QSyntaxHighlighter>
#include <QRegularExpression>

class SyntaxHighlighter : public QSyntaxHighlighter
{
    Q_OBJECT
    Q_PROPERTY(QQuickTextDocument* document READ document WRITE setDocument NOTIFY documentChanged)
    Q_PROPERTY(Language language READ language WRITE setLanguage NOTIFY languageChanged)
public:
    enum Language { JSON, CPP, Python, JavaScript };
    Q_ENUM(Language)
    SyntaxHighlighter(QTextDocument *parent = nullptr);

    // 设置语法格式
    Language language() const;
    void setLanguage(Language lang);

    // 设置文本内容
    QQuickTextDocument* document() const { return m_quickDocument; }
    void setDocument(QQuickTextDocument* doc);


protected:
    void highlightBlock(const QString &text) override;

private:
    QQuickTextDocument* m_quickDocument = nullptr;
    Language m_language;
    QVector<QPair<QRegularExpression, QTextCharFormat>> m_rules;

signals:
    void documentChanged();
    void languageChanged();

};

#endif // SYNTAXHIGHLIGHTER_H

SyntaxHighlighter.cpp

#include "SyntaxHighlighter.h"
#include "CPPRules.h"
#include "JavaScriptRules.h"
#include "PythonRules.h"
#include "JsonRules.h"

// SyntaxHighlighter.cpp
SyntaxHighlighter::SyntaxHighlighter(QTextDocument *parent) : QSyntaxHighlighter(parent) {
}

SyntaxHighlighter::Language SyntaxHighlighter::language() const
{
    return m_language;
}

void SyntaxHighlighter::setLanguage(Language lang)
{
    if (m_language == lang) return ;

    m_language = lang;
    m_rules.clear();

    switch (lang) {
    case JSON: {
        m_rules = getJsonRules();
        break;
    }
    case CPP: {
        m_rules = getCPPRules();
        break;
    }
    case Python: {
        m_rules = getPythonRules();
        break;
    }
    case JavaScript:{
        m_rules = getJavaScriptRules();
        break;
    }
    }

    rehighlight(); // 重新应用高亮
    emit languageChanged(); // 触发信号
}

void SyntaxHighlighter::setDocument(QQuickTextDocument *doc)
{
    if (doc != m_quickDocument) {
        m_quickDocument = doc;
        QSyntaxHighlighter::setDocument(doc->textDocument()); // 关键转换
        emit documentChanged();
    }
}

void SyntaxHighlighter::highlightBlock(const QString &text)
{
    for (const auto &rule : m_rules) {
        QRegularExpressionMatchIterator it = rule.first.globalMatch(text);
        while (it.hasNext()) {
            QRegularExpressionMatch match = it.next();
            setFormat(match.capturedStart(), match.capturedLength(), rule.second);
        }
    }
}


main.cpp

#include <QQmlContext>
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include "QMLFunction.h"
#include "SyntaxHighlighter.h"


int main(int argc, char *argv[])
{
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif
    QGuiApplication app(argc, argv);

    qmlRegisterType<SyntaxHighlighter>("CustomHighlighter", 1, 0, "SyntaxHighlighter");

    QQmlApplicationEngine engine;

    QMLFunction qmlFunction;
    engine.rootContext()->setContextProperty("QMLFunc", &qmlFunction);
    qmlRegisterType<QMLFunction>("QMLEnum",1,0,"QMLEnum");

    const QUrl url(QStringLiteral("qrc:/main.qml"));
    QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
                     &app, [url](QObject *obj, const QUrl &objUrl) {
        if (!obj && url == objUrl)
            QCoreApplication::exit(-1);
    }, Qt::QueuedConnection);
    engine.load(url);

    return app.exec();
}

再看一下qml的文件代码

main.qml

import QtQuick 2.15
import QtQuick.Window 2.15

Window {
    id: window_
    width: 640
    height: 480
    visible: true
    title: qsTr("Hello World")


    Item {
        anchors.fill: parent

        Header{
            id: header
        }

        LineCountList {
            id: breakListRec
        }

        MyTextArea {
            id: textArea
        }

    }
}

Header.qml

import QtQuick 2.15
import QtQuick.Dialogs 1.3
import QtQuick.Controls 2.15

Item {
    id: header
    width: parent.width
    height: 40


    Row {
        height: parent.height
        width: parent.width - 10
        anchors.horizontalCenter: parent.horizontalCenter
        spacing: 5

        Rectangle {
            id: fileOpen
            width: 70
            height: 30
            color: fileOpenMouse.pressed ? "#dcdcdc" : "transparent"
            border.width: fileOpenMouse.pressed ? 1 : 0
            border.color: "#bcbcbc"
            anchors.verticalCenter: parent.verticalCenter
            ToolTip.visible: fileOpenMouse.entered_
            ToolTip.text: qsTr("快捷键 Ctrl+O")
            Text {
                text: qsTr("打开")
                anchors.centerIn: parent
                font.pixelSize: 16
            }
            MouseArea {
                id: fileOpenMouse
                anchors.fill: parent
                hoverEnabled: true
                property bool entered_: false
                onClicked: {
                    fileDialog.open()
                }
                onEntered: {
                    entered_ = true
                }
                onExited: {
                    entered_ = false
                }
            }
        }

        Rectangle {
            id: fileSave
            width: 70
            height: 30
            color: fileSaveMouse.pressed ? "#dcdcdc" : "transparent"
            border.width: fileSaveMouse.pressed ? 1 : 0
            border.color: "#bcbcbc"
            anchors.verticalCenter: parent.verticalCenter
            ToolTip.visible: fileSaveMouse.entered_
            ToolTip.text: qsTr("快捷键 Ctrl+O")
            Text {
                text: qsTr("保存")
                anchors.centerIn: parent
                font.pixelSize: 16
            }
            MouseArea {
                id: fileSaveMouse
                anchors.fill: parent
                hoverEnabled: true
                property bool entered_: false
                onClicked: {
                    QMLFunc.saveFile(textArea.getText())
                }
                onEntered: {
                    entered_ = true
                }
                onExited: {
                    entered_ = false
                }
            }
        }

        Item {
            id: languageItem
            width: 150
            height: 35
            Text {
                id: languageTitle
                text: qsTr("语法选择")
                anchors.verticalCenter: parent.verticalCenter
                font.pixelSize: 16
            }

            ComboBox {
                id: languageSelect
                model: ["JSON", "CPP", "Python", "JavaScript"]
                onCurrentIndexChanged: {
                    if (currentIndex !== textArea.getLanguage()) {
                        textArea.setLanguage(currentIndex)
                    }
                }
            }
        }
    }

    Rectangle {
        width: parent.width
        height: 1
        color: "#444444"
        anchors.bottom: parent.bottom
    }


    FileDialog {
        id: fileDialog
        onAccepted: {
            var data = QMLFunc.readFile(fileUrl)
            textArea.setText(data)
            textArea.setLanguage(QMLFunc.fileLanguage())
            languageSelect.currentIndex = QMLFunc.fileLanguage()
        }
    }

    Shortcut {
        sequence: "Ctrl+O"
        onActivated: {
            fileDialog.open()
        }
    }

    Shortcut {
        sequence: "Ctrl+S"
        onActivated: {
            QMLFunc.saveFile(textArea.getText())
        }
    }

}

LineCountList.qml

import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15

Rectangle {
    id: breakListRec
    x: 5
    width: 50
    height: textArea.height
    anchors.verticalCenter: textArea.verticalCenter
    color: "#F1F1F1"
    clip: true

    ListView {
        id: breakListView
        anchors.fill: parent
        model: textArea.lineCount
        clip: true
        contentY: textArea.contentY
        interactive: false

        delegate: Item {
            width: breakListView.width
            height: index === 0 ?  (textArea.lineHeight + textArea.topPadding/2) : textArea.lineHeight
            Rectangle {
                width: 1
                height: parent.height
                color: "#999999"
                anchors.right: parent.right
            }

            Text {
                text: qsTr(String(index+1))
                anchors.verticalCenter: parent.verticalCenter
                anchors.right: parent.right
                anchors.rightMargin: 8
                font.pixelSize: 20
                color: "#888888"
            }
            MouseArea {
                anchors.fill: parent
                onClicked: {
                    textArea.selectLine(index)
                }
            }
        }

    }

}

MyTextArea.qml

import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import CustomHighlighter 1.0

Item {
    anchors.right: parent.right
    anchors.rightMargin: 5
    anchors.left: breakListRec.right
    anchors.top: header.bottom
    anchors.topMargin: 5
    anchors.bottom: parent.bottom
    anchors.bottomMargin: 5

    property int lineCount: textArea.lineCount
    property int lineHeight: textArea.cursorRectangle.height
    property real contentY: textAreaScroll.contentHeight * textAreaScroll.ScrollBar.vertical.position
    property int topPadding: textArea.topPadding

    ScrollView {
        id: textAreaScroll
        anchors.fill: parent
        clip: true
        background: Rectangle { color: "#F1F1F1" }


        TextArea {
            id: textArea
            background: Rectangle { color: "#F1F1F1" }
            font.pixelSize: 20
            selectByMouse: true
            selectionColor: "#87cefa"
            leftPadding: 0

            SyntaxHighlighter {
                id: highlighter
                document: textArea.textDocument
                language: SyntaxHighlighter.CPP
                onLanguageChanged: {
                    var data = textArea.text
                    textArea.text = ""
                    textArea.text = data
                }
            }
        }
    }


    function setText(text) {
        textArea.text = text
    }

    function getText() {
        return textArea.text
    }

    // 选中指定行的函数
    function selectLine(lineIndex) {
        var lines = textArea.text.split("\n");
        if (lineIndex < 0 || lineIndex >= lines.length) return;

        // 计算行首位置
        var startPos = 0;
        for (var i = 0; i < lineIndex; i++) {
            startPos += lines[i].length + 1; // +1 是换行符
        }

        // 计算行尾位置
        var endPos = startPos + lines[lineIndex].length+1;

        // 选中行并更新当前行
        textArea.select(startPos, endPos);
        forceActiveFocus();
    }

    function setLanguage(type) {
        highlighter.language = type
    }

    function getLanguage() {
        return highlighter.language;
    }

}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2372309.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Transformer编码器+SHAP分析,模型可解释创新表达!

目录 效果一览基本介绍程序设计参考资料 效果一览 基本介绍 基本介绍 基于SHAP分析的特征选择和贡献度计算&#xff0c;Matlab2023b代码实现&#xff1b;基于MATLAB的SHAP可解释Transformer编码器回归模型&#xff0c;敏感性分析方法。 详细介绍 引言 在正向渗透&#xff08…

[特殊字符]适合母亲节的SVG模版[特殊字符]

宝藏模版 往期推荐&#xff08;点击阅读&#xff09;&#xff1a; 趣味效果&#xff5c;高大上&#xff5c;可爱风&#xff5c;年终总结I&#xff5c;年终总结II&#xff5c;循环特效&#xff5c;情人节I&#xff5c;情人节II&#xff5c;情人节IIII&#xff5c;妇女节I&…

浅蓝色调风格人像自拍Lr调色预设,手机滤镜PS+Lightroom预设下载!

调色教程 浅蓝色调风格人像自拍 Lr 调色是利用 Adobe Lightroom 软件针对人像自拍照进行后期处理的一种调色方式。它通过对照片的色彩、对比度、亮度等参数进行精细调整&#xff0c;将画面的主色调打造为清新、柔和的浅蓝色系&#xff0c;赋予人像自拍独特的清新、文艺风格&…

isp流程介绍(yuv格式阶段)

一、前言介绍 前面两章里面&#xff0c;已经分别讲解了在Raw和Rgb域里面&#xff0c;ISP的相关算法流程&#xff0c;从前面文章里面可以看到&#xff0c;在Raw和Rgb域里面&#xff0c;很多ISP算法操作&#xff0c;更像是属于sensor矫正或者说sensor标定操作。本质上来说&#x…

数巅智能携手北京昇腾创新中心深耕行业大模型应用

当前&#xff0c;AI技术正在加速向各行业深度渗透,成为驱动产业转型和社会经济发展的重要引擎。构建开放协作的AI应用生态体系、推动技术和应用深度融合&#xff0c;已成为行业发展的重要趋势。 近日&#xff0c;数巅智能与北京昇腾人工智能计算中心&#xff08;北京昇腾创新中…

【LangChain高级系列】LangGraph第一课

前言 我们今天直接通过一个langgraph的基础案例&#xff0c;来深入探索langgraph的核心概念和工作原理。 基本认识 LangGraph是一个用于构建具有LLMs的有状态、多角色应用程序的库&#xff0c;用于创建代理和多代理工作流。与其他LLM框架相比&#xff0c;它提供了以下核心优…

常见降维算法分析

一、常见的降维算法 LDA线性判别PCA主成分分析t-sne降维 二、降维算法原理 2.1 LDA 线性判别 原理 &#xff1a;LDA&#xff08;Linear Discriminant Analysis&#xff09;线性判别分析是一种有监督的降维方法。它的目标是找到一个投影方向&#xff0c;使得不同类别的数据在…

计算机二级(C语言)已过

非线性结构&#xff1a;树、图 链表和队列的结构特性不一样&#xff0c;链表可以在任何位置插入、删除&#xff0c;而队列只能在队尾入队、队头出队 对长度为n的线性表排序、在最坏情况下时间复杂度&#xff0c;二分查找为O(log2n)&#xff0c;顺序查找为O(n)&#xff0c;哈希查…

2025年3月,​韩先超对国网宁夏进行Python线下培训

大家好&#xff0c;我是韩先超&#xff01;在2025年3月3号和4号&#xff0c;为 宁夏国网 的运维团队进行了一场两天的 Python培训 &#xff0c;培训目标不仅是让大家学会Python编程&#xff0c;更是希望大家能够通过这门技术解决实际工作中的问题&#xff0c;提升工作效率。 对…

[计算机网络]物理层

文章目录 物理层的概述与功能传输介质双绞线:分类:应用领域: 同轴电缆&#xff1a;分类: 光纤&#xff1a;分类: 无线传输介质&#xff1a;无线电波微波&#xff1a;红外线&#xff1a;激光&#xff1a; 物理层设备中继器(Repeater)&#xff1a;放大器&#xff1a;集线器(Hub)&…

幂等操作及处理措施

利用token模式去避免幂等操作 按以上图所示&#xff0c;除了token,应该也可以把传入的参数用MD5加密&#xff0c;当成key放入redis里面&#xff0c;业务执行完后再删除这个key.如还没有执行完&#xff0c;则请不要重复操作。纯属个人理解

Matlab 数控车床进给系统的建模与仿真

1、内容简介 Matlab217-数控车床进给系统的建模与仿真 可以交流、咨询、答疑 2、内容说明 略 摘 要:为提高数控车床的加工精度,对数控 车床进给系统中影响加工精度的主要因素进行了仿真分析研 动系统的数学模型,利用MATLAB软件中的动态仿真工具 究:依据机械动力学原理建立了…

低成本自动化改造的18个技术锚点深度解析

执行摘要 本文旨在深入剖析四项关键的低成本自动化技术&#xff0c;这些技术为工业转型提供了显著的运营和经济效益。文章将提供实用且深入的指导&#xff0c;涵盖老旧设备联网、AGV车队优化、空压机系统智能能耗管控以及此类项目投资回报率&#xff08;ROI&#xff09;的严谨…

我国脑机接口市场规模将破38亿元,医疗领域成关键突破口

当人类仅凭"意念"就能操控无人机编队飞行&#xff0c;当瘫痪患者通过"脑控"重新站立行走&#xff0c;这些曾只存在于科幻电影的场景&#xff0c;如今正通过脑机接口技术变为现实。作为"十四五"规划中重点发展的前沿科技&#xff0c;我国脑机接口…

Edu教育邮箱申请成功下号

这里是第2部分 如你所见&#xff0c;我根本就没有考虑流量的问题&#xff0c; 如果你有幸看到前面的内容&#xff0c;相信你能自己找到这个后续。

【Linux进程控制一】进程的终止和等待

【Linux进程控制一】进程的终止和等待 一、进程终止1.main函数的return2.strerror函数3.库函数exit4.系统调用_exit和库函数exit的区别5.异常信号6.变量errno 二、进程等待1.什么是进程等待&#xff1f;2.wait接口3.status4.waitpid接口 一、进程终止 1.main函数的return 写C…

今日行情明日机会——20250509

上证指数今天缩量&#xff0c;整体跌多涨少&#xff0c;走势处于日线短期的高位~ 深证指数今天缩量小级别震荡&#xff0c;大盘股表现更好~ 2025年5月9日涨停股主要行业方向分析 一、核心主线方向 服装家纺&#xff08;消费复苏出口链驱动&#xff09; • 涨停家数&#xf…

单片机-STM32部分:10、串口UART

飞书文档https://x509p6c8to.feishu.cn/wiki/W7ZGwKJCeiGjqmkvTpJcjT2HnNf 串口说明 电平标准是数据1和数据0的表达方式&#xff0c;是传输线缆中人为规定的电压与数据的对应关系&#xff0c;串口常用的电平标准有如下三种&#xff1a; TTL电平&#xff1a;3.3V或5V表示1&am…

RabittMQ-高级特性2-应用问题

文章目录 前言延迟队列介绍ttl死信队列存在问题延迟队列插件安装延迟插件使用事务消息分发概念介绍限流非公平分发&#xff08;负载均衡&#xff09; 限流负载均衡RabbitMQ应用问题-幂等性保障顺序性保障介绍1顺序性保障介绍2消息积压总结 前言 延迟队列介绍 延迟队列(Delaye…

React 播客专栏 Vol.5|从“显示”到“消失”:打造你的第一个交互式 Alert 组件!

&#x1f44b; 欢迎回到《前端达人 播客书单》第 5 期&#xff08;正文内容为学习笔记摘要&#xff0c;音频内容是详细的解读&#xff0c;方便你理解&#xff09;&#xff0c;请点击下方收听 &#x1f4cc; 今天我们不再停留在看代码&#xff0c;而是动手实现一个真正的 React…