一. 介绍

在浏览器扩展或者WebApp的项目经常用的脚本语言javascript有很多局限性,比如,javascript语言不能够夸窗口访问js对象,不能直接读写磁盘文件(这个也正是发明人设计的安全机制吧,要不然,谁还敢用浏览器啊,几行代码就可以把你偷窥的一览无余),我们可能在我们的程序中需要扩展这个功能。

那么,我们怎么解决这些问题呢?或许你可以参考一下下面的设计架构。

UI利用Html + CSS + JavaScript编写,核心业务层利用C++编写,C++和JavaScript对象主要通过WebKit通信。让相应的语言做它们擅长的事情,这就是这种架构的核心。

WebKit又是什么呢?简单言之就是一种浏览器内核引擎,Safari、Chrome、FireFox等浏览器都采用WebKit引擎作为内核,由此可见WebKit的地位了,我们正是利用WebKit来做javascript的扩展的,Qt界又对WebKit做了一层封装,这样,开发者对WebKit就更加容易上手了。

更幸运的是,QtWebKit提供了一种将QObject对象扩展到Javascript运行环境的机制,这样,JavaScript代码将有权限访问QObject对象, QObject对象的所有属性也能在Javascript上下文中被访问。

那么,如何利用QtWebKit使得js和c++相互通信呢?

二. 搭建框架

首先,我们需要一个js代码能够运行的环境

我们准备一个主窗口,在主窗口内部添加WebView控件,使得程序具有执行js的能力;

MainWindow的定义

  1. #include <QMainWindow>
  2. #include <QGraphicsView>
  3. #include <QGraphicsWebView>
  4. #include <QGraphicsScene>
  5. #include <QEvent>
  6. #include "External.h"
  7. class MainWindow : public QGraphicsView
  8. {
  9. Q_OBJECT
  10. public:
  11. MainWindow(QWidget *parent = 0);
  12. virtual ~MainWindow();
  13. private:
  14. QGraphicsWebView*   m_pWebView;
  15. QGraphicsScene*     m_pScene;
  16. };
  17. #endif // MAINWINDOW_H

MainWindow的实现

  1. #include "mainwindow.h"
  2. #include <QWebFrame>
  3. #include <QLayout>
  4. MainWindow::MainWindow(QWidget *parent)
  5. : QGraphicsView(parent)
  6. {
  7. this->resize( QSize( 800, 600) );
  8. m_pScene = new QGraphicsScene(this);
  9. if(NULL != m_pScene)
  10. {
  11. m_pWebView = new QGraphicsWebView;
  12. if( NULL != m_pWebView)
  13. {
  14. //enabled javascript
  15. QWebSettings *settings = m_pWebView->page()->settings();
  16. settings->setAttribute(QWebSettings::JavascriptEnabled,true);
  17. settings->setAttribute(QWebSettings::JavascriptCanAccessClipboard,true);
  18. settings->setAttribute(QWebSettings::DeveloperExtrasEnabled,true);
  19. settings->setAttribute(QWebSettings::LocalContentCanAccessRemoteUrls, true);
  20. settings->setAttribute(QWebSettings::LocalContentCanAccessFileUrls, true);
  21. settings->setAttribute(QWebSettings::JavascriptCanCloseWindows, true);
  22. settings->setAttribute(QWebSettings::AutoLoadImages,true);
  23. m_pScene->addItem( m_pWebView );
  24. this->setScene( m_pScene );
  25. }
  26. }
  27. }
  28. MainWindow::~MainWindow()
  29. {
  30. }

QWebSettings类是用于配置Web运行环境,我们首先配置了JavaScriptEnable=true,使得这个web环境能够运行js代码,其他的选项不是必要的。

 

三. 添加QObject到js上下文

利用QWebFrame提供的方法我们可以很轻松的完成添加对象:

 void addToJavaScriptWindowObject(const QString &name, QObject *object, ValueOwnership ownership = QtOwnership);

注解:

addToJavaScriptWindowObject这个方法可以使c++对象object在js的上下中以name为名字而出现,object对象将被当作这个QWebFrame的一个子对象,这样,我们就可以把这个对象当作js的一个对象来使用了;

object对象的属性和槽都作为js方法在js上下文中展开,因此,js拿到这个对象就可以很随意的访问这个对象的属性和方法;

当这个页面析构后,这个对象在js上下文中也将消失,监听到信号javaScriptWindowObjectCleared() 后来重新调用下addToJavaScriptWindowObject即可。

我们先写一个测试页面index.html,主要功能就是测试Js是否能够访问external对象;

  1. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  2. <html xmlns="http://www.w3.org/1999/xhtml">
  3. <head>
  4. <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  5. <title>QWebKitDemo</title>
  6. <script type="text/javascript">
  7. window.onload = function(){
  8. var btnTest = document.getElementById("testCObj");
  9. btnTest.onclick = function() {
  10. alert(external);
  11. }
  12. }
  13. </script>
  14. </head>
  15. <body>
  16. <input type="button" id="testCObj" value="测试C++对象">
  17. </body>
  18. </html>

我们先来定义一个继承自QObject的类External,其中,继承自QObject很关键,否则我们无法完成我们的功能;

External类的定义,这里我定义了一个带属性、信号、槽、带Q_INVOKABLE修饰的方法,这将在其他小节会用到

  1. #ifndef EXTERNAL_H
  2. #define EXTERNAL_H
  3. #include <QObject>
  4. class External : public QObject
  5. {
  6. Q_OBJECT
  7. Q_PROPERTY(QString msg READ getMsg WRITE setMsg) // 声明静态属性msg
  8. public:
  9. explicit External(QObject *parent = 0);
  10. signals:
  11. void mouseClicked();
  12. public slots:
  13. void TestPassObject(QObject*);
  14. public:
  15. QString getMsg() const { return msg; }
  16. void setMsg( const QString& strMsg ) { msg = strMsg; }
  17. // 提供给Javascript方法,需要用Q_INVOKABLE修饰
  18. Q_INVOKABLE int VerifyUserAccount(const QString& userName, const QString& userPwd);
  19. Q_INVOKABLE QString GetPropMsg();
  20. Q_INVOKABLE void TestPassObjectToNative(QObject*);
  21. QString msg;
  22. };
  23. #endif // EXTERNAL_H

我们为MainWindow类添加成员External对象指针

  1. External* m_pExternal;

在MainWindow的构造方法中对m_pExternal实例化,并为这个页面的javaScriptWindowObjectCleared信号关了槽函数AddJavascriptWindowObject

这样,我们的MainWindow.h和MainWindow.cpp就成这样子了:

MainWindow.h

  1. #include <QMainWindow>
  2. #include <QGraphicsView>
  3. #include <QGraphicsWebView>
  4. #include <QGraphicsScene>
  5. #include <QEvent>
  6. #include "External.h"
  7. class MainWindow : public QGraphicsView
  8. {
  9. Q_OBJECT
  10. public:
  11. MainWindow(QWidget *parent = 0);
  12. virtual ~MainWindow();
  13. public slots:
  14. void AddJavascriptWindowObject();
  15. private:
  16. QGraphicsWebView*   m_pWebView;
  17. QGraphicsScene*     m_pScene;
  18. External*           m_pExternal;
  19. };
  20. #endif // MAINWINDOW_H

MainWindow.cpp

  1. #include "mainwindow.h"
  2. #include <QWebFrame>
  3. #include <QLayout>
  4. MainWindow::MainWindow(QWidget *parent)
  5. : QGraphicsView(parent)
  6. {
  7. this->resize( QSize( 800, 600) );
  8. m_pExternal = new External();
  9. m_pScene = new QGraphicsScene(this);
  10. if(NULL != m_pScene)
  11. {
  12. m_pWebView = new QGraphicsWebView;
  13. if( NULL != m_pWebView)
  14. {
  15. //enabled javascript
  16. QWebSettings *settings = m_pWebView->page()->settings();
  17. settings->setAttribute(QWebSettings::JavascriptEnabled,true);
  18. settings->setAttribute(QWebSettings::JavascriptCanAccessClipboard,true);
  19. settings->setAttribute(QWebSettings::DeveloperExtrasEnabled,true);
  20. settings->setAttribute(QWebSettings::LocalContentCanAccessRemoteUrls, true);
  21. settings->setAttribute(QWebSettings::LocalContentCanAccessFileUrls, true);
  22. settings->setAttribute(QWebSettings::JavascriptCanCloseWindows, true);
  23. settings->setAttribute(QWebSettings::AutoLoadImages,true);
  24. m_pScene->addItem( m_pWebView );
  25. this->setScene( m_pScene );
  26. connect(m_pWebView->page()->mainFrame(), SIGNAL(javaScriptWindowObjectCleared()),
  27. this, SLOT(AddJavascriptWindowObject()));
  28. m_pWebView->setUrl(QUrl("file:///Users/cblogs_code/QWebKitDemo/index.html"));
  29. }
  30. }
  31. }
  32. MainWindow::~MainWindow()
  33. {
  34. }
  35. void MainWindow::AddJavascriptWindowObject()
  36. {
  37. m_pWebView->page()->mainFrame()->addToJavaScriptWindowObject("external", m_pExternal);
  38. }

m_pWebView->setUrl(QUrl("file:///Users/cblogs_code/QWebKitDemo/index.html"));

这一行是加载index.html,请尝试的同学自行修改成自己的index.html的绝对路径。

当网页加载时,会自动触发javaScriptWindowObjectCleared信号,继而我们的槽函数AddJavascriptWindowObject会被执行,上面关于AddJavaScriptWindowObject解释说:当这个页面析构后,这个对象在js上下文中也将消失,监听到信号javaScriptWindowObjectCleared() 后来重新调用下addToJavaScriptWindowObject即可,因此,想让external一直保持有效,这样做就可以了。

然后,运行一下程序吧,点击按钮,你会看到如下结果:

三. 调用QObject的方法

js要调用已经扩展的对象external的方法,需要一下约束:
 
1. 方法前需要Q_INVOKABLE修饰;
2. 方法返回值和参数必须是Qt内置类型或者c++内置类型,即使用typedef的也不行;
3. 方法返回值和参数可以是QObject*, 但参数不能传递js对象,传js对象用QVariant代替。
 
js中调用external的方法
 
  1. var ret = external.VerifyUserAccount("user01", "123456");
  2. if (ret == 1) {
  3. alert("验证通过");
  4. }else {
  5. alert("用户名或者密码错误");
  6. }
操作QObject对象的属性

这节我们来尝试访问一下External对象的msg属性,并且修改TA,看看在Js层和C++层是否被修改。

还是先看看测试页面吧:

 
  1. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  2. <html xmlns="http://www.w3.org/1999/xhtml">
  3. <head>
  4. <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  5. <title>QWebKitDemo</title>
  6. <script type="text/javascript">
  7. window.onload = function(){
  8. var btnTest = document.getElementById("testCObj");
  9. btnTest.onclick = function() {
  10. alert(external);
  11. }
  12. var btnVisit = document.getElementById("visitProp");
  13. btnVisit.onclick = function() {
  14. var output = "js层面 \tmsg: "+external.msg + "\n c++层面 \tmsg: "+external.GetPropMsg();
  15. alert(output);
  16. }
  17. var btnSetProp = document.getElementById("setProp");
  18. btnSetProp.onclick = function() {
  19. var tempValue = document.getElementById("msgProValue");
  20. external.msg = tempValue.value;
  21. }
  22. }
  23. </script>
  24. </head>
  25. <body>
  26. <input type="button" id="testCObj" value="测试C++对象">
  27. <p>测试属性msg</p>
  28. <input type="button" id="visitProp" value="visit">
  29. <input type="text" id="msgProValue" value="修改后的msg">
  30. <input type="button" id="setProp" value="修改">
  31. </body>
  32. </html>

运行程序,我们先点击visit按钮,可以看到,显示的msg属性都是空值,然后再文本框中输入字符,点击修改按钮,再点击visit按钮,试试看,弹出的msg是否有变化。

四. 绑定QObject对象的信号/槽

addJavaScriptWindowObject方法可以将对象的属性、信号、槽统统映射到Js上下文中。
绑定信号的方式如下

  1. external.mouseClicked.connect(mouseClickedSlot);
  2. function mouseClickedSlot() {
  3. logger("mouse clicked");
  4. }

mouseClicked通过connect方法连接到js的方法(槽),当mouseClicked被触发后,mouseClickedSlot将被执行

五. 为iframe添加QObject

当iframe被创建时,mainFrame下的webpage的信号frameCreated会被触发;
frameCreated的原型是:
  1. void frameCreated(QWebFrame*);

因此,我们可以为frameCreated关联一个槽,在这个槽中,为新创建的QWebFrame添加QObject;

代码如下:
1. 在MainWindow的构造函数添加
  1. connect(this->m_pWebView->page(), SIGNAL(frameCreated(QWebFrame*)),
  2. this, SLOT(ChildFrameCreated(QWebFrame*)));
 

2. 为MainWindow类添加槽函数

  1. public slots:
  2. void AddJavascriptWindowObject();
  3. void ChildFrameCreated(QWebFrame *frame);
3. 为MainWindow类添加如下实现
  1. void MainWindow::AddJavascriptWindowObject()
  2. {
  3. m_pWebView->page()->mainFrame()->addToJavaScriptWindowObject("external", m_pExternal);
  4. }
  5. void MainWindow::AddJavascriptWindowObject(QWebFrame *pFrame)
  6. {
  7. qDebug("AddJavascriptWindowObject");
  8. //add external object to main web frame
  9. pFrame->addToJavaScriptWindowObject("external", m_pExternal);
  10. }
  11. void MainWindow::ChildFrameCreated(QWebFrame *pFrame)
  12. {
  13. qDebug("ChildFrameCreated");
  14. if(pFrame == NULL)
  15. {
  16. qDebug("Child frame created, but it was NULL!");
  17. }
  18. else
  19. {
  20. AddJavascriptWindowObject(pFrame);
  21. }
  22. }

六. 总结

到目前为止,一个Hybrid模式的应用demo已经完成了,我们来分析一下这种模式的优劣;

1、优势:

  • 高效率开发UI丰富的应用;
  • 底层框架可复用;
  • 可实现跨平台;
  • 其他好处……
 

2、劣势:

  • 性能
  • 还是性能
  • 其他劣势……
 
随着硬件的强大、html5的发展,不论是在pc端还是移动端, 这种框架会得到普遍的认可,i think so。

 
转自http://blog.csdn.net/longsir_area/article/details/42965565

[Qt] 利用QtWebKit完成JavaScript访问C++对象的更多相关文章

  1. JavaScript 访问对象属性和方法及区别

    这篇文章主要介绍了浅析JavaScript访问对象属性和方法及区别的相关资料,仅供参考 属性是一个变量,用来表示一个对象的特征,如颜色.大小.重量等:方法是一个函数,用来表示对象的操作,如奔跑.呼吸. ...

  2. 全面理解Javascript中Function对象的属性和方法

    http://www.cnblogs.com/liontone/p/3970420.html 函数是 JavaScript 中的基本数据类型,在函数这个对象上定义了一些属性和方法,下面我们逐一来介绍这 ...

  3. JavaScript访问修改css样式表

    1.访问元素中style属性的css样式 可以根据属性的ID或name标签利用dom操作直接访问到内部的css样式,直接使用style对象访问 <div id="myid" ...

  4. Qt Quick 组件和动态创建的对象具体的解释

    在<Qt Quick 事件处理之信号与槽>一文中介绍自己定义信号时,举了一个简单的样例.定义了一个颜色选择组件,当用户在组建内点击鼠标时,该组件会发出一个携带颜色值的信号,当时我使用 Co ...

  5. 第一百一十三节,JavaScript文档对象,DOM基础

    JavaScript文档对象,DOM基础 学习要点: 1.DOM介绍 2.查找元素 3.DOM节点 4.节点操作 DOM(Document Object Model)即文档对象模型,针对HTML和XM ...

  6. JQuery制作网页—— 第三章 JavaScript操作DOM对象

    1. DOM:Document Object Model(文档对象模型):          DOM操作:                   ●DOM是Document Object Model的缩 ...

  7. JavaScript进阶 - 第7章 JavaScript内置对象

    第7章 JavaScript内置对象 7-1 什么是对象 JavaScript 中的所有事物都是对象,如:字符串.数值.数组.函数等,每个对象带有属性和方法. 对象的属性:反映该对象某些特定的性质的, ...

  8. javascript中的对象字面量为啥这么酷

    原文链接 : Why object literals in JavaScript are cool 原文作者 : Dmitri Pavlutin 译者 : neal1991 个人主页:http://n ...

  9. Redis的C++与JavaScript访问操作

    上篇简单介绍了Redis及其安装部署,这篇记录一下如何用C++语言和JavaScript语言访问操作Redis 1. Redis的接口访问方式(通用接口或者语言接口) 很多语言都包含Redis支持,R ...

随机推荐

  1. 关于Java Webproject中web.xml文件

    提及Java Webproject中web.xml文件无人不知,无人不识,呵呵呵:系统首页.servlet.filter.listener和设置session过期时限.张口就来,但是你见过该文件里的e ...

  2. 互联网创业原则与创业模式attilax大总结

    互联网创业原则与创业模式attilax大总结 1. 适合普通人的的创业模式1 1.1. 网络创业  兼职创业 概念创业 团队 创业  内部创业..1 2. 创业模式大总结1 2.1. 工作室创业1 2 ...

  3. 【Unity】第6章 Unity脚本开发基础

    分类:Unity.C#.VS2015 创建日期:2016-04-16 一.简介 游戏吸引人的地方在于它的可交互性.如果游戏没有交互,场景做得再美观和精致,也难以称其为游戏. 在Unity中,游戏交互通 ...

  4. 【Unity】2.7 检视器(Inspector)

    分类:Unity.C#.VS2015 创建日期:2016-03-31 一.简介 Unity中的游戏是以包含网格.脚本.声音或灯光 (Lights) 等其他图形元素的多个游戏对象 (GameObject ...

  5. 行为类模式(十一):访问者(Visitor)

    定义 表示一个作用于某对象结构中的各元素的操作.它使你可以在不改变各元素类的前提下定义作用于这些元素的新操作. UML 优点 符合单一职责原则:凡是适用访问者模式的场景中,元素类中需要封装在访问者中的 ...

  6. hot-warm-architecture-in-elasticsearch-5-x

    https://www.elastic.co/blog/hot-warm-architecture-in-elasticsearch-5-x https://www.elastic.co/blog/e ...

  7. 第二章 使用接口(Using Interfaces)-书籍翻译

    PDF预览 下载地址 http://files.cnblogs.com/DKSoft/CodingInDelphi.pdf 1.1. 解耦(Decoupling) All through this b ...

  8. jetty el表达式不支持三元运算

    在jetty跑web程序中不支持三元运算 要换一种格式写 这种代码在jsp页面用jetty跑起来是会报错的,然后调换一下顺序就可以了  或者在后面那个加个括号也可以 

  9. jetty debug 启动 jettyconfig配置文件

    jetty 代码启动 debug很简单  run----->>>debug as  代码启动配置文件 start 方法 @Test public void serverStrart( ...

  10. Windows下Python3+nose+appium自动化测试之Android篇

    [本文出自天外归云的博客园] 简介 以下用来做自动化测试的这款app叫最爱抓娃娃,以后会改名为网易抓娃娃. 下文提到的appiumier项目里会包含用来测试的apk包以及自动化测试代码. 先说一个坑 ...