**Cef视图(CefView)**是指在使用Chromium Embedded Framework(CEF)时,嵌入到应用程序中的浏览器视图。CEF是一个开源项目,它基于Google的Chromium浏览器,允许开发者将Web浏览器功能嵌入到自己的应用程序中。
html文件
<!doctype html>
<html lang="en">
<head><meta charset="UTF-8"><title>Login</title><link rel="stylesheet" type="text/css" href="Login.css"/>
</head>
<body onload="onLoad()" id="main" class="noselect"><div id="login"><!-- 消息发送区 --><form method="post"><input id="account" type="text" required placeholder="请输入" name="u"><button id="sendBtn" class="but" type="button" onclick="onCallBridgeQueryClicked('html')">发送</button><textarea id="output" required placeholder="内容" name="t"></textarea></form><!-- 测试按钮区 --><button class="but" type="button" onclick="onInvokeMethodClicked('message1', '标题', '这是message1', 'a', 1.1)">Message1</button><button class="but" type="button" onclick="onInvokeMethodClicked('message2', '标题', '这是message2', 'B', 2.2)">Message2</button></div><script>// ==================== 事件处理函数 ==================== ///*** 处理Qt发送的apChange事件* @param {string} flag 事件标识* @param {...any} arg 可变参数列表*///该函数响应"apChange"信号,负责将接收到的消息显示在页面的文本区域(output)中function ap(flag, ...arg) {const mess = arg[0]; // 获取第一个参数if (mess) {const output = document.getElementById('output'); //获取页面上的输出文本框//格式化消息:"[flag]: [mess]"const newText = `${flag}: ${mess}`;//将接收到的消息显示在页面的文本区域(output)中output.value = output.value ? `${output.value}\n${newText}` // 非空时换行追加: newText; // 空时直接显示}}/*** 处理Qt发送的sendFailChange事件(错误消息)*/function sendFail(flag, ...arg) {const output = document.getElementById('output');const newText = `${flag}: ${arg[0]}`;output.value = output.value ? `${output.value}\n${newText}` : newText;}// ==================== 初始化函数 ==================== ///*** 页面加载完成后初始化CEF通信*/function onLoad() {if (typeof CallBridge === "undefined") {alert("Not in CefView context");return;}// 注册Qt事件监听CallBridge.addEventListener("apChange", ap); //apchange信号绑定ap函数CallBridge.addEventListener("sendFailChange", sendFail);}// ==================== JS → Qt通信 ==================== ///*** 调用Qt方法(无返回值)* @param {string} name 方法名* @param {...any} arg 可变参数*/function onInvokeMethodClicked(name, ...arg) {window.CallBridge.invokeMethod(name, ...arg);}/*** 发送查询请求到Qt(需要返回值)*/function onCallBridgeQueryClicked(name) {const message = document.getElementById("account").value.trim();if (!message) return;// 清空输入框document.getElementById("account").value = '';// 更新本地消息显示const output = document.getElementById('output');const newText = `${name}: ${message}`;output.value = output.value ? `${output.value}\n${newText}` : newText;// 构建并发送请求window.CefViewQuery({request: `${name}|${message}`,onSuccess: (response) => console.log("成功:", response),onFailure: (code, msg) => {output.value += `\n错误(${code}): ${msg}`;}});}</script>
</body>
</html>
css文件
html{ width: 100%; height: 100%; overflow: hidden; font-style: sans-serif;
}
body{ width: 100%; height: 100%; font-family: 'Open Sans',sans-serif; margin: 0; background-color: #0f8fdf;
}
#login{ position: absolute; top: 50%; left:50%; margin: -150px 0 0 -150px; width: 300px; height: 300px;
}
#login h1{ color: #fff; text-shadow:0 0 10px; letter-spacing: 1px; text-align: center;
}
h1{ font-size: 2em; margin: 0.67em 0;
}
input{ width: 278px; height: 18px; margin-bottom: 10px; outline: none; padding: 10px; font-size: 13px; color: #fff; //text-shadow:1px 1px 1px; border-top: 1px solid #312E3D; border-left: 1px solid #312E3D; border-right: 1px solid #312E3D; border-bottom: 1px solid #56536A; border-radius: 4px; background-color: #2D2D3F;
}
.but{ width: 300px; min-height: 20px; display: block; background-color: #9cc7e3; border: 1px solid #9cc7e3; color: #fff; padding: 9px 14px; font-size: 15px; line-height: normal; border-radius: 5px; margin: 0;
} textarea{ width: 300px; height: 200px; margin-bottom: 10px; outline: none; resize: none; pointer-events: none; font-size: 13px; border-top: 1px solid #312E3D; border-left: 1px solid #312E3D; border-right: 1px solid #312E3D; border-bottom: 1px solid #56536A; border-radius: 4px;
}
Qt应用程序中嵌入Cef浏览器
获取html文件路径:
//获取html文件路径QDir dir = QCoreApplication::applicationDirPath();QString path = QDir::toNativeSeparators(dir.filePath("html"));
将本地html文件夹映射为一个URL,在cef浏览器里加载我们的html文件
QCefContext::instance()->addLocalFolderResource(path, "my://cpp_learners");
new一个cef浏览器对象,加载指定html文件
cefViewWidget = new QCefView("my://cpp_learners/QCefViewTest.html", &setting, this);
把cef浏览器嵌入QT界面
QGridLayout* layout = new QGridLayout(this);layout->addWidget(cefViewWidget, 0, 0, 1, 1);ui.widgetHtml->setLayout(layout);
Qt → HTML 通信详解
Qt主动向html发送数据:
QVariantList data;QString str=ui.lineEditInput->text(); data << "qt" << "str";QCefEvent event("apChange"); //创建事件,名称为"apChange"event.setArguments(list); //绑定发给前端的数据cefViewWidget->broadcastEvent(event); //发送事件到前端ui.lineEditInput->clear(); //清空输入框
HTML接收
// 注册事件监听function onLoad() //页面加载完成后初始化CEF通信{CallBridge.addEventListener("apChange", ap); //apchange信号绑定ap函数}//该函数响应"apChange"信号,负责将接收到的消息显示在页面的文本区域(output)中function ap(flag, ...arg) {const mess = arg[0]; // 获取第一个参数if (mess) {const output = document.getElementById('output'); //获取页面上的输出文本框//格式化消息:"[flag]: [mess]"const newText = `${flag}: ${mess}`;//将接收到的消息显示在页面的文本区域(output)中output.value = output.value ? `${output.value}\n${newText}` // 非空时换行追加: newText; // 空时直接显示}}
HTML → Qt 通信详解
HTML端发送
单击发送按钮会触发onCallBridgeQueryClicked槽函数:
<button id="loginBtn" class="but" type="button" onclick="onCallBridgeQueryClicked('html')">发送</button>
onCallBridgeQueryClicked函数内部操作
(1) 获取用户输入
var message = document.getElementById("account").value;
(2) 构建请求字符串
var str = "html" + "|" + message; // 例如:"html|你好Qt"
(3) 发送到Qt(通过CEF的 CefViewQuery
API发送数据)
window.CefViewQuery({request: str, // 要发送的数据onSuccess: on_success, // 成功回调(当前未实现具体逻辑)onFailure: on_failure // 失败回调(会显示错误信息)
});
QT端接收
- 当 HTML/JavaScript 调用
window.CefViewQuery()
时,CEF 会触发cefQueryRequest
信号。 - Qt 通过连接这个信号到
onQCefQueryRequest
槽函数,接收并处理来自网页的请求。
// 绑定信号CefViewQueryconnect(cefViewWidget, &QCefView::cefQueryRequest, this, &QCefView_Test::onQCefQueryRequest);
onQCefQueryRequest函数(接收html发来的数据)
void QCefView_Test::onQCefQueryRequest(int browserId, int frameId, const QCefQuery& query) {// 分割字符串auto parts = query.request().split("|");QString messageType = parts[0]; // "html"QString content = parts[1]; // 用户输入的内容// 处理消息...query.setResponseResult(true, "处理成功"); // 可选:返回响应cefViewWidget->responseQCefQuery(query); // //给js返回结果
}
更新ui
// 在output文本区域显示发送的内容
document.getElementById('output').value += "\nhtml: " + message;
js->Qt发送弹窗
单击Message按钮调用onInvokeMethodClicked槽函数
<button class="but" type="button" onclick="onInvokeMethodClicked('message1', '标题', '这是message1', 'a', 1.1)">
onInvokeMethodClicked()函数进行js调用
function onInvokeMethodClicked(name, ...arg) {window.CallBridge.invokeMethod(name, ...arg);}
具体调用内容:
window.CallBridge.invokeMethod("message1", "标题", // → str1"这是消息1", // → str2"这是消息2", // → str31.1 // → f1
);
- 当 HTML/JavaScript 调用
window.CallBridge.invokeMethod()
时,CEF 会触发invokeMethod
信号。 - Qt 通过连接这个信号到
onInvokeMethod
槽函数,执行对应的逻辑。
connect
// 绑定信号invokeMethodconnect(cefViewWidget, &QCefView::invokeMethod, this, &QCefView_Test::onInvokeMethod);
onInvokeMethod()函数
void QCefView_Test::onInvokeMethod(int browserId, int frameId, const QString& method, const QVariantList& arguments)
{
if (0 == method.compare("message1")) {// 从参数列表提取数据QString str1 = arguments[0].toString(); // 第1个参数 → 标题QString str2 = arguments[1].toString(); // 第2个参数 → 消息1QString str3 = arguments[2].toString(); // 第3个参数 → 消息2float f1 = arguments[3].toFloat(); // 第4个参数 → 浮点数int in1 = arguments.size(); // 参数总数// 在Qt界面显示日志ui.textEditText->append("您有消息弹窗提醒1"); // 格式化消息并弹窗QString str = QString("%1,%2,%3,%4").arg(str2).arg(str3).arg(f1).arg(in1);QMessageBox::information(this, str1, str); // 弹窗显示
}
}
代码仓库:
https://gitee.com/sun-Penghu/qt/tree/master/QCefView1