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


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


    前言

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


    一、效果

    直接上图

    请添加图片描述

    二、实现过程

    1. Qt端

    1. 新建一个Qt Gui项目,取名WebChannelDemo
      在这里插入图片描述
    2. 一路下一步,最后选择QWidget基类
      在这里插入图片描述
    3. 得到一个这样结构的项目
      在这里插入图片描述
    4. 使用Qt Designer打开webchanneldemo.ui,拖一个界面
      在这里插入图片描述
    5. 这个时候再来创建一个用于通过WebChannel通信的WebTransport类,基类选择QObject
      在这里插入图片描述
      在这里插入图片描述
    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目录,放在项目生成目录,和生成的可执行文件同级
      在这里插入图片描述
    2. 使用vsCode打开这个目录,并创建如下项目结构
      在这里插入图片描述
    3. 打开index.html文件,先编写一个这样的界面
      在这里插入图片描述
    <!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 + "
    ";
            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);
    }
    

    四、项目完整源码

    https://gitee.com/doyoung126/qt_-demo.git

    下一篇:Qt和JavaScript使用QWebChannel交互二——和浏览器打开的网页交互(还没写,抽时间写)

    五、总结

    总结:遇到凡事不要慌,先掏出手机拍个朋网友圈
    在这里插入图片描述

    做一条有理想的咸鱼
  • 相关阅读:
    验证回文串
    03-Python控制语句
    02-Python运算符
    ACwing(基础) --- Bellman-Ford&SPFA
    ACwing(基础)--- Dijkstra算法(含堆优化版)
    ACwing(基础)--- 区间合并
    ACwing(基础)--- 位运算
    ACwing(基础)--- 双指针算法
    ACwing(基础)--- 高精度
    ACwing(基础)---790. 数的三次方根
  • 原文地址:https://www.cnblogs.com/Doyoung/p/15359518.html
Copyright © 2020-2023  润新知