http://blog.csdn.net/longsir_area/article/details/42965565
一. 介紹?? ??? ??
?在瀏覽器擴(kuò)展或者WebApp的項(xiàng)目經(jīng)常用的腳本語言JavaScript有很多局限性,比如,javascript語言不能夠夸窗口訪問js對(duì)象,不能直接讀寫磁盤文件(這個(gè)也正是發(fā)明人設(shè)計(jì)的安全機(jī)制吧,要不然,誰還敢用瀏覽器啊,幾行代碼就可以把你偷窺的一覽無余),我們可能在我們的程序中需要擴(kuò)展這個(gè)功能。
那么,我們?cè)趺唇鉀Q這些問題呢?或許你可以參考一下下面的設(shè)計(jì)架構(gòu)。
UI利用Html + CSS + JavaScript編寫,核心業(yè)務(wù)層利用C++編寫,C++和JavaScript對(duì)象主要通過WebKit通信。讓相應(yīng)的語言做它們擅長的事情,這就是這種架構(gòu)的核心。
WebKit又是什么呢?簡單言之就是一種瀏覽器內(nèi)核引擎,Safari、Chrome、FireFox等瀏覽器都采用WebKit引擎作為內(nèi)核,由此可見WebKit的地位了,我們正是利用WebKit來做javascript的擴(kuò)展的,Qt界又對(duì)WebKit做了一層封裝,這樣,開發(fā)者對(duì)WebKit就更加容易上手了。
更幸運(yùn)的是,QtWebKit提供了一種將QObject對(duì)象擴(kuò)展到Javascript運(yùn)行環(huán)境的機(jī)制,這樣,JavaScript代碼將有權(quán)限訪問QObject對(duì)象,?QObject對(duì)象的所有屬性也能在Javascript上下文中被訪問。
那么,如何利用QtWebKit使得js和c++相互通信呢?
二. 搭建框架
首先,我們需要一個(gè)js代碼能夠運(yùn)行的環(huán)境
我們準(zhǔn)備一個(gè)主窗口,在主窗口內(nèi)部添加WebView控件,使得程序具有執(zhí)行js的能力;
MainWindow的定義
[cpp]?view plaincopy
#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的實(shí)現(xiàn)
[cpp]?view plaincopy
#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)?? ????????{?? ?????????????? ????????????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運(yùn)行環(huán)境,我們首先配置了JavaScriptEnable=true,使得這個(gè)web環(huán)境能夠運(yùn)行js代碼,其他的選項(xiàng)不是必要的。
三. 添加QObject到j(luò)s上下文
利用QWebFrame提供的方法我們可以很輕松的完成添加對(duì)象:
void addToJavaScriptWindowObject(const QString &name, QObject *object, ValueOwnership ownership = QtOwnership);
注解:
addToJavaScriptWindowObject這個(gè)方法可以使c++對(duì)象object在js的上下中以name為名字而出現(xiàn),object對(duì)象將被當(dāng)作這個(gè)QWebFrame的一個(gè)子對(duì)象,這樣,我們就可以把這個(gè)對(duì)象當(dāng)作js的一個(gè)對(duì)象來使用了;
object對(duì)象的屬性和槽都作為js方法在js上下文中展開,因此,js拿到這個(gè)對(duì)象就可以很隨意的訪問這個(gè)對(duì)象的屬性和方法;
當(dāng)這個(gè)頁面析構(gòu)后,這個(gè)對(duì)象在js上下文中也將消失,監(jiān)聽到信號(hào)javaScriptWindowObjectCleared() 后來重新調(diào)用下addToJavaScriptWindowObject即可。
我們先寫一個(gè)測(cè)試頁面index.html,主要功能就是測(cè)試Js是否能夠訪問external對(duì)象;
[html]?view plaincopy
<!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è)試C++對(duì)象">?? </body>?? </html>??
我們先來定義一個(gè)繼承自QObject的類External,其中,繼承自QObject很關(guān)鍵,否則我們無法完成我們的功能;
External類的定義,這里我定義了一個(gè)帶屬性、信號(hào)、槽、帶Q_INVOKABLE修飾的方法,這將在其他小節(jié)會(huì)用到
[cpp]?view plaincopy
#ifndef?EXTERNAL_H?? #define?EXTERNAL_H?? ?? #include?<QObject>?? ?? class?External?:?public?QObject?? {?? ????Q_OBJECT?? ????Q_PROPERTY(QString?msg?READ?getMsg?WRITE?setMsg)??? 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;?}?? ?? ?????? ????Q_INVOKABLE?int?VerifyUserAccount(const?QString&?userName,?const?QString&?userPwd);?? ?? ????Q_INVOKABLE?QString?GetPropMsg();?? ?? ????Q_INVOKABLE?void?TestPassObjectToNative(QObject*);?? ?? ????QString?msg;?? };?? ?? #endif?//?EXTERNAL_H??
我們?yōu)镸ainWindow類添加成員External對(duì)象指針
[cpp]?view plaincopy
External*?m_pExternal;??
在MainWindow的構(gòu)造方法中對(duì)m_pExternal實(shí)例化,并為這個(gè)頁面的javaScriptWindowObjectCleared信號(hào)關(guān)了槽函數(shù)AddJavascriptWindowObject
這樣,我們的MainWindow.h和MainWindow.cpp就成這樣子了:
MainWindow.h
[cpp]?view plaincopy
#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 plaincopy
#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)?? ????????{?? ?????????????? ????????????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,請(qǐng)嘗試的同學(xué)自行修改成自己的index.html的絕對(duì)路徑。
當(dāng)網(wǎng)頁加載時(shí),會(huì)自動(dòng)觸發(fā)javaScriptWindowObjectCleared信號(hào),繼而我們的槽函數(shù)AddJavascriptWindowObject會(huì)被執(zhí)行,上面關(guān)于AddJavaScriptWindowObject解釋說:當(dāng)這個(gè)頁面析構(gòu)后,這個(gè)對(duì)象在js上下文中也將消失,監(jiān)聽到信號(hào)javaScriptWindowObjectCleared() 后來重新調(diào)用下addToJavaScriptWindowObject即可,因此,想讓external一直保持有效,這樣做就可以了。
然后,運(yùn)行一下程序吧,點(diǎn)擊按鈕,你會(huì)看到如下結(jié)果:
三. 調(diào)用QObject的方法
js要調(diào)用已經(jīng)擴(kuò)展的對(duì)象external的方法,需要一下約束:
1. 方法前需要Q_INVOKABLE修飾;
2. 方法返回值和參數(shù)必須是Qt內(nèi)置類型或者c++內(nèi)置類型,即使用typedef的也不行;
3. 方法返回值和參數(shù)可以是QObject*, 但參數(shù)不能傳遞js對(duì)象,傳js對(duì)象用QVariant代替。
js中調(diào)用external的方法
[html]?view plaincopy
var?ret?=?external.VerifyUserAccount("user01",?"123456");?? if?(ret?==?1)?{?? ?????alert("驗(yàn)證通過");?? ?}else?{?? ???????alert("用戶名或者密碼錯(cuò)誤");?? }??
操作QObject對(duì)象的屬性
這節(jié)我們來嘗試訪問一下External對(duì)象的msg屬性,并且修改TA,看看在Js層和C++層是否被修改。
還是先看看測(cè)試頁面吧:
[html]?view plaincopy
<!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è)試C++對(duì)象">?? ????<p>測(cè)試屬性msg</p>?? ????<input?type="button"?id="visitProp"?value="visit">?? ????<input?type="text"?id="msgProValue"?value="修改后的msg">?? ????<input?type="button"?id="setProp"?value="修改">?? </body>?? </html>??
運(yùn)行程序,我們先點(diǎn)擊visit按鈕,可以看到,顯示的msg屬性都是空值,然后再文本框中輸入字符,點(diǎn)擊修改按鈕,再點(diǎn)擊visit按鈕,試試看,彈出的msg是否有變化。
四.?綁定QObject對(duì)象的信號(hào)/槽
addJavaScriptWindowObject方法可以將對(duì)象的屬性、信號(hào)、槽統(tǒng)統(tǒng)映射到Js上下文中。
綁定信號(hào)的方式如下
[html]?view plaincopy
external.mouseClicked.connect(mouseClickedSlot);?? function?mouseClickedSlot()?{?? ????logger("mouse?clicked");?? }?? mouseClicked通過connect方法連接到j(luò)s的方法(槽),當(dāng)mouseClicked被觸發(fā)后,mouseClickedSlot將被執(zhí)行
五. 為iframe添加QObject
當(dāng)iframe被創(chuàng)建時(shí),mainFrame下的webpage的信號(hào)frameCreated會(huì)被觸發(fā);
frameCreated的原型是:
[cpp]?view plaincopy
void?frameCreated(QWebFrame*);?? 因此,我們可以為frameCreated關(guān)聯(lián)一個(gè)槽,在這個(gè)槽中,為新創(chuàng)建的QWebFrame添加QObject;
代碼如下:
1. 在MainWindow的構(gòu)造函數(shù)添加
[cpp]?view plaincopy
connect(this->m_pWebView->page(),?SIGNAL(frameCreated(QWebFrame*)),?? ????????????this,?SLOT(ChildFrameCreated(QWebFrame*)));??
2. 為MainWindow類添加槽函數(shù)
[cpp]?view plaincopy
public?slots:?? ????void?AddJavascriptWindowObject();?? ????void?ChildFrameCreated(QWebFrame?*frame);??
3. 為MainWindow類添加如下實(shí)現(xiàn)
[cpp]?view plaincopy
void?MainWindow::AddJavascriptWindowObject()?? {?? ????m_pWebView->page()->mainFrame()->addToJavaScriptWindowObject("external",?m_pExternal);?? }?? ?? void?MainWindow::AddJavascriptWindowObject(QWebFrame?*pFrame)?? {?? ????qDebug("AddJavascriptWindowObject");?? ?????? ????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);?? ????}?? }?? 六. 總結(jié)
到目前為止,一個(gè)Hybrid模式的應(yīng)用demo已經(jīng)完成了,我們來分析一下這種模式的優(yōu)劣;
1、優(yōu)勢(shì):
- 高效率開發(fā)UI豐富的應(yīng)用;
- 底層框架可復(fù)用;
- 可實(shí)現(xiàn)跨平臺(tái);
- 其他好處……
2、劣勢(shì):
隨著硬件的強(qiáng)大、html5的發(fā)展,不論是在pc端還是移動(dòng)端, 這種框架會(huì)得到普遍的認(rèn)可,i think so。
猛戳下載代碼
總結(jié)
以上是生活随笔為你收集整理的[Qt] 利用QtWebKit完成JavaScript访问C++对象的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。