[Qt] 利用QtWebKit完成JavaScript访问C++对象
2017-06-20 16:10
447 查看
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
plain copy
#include <QMainWindow>
#include <QGraphicsView>
#include <QGraphicsWebView>
#include <QGraphicsScene>
#include <QEvent>
#include "External.h"
class MainWindow : public QGraphicsView
{
Q_OBJECT
public:
MainWindow(QWidget *parent = 0);
virtual ~MainWindow();
private:
QGraphicsWebView* m_pWebView;
QGraphicsScene* m_pScene;
};
#endif // MAINWINDOW_H
MainWindow的实现
[cpp] view
plain copy
#include "mainwindow.h"
#include <QWebFrame>
#include <QLayout>
MainWindow::MainWindow(QWidget *parent)
: QGraphicsView(parent)
{
this->resize( QSize( 800, 600) );
m_pScene = new QGraphicsScene(this);
if(NULL != m_pScene)
{
m_pWebView = new QGraphicsWebView;
if( NULL != m_pWebView)
{
//enabled javascript
QWebSettings *settings = m_pWebView->page()->settings();
settings->setAttribute(QWebSettings::JavascriptEnabled,true);
settings->setAttribute(QWebSettings::JavascriptCanAccessClipboard,true);
settings->setAttribute(QWebSettings::DeveloperExtrasEnabled,true);
settings->setAttribute(QWebSettings::LocalContentCanAccessRemoteUrls, true);
settings->setAttribute(QWebSettings::LocalContentCanAccessFileUrls, true);
settings->setAttribute(QWebSettings::JavascriptCanCloseWindows, true);
settings->setAttribute(QWebSettings::AutoLoadImages,true);
m_pScene->addItem( m_pWebView );
this->setScene( m_pScene );
}
}
}
MainWindow::~MainWindow()
{
}
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
plain copy
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>QWebKitDemo</title>
<script type="text/javascript">
window.onload = function(){
var btnTest = document.getElementById("testCObj");
btnTest.onclick = function() {
alert(external);
}
}
</script>
</head>
<body>
<input type="button" id="testCObj" value="测试C++对象">
</body>
</html>
我们先来定义一个继承自QObject的类External,其中,继承自QObject很关键,否则我们无法完成我们的功能;
External类的定义,这里我定义了一个带属性、信号、槽、带Q_INVOKABLE修饰的方法,这将在其他小节会用到
[cpp] view
plain copy
#ifndef EXTERNAL_H
#define EXTERNAL_H
#include <QObject>
class External : public QObject
{
Q_OBJECT
Q_PROPERTY(QString msg READ getMsg WRITE setMsg) // 声明静态属性msg
public:
explicit External(QObject *parent = 0);
signals:
void mouseClicked();
public slots:
void TestPassObject(QObject*);
public:
QString getMsg() const { return msg; }
void setMsg( const QString& strMsg ) { msg = strMsg; }
// 提供给Javascript方法,需要用Q_INVOKABLE修饰
Q_INVOKABLE int VerifyUserAccount(const QString& userName, const QString& userPwd);
Q_INVOKABLE QString GetPropMsg();
Q_INVOKABLE void TestPassObjectToNative(QObject*);
QString msg;
};
#endif // EXTERNAL_H
我们为MainWindow类添加成员External对象指针
[cpp] view
plain copy
External* m_pExternal;
在MainWindow的构造方法中对m_pExternal实例化,并为这个页面的javaScriptWindowObjectCleared信号关了槽函数AddJavascriptWindowObject
这样,我们的MainWindow.h和MainWindow.cpp就成这样子了:
MainWindow.h
[cpp] view
plain copy
#include <QMainWindow>
#include <QGraphicsView>
#include <QGraphicsWebView>
#include <QGraphicsScene>
#include <QEvent>
#include "External.h"
class MainWindow : public QGraphicsView
{
Q_OBJECT
public:
MainWindow(QWidget *parent = 0);
virtual ~MainWindow();
public slots:
void AddJavascriptWindowObject();
private:
QGraphicsWebView* m_pWebView;
QGraphicsScene* m_pScene;
External* m_pExternal;
};
#endif // MAINWINDOW_H
MainWindow.cpp
[cpp] view
plain copy
#include "mainwindow.h"
#include <QWebFrame>
#include <QLayout>
MainWindow::MainWindow(QWidget *parent)
: QGraphicsView(parent)
{
this->resize( QSize( 800, 600) );
m_pExternal = new External();
m_pScene = new QGraphicsScene(this);
if(NULL != m_pScene)
{
m_pWebView = new QGraphicsWebView;
if( NULL != m_pWebView)
{
//enabled javascript
QWebSettings *settings = m_pWebView->page()->settings();
settings->setAttribute(QWebSettings::JavascriptEnabled,true);
settings->setAttribute(QWebSettings::JavascriptCanAccessClipboard,true);
settings->setAttribute(QWebSettings::DeveloperExtrasEnabled,true);
settings->setAttribute(QWebSettings::LocalContentCanAccessRemoteUrls, true);
settings->setAttribute(QWebSettings::LocalContentCanAccessFileUrls, true);
settings->setAttribute(QWebSettings::JavascriptCanCloseWindows, true);
settings->setAttribute(QWebSettings::AutoLoadImages,true);
m_pScene->addItem( m_pWebView );
this->setScene( m_pScene );
connect(m_pWebView->page()->mainFrame(), SIGNAL(javaScriptWindowObjectCleared()),
this, SLOT(AddJavascriptWindowObject()));
m_pWebView->setUrl(QUrl("file:///Users/cblogs_code/QWebKitDemo/index.html"));
}
}
}
MainWindow::~MainWindow()
{
}
void MainWindow::AddJavascriptWindowObject()
{
m_pWebView->page()->mainFrame()->addToJavaScriptWindowObject("external", m_pExternal);
}
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
plain copy
var ret = external.VerifyUserAccount("user01", "123456");
if (ret == 1) {
alert("验证通过");
}else {
alert("用户名或者密码错误");
}
操作QObject对象的属性
这节我们来尝试访问一下External对象的msg属性,并且修改TA,看看在Js层和C++层是否被修改。
还是先看看测试页面吧:
[html] view
plain copy
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>QWebKitDemo</title>
<script type="text/javascript">
window.onload = function(){
var btnTest = document.getElementById("testCObj");
btnTest.onclick = function() {
alert(external);
}
var btnVisit = document.getElementById("visitProp");
btnVisit.onclick = function() {
var output = "js层面 \tmsg: "+external.msg + "\n c++层面 \tmsg: "+external.GetPropMsg();
alert(output);
}
var btnSetProp = document.getElementById("setProp");
btnSetProp.onclick = function() {
var tempValue = document.getElementById("msgProValue");
external.msg = tempValue.value;
}
}
</script>
</head>
<body>
<input type="button" id="testCObj" value="测试C++对象">
<p>测试属性msg</p>
<input type="button" id="visitProp" value="visit">
<input type="text" id="msgProValue" value="修改后的msg">
<input type="button" id="setProp" value="修改">
</body>
</html>
运行程序,我们先点击visit按钮,可以看到,显示的msg属性都是空值,然后再文本框中输入字符,点击修改按钮,再点击visit按钮,试试看,弹出的msg是否有变化。
四. 绑定QObject对象的信号/槽
addJavaScriptWindowObject方法可以将对象的属性、信号、槽统统映射到Js上下文中。绑定信号的方式如下
[html] view
plain copy
external.mouseClicked.connect(mouseClickedSlot);
function mouseClickedSlot() {
logger("mouse clicked");
}
mouseClicked通过connect方法连接到js的方法(槽),当mouseClicked被触发后,mouseClickedSlot将被执行
五. 为iframe添加QObject
当iframe被创建时,mainFrame下的webpage的信号frameCreated会被触发;frameCreated的原型是:
[cpp] view
plain copy
void frameCreated(QWebFrame*);
因此,我们可以为frameCreated关联一个槽,在这个槽中,为新创建的QWebFrame添加QObject;
代码如下:
1. 在MainWindow的构造函数添加
[cpp] view
plain copy
connect(this->m_pWebView->page(), SIGNAL(frameCreated(QWebFrame*)),
this, SLOT(ChildFrameCreated(QWebFrame*)));
2. 为MainWindow类添加槽函数
[cpp] view
plain copy
public slots:
void AddJavascriptWindowObject();
void ChildFrameCreated(QWebFrame *frame);
3. 为MainWindow类添加如下实现
[cpp] view
plain copy
void MainWindow::AddJavascriptWindowObject()
{
m_pWebView->page()->mainFrame()->addToJavaScriptWindowObject("external", m_pExternal);
}
void MainWindow::AddJavascriptWindowObject(QWebFrame *pFrame)
{
qDebug("AddJavascriptWindowObject");
//add external object to main web frame
pFrame->addToJavaScriptWindowObject("external", m_pExternal);
}
void MainWindow::ChildFrameCreated(QWebFrame *pFrame)
{
qDebug("ChildFrameCreated");
if(pFrame == NULL)
{
qDebug("Child frame created, but it was NULL!");
}
else
{
AddJavascriptWindowObject(pFrame);
}
}
六. 总结
到目前为止,一个Hybrid模式的应用demo已经完成了,我们来分析一下这种模式的优劣;1、优势:
高效率开发UI丰富的应用;底层框架可复用;
可实现跨平台;
其他好处……
2、劣势:
性能还是性能
其他劣势……
随着硬件的强大、html5的发展,不论是在pc端还是移动端, 这种框架会得到普遍的认可,i think so。
猛戳下载代码
相关文章推荐
- [Qt] 利用QtWebKit完成JavaScript访问C++对象
- [Qt] 利用QtWebKit完成JavaScript访问C++对象
- [Qt] 利用QtWebKit完成JavaScript访问C++对象
- Qt利用JavaScript访问网页元素(百度博客下载例子)
- Qt利用JavaScript访问网页元素(百度博客下载例子)
- formExtractor: 在QtWebkit的javascript访问QObject的例子
- javascript - C++, Qt, QtWebKit: How to create an html rendering window so that your application would get callbacks from JS calls? - Stack Overflow
- JavaScript重构(五):利用原型和闭包,完成组件方法
- 利用JavaScript脚本改变访问过链接的颜色
- 利用JavaScript访问数据库
- Qt/WebKit::Qt javascript扩展
- CS001496 - Gather data from web page with JavaScript, WebKit, and Qt
- CS001497 - Add data to a web page with JavaScript, WebKit, and Qt
- javascript 利用xmlhttp访问asp.net 2.0 的webservice
- 利用第三方qextserialport类完成QT下串口MyComm程序
- Qt webKit--实现本地QObject(c++)和JavaScript交互
- Qt webKit--实现本地QObject(c++)和JavaScript交互
- Qt利用DOM API访问网页元素(百度博客下载例子)
- Qt webKit可以做什么(四)——实现本地QObject和JavaScript交互
- 利用JavaScript访问HTML元素(再次补充)