WebKit 是一個開源的瀏覽器引擎,目前 Safari,Chrome 等瀏覽器均使用了 WebKit 作為核心。Qt 從 4.5 版本開始,集成了 WebKit 作為 Qt 的平台組件,用戶可以像使用其他組件一樣將 WebKit 引擎集成到自己的應用程序中,以提供 Web 的支持。
Qt 中對 WebKit 做了封裝,主要有以下幾個類:
QWebView 最常用的類,這是一個窗體控件,可以用來渲染網頁 。
QWebPage 被 QWebView 包含,表示一個 document。
QWebFrame 被 QWebPage 包含,表示一個 frame。
QWebSettings Web 渲染的全局設置。
QWebHistory 用於瀏覽的歷史記錄。
QWebView 使用 QWebPage 來實現頁面,QWebPage 使用 QWebFrame 來實現頁面元素。
以下是參考文章
之前在做CS架構的時候,顯示圖表總是做得不好。只有C#有相應的組件,QT需要手畫或者加載一些插件。做了BS架構之后,知道了很多在前端方面表現極佳的圖表制作工具,如Echarts。在上一次的大作業之中,也使用了這一方法。那么又知道QT里可以加載QtWebKit,因此便可以進行Web與本地應用的混合開發。
新建一個Qt Gui項目,記得選上QtWebKit和QNetwork。
QWebView類
使用QWebView類只要幾行代碼就可以做出一個最簡單的瀏覽器。QWebView的主要功能是用於瀏覽網頁,每個QWebView都包含着一個QWebPage,而QWebPage是用於存儲和編輯網頁的類。
下面是一個最簡單的瀏覽器了:main.cpp
#include <QtGui/QApplication> #include <QWebView> #include <QMainWindow> int main(int argc, char *argv[]) { QApplication a(argc, argv); QMainWindow window; QWebView view(&window); view.setGeometry(0, 0, 600, 400); view.setUrl(QUrl("https://github.com/Mr-Phoebe")); window.show(); return a.exec(); }
webpage.pro:
TEMPLATE = app TARGET = DEPENDPATH += . INCLUDEPATH += . CONFIG += qt QT += webkit # Input SOURCES += main.cpp
QWebView有兩種方法可以用來設定要顯示的內容,一個是setUrl方法,一個是setContent方法。 這個很簡單,試一下就會,不多說了。
不過對這兩種方法會有兩種不同的開發方式:
setContent的話,需要手動將網頁代碼生成出來放到QWebView中,網頁中的元素(如:圖片,樣式,腳本)就只能采用“file:///”的方式了。
setUrl的話,更用技術含量一點,可能需要自己做一個簡單的Http服務器,然后監聽本地端口,掉用QWebView的setUrl(QUrl(http://127.0.0.1:XXXX))就可以了。
可以使用一種更省事的方法:直接把要顯示的html放到apache上去了。
顯示出來並不難,最主要的是如何同界面雙向交互,又不是asp,總不能沒點按鈕就刷一次頁面吧。
最容量想到的是傳統Web開發的中常用的Ajax,不過就有兩個缺點:
一個是要監聽本地端口,第二個更致命,Ajax不是雙向的,Server向Client發消息就不行了。
下面說一種更好的方法,實現js與C++的雙向調用。
js調用c++方法
首先要將一個C++的對象“送”到js中,js拿到這個對象以后就可以像其它對象一樣,自由的調用它的方法了。
這一步有兩種實現方式:
1. 在網頁中插入控件
JS端
在網頁中插入下面一段代碼:
<object type="application/x-qt-plugin" id="qt"></object>
之后就可以通過document.getElementById(‘qt’);獲取這個對象,並調用方法了。
C++端
首先要自定義一個類繼承自QWebPage,在構造函數中加入如下一句話開起plugin的支持。
settings()->setAttribute(QWebSettings::PluginsEnabled, true);
然后重載QWebPage中的如下方法(protected的)
virtual QObject *createPlugin(const QString &classid, const QUrl &url, const QStringList ¶mNames, const QStringList ¶mValues);
方法的返回值就是要傳遞紿js的對象。
注意:返回值要是一個QWidget *類型的,所以可能還要自己自己寫一個Widget,寫好后,返回的QWidget中的所有public slots在js中都是可見的方法。
例子如下,有點長,不過很簡單:
MyWidget.h
#ifndef MYWIDGET_H #define MYWIDGET_H #include<QWidget> #include<QWebPage> #include<QWebFrame> class MyWidget :public QWidget { Q_OBJECT private: QWebPage *page; public: MyWidget(QWebPage *page) : page(page) { } public slots: void func(QString arg) { this->page->mainFrame()->evaluateJavaScript("document.body.innerHTML += '" + arg + "';"); } }; #endif // MYWIDGET_H
MyPage.h
#ifndef MYPAGE_H #define MYPAGE_H #include<QWebPage> #include<QWebFrame> #include"MyWidget.h" class MyPage : public QWebPage { Q_OBJECT public: MyPage(QObject *parent = 0) : QWebPage(parent) { settings()->setAttribute(QWebSettings::PluginsEnabled, true); } protected: QObject *createPlugin(const QString &classid, const QUrl &url, const QStringList ¶mNames, const QStringList ¶mValues){ return new MyWidget(this); } }; #endif // MYPAGE_H
main.cpp
#include <QtGui/QApplication> #include <QMainWindow> #include <QWebView> #include <QWebPage> #include <QWebFrame> #include "MyPage.h" int main(int argc, char *argv[]) { QApplication a(argc, argv); QMainWindow window; QWebView view(&window); MyPage page; view.setPage(&page); view.setGeometry(0, 0, 600, 400); // Object QString content("<object type='application/x-qt-plugin' height='1' width='1' id='qt'></object>"); // JS Function f() : Invoke the 'func' function content += "<script>document.getElementById('qt').func('https://github.com/Mr-Phoebe');</script>"; view.setContent(content.toAscii()); window.show(); return a.exec(); }
2. 用QWebFrame的addToJavaScriptWindowObject方法
相比上一個方法,個人推薦這種方法。因為上一個在Linux下遇到過很詭異的問題。
例子是最好的說明方式,於是再紿出一下例子:
注意:addToJavaScriptWindowObject的第一個參數是對象在js中的變量名,第二個參數的QObject不要求是QWidget。
MyObject.h
#ifndef MYOBJECT_H #define MYOBJECT_H #include<QObject> #include<QWebPage> #include<QWebFrame> // !! ATTENTION !! : The object do NOT need to inherit from QWidget anymore. class MyObject :public QObject { Q_OBJECT private: QWebPage *page; public: MyObject(QWebPage *page) : page(page) { } public slots: void func(QString arg) { this->page->mainFrame()->evaluateJavaScript("document.body.innerHTML += '" + arg + "';"); } }; #endif // MYOBJECT_H
main.cpp
#include <QtGui/QApplication> #include <QMainWindow> #include <QWebView> #include <QWebPage> #include <QWebFrame> #include "MyObject.h" int main(int argc, char *argv[]) { QApplication a(argc, argv); QMainWindow window; QWebView view(&window); QWebPage page; view.setPage(&page); view.setGeometry(0, 0, 600, 400); MyObject obj(&page); page.mainFrame()->addToJavaScriptWindowObject("qt", &obj); QString content("<script>function f() { qt.func('https://github.com/Mr-Phoebe'); }</script>"); content += "<a href='javascript:f()'>Click Me</a>"; view.setContent(content.toAscii()); window.show(); return a.exec(); }
C++調用js代碼
依舊是兩種方法。
1. evaluateJavaScript
這個超簡單了,上面的例子中就用到了。不多說了。
2. connect
這個更符合Qt的風格一點。首先用上一部分介紹的兩種方法中的任意一種將一個C++對象“送”到js中去。然后調用這個對象的connect方法,將自己的signals和js方法進行bind。
再放個例子吧,不過例子中竟然用evaluateJavaScript來bind。
當QWebView加載好后,綁定MyObject的Miku Signal到js本地方法f,再觸發Miku Signal。
MyObject.h
#ifndef MYOBJECT_H #define MYOBJECT_H #include<QObject> #include<QWebPage> #include<QWebFrame> class MyObject :public QObject { Q_OBJECT private: QWebPage *page; public: MyObject(QWebPage *page) : page(page) { } signals: void Miku(); public slots: void viewLoad() { this->page->mainFrame()->evaluateJavaScript("qt.Miku.connect(f);"); this->Miku(); } }; #endif // MYOBJECT_H
main.cpp
#include <QtGui/QApplication> #include <QMainWindow> #include <QWebView> #include <QWebPage> #include <QWebFrame> #include "MyObject.h" int main(int argc, char *argv[]) { QApplication a(argc, argv); QMainWindow window; QWebView view(&window); QWebPage page; view.setPage(&page); view.setGeometry(0, 0, 600, 400); MyObject obj(&page); QObject::connect(&view, SIGNAL(loadFinished(bool)), &obj, SLOT(viewLoad())); page.mainFrame()->addToJavaScriptWindowObject("qt", &obj); QString content("<script>function f() { document.body.innerHTML += 'http://pnuts.cc'; }</script>"); view.setContent(content.toAscii()); window.show(); return a.exec(); }
轉自:http://blog.csdn.net/u013007900/article/details/52159795