Qt与javascript交互数据的实现

本文详细介绍了Qt 5中使用QJSEngine与JavaScript交互的数据传递方法,包括双向数据流动,如数组传递、对象传递,以及QJSEngine的配置、异常处理和QObject集成。重点讲解了QJSEngine与全局变量、脚本化编程的实践案例。
摘要由CSDN通过智能技术生成


如今QtScript是Qt纯粹兼容的一部分,缺乏QML集成以及它已经在一段时间内不会更新/维护,而是有利于新引擎。
所以在qt5中将使用QJSEngine来代替 QtScript

Qt与javascript交互数据的实现

原文链接: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

一、数据从QT流向JS

1、QT调用JS的函数,JS通过形参获得QT的值

2、JS调用QT的函数,QT函数的返回值进入JS

二、数据从JS流向QT

1、JS调用QT的函数,QT通过形参获得JS的值

2、QT调用JS的函数,JS函数的返回值进入QT

1、QT向JS传递数组

基本类型可以直接传递,例如 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个元素

}

2、JS向QT传递数组

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;

}

3、JS向QT传递任意类型数据

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传来的任意数据了。


Javascript/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模块的实现历史及原因:

  1. QScriptEngine—使用自建的js引擎:功能落后、运行非常慢。

  2. QScriptEngine—使用JavaScriptCore引擎(WebKit的主引擎):

由此封装提供的JS API暴露了实现细节。
由于设计使用方式的不同一些原有函数无法实现(例如QScriptContext)。
JavaScriptCore变化太大,没有一个稳定的API来供QtScript实现想要的功能,每一次引擎的变化都需要QtScript模块内部进行大的调整。

  1. QScriptEngine—使用V8引擎:

V8对外提供的API稳定,可嵌入到程序中;但是V8与JavaScriptCore内部细节不同,QtScript API的某些概念无法自然映射到V8上,用V8实现相同性能的旧接口需要相当大的投入,然而QML团队无法接受这样的投入花费。

  • QJSEngine-------使用V8引擎。

1 QScriptEngine VS QJSEngine

从两个主要的引擎类上来说,相比QScriptEngine,虽然QJSEngine出来的迟,但是核心的功能(加粗)也是支持的,仅在其他一些小功能上有所欠缺(未加粗):

  • 执行脚本字符串。
  • 引擎全局变量配置。
  • 异常处理。
  • Js对象创建
  • Qt类与Js的交互集成。
  • Js扩展。
  • 自定义C++类(非Qt内建)。
  • C++函数与Js的交互集成。
  • Long-running脚本优化处理。
  • 调试跟踪。

但是毕竟对JavaScriptCore引擎的封装比较成熟,从QScriptEngine衍生出的技术支持肯定是比较丰富,使用也较为方便。例如QtScript模块同时包含QScriptClassPropertyIterator类来提供java风格的属性遍历功能、QScriptContext类来提供上下文信息,等等。但是随着Qt新版本的发布,QJsEngine肯定是越来越成熟的。需要注意的是,这两个应该都不能与Qt的Web模块交互使用(亲测QJSEngine与QWebEngineView交互无效),毕竟都分成了两个不同的模块。

2 QJSEngine介绍

这里我们简单学习QJSEngine,一如既往,我们通过一个小例子来学习当前js引擎提供的主要功能, 实际上使用非常简单。

2.1 执行脚本

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());
}

在这里插入图片描述

2.2 配置引擎的全局变量(C++/Js交互)

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());
}

在这里插入图片描述

2.3 Qt/Js交互(脚本化)

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()

在这里插入图片描述

3. 运行结果

在这里插入图片描述


qt 之 QJSEngine 相关


原文链接: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对象的属性。以下属性可用:

  • name
  • message
  • fileName
  • lineNumber
  • stack
  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 数组对象。

QObject 集成

使用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 属性

不支持动态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

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());
  }

示例


jsrunfunc.h

#ifndef JSRUNFUNC_H
#define JSRUNFUNC_H

#include <QObject>
#include <QJsonObject>
#include <QJsonDocument>
#include <QDebug>

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

jsrunfunc.cpp

#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;
}

widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QJSEngine>
#include <QString>
#include <QFile>
#include <QFileDialog>
#include <QTextStream>
#include <QDebug>
#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

widget.cpp

#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);
}

在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值