Qt应用程序脚本技术QScriptEngine详解

一、QScriptEngine 概述

QScriptEngine 是 Qt 框架中用于执行 JavaScript 代码的核心类,属于 QtScript 模块(需注意 Qt 5 后部分功能被 QJSEngine 替代)。它提供了 JavaScript 解释、对象绑定、信号槽机制与 C++ 交互的能力,适用于脚本扩展、动态逻辑实现等场景。


二、核心功能

脚本执行
通过 evaluate() 方法执行 JavaScript 代码,返回 QScriptValue 对象存储结果:

#include <QScriptEngine>
#include <QDebug>

void runSimpleScript()
{
    QScriptEngine engine;
    
    // 执行简单脚本
    QScriptValue result = engine.evaluate("var x = 10; var y = 20; x + y;");
    
    if (engine.hasUncaughtException()) {
        qDebug() << "Error at line" << engine.uncaughtExceptionLineNumber()
                 << ":" << engine.uncaughtException().toString();
    } else {
        qDebug() << "Result:" << result.toNumber(); // 输出30
    }
}

对象绑定
将 C++ 对象暴露给脚本环境,使用 newQObject() 方法:

// 向脚本环境暴露C++对象
QScriptEngine engine;
QPushButton button;
QScriptValue scriptButton = engine.newQObject(&button);
engine.globalObject().setProperty("button", scriptButton);

// 脚本中可以操作button对象
engine.evaluate("button.text = 'Click Me'; button.show();");

信号槽连接
支持 JavaScript 函数作为槽:

QScriptValue func = engine.evaluate("(function(msg) { print(msg); })");
QObject::connect(myObject, SIGNAL(signal(QString)), func);

三、关键方法

  • globalObject():获取全局对象,用于注册变量或函数。
  • newFunction():封装 C++ 函数为脚本可调用的函数。
  • pushContext() / popContext():管理执行上下文。

示例:注册全局函数

QScriptValue add(QScriptContext* context, QScriptEngine* engine) {
    return context->argument(0).toNumber() + context->argument(1).toNumber();
}
engine->globalObject().setProperty("add", engine->newFunction(add));
// 脚本中调用:add(2, 3)

四、错误处理

通过 hasUncaughtException()uncaughtException() 检查执行错误:

QScriptValue result = engine.evaluate("invalidCode");
if (engine.hasUncaughtException()) {
    qDebug() << "Error:" << engine.uncaughtException().toString();
}

在这里插入图片描述


五、注意事项

  • 内存管理:QScriptEngine 不自动管理 C++ 对象生命周期,需手动处理。
  • 模块支持:Qt 5 推荐使用 QJSEngine(QtQml 模块)作为替代,但 QScriptEngine 仍可用于复杂脚本需求。
  • 性能:频繁调用脚本可能影响性能,建议预编译或优化逻辑。

六、典型应用场景

  • 动态配置规则(如公式计算)。
  • 插件系统扩展(用户编写脚本逻辑)。
  • 自动化测试(模拟交互行为)。

如需进一步优化或扩展功能,可结合 QScriptClass 实现自定义对象原型。

七、实例展示

1、实现脚本控制台

// scriptconsole.h
#include <QWidget>
#include <QScriptEngine>

class ScriptConsole : public QWidget
{
    Q_OBJECT
    
public:
    ScriptConsole(QWidget *parent = nullptr);
    
public slots:
    void execute(const QString &script);
    
private:
    QScriptEngine *engine;
    QTextEdit *outputWidget;
    QLineEdit *inputLine;
    
    void setupUi();
    void print(const QString &message);
};

// scriptconsole.cpp
ScriptConsole::ScriptConsole(QWidget *parent)
    : QWidget(parent), engine(new QScriptEngine(this))
{
    setupUi();
    
    // 将print函数暴露给脚本
    QScriptValue printFunc = engine->newFunction([](QScriptContext *context, QScriptEngine *engine) {
        QString message;
        for (int i = 0; i < context->argumentCount(); ++i) {
            if (i > 0) message.append(" ");
            message.append(context->argument(i).toString());
        }
        qobject_cast<ScriptConsole*>(engine->parent())->print(message);
        return engine->undefinedValue();
    });
    
    engine->globalObject().setProperty("print", printFunc);
}

void ScriptConsole::execute(const QString &script)
{
    print("> " + script);
    QScriptValue result = engine->evaluate(script);
    
    if (engine->hasUncaughtException()) {
        print(QString("Exception at line %1: %2")
              .arg(engine->uncaughtExceptionLineNumber())
              .arg(engine->uncaughtException().toString()));
    } else if (!result.isUndefined()) {
        print(result.toString());
    }
}

2、动态加载UI并绑定脚本

// scriptedwindow.h
#include <QMainWindow>
#include <QScriptEngine>

class ScriptedWindow : public QMainWindow
{
    Q_OBJECT
    
public:
    ScriptedWindow(const QString &uiFile, const QString &scriptFile, QWidget *parent = nullptr);
    
private:
    QScriptEngine *engine;
    void loadUi(const QString &uiFile);
    void loadScript(const QString &scriptFile);
};

// scriptedwindow.cpp
ScriptedWindow::ScriptedWindow(const QString &uiFile, const QString &scriptFile, QWidget *parent)
    : QMainWindow(parent), engine(new QScriptEngine(this))
{
    loadUi(uiFile);
    loadScript(scriptFile);
}

void ScriptedWindow::loadUi(const QString &uiFile)
{
    QUiLoader loader;
    QFile file(uiFile);
    if (!file.open(QFile::ReadOnly)) {
        qWarning() << "Cannot open UI file:" << uiFile;
        return;
    }
    
    QWidget *ui = loader.load(&file, this);
    setCentralWidget(ui);
    
    // 将UI对象暴露给脚本
    foreach (QObject *obj, ui->findChildren<QObject*>()) {
        if (!obj->objectName().isEmpty()) {
            engine->globalObject().setProperty(obj->objectName(), 
                                            engine->newQObject(obj));
        }
    }
}

void ScriptedWindow::loadScript(const QString &scriptFile)
{
    QFile file(scriptFile);
    if (!file.open(QFile::ReadOnly)) {
        qWarning() << "Cannot open script file:" << scriptFile;
        return;
    }
    
    QTextStream stream(&file);
    QString script = stream.readAll();
    
    QScriptValue result = engine->evaluate(script);
    if (engine->hasUncaughtException()) {
        qWarning() << "Script error at line" << engine->uncaughtExceptionLineNumber()
                  << ":" << engine->uncaughtException().toString();
    }
}

3、创建可脚本化的C++类

// calculator.h
#include <QObject>

class Calculator : public QObject
{
    Q_OBJECT
    
public:
    explicit Calculator(QObject *parent = nullptr);
    
public slots:
    double add(double a, double b) { return a + b; }
    double subtract(double a, double b) { return a - b; }
    double multiply(double a, double b) { return a * b; }
    double divide(double a, double b) { return b != 0 ? a / b : 0; }
    
signals:
    void calculationPerformed(const QString &op, double result);
};

// 注册到脚本引擎
QScriptValue calculatorConstructor(QScriptContext *context, QScriptEngine *engine)
{
    return engine->newQObject(new Calculator(engine));
}

void registerCalculator(QScriptEngine *engine)
{
    QScriptValue ctor = engine->newFunction(calculatorConstructor);
    QScriptValue metaObject = engine->newQMetaObject(&Calculator::staticMetaObject, ctor);
    engine->globalObject().setProperty("Calculator", metaObject);
}

在脚本中使用自定义类

// 使用示例
QScriptEngine engine;
registerCalculator(&engine);

engine.evaluate(R"(
    var calc = new Calculator;
    print(calc.add(5, 3)); // 输出8
    
    calc.calculationPerformed.connect(function(op, result) {
        print("Operation " + op + " produced " + result);
    });
    
    calc.multiply(4, 5); // 会触发信号
)");

4、脚本扩展插件系统

// scriptextension.h
#include <QObject>
#include <QScriptable>
#include <QScriptValue>
#include <QScriptEngine>

class ScriptExtension : public QObject, protected QScriptable
{
    Q_OBJECT
    
public:
    ScriptExtension(QObject *parent = nullptr);
    
public slots:
    QScriptValue systemInfo();
    QScriptValue fileExists(const QString &path);
    void delay(int milliseconds);
};

// 注册函数
void registerExtensions(QScriptEngine *engine)
{
    QScriptValue extensionObject = engine->newQObject(new ScriptExtension(engine));
    engine->globalObject().setProperty("ext", extensionObject);
}

// 脚本中使用
engine.evaluate(R"(
    print(ext.systemInfo().os);
    if (ext.fileExists('/path/to/file')) {
        print('File exists');
    }
    ext.delay(1000); // 延迟1秒
)");

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小灰灰搞电子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值