**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
槽函数,接收并处理来自网页的请求。
// 绑定信号CefViewQuery
connect(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", // → str3
1.1 // → f1
);
- 当 HTML/JavaScript 调用
window.CallBridge.invokeMethod()
时,CEF 会触发invokeMethod
信号。 - Qt 通过连接这个信号到
onInvokeMethod
槽函数,执行对应的逻辑。
connect
// 绑定信号invokeMethod
connect(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个参数 → 消息1
QString str3 = arguments[2].toString(); // 第3个参数 → 消息2
float 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