Qt和JavaScript使用QWebChannel交互一——和Qt内嵌网页交互

发布时间:2021年10月03日 阅读:13 次

前言

    Qt提供了QWebChannel来和网页进行通信,只需要注册自定义对象一下,就可以直接绑定信号槽来进行Qt程序和网页之前的通信,非常方便
    下面使用一个案例来学习QWebChannel
    环境:vs2017+Qt5.11.2 写Qt代码,vsCode写js代码


一、效果

直接上图

Qt和JavaScript使用QWebChannel交互一——和Qt内嵌网页交互 javascript 第1张

二、实现过程

1. Qt端

  1. 新建一个Qt Gui项目,取名WebChannelDemo
    Qt和JavaScript使用QWebChannel交互一——和Qt内嵌网页交互 javascript 第2张

  2. 一路下一步,最后选择QWidget基类
    Qt和JavaScript使用QWebChannel交互一——和Qt内嵌网页交互 javascript 第3张

  3. 得到一个这样结构的项目
    Qt和JavaScript使用QWebChannel交互一——和Qt内嵌网页交互 javascript 第4张

  4. 使用Qt Designer打开webchanneldemo.ui,拖一个界面
    Qt和JavaScript使用QWebChannel交互一——和Qt内嵌网页交互 javascript 第5张

  5. 这个时候再来创建一个用于通过WebChannel通信的WebTransport类,基类选择QObject
    Qt和JavaScript使用QWebChannel交互一——和Qt内嵌网页交互 javascript 第6张
    Qt和JavaScript使用QWebChannel交互一——和Qt内嵌网页交互 javascript 第7张

  6. 为了方便,不用每次使用都创建一个WebTransport对象,我们将这个类写成单例类,然后定义和js交互的信号的槽函数,最近定义一个宏来使用实例

#pragma once#include <QObject>class WebTransport : public QObject
{	Q_OBJECT	WebTransport(QObject *parent = nullptr);
	~WebTransport();public:	// 获取实例
	static WebTransport* instance();
signals:	// 向js发送信号
	void msgToJs(const QString& msg);	// 将从js接收的数据发送出去
	void receviedJsMsg(const QString& msg);public slots:	// js调用此函数,接收js传入的数据
	void msgToQt(const QString& msg);
};// 定义一个宏#ifndef WEB_TRSPT#define WEB_TRSPT WebTransport::instance()#endif // !WEB_TRSPT
  1. 最后,我们到WebChannelDemo类中来初始化一下,主要做了以下几件事:

    1. 关联信号槽

    2. 实例化一个QWebChannel对象

    3. WebTransport单例对象注册到QWebChannel

    4. QWebChannel对象设置到网页中去

    5. 最后再加载本地网页

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);
	});	// 构造一个channel对象
	QWebChannel* channel = new QWebChannel(this);	// 向channel对象注册自定义对象
	channel->registerObject(QStringLiteral("webBridge"), WEB_TRSPT);	// 使用webview的page设置channel对象
	ui.webEngineView->page()->setWebChannel(channel);	// 最后加载网页
    ui.webEngineView->load(qApp->applicationDirPath()+"/channel/index.html");
}

2. 网页端

  1. 先创建一个channel目录,放在项目生成目录,和生成的可执行文件同级
    Qt和JavaScript使用QWebChannel交互一——和Qt内嵌网页交互 javascript 第8张

  2. 使用vsCode打开这个目录,并创建如下项目结构
    Qt和JavaScript使用QWebChannel交互一——和Qt内嵌网页交互 javascript 第9张

  3. 打开index.html文件,先编写一个这样的界面
    Qt和JavaScript使用QWebChannel交互一——和Qt内嵌网页交互 javascript 第10张

<!DOCTYPE html><html><head>
    <meta charset="UTF-8">
    <!-- 样式 -->
    <link rel="stylesheet" href="./css/style.css">
    <!-- js -->
    <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>
  1. 打开main.js文件,编写js代码,重点在初始化函数中,做了以下几件事

    1. 直接构造一个QWebChannel对象,用回调函数函数接收创建好的QWebChannel对象

    2. 使用QWebChannel对象获取Qt端注册的对象

    3. 给注册对象的信号绑定一个回调函数(槽函数),当Qt端注册的对象此信号发送时,回调函数被调用

    4. 调用注册对象的槽函数,给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');    // 调用Qt对象函数
    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) {            // 获取Qt注册的对象,设置到主窗口(这里的webTransport就是Qt端注册时的字符串id)
            window.webTransport = channel.objects.webTransport;            // 绑定注册对象的信号
            webTransport.msgToJs.connect(function(msg) {
                outputMsg("接收Qt信息:" + msg);
            });
            webTransport.msgToQt("初始化channel成功!");
        });
    } else {
        alert("初始化qwebchannel失败")
    }
}

三、过程中出现的问题

问题一

  1. 问题描述:运行时,Qt向Js端发送消息没有问题,Js端向Qt端发送消息时失败,会报如下错误

Cannot invoke unknown method of index -1 on object webTransport(0x...)
  1. 问题原因及解决办法:
    使用Qt 5.11.2编译生成的可执行程序,而网页端用的是Qt 5.14qwebchannel.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()函数加载网页时,内部会直接拿到设置好QWebChannelQWebEnginePage对象去加载网页

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);
}


Tag:javascript
相关文章

发表评论: