前面一节已经介绍了如何编译miniblink库以及如何在qt下使用,但是创立的是个独立窗体,应用中我们经常用到的是作为一个子窗体嵌入到其他窗体之中,类似QWebengnieView,本节将实现这个功能。
首先参考前一节将需要的库文件,头文件引入工程。
1.创建一个类QMiniblinkWebView继承自QWidget
2.初始化initialize接口
由于miniblink使用需要先初始化,这里设计在构造函数中处理这个问题,多个QMiniblinkWebView只需要初始化一次,所以这里定义两个静态变量
static bool _isInit;
static int _viewCount;
_isInit 用户判断是否初始化,_viewCount用户判断当前有多少个QMiniblinkWebView实例, 每次创建实力自动加一,当_viewCount为0时自动释放miniblink。为了方式 拷贝构造和复制私有化这两个函数
private:
QMiniblinkWebView & operator == (const QMiniblinkWebView&){}
QMiniblinkWebView(const QMiniblinkWebView&){}
初始化函数实现如下:
void QMiniblinkWebView::initialize()
{
QString nodePath = QApplication::applicationDirPath() + "/node.dll";
if(!QFile::exists(nodePath))
{
qDebug()<<QStringLiteral("请将node.dll拷贝到运行目录");
return;
}
std::vector<wchar_t> tempPath;
tempPath.resize(MAX_PATH);
nodePath.toWCharArray(&tempPath[0]);
wkeSetWkeDllPath(&tempPath[0]);
wkeInitialize();
_isInit = true;
++QMiniblinkWebView::_viewCount;
}
3. 释放接口
当前实力为0时 释放miniblink
void QMiniblinkWebView::finalize()
{
if(--QMiniblinkWebView::_viewCount == 0)
{
wkeFinalize();
_isInit = false;
}
}
4.创建miniblinkview
调用wkeCreateWebWindow(WKE_WINDOW_TYPE_TRANSPARENT, (HWND)this->winId(), 0, 0, width(), height()); 进行创建,此处创建的是一个window
5.将步骤4创建的view子类化为QtWidget子窗体
通过wkeGetWindowHandle(viewer)可以拿到wkeCreateWebWindow创建的viewer的窗口句柄,然后调用windows函数SetParent建立父子关系
SetParent(wkeGetWindowHandle(viewer), (HWND)this->winId());
6.大小改变
4,5创建的窗体是固定大小,当我们改变QWidget大小时,wkeCreateWebWindow创建的viewer大小并不会随之改变,我们需要截获QWidget大小改变通知。你可能会想到继承QWidget::resizeEvent,经我实践,wkeCreateWebWindow创建的窗口影响了这个事件
获取的大小不正确。所以这里依旧使用windows窗口回调来实现。
调用SetWindowSubclass为qt窗体设置一个回调函数 SetWindowSubclass((HWND)this->winId(), subClassProc, 0, (DWORD_PTR)this);
回调函数的实现如下,这是一个静态成员函数,也可以写成一个全局函数。
LRESULT CALLBACK QMiniblinkWebView::subClassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
QMiniblinkWebView *obj = (QMiniblinkWebView *)dwRefData;
if(obj == nullptr)
return DefSubclassProc(hWnd, uMsg, wParam, lParam);
switch (uMsg) {
case WM_SIZE:
obj->autojustWebViewSize();
break;
default:
break;
}
return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}
这里只做了一件事,就是调用接口autojustWebViewSize实现viewer跟谁qt父窗口大小改变。
void QMiniblinkWebView::autojustWebViewSize()
{
//此处如果用qt得方法获取大小在最大化最小化有问题,故改为通过windows函数获取,可能是qt版本问题
//wkeMoveWindow(_viewer, 0, 0, width(), height());
RECT rect;
GetWindowRect((HWND)this->winId(), &rect);
wkeMoveWindow(_viewer, 0, 0, rect.right - rect.left, rect.bottom - rect.top);
}
此处如果用qt得方法获取大小在最大化最小化有问题,故改为通过windows函数获取,可能是qt版本问题。
自此我们就基本完成了miniblink集成到qt了,可以将QMiniblinkWebView作为独立窗口使用,也可以作为任意一个qt窗口的子窗体使用。
7.事件通知
miniblink的通知是回调机制,wke.h 包含了所有的函数定义以及回调的函数指针定义,我们需要获取到什么通知就定义个相应函数指针类型的回调函数就可以活得通知,我这里实现了常用的几个通知,然后转化为qt信号的方式发出去。
wkeWebView viewer = wkeCreateWebWindow(WKE_WINDOW_TYPE_TRANSPARENT, (HWND)this->winId(), 0, 0, width(), height());
if (!viewer)
return nullptr;
wkeSetWindowTitleW(viewer, title.toStdWString().c_str());
wkeOnDocumentReady(viewer, handleDocumentReady, nullptr);
wkeOnLoadUrlBegin(viewer, onLoadUrlBegin, (void *)this);
wkeOnLoadUrlEnd(viewer, onLoadUrlEnd, (void *)this);
wkeOnLoadUrlFail(viewer, onLoadUrlFailed, (void *)this);
wkeOnLoadingFinish(viewer, onLoadingFinish, (void *)this);
//wkeMoveToCenter(viewer);
SetWindowSubclass((HWND)this->winId(), subClassProc, 0, (DWORD_PTR)this);
SetParent(wkeGetWindowHandle(viewer), (HWND)this->winId());
wkeMoveWindow(viewer, 0, 0, width(), height());
//文档加载完成
static void handleDocumentReady(wkeWebView webWindow, void* param);
//开始加载
static bool onLoadUrlBegin(wkeWebView webView, void* param, const char* url, void *job);
//加载完,并不一定是成功的
static void onLoadUrlEnd(wkeWebView webView, void* param, const char *url, void *job, void* buf, int len);
//加载失败
static void onLoadUrlFailed(wkeWebView webView, void* param, const utf8* url, wkeNetJob job);
//加载完成,failedReason包含了失败的原因
static void onLoadingFinish(wkeWebView webView, void* param, const wkeString url, wkeLoadingResult result, const wkeString failedReason);
由于都是回调函数,需要定义成静态成员函数或者全局函数,函数的参数可以直接看wke.h。需要其他回调的可以参考wke.h自己添加比如下载的进度等等。
bool QMiniblinkWebView::onLoadUrlBegin(wkeWebView webView, void* param, const char* url, void *job)
{
QMiniblinkWebView *obj = (QMiniblinkWebView *)param;
if(obj != nullptr)
{
emit obj->loadStarted();
}
return true;
}
void QMiniblinkWebView::onLoadUrlEnd(wkeWebView webView, void* param, const char *url, void *job, void* buf, int len)
{
QMiniblinkWebView *obj = (QMiniblinkWebView *)param;
if(obj != nullptr)
{
emit obj->loadFinished(true);
}
}
void QMiniblinkWebView::onLoadUrlFailed(wkeWebView webView, void* param, const utf8* url, wkeNetJob job)
{
QMiniblinkWebView *obj = (QMiniblinkWebView *)param;
if(obj != nullptr)
{
emit obj->loadUrlFail();
}
}
void QMiniblinkWebView::onLoadingFinish(wkeWebView webView, void* param, const wkeString url, wkeLoadingResult result, const wkeString failedReason)
{
QMiniblinkWebView *obj = (QMiniblinkWebView *)param;
if(obj != nullptr && result == WKE_LOADING_SUCCEEDED)
{
emit obj->loadFinished(true);
}
}
自此,一个简单的qt版miniblink封装已经完成
8.使用
1.独立窗口
QMiniblinkWebView w2;
w2.show();
//w2.loadFile("./debug/2.html");
w2.load("http://baidu.com");
2.子窗体
QMiniblinkWebView *w2 = new QMiniblinkWebView("http://baidu.com", this);
setCentralWidget(w2);
代码都很简单,主要步骤为5.将步骤4创建的view子类化为QtWidget子窗体,如果需要完整源码,可以留言。
下一节将介绍如何实现js与C++得通信