如今QtScript是Qt纯粹兼容的一部分,缺乏QML集成以及它已经在一段时间内不会更新/维护,而是有利于新引擎。
所以在qt5中将使用QJSEngine来代替 QtScript
原文链接:https://blog.csdn.net/weixin_39614675/article/details/117841450?utm_medium=distribute.pc_relevant.none-task-blog-2defaultbaidujs_title~default-1.pc_relevant_antiscanv2&spm=1001.2101.3001.4242.2&utm_relevant_index=4
1、QT调用JS的函数,JS通过形参获得QT的值
2、JS调用QT的函数,QT函数的返回值进入JS
1、JS调用QT的函数,QT通过形参获得JS的值
2、QT调用JS的函数,JS函数的返回值进入QT
基本类型可以直接传递,例如 int bool string double等
qt向js传递数组,需要把数组转成QJsonArray,再把QJsonArray转成QString, 这样js就会接收到一个基本类型string,而这个string在js中直接就是一个标准的js数组。
QT代码示例:调用js函数,并给这个js函数传递一个数组作为参数
//方法1:构造QJsonArray,然后转成QString
QJsonArray ja;
ja << 3 << 4 << 5;
QString jpar = QString(QJsonDocument(ja).toJson());
QString cmd = QString(“qtPara(%0)”).arg(QString(QJsonDocument(ja).toJson()));
//方法2:直接把数组写成string
// QString cmd = QString(“qtPara([13,14,15])”);
//运行js函数
webView->page()->runJavaScript(cmd);
上面代码调用的javascript函数为:
function qtPara(numList)
{
alert("js alert: " + numList);//显示qt传来的整个数组
alert("js alert[0]: " + numList[0]);//显示qt传来数组第0个元素
}
JS如果向QT传递数组,那么QT就要把这个值转成 QJsonArray
JS还可以向QT传递任意JS对象,那么QT要转成QJsonObject
QT端示例代码:
QString cmd = QString(“jsString()”);
webView->page()->runJavaScript(cmd, [](const QVariant &v)
{
//情形1:当js返回数字时
qDebug() << "qt call js = " << v.toDouble();
//情形2:当js返回string值时
qDebug() << "qt call js = " << v.toString();
//情形3:当js返回 js数组时
QJsonArray ja = v.toJsonArray();
qDebug() << "j[0] = " << ja.at(0).toDouble();
//情形4:当js返回 js 对象时
QJsonObject jo = v.toJsonObject();
qDebug() << jo;
});
上述代码所调用的JS端的函数:
// var jArr = [120.123456789, 22, 33, 44];//js array
// var jObj = {“num”:[120.123456789, 22, 33, 44], “name”:“Tom”};//json
var jNum = 120.1234567;
function jsString()
{
alert(“jsString”);
//return jNum ;
//return jArr;
//return jObj;
}
QT端用QVariant类型来接收,然后qDebug这个值,就能看到这个JS值是如何被封装为QVariant的,然后我们就能向QT传递任意类型数据。
例如,JS向QT返回一个这样的值,这是一个JS对象数组,每个元素都是一个Point对象,且这个Point对象有lng和lat属性值。
path = [new Point(116.387112,39.920977), new Point(116.387112,39.920977)];
QT接收到以后qDebug它,如下:
QVariant(QVariantList,
(QVariant(QVariantMap, QMap((“lat”, QVariant(double, 39.921))(“lng”, QVariant(double, 116.387)))),
QVariant(QVariantMap, QMap((“lat”, QVariant(double, 39.921))(“lng”, QVariant(double, 116.387))))))
我们发现,
① QT把JS的对象数组,封装成了QVariantList, 也即QList,
② 这个list的每个成员又都被QT封装成了QVariantMap, 也即QMap
③ 每个map中的key都是QString,value都是QVariant,且这个QVariant是double。
经过上述分析步骤,我们就可轻易地解析出JS向QT传来的任意数据了。
https://blog.csdn.net/weixin_30674525/article/details/95669011?spm=1001.2101.3001.6650.1&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1.pc_relevant_antiscanv2&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1.pc_relevant_antiscanv2&utm_relevant_index=2
Qt提供了对Javascript的良好支持, 如果查阅过文档你就知道Qt有两个不同的Js封装引擎:
- QScriptEngine
- QJSEngine
QScriptEngine出现的比较早(自Qt4.3始),基于WebKit的JavaScriptCore引擎,提供的api相对来说比较丰富,但是已经被官方标注为deprecated;QJSEngine则是从Qt5.0开始提供,基于谷歌的V8引擎,是官方建议使用的版本。至于为什么QScriptEngine会被Qt废弃,各种原因就比较复杂了,有兴趣的朋友可以看这个链接,我这里简要概括讲一下Qt js模块的实现历史及原因:
QScriptEngine—使用自建的js引擎:功能落后、运行非常慢。
QScriptEngine—使用JavaScriptCore引擎(WebKit的主引擎):
由此封装提供的JS API暴露了实现细节。
由于设计使用方式的不同一些原有函数无法实现(例如QScriptContext)。
JavaScriptCore变化太大,没有一个稳定的API来供QtScript实现想要的功能,每一次引擎的变化都需要QtScript模块内部进行大的调整。
V8对外提供的API稳定,可嵌入到程序中;但是V8与JavaScriptCore内部细节不同,QtScript API的某些概念无法自然映射到V8上,用V8实现相同性能的旧接口需要相当大的投入,然而QML团队无法接受这样的投入花费。
从两个主要的引擎类上来说,相比QScriptEngine,虽然QJSEngine出来的迟,但是核心的功能(加粗)也是支持的,仅在其他一些小功能上有所欠缺(未加粗):
但是毕竟对JavaScriptCore引擎的封装比较成熟,从QScriptEngine衍生出的技术支持肯定是比较丰富,使用也较为方便。例如QtScript模块同时包含QScriptClassPropertyIterator类来提供java风格的属性遍历功能、QScriptContext类来提供上下文信息,等等。但是随着Qt新版本的发布,QJsEngine肯定是越来越成熟的。需要注意的是,这两个应该都不能与Qt的Web模块交互使用(亲测QJSEngine与QWebEngineView交互无效),毕竟都分成了两个不同的模块。
这里我们简单学习QJSEngine,一如既往,我们通过一个小例子来学习当前js引擎提供的主要功能, 实际上使用非常简单。
QJSValue QJSEngine::evaluate(const QString &program, const QString &fileName = QString(), int lineNumber = 1)
我们只需要把包含js代码的字符串传给QJSEngine::evaluate()这个函数,就可以直接执行该js代码。该函数的后两个参数是可选的文件名和行号,会在js出错的时候包含在出错信息里。示例程序中当用户点击执行按钮,我们直接就执行用户输入的js代码:
void MainWindow::on_buttonEvaluateJs_clicked(bool)
{
ui->lineEditJsResult->setText(
m_jsEngine.evaluate(ui->lineEditEvaluateJs->text()).toString());
ui->lineEditJsResult->setEnabled(ui->buttonEvaluateJs->isEnabled());
}
QJSValue QJSEngine::globalObject() const
QJSValue QJSEngine::newObject()
void QJSValue::setProperty(const QString &name, const QJSValue &value)
通过globalObject()函数我们获得Js引擎的全局变量,这个变量是一个QJSValue,是Qt对Js数据类型的一个封装,基本上支持所有Js对象的操作。例如,我们可以判断两个QJSValue是否相等、是否严格相等、设置属性、设置原型等。全局对象就是一个可以在Js代码中直接使用的Js变量,通常我们做的就是在C++代码里设置全局变量的属性,然后在Js中直接使用。
newObject()函数用来新建一个Js对象,示例中我们在新建的Js对象上分别设置3个属性(setProperty())为用户输入的左操作数、右操作数和运算符,然后把这个对象设置为全局对象的一个属性,接着我们在Js代码中直接调用这3个属性来进行计算:
void MainWindow::on_buttonEvaluatePropertyCalculateResult_clicked(bool)
{
auto jsObject = m_jsEngine.newObject();
jsObject.setProperty("leftOperand", ui->lineEditPropertyLeft->text());
jsObject.setProperty("rightOperand", ui->lineEditPropertyRight->text());
m_jsEngine.globalObject().setProperty("cppObject", jsObject);
ui->lineEditEvaluatePropertyResult->setText(m_jsEngine.evaluate(
"cppObject.leftOperand" +
ui->lineEditPropertyOperator->text() +
"cppObject.rightOperand").toString());
ui->lineEditEvaluatePropertyResult->setEnabled(
ui->buttonEvaluatePropertyCalculateResult->isEnabled());
}
QJSValue newQObject(QObject *object)
Signals and slots, properties and children of object are available as properties of the created QJSValue.
通过newQObject()这个函数,我们可以将Qt类封装成Js对象,集成到Js引擎中。Qt类的信号槽、属性和子对象可以在Js中通过属性来使用,Qt提供强大的本地功能支持,Js提供灵活的使用方式,想想就很激动。我们可以借此在Js中操控导出的Qt对象、更改界面外观、实现程序功能的脚本化。
示例中我们导出街面上的一个QPushButton,把它设置为Js引擎全局对象的一个属性:
m_jsEngine.globalObject().setProperty("cppButton", m_jsEngine.newQObject(ui->buttonChangeInJs));
当用户点击这个按钮的时候,我们读取本地的Js文件到QString中并执行这段代码,该Js代码会调用setStyleSheet()函数(注意这是一个slot)来更改这个按钮的外观样式:
void MainWindow::on_buttonChangeInJs_clicked(bool)
{
QFile jsFile(":/js/demo.js");
if (jsFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
auto jsStr = QString::fromStdString(jsFile.readAll().toStdString());
auto jsResult = m_jsEngine.evaluate(jsStr);
if (jsResult.isError())
ui->buttonChangeInJs->setText(jsResult.toString());
}
}
function func() {
cppButton.setStyleSheet('QPushButton { background-color: qlineargradient(\
x0:0, y0:0, x1:1, y1:1, \
stop: 0.0 #111111,\
stop: 0.2 #222222,\
stop: 0.4 #444444,\
stop: 0.6 #888888,\
stop: 0.8 #aaaaaa,\
stop: 1.0 #ffffff);\
color:white;}\
QPushButton:hover { border:2px solid blue;\
padding:1ex; }\
QPushButton:pressed { background-color: qlineargradient(\
x0:0, y0:0, x1:1, y1:1, \
stop: 0.0 #ff1111,\
stop: 0.2 #22ff22,\
stop: 0.4 #4444ff,\
stop: 0.6 #88ee88,\
stop: 0.8 #aaeeaa,\
stop: 1.0 #ffffff); }')
cppButton.text = 'Changed in JS'
}
func()
原文链接:https://blog.csdn.net/u011942101/article/details/123975565
QJSEngine myEngine;
QJSValue three = myEngine.evaluate("1 + 2");
evaluate () 返回一个保存评估结果的QJSValue。QJSValue类提供了将结果转换为各种 C++ 类型的函数(例如QJSValue::toString () 和QJSValue::toNumber ())。
以下代码片段显示了如何定义脚本函数,然后使用QJSValue::call () 从 C++ 调用:
QJSValue fun = myEngine.evaluate("(function(a, b) { return a + b; })");
QJSValueList args;
args << 1 << 2;
QJSValue threeAgain = fun.call(args);
从上面的片段可以看出,脚本是以字符串的形式提供给引擎的。加载脚本的一种常见方法是读取文件的内容并将其传递给evaluate ():
QString fileName = "helloworld.qs";
QFile scriptFile(fileName);
if (!scriptFile.open(QIODevice::ReadOnly))
// handle error
QTextStream stream(&scriptFile);
QString contents = stream.readAll();
scriptFile.close();
myEngine.evaluate(contents, fileName);
这里我们将文件名作为第二个参数传递给evaluate ()。这不会以任何方式影响评估;第二个参数是存储在Error对象中用于调试目的的通用字符串。
对于更大的功能,您可能希望将代码和数据封装到模块中。模块是包含脚本代码、变量等的文件,并使用导出语句来描述其与应用程序其余部分的接口。在 import 语句的帮助下,一个模块可以引用其他模块的功能。这允许以安全的方式从较小的连接构建块构建脚本应用程序。相比之下,使用evaluate () 的方法存在一个风险,即来自一次evaluate () 调用的内部变量或函数会意外污染全局对象并影响后续评估。
以下示例提供了一个可以添加数字的模块:
export function sum(left, right)
{
return left + right
}
这个模块可以用 QJSEngine::import() 加载,如果它保存在 name 下math.mjs:
QJSvalue module = myEngine.importModule("./math.mjs");
QJSValue sumFunction = module.property("sum");
QJSValue result = sumFunction.call(args);
模块还可以使用 import 语句使用其他模块的功能:
import { sum } from "./math.mjs";
export function addTwice(left, right)
{
return sum(left, right) * 2;
}
globalObject () 函数返回与脚本引擎关联的全局对象。可以从任何脚本代码访问全局对象的属性(即它们是全局变量)。通常,在评估“用户”脚本之前,您需要通过向全局对象添加一个或多个属性来配置脚本引擎:
myEngine.globalObject().setProperty("myNumber", 123);
...
QJSValue myNumberPlusOne = myEngine.evaluate("myNumber + 1");
向脚本环境添加自定义属性是提供特定于您的应用程序的脚本 API 的标准方法之一。通常这些自定义属性是由newQObject () 或newObject () 函数创建的对象。
evaluate () 可以抛出脚本异常(例如由于语法错误)。如果是,那么evaluate () 返回抛出的值(通常是一个Error对象)。使用QJSValue::isError () 来检查异常。
有关错误的详细信息,请使用QJSValue::toString () 获取错误消息,并使用QJSValue::property () 查询Error对象的属性。以下属性可用:
QJSValue result = myEngine.evaluate(...);
if (result.isError())
qDebug()
<< "Uncaught exception at line"
<< result.property("lineNumber").toInt()
<< ":" << result.toString();
使用newObject () 创建一个 JavaScript 对象;这是脚本语句的 C++ 等效项new Object()。您可以使用QJSValue中的对象特定功能来操作脚本对象(例如QJSValue::setProperty ())。同样,使用newArray () 创建一个 JavaScript 数组对象。
使用newQObject () 包装一个QObject(或子类)指针。newQObject () 返回一个代理脚本对象;QObject的属性、子项以及信号和槽可用作代理对象的属性。不需要绑定代码,因为它是使用 Qt 元对象系统动态完成的。
QPushButton *button = new QPushButton;
QJSValue scriptButton = myEngine.newQObject(button);
myEngine.globalObject().setProperty("button", scriptButton);
myEngine.evaluate("button.checkable = true");
qDebug() << scriptButton.property("checkable").toBool();
scriptButton.property("show").call(); // call the show() slot
使用newQMetaObject () 包装一个QMetaObject;这为您提供了基于QObject的类的“脚本表示” 。newQMetaObject () 返回一个代理脚本对象;类的枚举值可用作代理对象的属性。
可以从脚本调用暴露给元对象系统的构造函数(使用Q_INVOKABLE )以使用 JavaScriptOwnership 创建一个新的QObject实例。例如,给定以下类定义:
class MyObject : public QObject
{
Q_OBJECT
public:
Q_INVOKABLE MyObject() {}
};
staticMetaObjectfor 类可以像这样暴露给 JavaScript :
QJSValue jsMetaObject = engine.newQMetaObject(&MyObject::staticMetaObject);
engine.globalObject().setProperty("MyObject", jsMetaObject);
然后可以在 JavaScript 中创建类的实例:
engine.evaluate("var myObject = new MyObject()");
注意:目前只支持使用Q_OBJECT宏的类;不可能将Q_GADGET staticMetaObject类暴露给JavaScript。
不支持动态QObject属性。例如,以下代码将不起作用:
QJSEngine engine;
QObject *myQObject = new QObject();
myQObject->setProperty("dynamicProperty", 3);
QJSValue myScriptQObject = engine.newQObject(myQObject);
engine.globalObject().setProperty("myObject", myScriptQObject);
qDebug() << engine.evaluate("myObject.dynamicProperty").toInt();
QJSEngine 提供了一个兼容的 ECMAScript 实现。默认情况下,日志等熟悉的实用程序不可用,但可以通过installExtensions () 函数安装它们。
QJSValue支持ECMA-262标准中定义的类型:基本类型,未定义、空、布尔、数字和字符串;以及对象和数组类型。此外,还为QVariant和QObject等Qt/C++类型提供了内置支持。
对于基于对象的类型(包括Date和RegExp),使用QJSEngine中的newT()函数(例如QJSEngine::newObject())创建所需类型的QJSValue。对于基元类型,使用QJSValue构造函数重载之一。对于其他类型,例如注册的小工具类型,如QPoint,可以使用QJSEngine::toScriptValue。
名为isT()的方法(例如isBool()、isUndefined())可用于测试某个值是否属于特定类型。名为toT()的方法(例如toBool()、toString())可用于将QJSValue转换为另一种类型。还可以使用通用的qjsvalue_cast()函数。
对象值具有零个或多个属性,这些属性本身就是QJSValue。使用setProperty()设置对象的属性,并调用property()检索属性的值。
QJSEngine myEngine;
QJSValue myObject = myEngine.newObject();
QJSValue myOtherObject = myEngine.newObject();
myObject.setProperty("myChild", myOtherObject);
myObject.setProperty("name", "John Doe");
QJSValue for 数组
要使用QJSValue创建数组,请使用QJSEngine::newArray()
// Assumes that this class was declared in QML.
QJSValue jsArray = engine->newArray(3);
要设置数组中的单个元素,请使用 setProperty(quint32 arrayIndex, const QJSValue &value) 重载。例如,要用整数填充上面的数组:
for (int i = 0; i < 3; ++i) {
jsArray.setProperty(i, QRandomGenerator::global().generate());
}
要确定数组的长度,请访问“length”属性。要访问数组元素,请使用属性(quint32 arrayIndex)重载。下面的代码将我们在上面创建的数组读回一个列表:
QVector<int> integers;
const int length = jsArray.property("length").toInt();
for (int i = 0; i < length; ++i) {
integers.append(jsArray.property(i).toInt());
}
#ifndef JSRUNFUNC_H
#define JSRUNFUNC_H
#include
#include
#include
#include
class JsRunFunc : public QObject
{
Q_OBJECT
public:
Q_INVOKABLE JsRunFunc(QObject *parent = nullptr);
~JsRunFunc();
Q_INVOKABLE int callFunc(int number1, int number2);
Q_INVOKABLE QString callFunc2();
Q_INVOKABLE QString callFunc3();
signals:
private:
QJsonObject obj;
};
#endif // JSRUNFUNC_H
#include "jsrunfunc.h"
JsRunFunc::JsRunFunc(QObject *parent) : QObject(parent)
{
qDebug() << __FUNCTION__;
}
JsRunFunc::~JsRunFunc()
{
}
int JsRunFunc::callFunc(int number1, int number2)
{
qDebug() << __FUNCTION__;
return number1 + number2;
}
QString JsRunFunc::callFunc2()
{
return "this is a string";
}
QString JsRunFunc::callFunc3()
{
obj.insert("name","njzy");
obj.insert("sex","male");
// 构建 JSON 文档
QJsonDocument document;
document.setObject(obj);
QByteArray byteArray = document.toJson(QJsonDocument::Compact);
QString strJson(byteArray);
return strJson;
}
#ifndef WIDGET_H
#define WIDGET_H
#include
#include
#include
#include
#include
#include
#include
#include "jsrunfunc.h"
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
void testJsEngine(QString program);
void initJsEngine();
private slots:
void on_pushButton_clicked();
private:
Ui::Widget *ui;
QJSEngine jsEngine;
};
#endif // WIDGET_H
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
initJsEngine();
}
Widget::~Widget()
{
delete ui;
}
void Widget::testJsEngine(QString program)
{
// QJSValue value = jsEngine.evaluate("jsRunFuncObject.callFunc(10,50)");
QJSValue value = jsEngine.evaluate(program);
qDebug()<< "CallFunc result is: "<< value.toInt();
QString str = value.toString();
ui->lineEdit3->setText(str);
QJSValue value2 = jsEngine.evaluate("jsRunFuncObject.callFunc2()");
qDebug()<< "CallFunc2 result is: "<< value2.toString();
QJSValue value3 = jsEngine.evaluate("jsRunFuncObject.callFunc3()");
qDebug()<< "CallFunc3 result is: "<< value3.toString();
}
void Widget::initJsEngine()
{
JsRunFunc* object = new JsRunFunc(this);
QJSValue jsobject = jsEngine.newQObject(object);
jsEngine.globalObject().setProperty("jsRunFuncObject", jsobject);
}
void Widget::on_pushButton_clicked()
{
QString str1 = ui->lineEdit1->text();
QString str2 = ui->lineEdit2->text();
QString strProgram = "jsRunFuncObject.callFunc(" + str1 + "," + str2 + ")";
testJsEngine(strProgram);
}