Qt和JavaScript使用QWebChannel交互一——和Qt内嵌网页交互
前言
????Qt 提供了QWebChannel 来和网页进行通信,只需要注册自定义对象一下,就可以直接绑定信号槽来进行Qt程序和网页之前的通信,非常方便 ????下面使用一个案例来学习QWebChannel ????环境 :vs2017+Qt5.11.2 写Qt 代码,vsCode写js 代码
一、效果
直接上图
二、实现过程
1. Qt端
- 新建一个
Qt Gui 项目,取名WebChannelDemo - 一路下一步,最后选择
QWidget 基类 - 得到一个这样结构的项目
- 使用
Qt Designer 打开webchanneldemo.ui ,拖一个界面 - 这个时候再来创建一个用于通过
WebChannel 通信的WebTransport 类,基类选择QObject - 为了方便,不用每次使用都创建一个
WebTransport 对象,我们将这个类写成单例 类,然后定义和js交互的信号的槽函数,最近定义一个宏来使用实例
#pragma once
#include <QObject>
class WebTransport : public QObject
{
Q_OBJECT
WebTransport(QObject *parent = nullptr);
~WebTransport();
public:
static WebTransport* instance();
signals:
void msgToJs(const QString& msg);
void receviedJsMsg(const QString& msg);
public slots:
void msgToQt(const QString& msg);
};
#ifndef WEB_TRSPT
#define WEB_TRSPT WebTransport::instance()
#endif
- 最后,我们到
WebChannelDemo 类中来初始化一下,主要做了以下几件事:
- 关联信号槽
- 实例化一个
QWebChannel 对象 - 将
WebTransport 单例对象注册到QWebChannel - 将
QWebChannel 对象设置到网页中去 - 最后再加载本地网页
void WebChannelDemo::setup()
{
connect(ui.pushButton, &QPushButton::clicked, [this]() {
ui.plainTextEdit->appendPlainText(QStringLiteral("发送消息到js:") + ui.lineEdit->text());
emit WEB_TRSPT->msgToJs(ui.lineEdit->text());
});
connect(WEB_TRSPT, &WebTransport::receviedJsMsg, [this](const QString& msg) {
ui.plainTextEdit->appendPlainText(QStringLiteral("接收js信息:") + msg);
});
QWebChannel* channel = new QWebChannel(this);
channel->registerObject(QStringLiteral("webBridge"), WEB_TRSPT);
ui.webEngineView->page()->setWebChannel(channel);
ui.webEngineView->load(qApp->applicationDirPath()+"/channel/index.html");
}
2. 网页端
- 先创建一个
channel 目录,放在项目生成目录,和生成的可执行文件同级 - 使用vsCode打开这个目录,并创建如下项目结构
- 打开
index.html 文件,先编写一个这样的界面
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="./css/style.css">
<script src="./js/qwebchannel.js"></script>
<script src="./js/main.js"></script>
</head>
<body>
<p>我是网页</p>
<textarea name="textArea" id="textArea"></textarea><br>
<input type="text" id="lineEdit"><br>
<button id="sendBtn" onclick="senBtnClicked();">发送</button>
<button id="clearBtn" onclick="clearBtnClicked();">清空</button>
</body>
</html>
- 打开
main.js 文件,编写js 代码,重点在初始化函数中,做了以下几件事
- 直接构造一个
QWebChannel 对象,用回调函数函数接收创建好的QWebChannel对象 - 使用
QWebChannel对象 获取Qt 端注册的对象 - 给注册对象的信号绑定一个回调函数(槽函数),当
Qt 端注册的对象此信号发送时,回调函数被调用 - 调用注册对象的槽函数,给
Qt 端发送消息,直接使用Qt 端注册对象的槽函数名来调用
function outputMsg(msg) {
var textArea = document.getElementById("textArea");
if (textArea) {
textArea.innerHTML = textArea.innerHTML + msg + "\n";
textArea.scrollTop = textArea.scrollHeight;
}
}
function senBtnClicked() {
var lineEdit = document.getElementById('lineEdit');
webTransport.msgToQt(lineEdit.value);
outputMsg("发送信息到Qt:" + lineEdit.value);
}
function clearBtnClicked() {
document.getElementById("textArea").innerHTML = "";
}
window.onload = function init() {
if (typeof qt !== "undefined") {
new QWebChannel(qt.webChannelTransport, function(channel) {
window.webTransport = channel.objects.webTransport;
webTransport.msgToJs.connect(function(msg) {
outputMsg("接收Qt信息:" + msg);
});
webTransport.msgToQt("初始化channel成功!");
});
} else {
alert("初始化qwebchannel失败")
}
}
三、过程中出现的问题
问题一
- 问题描述:运行时,Qt向Js端发送消息没有问题,Js端向Qt端发送消息时失败,会报如下错误
Cannot invoke unknown method of index -1 on object webTransport(0x...)
- 问题原因及解决办法:
使用Qt 5.11.2 编译生成的可执行程序,而网页端用的是Qt 5.14 的qwebchannel.js 文件,版本不兼容导致的,换成对应的qwebchannel.js 文件就好了
问题二
- 在加载本地网页时,为什么是先设置
QWebChannel 再加载网页? - 因为最开始的想法是,如果直接先使用
ui.webEngineView->page() 获取QWebEnginePage 对象,应该获取到的是个nullptr ,这个时候直接设置QWebChannel ,程序会崩掉,那我先加载网页,然后QWebEngineView 会内部构造一个QWebEnginePage 对象,我再通过page() 函数获取,这个时候肯定不是nullptr 了,再去设置QWebChannel ,这个过程简直完美,但是出人意料的是,按照这个过程设置的QWebChannel 并没有生效,也不能和js 交互 - 解决办法就是先设置
QWebChannel 再加载网页,通过查看Qt 源码发现,通过page() 函数获取QWebEnginePage 对象时,内部做出了判断,如果内部维护的QWebEnginePage 对象为空时,会直接构造一个QWebEnginePage 对象,并不会返回空指针,设置完QWebChannel 后再去调用load() 函数加载网页时,内部会直接拿到设置好QWebChannel 的QWebEnginePage 对象去加载网页
QWebEnginePage* QWebEngineView::page() const
{
Q_D(const QWebEngineView);
if (!d->page) {
QWebEngineView *that = const_cast<QWebEngineView*>(this);
that->setPage(new QWebEnginePage(that));
}
return d->page;
}
void QWebEngineView::load(const QUrl& url)
{
page()->load(url);
}
四、项目完整源码
https://gitee.com/doyoung126/qt_-demo.git
下一篇:Qt和JavaScript使用QWebChannel交互二——和浏览器打开的网页交互(还没写,抽时间写)
五、总结
总结:遇到凡事不要慌,先掏出手机拍个朋网友圈
|