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模块内部进行大的调整。
  3. QScriptEngine---使用V8引擎:V8对外提供的API稳定可嵌入到程序中;但是V8与JavaScriptCore内部细节不同,QtScript API的某些概念无法自然映射到V8上,用V8实现相同性能的旧接口需要相当大的投入,然而QML团队无法接受这样的投入花费。
  4. 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---Javascript/Qt交互、脚本化的更多相关文章

  1. Javascript学习8 - 脚本化文档(Document对象)

    原文:Javascript学习8 - 脚本化文档(Document对象) 每个Web浏览器窗口(或帧)显示一个HTML文档,表示这个窗口的Window对象有一个document属性,它引用了一个Doc ...

  2. Javascript学习7 - 脚本化浏览器窗口

    原文:Javascript学习7 - 脚本化浏览器窗口 本节讨论了文档对象模型.客户端Javascript下Window中的各项属性,包括计时器.Location对象.Histroy对象.窗口.浏览器 ...

  3. JavaScript权威指南--脚本化文档

    知识要点 脚本化web页面内容是javascript的核心目标. 第13章和14章解释了每一个web浏览器窗口.标签也和框架由一个window对象所示.每个window对象有一个document对象, ...

  4. JavaScript权威指南--脚本化CSS

    知识要点 客户端javascript程序员对CSS感兴趣的是因为样式可以通过脚本编程.脚本化css启用了一系列有趣的视觉效果.例如:可以创建动画让文档从右侧“滑入”.创造这些效果的javascript ...

  5. JavaScript权威指南--脚本化HTTP

    知识要点 超文本传输协议(HTTP)规定web浏览器如何从web服务器获取文档和向web服务器发送表单内容,以及web服务器如何响应这些请求和提交.web浏览器会处理大量的HTTP.通常,HTTP并不 ...

  6. JavaScript 客户端JavaScript之脚本化HTTP(通过XMLHttpRequest)

    XMLHttpRequest对象的设计目的是为了处理由普通文本或XML组成的响应:但是,一个响应也可能是另外一种类型,如果用户代理(UA)支持这种内容类型的话.   大多数浏览的客户端JavaScri ...

  7. JavaScript 客户端JavaScript之 脚本化浏览器窗口

    1.计时器 客户端Javascript以全局函数setTimeOut().clearTimeOut().setInterval().clearInterval()提供这一功能.   前者是从运行的那一 ...

  8. JavaScript 客户端JavaScript之 脚本化文档

    客户端JavaScript的存在把静态HTML转变为交互式的Web应用程序,脚本化Web页面的内容正是JavaScript存在的理由.   一个文档对象模型或者说DOM就是一个API,它定义了如何访问 ...

  9. 深入理解脚本化CSS系列第六篇——脚本化伪元素的6种方法

    × 目录 [1]动态样式 [2]CSS类[3]setAttribute()[4]CSSRule对象添加[5]空样式覆盖[6]CSSRule对象删除 前面的话 我们可以通过计算样式来读取伪元素的样式信息 ...

随机推荐

  1. 《用Java写一个通用的服务器程序》03 处理新socket

    在讲监听器时说过处理的新的socket要尽快返回,监听器调用的是ClientFactory的createPhysicalConnection方法,那么就来看这个方法: public boolean c ...

  2. ES7前端异步玩法:async/await理解

    在最新的ES7(ES2017)中提出的前端异步特性:async.await. 什么是async.await? async顾名思义是"异步"的意思,async用于声明一个函数是异步的 ...

  3. 免费人脸识别APi

    今天对应一些免费的人脸识别的api 做了一下简单的对比,觉得百度开发出来的人脸识别接口还是最符合的我的要求,简单易用,容易上手. 据说百度的一些门禁也使用上了人脸识别的功能了,功能很强大,而且能识别出 ...

  4. php数据分页显示基础

    一:分页原理: 所谓分页显示,也就是将数据库中的结果集认为的分成一段一段的来显示,需要两个初始的参数: 每页多少条记录 ($PageSize)? 当前是第几页($CurrentPageID)? 还有其 ...

  5. Django contrib Comments 评论模块详解

    老版本的Django中自带一个评论框架.但是从1.6版本后,该框架独立出去了,也就是本文的评论插件. 这个插件可给models附加评论,因此常被用于为博客文章.图片.书籍章节或其它任何东西添加评论. ...

  6. Android7.0 PowerManagerService 之亮灭屏(一)

    本篇从按下power按键后,按键事件从InputManagerService 传到PhoneWindowManager.java开始分析power 按键做屏幕亮灭过程的分析,关于power 按键的其他 ...

  7. C++反汇编第六讲,认识C++中的Try catch语法,以及在反汇编中还原

    C++反汇编第六讲,认识C++中的Try catch语法,以及在反汇编中还原 我们以前讲SEH异常处理的时候已经说过了,C++中的Try catch语法只不过是对SEH做了一个封装. 如果不懂SEH异 ...

  8. python之 json里字典中的value去重

    import simplejson as json with open(r'C:\Users\Desktop\test.txt', 'r' ) as f1, open(r'C:\Users\Deskt ...

  9. eclipse中Cannot change version of project facet Dynamic Web Module to 2.5.

    Cannot change version of project facet Dynamic Web Module to 2.5.这个错误可能很多人都碰到过,这里网上查了一些资料,解决的问题.所以这里 ...

  10. DDD峰会归来话DDD

    一场大戏落幕,首届DDD中国峰会如大会主题色一般的红.或许在12月9日这一天,全中国的DDD粉丝大约有一半都汇聚在了国家会议中心.听起来是幸,其实是不幸,因为DDD在中国的人群基数实在是太少了. 因为 ...