qt cef嵌入web

    最近项目需要,研究了下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我就不过多解释,其实我也不太了解。刚开始使用的时候一定不能怕,可能有些人看了源码之后会发现源码异常的复杂,这个时候我建议网上多差点儿资料,因为我学的时候也是在网上找到了不少好的文章qt <wbr>cef嵌入web
    下边是我在编译cef库的时候遇到的一些问题:
  • 因为我的项目是基于qt的来做的,而qt的运行时库是MDd类型的,因此cef3编译的时候也应该遵循这个运行时库的编译方式
  • 我在网上也看到了不少介绍创建cef项目的办法,不过个人觉得好多都是只讲过程,不讲原理,其实使用这个库很简单,我直说debug模式,release照搬。首先拷贝exe执行所需资源文件和运行时库(Resources目录下的除include文件夹、Debug目录下所有动态库),然后拷贝连接器的静态库(out/Debug/lib目录下的静态库、Debug目录下的所有静态库)
  • 根据个人使用工具的不同自行包含头文化和静态库,我使用的是vs2013,工程属性->配置属性->VC++目录,添加包含目录和库目录,在工程属性->配置属性->连接器->输入,页面附加依赖项添加依赖动态库
    以上这3项基本就满足了创建包含cef项目的所有注意事项,如果自己想定制更好使用的控件,则需要相应的代码级别操作,接下来,我就讲讲代码级别基本的操作,复杂的操作我现在也了解不多qt <wbr>cef嵌入web
 
    注释:关于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());
qt <wbr>cef嵌入web
图1
qt <wbr>cef嵌入web
图2
qt <wbr>cef嵌入web
图3
qt <wbr>cef嵌入web
图4
       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);
}
View Code
 
注意:以上代码只提供了一个思路,还有部分处理代码我没有贴出来,但是在论述的时候我都已经提到了,有C++基础的就基本可以搞定了,上述功能完成后就基本可以实现嵌入web的功能了。
 
 
如果您觉得文章不错,不妨给个打赏,写作不易,感谢各位的支持。您的支持是我最大的动力,谢谢!!! 

 

  


很重要--转载声明

  1. 本站文章无特别说明,皆为原创,版权所有,转载时请用链接的方式,给出原文出处。同时写上原作者:朝十晚八 or Twowords
  2. 如要转载,请原文转载,如在转载时修改本文,请事先告知,谢绝在转载时通过修改本文达到有利于转载者的目的。 

posted @ 2016-06-27 10:53  朝十晚八  阅读(14355)  评论(0编辑  收藏  举报

返回顶部