[Qt] 利用QtWebKit完成JavaScript访问C++对象

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

一. 介绍         

 在浏览器扩展或者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的定义

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

MainWindow的实现

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

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对象;

[html] view plaincopy
  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.   
  8. window.onload = function(){  
  9.   
  10.     var btnTest = document.getElementById("testCObj");  
  11.     btnTest.onclick = function() {  
  12.         alert(external);  
  13.     }  
  14. }  
  15. </script>  
  16. </head>  
  17. <body>  
  18.     <input type="button" id="testCObj" value="测试C++对象">  
  19. </body>  
  20. </html>  

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

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

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


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

[cpp] view plaincopy
  1. External* m_pExternal;  

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

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


MainWindow.h

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

MainWindow.cpp

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

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的方法

[html] view plaincopy
  1. var ret = external.VerifyUserAccount("user01", "123456");  
  2. if (ret == 1) {  
  3.      alert("验证通过");  
  4.  }else {  
  5.        alert("用户名或者密码错误");  
  6. }  


操作QObject对象的属性

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

还是先看看测试页面吧:

[html] view plaincopy
  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.   
  8. window.onload = function(){  
  9.   
  10.     var btnTest = document.getElementById("testCObj");  
  11.     btnTest.onclick = function() {  
  12.         alert(external);  
  13.     }  
  14.   
  15.     var btnVisit = document.getElementById("visitProp");  
  16.     btnVisit.onclick = function() {        
  17.         var output = "js层面 \tmsg: "+external.msg + "\n c++层面 \tmsg: "+external.GetPropMsg();  
  18.         alert(output);  
  19.     }  
  20.   
  21.     var btnSetProp = document.getElementById("setProp");  
  22.     btnSetProp.onclick = function() {  
  23.         var tempValue = document.getElementById("msgProValue");  
  24.         external.msg = tempValue.value;  
  25.     }  
  26. }  
  27. </script>  
  28. </head>  
  29. <body>  
  30.     <input type="button" id="testCObj" value="测试C++对象">  
  31.     <p>测试属性msg</p>  
  32.     <input type="button" id="visitProp" value="visit">  
  33.     <input type="text" id="msgProValue" value="修改后的msg">  
  34.     <input type="button" id="setProp" value="修改">  
  35. </body>  
  36. </html>  

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


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

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

[html] view plaincopy
  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的原型是:
[cpp] view plaincopy
  1. void frameCreated(QWebFrame*);  
因此,我们可以为frameCreated关联一个槽,在这个槽中,为新创建的QWebFrame添加QObject;
代码如下:
1. 在MainWindow的构造函数添加
[cpp] view plaincopy
  1. connect(this->m_pWebView->page(), SIGNAL(frameCreated(QWebFrame*)),  
  2.             this, SLOT(ChildFrameCreated(QWebFrame*)));  

2. 为MainWindow类添加槽函数
[cpp] view plaincopy
  1. public slots:  
  2.     void AddJavascriptWindowObject();  
  3.     void ChildFrameCreated(QWebFrame *frame);  

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


六. 总结

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

1、优势:

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

2、劣势:

  • 性能
  • 还是性能
  • 其他劣势……

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

猛戳下载代码

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/432561.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

mysql三大范式_MySQL学习笔记

1、数据库结构设计1、总-总体流程图2、分-【提取属性】业务分析评价的属性:{用户&#xff0c;课程主标题&#xff0c;内容&#xff0c;综合评分&#xff0c;内容实用&#xff0c;简洁易懂&#xff0c;逻辑分析&#xff0c;发布时间} 问答评论属性&#xff1a;{类型&#xff0c;…

QT webkit 各个类之间关系--QWebView-QWebPag

一、QT webkit简介 1.Qt Qt&#xff08;发音同 cute&#xff09;是一个跨平台的C应用程式开发框架&#xff0c;有时又被称为C部件工具箱。Qt被用在KDE桌面环境、Opera、Google Earth、Skype、Adobe Photoshop Album和VirtualBox的开发中。它是挪威Qt Software 的产品&#xff0…

vue 文件转换二进制_在vue中使用axios实现post方式获取二进制流下载文件(实例代码)...

需求点击导出下载表格对应的excel文件在 vue 项目中,使用的 axios ,后台 java 提供的 post 接口 api实现第一步,在 axios 请求中加入参数,表示接收的数据为二进制文件流responseType: "blob"第二步,在拿到数据流之后,把流转为指定文件格式并创建a标签,模拟点击下载,实…

vs生成qt moc文件

1. 右键需要生成moc文件的头文件 2. 将生产的moc加入工程中

javascript好文---深入理解定位父级offsetParent及偏移大小

前面的话 偏移量(offset dimension)是javascript中的一个重要的概念。涉及到偏移量的主要是offsetLeft、offsetTop、offsetHeight、offsetWidth这四个属性。当然&#xff0c;还有一个偏移参照——定位父级offsetParent。本文将详细介绍该部分内容 offsetParent定位父级 在理解…

bash中将字符串split成数组的方法

相信编程时&#xff0c;字符串的处理是很频繁被处理的问题&#xff0c;其中大家肯定不陌生各种语言的string.split(sp)将字符串按照某个字符或子串切分成一个数组。 同样&#xff0c;我们在用shell处理文本信息时也可以方便地实现该功能。 这里主要使用了bash中关于字符串变量的…

理解 e.clientX,e.clientY e.pageX

event.clientX、event.clientY 鼠标相对于浏览器窗口可视区域的X&#xff0c;Y坐标&#xff08;窗口坐标&#xff09;&#xff0c;可视区域不包括工具栏和滚动条。IE事件和标准事件都定义了这2个属性 event.pageX、event.pageY 类似于event.clientX、event.clientY&#xff0c;…

基于FlashPaper的文档播放器

本文主要讨论、描述了使用Adobe公司的Flex与FlashPaper产品完成对发布到网上的文档资料进行只读控制&#xff0c;也就是说只允许浏览操作、对下载、打印进行控制。FlashPaper FlashPaper是Macromedia的一款用于将操作系统所识别的文档的内容通过虚拟打印机制将内容转换为swf文件…

python经纬度转换xy坐标公式 pyqt_EXCEL公式进行经纬度与XY坐标的相互转换

一、用EXCEL进行高斯投影换算从经纬度B、L换算到高斯平面直角坐标X、Y(高斯投影正算)&#xff0c;或从X、Y换算成B、L(高斯投影反算)&#xff0c;一般需要专用计算机软件完成。在目前流行的换算软件中不足之处&#xff0c;就是灵活性较差&#xff0c;大都需要一个点一个点地进行…

java桥_JAVA 桥模式

桥梁模式的用意是“将抽象化(Abstraction)与实现化(Implementation)脱耦&#xff0c;使得二者可以独立地变化”。这句话很短&#xff0c;但是第一次读到这句话的人很可能都会思考良久而不解其意。这句话有三个关键词&#xff0c;也就是抽象化、实现化和脱耦。理解这三个词所代表…

java utf8 byte_byte以及UTF-8的转码规则

https://www.cnblogs.com/hell8088/p/9184336.html多年来闲麻烦&#xff0c;只记录笔记&#xff0c;不曾编写BLOG&#xff0c;本文为原创&#xff0c;如需转载请标明出处废话不说&#xff0c;直奔主题ascii计算机只接受 “高”、“低”电压&#xff0c;所以使用二进制 1 和 …

理解GL_TRIANGLE_STRIP等绘制三角形序列的三种方式

GL_TRIANGLE_STRIP绘制三角形方式很多时候令人疑惑&#xff0c;在这里对其运作机理进行解释。 一般情况下有三种绘制一系列三角形的方式&#xff0c;分别是GL_TRIANGLES、GL_TRIANGLE_STRIP和GL_TRIANGLE_FAN。 如下图所示&#xff1a; GL_TRIANGLES是以每三个顶点绘制一个三…

jpa mysql乐观锁_【快学springboot】8.JPA乐观锁OptimisticLocking

介绍当涉及到企业应用程序时&#xff0c;正确地管理对数据库的并发访问是至关重要的。为此&#xff0c;我们可以使用Java Persistence API提供的乐观锁定机制。它导致在同一时间对同一数据进行多次更新不会相互干扰。为了使用OptimisticLocking&#xff0c;我们需要一个实体(En…

php mysql 库存变负数_php解决秒杀并发入库导致的库存负数

我们知道数据库处理sql是一条条处理的&#xff0c;假设购买商品的流程是这样的&#xff1a;sql1:查询商品库存1 if(库存数量 > 0)2 {3 //生成订单4 //库存-15 >当没有并发时&#xff0c;上面的流程看起来是如此完美&#xff0c;假设同时两个人下单&#xff0c;而库存只有…

python if else格式_Python if else条件语句详解

我们看到的代码都是顺序执行的&#xff0c;也就是先执行第1条语句&#xff0c;然后是第2条、第3条……一直到最后一条语句&#xff0c;这称为顺序结构。但是对于很多情况&#xff0c;顺序结构的代码是远远不够的&#xff0c;比如一个程序限制了只能成年人使用&#xff0c;儿童因…

UE4 HTC VIVE - 番外篇 - 局域网联机(一)

--------------------引擎环境配置文件修改与项目在线模式启动修改--------------------1&#xff09;我们就直接用默认名创建一个第三人称项目Paste_Image.png2&#xff09;右键资源栏&#xff0c;创建一个新的C类创建一个C类选择不继承任何UE提供的基类3&#xff09;打开【解…

UE4 HTC VIVE - 番外篇 - 局域网联机(二)

开始之前先说一下网游中服务器与客户端的大致关系&#xff1a;网络游戏中各段关系图客户端职责&#xff1a;1&#xff09;接收玩家的输入翻译得到【玩家指令】上传服务器&#xff1b;2&#xff09;接收服务器下发的【游戏指令】并将其实现服务器职责&#xff1a;1&#xff09;接…

启动文件、简单的消息框

C中打开文件的方法。 1.system&#xff08;&#xff09;; 函数原型&#xff1a; int system(char *command); 作用&#xff1a;发出一个DOS命令。 特点&#xff1a;该函数是同步的&#xff0c;不灵活。只是能够改为system("start XXX"); 2.WinExec&#xff08;&#…

UE4 HTC VIVE 多人联机

1. editor的VR模式不支持网络&#xff0c;所以在VR模式下没法调试多人联机程序 2. editor的standalone模式&#xff0c;引擎的源码里面把VR模式关闭了&#xff0c;所以需要修改引擎源码 3.可以在命令行下打开VR模式

.net开发微信公众平台

一、说明&#xff1a;公众平台信息接口为开发者提供了一种新的消息处理方式&#xff0c;只有申请成为开发者后&#xff0c;你才能使用公众平台的开发功能&#xff0c;在这里你需要填写一个URL和一个Token&#xff0c;这两项信息也需要你拥有自己的服务器&#xff08;外网服务器…