海南专业做网站的公司,完备的常州网站推广,四川游戏seo整站优化,网站建设理由和目的http://blog.csdn.net/longsir_area/article/details/42965565
一. 介绍 在浏览器扩展或者WebApp的项目经常用的脚本语言JavaScript有很多局限性#xff0c;比如#xff0c;javascript语言不能够夸窗口访问js对象#xff0c;不能直接读写磁盘文件#xff08;这个…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 #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 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) { //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运行环境我们首先配置了JavaScriptEnabletrue使得这个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 !DOCTYPE html PUBLIC -//W3C//DTD XHTML 1.0 Transitional//EN http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd html xmlnshttp://www.w3.org/1999/xhtml head meta http-equivContent-Type contenttext/html; charsetutf-8 / titleQWebKitDemo/title script typetext/javascript window.onload function(){ var btnTest document.getElementById(testCObj); btnTest.onclick function() { alert(external); } } /script /head body input typebutton idtestCObj value测试C对象 /body /html 我们先来定义一个继承自QObject的类External其中继承自QObject很关键否则我们无法完成我们的功能 External类的定义这里我定义了一个带属性、信号、槽、带Q_INVOKABLE修饰的方法这将在其他小节会用到 [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) // 声明静态属性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 plaincopy External* m_pExternal; 在MainWindow的构造方法中对m_pExternal实例化并为这个页面的javaScriptWindowObjectCleared信号关了槽函数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) { //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 plaincopy var ret external.VerifyUserAccount(user01, 123456); if (ret 1) { alert(验证通过); }else { alert(用户名或者密码错误); } 操作QObject对象的属性这节我们来尝试访问一下External对象的msg属性并且修改TA看看在Js层和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 xmlnshttp://www.w3.org/1999/xhtml head meta http-equivContent-Type contenttext/html; charsetutf-8 / titleQWebKitDemo/title script typetext/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 typebutton idtestCObj value测试C对象 p测试属性msg/p input typebutton idvisitProp valuevisit input typetext idmsgProValue value修改后的msg input typebutton idsetProp value修改 /body /html 运行程序我们先点击visit按钮可以看到显示的msg属性都是空值然后再文本框中输入字符点击修改按钮再点击visit按钮试试看弹出的msg是否有变化。四. 绑定QObject对象的信号/槽 addJavaScriptWindowObject方法可以将对象的属性、信号、槽统统映射到Js上下文中。 绑定信号的方式如下 [html] view plaincopy external.mouseClicked.connect(mouseClickedSlot); function mouseClickedSlot() { logger(mouse clicked); } mouseClicked通过connect方法连接到js的方法(槽)当mouseClicked被触发后mouseClickedSlot将被执行五. 为iframe添加QObject
当iframe被创建时mainFrame下的webpage的信号frameCreated会被触发frameCreated的原型是[cpp] view plaincopy void frameCreated(QWebFrame*); 因此我们可以为frameCreated关联一个槽在这个槽中为新创建的QWebFrame添加QObject代码如下1. 在MainWindow的构造函数添加[cpp] view plaincopy connect(this-m_pWebView-page(), SIGNAL(frameCreated(QWebFrame*)), this, SLOT(ChildFrameCreated(QWebFrame*))); 2. 为MainWindow类添加槽函数[cpp] view plaincopy public slots: void AddJavascriptWindowObject(); void ChildFrameCreated(QWebFrame *frame); 3. 为MainWindow类添加如下实现[cpp] view plaincopy 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。 猛戳下载代码