想在 Qt 中集成 CEF (Chromium Embedded Framework) ,并且实现 JS 与 C++ 交互 ,本文是一个最小可运行的 Demo 示例。
⚠️ 提示:
-
代码展示了 Qt 调用 CEF,创建一个简单的浏览器窗口,并实现 JS 调用 C++ 和 C++ 调用 JS 的交互。
1. main.cpp
#include <QApplication>
#include "cef_app_qt.h"
#include "cef_browser_widget.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
// 初始化 CEF
CefEnableHighDPISupport();
CefMainArgs main_args(argc, argv);
CefRefPtr<CefAppQt> cefApp(new CefAppQt);
int exit_code = CefExecuteProcess(main_args, cefApp, nullptr);
if (exit_code >= 0) {
return exit_code; // 子进程
}
CefSettings settings;
settings.no_sandbox = true;
CefInitialize(main_args, settings, cefApp, nullptr);
// 创建 Qt + CEF 的窗口
CefBrowserWidget browser;
browser.resize(800, 600);
browser.show();
int result = app.exec();
// 关闭 CEF
CefShutdown();
return result;
}
2. cef_app_qt.h
#pragma once
#include "include/cef_app.h"
// 简单的 CefApp 派生类
class CefAppQt : public CefApp, public CefRenderProcessHandler {
public:
CefAppQt() = default;
CefRefPtr<CefRenderProcessHandler> GetRenderProcessHandler() override {
return this;
}
// 处理渲染进程中 JS 与 C++ 的交互
void OnContextCreated(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefV8Context> context) override {
CEF_REQUIRE_RENDERER_THREAD();
// 注册 JS -> C++ 的函数
CefRefPtr<CefV8Value> global = context->GetGlobal();
CefRefPtr<CefV8Handler> handler = new JsHandler();
global->SetValue("callCpp", CefV8Value::CreateFunction("callCpp", handler), V8_PROPERTY_ATTRIBUTE_NONE);
}
private:
class JsHandler : public CefV8Handler {
public:
JsHandler() = default;
bool Execute(const CefString& name,
CefRefPtr<CefV8Value> object,
const CefV8ValueList& arguments,
CefRefPtr<CefV8Value>& retval,
CefString& exception) override {
if (name == "callCpp") {
if (arguments.size() > 0 && arguments[0]->IsString()) {
std::string msg = arguments[0]->GetStringValue();
qDebug("JS 调用了 C++,传递参数: %s", msg.c_str());
// 返回一个值给 JS
retval = CefV8Value::CreateString("C++ 已收到: " + msg);
return true;
}
}
return false;
}
IMPLEMENT_REFCOUNTING(JsHandler);
};
IMPLEMENT_REFCOUNTING(CefAppQt);
};
3. cef_browser_widget.h
#pragma once
#include <QWidget>
#include "include/cef_browser.h"
#include "include/cef_client.h"
class CefBrowserWidget : public QWidget, public CefClient, public CefLifeSpanHandler {
Q_OBJECT
public:
CefBrowserWidget(QWidget* parent = nullptr) : QWidget(parent) {
CefWindowInfo window_info;
#if defined(Q_OS_WIN)
window_info.SetAsChild((HWND)winId(), {0, 0, width(), height()});
#endif
CefBrowserSettings browser_settings;
CefBrowserHost::CreateBrowser(window_info, this,
"file:///index.html", browser_settings, nullptr, nullptr);
}
CefRefPtr<CefLifeSpanHandler> GetLifeSpanHandler() override {
return this;
}
void OnAfterCreated(CefRefPtr<CefBrowser> browser) override {
CEF_REQUIRE_UI_THREAD();
this->browser = browser;
}
// C++ 调用 JS
void callJs(const std::string& msg) {
if (browser) {
CefRefPtr<CefFrame> frame = browser->GetMainFrame();
std::string jsCode = "onCppMessage('" + msg + "')";
frame->ExecuteJavaScript(jsCode, frame->GetURL(), 0);
}
}
private:
CefRefPtr<CefBrowser> browser;
IMPLEMENT_REFCOUNTING(CefBrowserWidget);
};
4. index.html
(放在可访问路径下)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Qt + CEF Demo</title>
<script>
// JS -> C++
function sendToCpp() {
let msg = document.getElementById("msg").value;
let result = callCpp(msg); // 调用绑定的 C++ 函数
alert("C++ 返回: " + result);
}
// C++ -> JS
function onCppMessage(text) {
alert("来自 C++ 的消息: " + text);
}
</script>
</head>
<body>
<h2>Qt + CEF + JS 交互 Demo</h2>
<input type="text" id="msg" value="你好 C++">
<button onclick="sendToCpp()">发送给 C++</button>
</body>
</html>
效果:
-
在 Qt 窗口中打开
index.html
。 -
输入内容并点击按钮,JS 调用
callCpp()
,C++ 收到参数并返回字符串。 -
C++ 可以调用
callJs("Hello from C++")
,触发 JS 函数onCppMessage()
。