qt cef嵌入web

原文http://blog.sina.com.cn/s/blog_9e59cf590102vnfc.html

最近项目需要,研究了下libcef库。

Cef(Chromium Embedded Framework)简述

嵌入式Chromium框架(简称CEF) 是一个由Marshall Greenblatt在2008建立的开源项目,它主要目的是开发一个基于Google Chromium的Webbrowser控件。CEF支持一系列的编程语言和操作系统,并且能很容易地整合到新的或已有的工程中去。

它的设计思想政治就是易用且兼顾性能。CEF基本的框架包含C/C++程序接口,通过本地库的接口来实现,而这个库则会隔离宿主程序和Chromium&Webkit的操作细节。它在浏览器控件和宿主程序之间提供紧密的整合,它支持用户插件,协议,javascript对象以及javascript扩展,宿主程序可以随意地控件资源下载,导航和打印等,并且可以跟Google Chrome浏览器一起,支持高性能和Html5 技术。

Cef使用

首先下载cef库的源码,源码有2个大的版本,cef1和cef3,我使用的是cef3,因此cef1我就不过多解释,其实我也不太了解。刚开始使用的时候一定不能怕,可能有些人看了源码之后会发现源码异常的复杂,这个时候我建议网上多差点儿资料,因为我学的时候也是在网上找到了不少好的文章。

下边是我在编译cef库的时候遇到的一些问题:

  • 因为我的项目是基于qt的来做的,而qt的运行时库是MDd类型的,因此cef3编译的时候也应该遵循这个运行时库的编译方式

  • 我在网上也看到了不少介绍创建cef项目的办法,不过个人觉得好多都是只讲过程,不讲原理,其实使用这个库很简单,我直- 说debug模式,release照搬。首先拷贝exe执行所需资源文件和运行时库(Resources目录下的除include文件夹、Debug目录下所有动态库),然后拷贝连接器的静态库(out/Debug/lib目录下的静态库、Debug目录下的所有静态库)

  • 根据个人使用工具的不同自行包含头文化和静态库,我使用的是vs2013,工程属性->配置属性->VC++目录,添加包含目录和库目录,在工程属性->配置属性->连接器->输入,页面附加依赖项添加依赖动态库

以上这3项基本就满足了创建包含cef项目的所有注意事项,如果自己想定制更好使用的控件,则需要相应的代码级别操作,接下来,我就讲讲代码级别基本的操作,复杂的操作我现在也了解不多。

  • 注释:关于libCef库中每个类的作用,我就不多说了,自己网上随便一搜索一大堆,在这里我直说几个重要的,在我的项目里使用到的:

  • CefDownloadHandler:下载回调类,当web页面上有文件下载的时候,会调用该类中的相应接口。注意一点,cef库默认是禁止了文件下载,如果想要响应这一事件,需要在OnBeforeDownload重写接口中加入代码:callback->Continue(suggested_name, true);

  • CefClient:获取注册回调类

  • CefDisplayHandler:地址、标题等改变调用接口,重写此类可以处理导航相关事件

  • CefRenderProcessHandler:渲染进程,当浏览器创建的时候,该类中的接口会被调用,因此可是在该类的接口中注册方法或者对象到web。包含webkit初始化、导航、上下文创建等回调接口

  • CefBrowserProcessHandler:浏览器进程,上下文初始化、渲染进程创建等回调接口

cef库嵌入已有工程步骤:

1、首先需要自己集成QWidget,重写一个web窗口,如图1所示;

2、main.cpp函数添加如图2所示方法,main方法中初始化Cef库,代码如下,退出时调用CefQuit();

int result = CefInit(argc, argv);
if (result >= 0)
{
return result;
}
CefLoadPlugins(IsWow64());

3、QCefWebView,重写ClientApp::RenderDelegate的方法OnContextCreated,完成对象和方法的注册,代码如图3,图中CefMapV8handler是js在调用该接口时的回调类,该类继承自CefV8Handler,我们只需要重写该类中的Execute接口,然后根据参数name来获取js调用的是qt的哪个接口,如图4所示。

4、最后也是最终的部分,我贴上cef库初始化和我自己封装的类文件源代码,当然了,有很大一部分代码也是从网上找的

相关代码如下

cefclient.h如下:

// Initialize CEF.
int CefInit(int &argc, char **argv); // Load web plugins.
void CefLoadPlugins(bool isWow64); // Quit CEF.
void CefQuit(); // Quit CEF until all browser windows have closed.
void CefQuitUntilAllBrowserClosed(); // Returns the application working directory.
QString AppGetWorkingDirectory(); // Notify all browser windows have closed.
void NotifyAllBrowserClosed();

cefclient.cpp如下

namespace {
// Initialize the CEF settings.
void CefInitSettings(CefSettings& settings)
{
// Make browser process message loop run in a separate thread.
settings.multi_threaded_message_loop = true;
// Store cache data will on disk.
std::string cache_path = QString2StdStr(AppGetWorkingDirectory()) + "/.cache";
CefString(&settings.cache_path) = CefString(cache_path);
// Completely disable logging.
settings.log_severity = LOGSEVERITY_DISABLE;
// The resources(cef.pak and/or devtools_resources.pak) directory.
CefString(&settings.resources_dir_path) = CefString();
// The locales directory.
CefString(&settings.locales_dir_path) = CefString();
// Enable remote debugging on the specified port.
settings.remote_debugging_port = 8088;
// Ignore errors related to invalid SSL certificates.
//settings.ignore_certificate_errors = true;
} } // namespace CefRefPtr g_handler;
CefRefPtr g_appHandler; int CefInit(int &argc, char **argv)
{
qDebug() << __FUNCTION__;
HINSTANCE hInstance = (HINSTANCE)GetModuleHandle(NULL); CefMainArgs main_args(hInstance);
g_appHandler = new ClientApp;
CefRefPtr app(g_appHandler); // Execute the secondary process, if any.
int exit_code = CefExecuteProcess(main_args, app.get(), NULL);
if (exit_code >= 0)
return exit_code; CefSettings settings;
CefInitSettings(settings); #ifndef SUB_PROCESS_DISABLED
// Specify the path for the sub-process executable.
CefString(&settings.browser_subprocess_path).FromASCII("cefclient_process.exe");
#endif settings.single_process = true; settings.no_sandbox = true;
settings.multi_threaded_message_loop = true; // Initialize CEF.
CefInitialize(main_args, settings, app.get(), NULL); g_handler = new ClientHandler(); return -1;
} void CefLoadPlugins(bool isWow64)
{
CefString flash_plugin_dir = isWow64 ? "C:\\Windows\\SysWOW64\\Macromed\\Flash"
: "C:\\Windows\\System32\\Macromed\\Flash";
CefAddWebPluginDirectory(flash_plugin_dir); CefRefreshWebPlugins();
} void CefQuit()
{
qDebug() << __FUNCTION__;
// Shut down CEF.
CefShutdown();
}

qcefwebview.h如下:

class QCefWebView : public QWidget
, public ClientHandler::Listener
, public ClientApp::RenderDelegate
{
Q_OBJECT public:
enum BrowserState
{
kNone,
kCreating,
kCreated,
}; static const QString kUrlBlank; QCefWebView(QWidget* parent = 0);
virtual ~QCefWebView(); void load(const QUrl& url);
void setHtml(const QString& html, const QUrl& baseUrl = QUrl());
QUrl url() const; public slots:
void back();
void forward();
void reload();
void stop(); QVariant evaluateJavaScript(const QString& scriptSource);//执行js脚本 signals:
void titleChanged(QString title);
void urlChanged(QUrl url);
void loadStarted();
void loadFinished(bool ok);
void webRequest(const QString & title); void navStateChanged(bool canGoBack, bool canGoForward);
void jsMessage(QString name, QVariantList args); protected:
virtual void resizeEvent(QResizeEvent*);
virtual void closeEvent(QCloseEvent*);
virtual void showEvent(QShowEvent*);
virtual void customEvent(QEvent*);
//ClientHandler::Listener
virtual void OnAddressChange(const QString& url);
virtual void OnTitleChange(const QString& title);
virtual void SetLoading(bool isLoading);
virtual void SetNavState(bool canGoBack, bool canGoForward);
virtual void OnAfterCreated();
virtual void OnMessageEvent(MessageEvent* e);
virtual void OnWebRequest(const QString & order); //ClientApp::RenderDelegate
virtual void OnContextCreated(CefRefPtr app,
CefRefPtr browser,
CefRefPtr frame,
CefRefPtr context);
private:
bool CreateBrowser(const QSize& size);
CefRefPtr GetBrowser() const;
void ResizeBrowser(const QSize& size);
bool BrowserLoadUrl(const QUrl& url); BrowserState browser_state_;
bool need_resize_;
bool need_load_;
QUrl url_;
QMutex mutex_; Q_DISABLE_COPY(QCefWebView);
IMPLEMENT_REFCOUNTING(QCefWebView);
};

qcefwebview.cpp如下:

extern CefRefPtr g_handler;
extern CefRefPtr g_appHandler; const QString QCefWebView::kUrlBlank = "about:blank"; QCefWebView::QCefWebView(QWidget* parent)
: QWidget(parent),
browser_state_(kNone),
need_resize_(false),
need_load_(false)
{
setAttribute(Qt::WA_NativeWindow);
setAttribute(Qt::WA_DontCreateNativeAncestors);
} QCefWebView::~QCefWebView()
{
} void QCefWebView::load(const QUrl& url)
{
url_ = url;
switch (browser_state_)
{
case kNone:
CreateBrowser(size()); break;
case kCreating:
// If resizeEvent()/showEvent() before you load a url, it will
// CreateBrowser() as soon as possible with "about:blank".
need_load_ = true; break;
default: // The browser should have been created.
BrowserLoadUrl(url);
}
} void QCefWebView::setHtml(const QString& html, const QUrl& baseUrl)
{
if (GetBrowser().get())
{
QUrl url = baseUrl.isEmpty() ? this->url() : baseUrl;
if (!url.isEmpty())
{
CefRefPtr frame = GetBrowser()->GetMainFrame();
frame->LoadString(CefString(html.toStdWString()),
CefString(url.toString().toStdWString()));
}
}
} QUrl QCefWebView::url() const {
if (GetBrowser().get())
{
CefString url = GetBrowser()->GetMainFrame()->GetURL();
return QUrl(QString::fromStdWString(url.ToWString()));
}
return QUrl();
} void QCefWebView::back()
{
CefRefPtr browser = GetBrowser();
if (browser.get())
browser->GoBack();
} void QCefWebView::forward()
{
CefRefPtr browser = GetBrowser();
if (browser.get())
browser->GoForward();
} void QCefWebView::reload()
{
CefRefPtr browser = GetBrowser();
if (browser.get())
browser->Reload();
} void QCefWebView::stop()
{ CefRefPtr browser = GetBrowser();
if (browser.get())
browser->StopLoad();
} QVariant QCefWebView::evaluateJavaScript(const QString& scriptSource)
{
if (GetBrowser().get())
{
CefString code(scriptSource.toStdWString());
GetBrowser()->GetMainFrame()->ExecuteJavaScript(code, "", 0);
return true;
}
return false;
} void QCefWebView::resizeEvent(QResizeEvent* e)
{
switch (browser_state_)
{
case kNone:
CreateBrowser(e->size()); break;
case kCreating:
need_resize_ = true; break;
default:
ResizeBrowser(e->size());
}
} void QCefWebView::closeEvent(QCloseEvent* e)
{
if (g_handler.get() && !g_handler->IsClosing())
{
CefRefPtr browser = g_handler->GetBrowser();
if (browser.get())
{
browser->GetHost()->CloseBrowser(false);
}
}
e->accept();
} void QCefWebView::showEvent(QShowEvent* e)
{
CreateBrowser(size());
} void QCefWebView::customEvent(QEvent* e)
{
if (e->type() == MessageEvent::MessageEventType)
{
MessageEvent* event = static_cast(e);
QString name = event->name();
QVariantList args = event->args();
emit jsMessage(name, args);
}
} void QCefWebView::OnAddressChange(const QString& url)
{
if (url != "about:blank")
{
emit urlChanged(QUrl(url));
}
} void QCefWebView::OnTitleChange(const QString& title)
{
emit titleChanged(title);
} void QCefWebView::SetLoading(bool isLoading)
{
if (isLoading)
{
if (!need_load_ && !url_.isEmpty())
emit loadStarted();
}
else
{
if (need_load_)
{
BrowserLoadUrl(url_);
need_load_ = false;
}
else if (!url_.isEmpty())
{
emit loadFinished(true);
}
}
} void QCefWebView::SetNavState(bool canGoBack, bool canGoForward)
{
emit navStateChanged(canGoBack, canGoForward);
} void QCefWebView::OnAfterCreated()
{
browser_state_ = kCreated;
if (need_resize_)
{
ResizeBrowser(size());
need_resize_ = false;
}
} void QCefWebView::OnMessageEvent(MessageEvent* e)
{
QCoreApplication::postEvent(this, e, Qt::HighEventPriority);
} void QCefWebView::OnWebRequest(const QString & order)
{
emit webRequest(order);
} bool QCefWebView::CreateBrowser(const QSize& size)
{
if (browser_state_ != kNone || size.isEmpty())
{
return false;
}
mutex_.lock();
if (browser_state_ != kNone)
{
mutex_.unlock();
return false;
}
RECT rect;
rect.left = 0;
rect.top = 0;
rect.right = size.width();
rect.bottom = size.height(); CefWindowInfo info;
CefBrowserSettings settings; info.SetAsChild((HWND)this->winId(), rect); g_handler->set_listener(this);
g_appHandler->insertMainRenderDelegate(this); QString url = url_.isEmpty() ? kUrlBlank : url_.toString();
CefBrowserHost::CreateBrowser(info,
g_handler.get(),
CefString(url.toStdWString()),
settings,
NULL); browser_state_ = kCreating;
mutex_.unlock();
return true;
} CefRefPtr QCefWebView::GetBrowser() const
{
CefRefPtr browser;
if (g_handler.get())
browser = g_handler->GetBrowser();
return browser;
} void QCefWebView::ResizeBrowser(const QSize& size)
{
if (g_handler.get() && g_handler->GetBrowser())
{
CefWindowHandle hwnd =
g_handler->GetBrowser()->GetHost()->GetWindowHandle();
if (hwnd)
{
HDWP hdwp = BeginDeferWindowPos(1);
hdwp = DeferWindowPos(hdwp, hwnd, NULL,
0, 0, size.width(), size.height(),
SWP_NOZORDER);
EndDeferWindowPos(hdwp);
}
}
} bool QCefWebView::BrowserLoadUrl(const QUrl& url)
{
if (!url.isEmpty() && GetBrowser().get())
{
CefString cefurl(url_.toString().toStdWString());
GetBrowser()->GetMainFrame()->LoadURL(cefurl);
return true;
}
return false;
} void QCefWebView::OnContextCreated(CefRefPtr app
, CefRefPtr browser
, CefRefPtr frame
, CefRefPtr context)
{
CefRefPtr object = context->GetGlobal(); CefRefPtr myV8Acc = new QCefV8Accessor;
CefRefPtr val = CefV8Value::CreateString(L"videoHandler");
CefString cefException;
myV8Acc->Set(L"videoHandler", object, val, cefException);
CefRefPtr pObjApp = CefV8Value::CreateObject(myV8Acc);
object->SetValue(L"videoHandler", pObjApp, V8_PROPERTY_ATTRIBUTE_NONE); CefRefPtr mapHandler = new CefMapV8Handler(g_appHandler); CefRefPtr ExecuteRequestOrder =
CefV8Value::CreateFunction("ExecuteRequestOrder", mapHandler); object->SetValue("ExecuteRequestOrder", ExecuteRequestOrder, V8_PROPERTY_ATTRIBUTE_NONE);
}

注意:以上代码只提供了一个思路,还有部分处理代码我没有贴出来,但是在论述的时候我都已经提到了,有C++基础的就基本可以搞定了,上述功能完成后就基本可以实现嵌入web的功能了。

后续

上述并不完美,因为如果需要在多个窗口上创建cef浏览器部件的话,在消息监听部分没有办法做区分多个浏览器事件,在这篇文章中我将会把上一篇相关文章中的程序结构稍作改动,以支持多个主浏览器窗口,主要是针对不同的浏览器产生事件时分发给不同的监听者,说到这儿,有的读者应该就能想到了,监听者肯定是一个数组了,而且 个数和浏览器数目一样多。

在接着往一下看的时候,我默认大家都已经完成创建浏览器并可以成功监听事件。

我先讲下思路:主要围绕每一个窗口创建一个浏览器每一个浏览器同时又是一个监听者。当显示窗口的时候,创建浏览器,并把当前窗口的id和指针保存到ClientHandler对象中,在浏览器创建成功并回调OnAfterCreated接口时,使用当前浏览器句柄匹配保存在ClientHandler中的浏览器父窗口句柄,如果匹配成功,则把以监听者为键,以浏览器为值的简直对保存起来,这个键值对是作为后面libcef事件回调时通知指定监听者的依据。

上面的思路可能理解起来有点儿绕,接下来我直接上关键性代码,希望大家能更好的理解

typedef std::map ListenerHwnds; 

ListenerHwnds listenerHwnds_;//浏览器父窗口、监听者对

typedef std::map > ListenerBrowserMap;

ListenerBrowserMap m_MainBrowsers;//监听者、浏览器对

图1 是浏览器创建成功后的回调,在该方法中添加监听者/浏览器对到ClientHandler中,当浏览器发生事件回调的时候,调用指定的监听者

图2 的代码是设置是否进行加载,根据ClientHanlder中已经注册的监听者/浏览器对,把浏览器事件发送给指定监听者

图3 是有两个方法,方法1是保存监听者到CliendHandler对象中,在方法在浏览器创建之前调用;方法2是获取当前所有浏览器

图4 是创建浏览器代码,在调用CreateVrowser方法创建浏览器之前需要保存窗口句柄和指针到ClientHandler对象中,以备图1所示代码所用,保存代码如上图中有下划线部分。

由于在上一篇相关文档中已经可以成功创建出浏览器,故这篇文章跳跃性比较大,如果有不理解的地方可以去看上一篇文章。

至此,文章都是个人理解,有问题请留言

qt添加cef库嵌入web [转]的更多相关文章

  1. qt添加cef库嵌入web,linux 下Qt WebEngine 程序打包简单记录

    http://www.cnblogs.com/oloroso/p/6051631.html http://www.cnblogs.com/oloroso/p/6149000.html

  2. Qt 添加外部库文件(四种方法)

    Qt添加外部库文件, 一种就是直接加库文件的绝对路劲,这种方法简单,但是遇到多个库文件的时候,会很麻烦,而且,如果工程移动位置以后还需要重新配置 另一种就是相对路径了,不过Qt 编译的文件会在一个单独 ...

  3. 【转】QT 添加外部库文件

    转自:Qt 添加外部库文件 LIBS += D:\Code\Opengltest\OpenGL32.Lib D:\Code\Opengltest\GlU32.Lib # 直接加绝对路径 LIBS += ...

  4. Qt 添加 QtNetwork 库文件

    Qt应用程序默认没有加QtNetwork库.如下图: 在开发过程中,因处理业务需要手动添加QtNetwork库.根据常见情况分为以下两种: [1]若使用QTCreator开发程序 在工程的pro文件中 ...

  5. QT 添加 lib库

    扒自网友文章: 一.添加第三方的头文件 首先,添加头文件  #include "ControlCAN.h" 然后,再将这个头文件放到工程的目录下 二.添加.lib文件 首先,将.l ...

  6. QT 添加外部库文件

    LIBS += D:\Code\Opengltest\OpenGL32.Lib D:\Code\Opengltest\GlU32.Lib LIBS += OpenGL32.Lib GlU32.Lib  ...

  7. qt cef嵌入web(二)

    在qt cef嵌入web文章中已经讲述了怎么把cef页面嵌入到qt程序中,但是这样并不完美,因为如果需要在多个窗口上创建cef浏览器部件的话,在 消息监听部分没有办法做区分多个浏览器事件,在这篇文章中 ...

  8. QT中添加 动态库(.so) 和 静态库 (.a) 的方法

    在QT 的Makefile文件中: 1 添加动态库,如lipcap.so 则,在LIBS一行中添加“-L/usr/local/lib -lpcap”,依据自己的情况修改libpcap.so的路径 2 ...

  9. Qt中添加静态库.lb,.a和动态库.dll,.so,头文件和.cpp文件

    添加步骤 1.-Qt Creator中,"项目"------"添加库"2.把静态库和动态库文件放到项目文件夹中3.在.pro文件中会添加如下代码: - 添加动态 ...

随机推荐

  1. AppBox_v2.0完整版免费下载,暨AppBox_v3.0正式发布!

    文章更新: AppBox v6.0中实现子页面和父页面的复杂交互 AppBox 是基于 FineUI 的通用权限管理框架,包括用户管理.职称管理.部门管理.角色管理.角色权限管理等模块. AppBox ...

  2. 关于mybatis中mapper.xmlSQL语句书写的心得

    本文主要针对MySQL ---------------------------------------------------------------------------------------- ...

  3. JavaIO学习笔记(五)

    JavaIO前期准备 什么是同步 指的是用户进程触发IO操作并等待或者轮询的去查看IO操作是否就绪 什么是异步 异步是指用户进程触发IO操作以后便开始做自己的事情,而当IO操作已经完成的时候会得到IO ...

  4. java学习笔记之正则表达式

    一般来说,正则表达式就是以某种方式来描述字符串,因此你可与说:"如果一个字符串符合有这些东西,那么它就是我正在找的东西. 1.要找一个数字,如果它可能有一个负号在最前面(可能没有,没有也是匹 ...

  5. CodeForces - 453A Little Pony and Expected Maximum

    http://codeforces.com/problemset/problem/453/A 题目大意: 给定一个m面的筛子,求掷n次后,得到的最大的点数的期望 题解 设f[i]表示掷出 <= ...

  6. bzoj3157国王奇遇记(秦九韶算法+矩乘)&&bzoj233AC达成

    bz第233题,用一种233333333的做法过掉了(为啥我YY出一个算法来就是全网最慢的啊...) 题意:求sigma{(i^m)*(m^i),1<=i<=n},n<=10^9,m ...

  7. JavaScript系列文章:自动类型转换

    我们都知道,JavaScript是类型松散型语言,在声明一个变量时,我们是无法明确声明其类型的,变量的类型是根据其实际值来决定的,而且在运行期间,我们可以随时改变这个变量的值和类型,另外,变量在运行期 ...

  8. strchr()函数 和 strrchr() 函数

    strchr 定义于头文件 <string.h>char *strchr( const char *str, int ch );寻找ch(按照如同(char)ch的方式转换成char后)在 ...

  9. 2016福州大学软件工程第五、六次团队作业-Alpha阶段成绩汇总

    1.本次作业成绩统计结果: 本次Alpha阶段团队作业公布如下: 表格说明: PE:贡献百分比 YS:演示评分(满分15分) BK:博客评分(满分15分) SH:事后诸葛亮环节(满分5分) P:个人分 ...

  10. 关于SQL的相关笔记【长期更新,只发一帖】

    场景[1]多表联查时,主表与关联表同时与同一张(第三张表)有关联,类似三角恋关系- - 涉及表: HOUSE:记录了房屋信息 ROOMS:记录了房间信息 HOUSE_STATUS:记录了状态信息的中文 ...