贰叁丶 發表於 2021-10-1 15:51:00

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

<h1 id="qt和javascript使用qwebchannel交互一和qt内嵌网页交互">Qt和JavaScript使用QWebChannel交互一——和Qt内嵌网页交互</h1>
<p></p><div class="toc"><div class="toc-container-header">目录</div><ul><li>Qt和JavaScript使用QWebChannel交互一——和Qt内嵌网页交互</li><li>前言</li><li>一、效果</li><li>二、实现过程<ul><li>1. Qt端</li><li>2. 网页端</li></ul></li><li>三、过程中出现的问题<ul><li>问题一</li><li>问题二</li></ul></li><li>四、项目完整源码</li><li>五、总结</li></ul></div><p></p>
<hr style="border: solid; width: 100px; height: 1px" color="#000000" size="1&quot;">
<h1 id="前言">前言</h1>
<p>&nbsp;&nbsp;&nbsp;&nbsp;<font color="#999AAA"><code>Qt</code>提供了<code>QWebChannel</code>来和网页进行通信,只需要注册自定义对象一下,就可以直接绑定信号槽来进行Qt程序和网页之前的通信,非常方便</font><br>
&nbsp;&nbsp;&nbsp;&nbsp;<font color="#999AAA">下面使用一个案例来学习<code>QWebChannel</code><br>
&nbsp;&nbsp;&nbsp;&nbsp;<font color="#999AAA"><code>环境</code>:vs2017+Qt5.11.2 写<code>Qt</code>代码,vsCode写<code>js</code>代码</font></font></p><font color="#999AAA"><font color="#999AAA">
<hr style="border: solid; width: 100px; height: 1px" color="#000000" size="1&quot;">
<h1 id="一效果">一、效果</h1>
<p><font color="#999AAA"> 直接上图</font></p><font color="#999AAA">
<p><img src="https://img-blog.csdnimg.cn/fccd17eb7edb4564923105d86d8e6c3d.gif"></p>
<h1 id="二实现过程">二、实现过程</h1>
<h2 id="1-qt端">1. Qt端</h2>
<ol>
<li>新建一个<code>Qt Gui</code>项目,取名<code>WebChannelDemo</code><br>
<img src="https://img-blog.csdnimg.cn/08366d1a7b5f45f4a097ac26c1820b41.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA5ZK46bG8RG9Zb3VuZw==,size_20,color_FFFFFF,t_70,g_se,x_16"></li>
<li>一路下一步,最后选择<code>QWidget</code>基类<br>
<img src="https://img-blog.csdnimg.cn/5aa774066c0c4261bcb06d93f0ccb572.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA5ZK46bG8RG9Zb3VuZw==,size_17,color_FFFFFF,t_70,g_se,x_16"></li>
<li>得到一个这样结构的项目<br>
<img src="https://img-blog.csdnimg.cn/e4577f920eb048568cfcc6d198354158.png"></li>
<li>使用<code>Qt Designer</code>打开<code>webchanneldemo.ui</code>,拖一个界面<br>
<img src="https://img-blog.csdnimg.cn/388038c0194b48a388b1b95d94854e77.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA5ZK46bG8RG9Zb3VuZw==,size_20,color_FFFFFF,t_70,g_se,x_16"></li>
<li>这个时候再来创建一个用于通过<code>WebChannel</code>通信的<code>WebTransport</code>类,基类选择<code>QObject</code><br>
<img src="https://img-blog.csdnimg.cn/26bac12dea554cf0b55370a5045fc820.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA5ZK46bG8RG9Zb3VuZw==,size_20,color_FFFFFF,t_70,g_se,x_16"><br>
<img src="https://img-blog.csdnimg.cn/9f6046e707c142698ce3c15bbc454205.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA5ZK46bG8RG9Zb3VuZw==,size_17,color_FFFFFF,t_70,g_se,x_16"></li>
<li>为了方便,不用每次使用都创建一个<code>WebTransport</code>对象,我们将这个类写成<code>单例</code>类,然后定义和js交互的信号的槽函数,最近定义一个宏来使用实例</li>
</ol>
<pre><code class="language-cpp">#pragma once

#include &lt;QObject&gt;

class WebTransport : public QObject
{
        Q_OBJECT

        WebTransport(QObject *parent = nullptr);
        ~WebTransport();
public:
        // 获取实例
        static WebTransport* instance();
signals:
        // 向js发送信号
        void msgToJs(const QString&amp; msg);
        // 将从js接收的数据发送出去
        void receviedJsMsg(const QString&amp; msg);
public slots:
        // js调用此函数,接收js传入的数据
        void msgToQt(const QString&amp; msg);
};
// 定义一个宏
#ifndef WEB_TRSPT
#define WEB_TRSPT WebTransport::instance()
#endif // !WEB_TRSPT
</code></pre>
<ol start="7">
<li>最后,我们到<code>WebChannelDemo</code>类中来初始化一下,主要做了以下几件事:
<ol>
<li>关联信号槽</li>
<li>实例化一个<code>QWebChannel</code>对象</li>
<li>将<code>WebTransport</code>单例对象注册到<code>QWebChannel</code></li>
<li>将<code>QWebChannel</code>对象设置到网页中去</li>
<li>最后再加载本地网页</li>
</ol>
</li>
</ol>
<pre><code class="language-cpp">void WebChannelDemo::setup()
{
        // 绑定信号槽
        connect(ui.pushButton, &amp;QPushButton::clicked, () {
                ui.plainTextEdit-&gt;appendPlainText(QStringLiteral("发送消息到js:") + ui.lineEdit-&gt;text());
                emit WEB_TRSPT-&gt;msgToJs(ui.lineEdit-&gt;text());
        });
        connect(WEB_TRSPT, &amp;WebTransport::receviedJsMsg, (const QString&amp; msg) {
                ui.plainTextEdit-&gt;appendPlainText(QStringLiteral("接收js信息:") + msg);
        });

        // 构造一个channel对象
        QWebChannel* channel = new QWebChannel(this);
        // 向channel对象注册自定义对象
        channel-&gt;registerObject(QStringLiteral("webBridge"), WEB_TRSPT);
        // 使用webview的page设置channel对象
        ui.webEngineView-&gt;page()-&gt;setWebChannel(channel);
        // 最后加载网页
    ui.webEngineView-&gt;load(qApp-&gt;applicationDirPath()+"/channel/index.html");
}
</code></pre>
<h2 id="2-网页端">2. 网页端</h2>
<ol>
<li>先创建一个<code>channel</code>目录,放在项目生成目录,和生成的可执行文件同级<br>
<img src="https://img-blog.csdnimg.cn/bea93384c54449b1ad81312055a28536.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA5ZK46bG8RG9Zb3VuZw==,size_14,color_FFFFFF,t_70,g_se,x_16"></li>
<li>使用vsCode打开这个目录,并创建如下项目结构<br>
<img src="https://img-blog.csdnimg.cn/a3d17bf845b84fc687e973e0a5407095.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA5ZK46bG8RG9Zb3VuZw==,size_17,color_FFFFFF,t_70,g_se,x_16"></li>
<li>打开<code>index.html</code>文件,先编写一个这样的界面<br>
<img src="https://img-blog.csdnimg.cn/b78b088d776449c391d08709c2e4cbbf.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA5ZK46bG8RG9Zb3VuZw==,size_9,color_FFFFFF,t_70,g_se,x_16"></li>
</ol>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
    &lt;meta charset="UTF-8"&gt;
    &lt;!-- 样式 --&gt;
    &lt;link rel="stylesheet" href="./css/style.css"&gt;
    &lt;!-- js --&gt;
    &lt;script src="./js/qwebchannel.js"&gt;&lt;/script&gt;
    &lt;script src="./js/main.js"&gt;&lt;/script&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;p&gt;我是网页&lt;/p&gt;
    &lt;textarea name="textArea" id="textArea"&gt;&lt;/textarea&gt;&lt;br&gt;
    &lt;input type="text" id="lineEdit"&gt;&lt;br&gt;
    &lt;button id="sendBtn" onclick="senBtnClicked();"&gt;发送&lt;/button&gt;
    &lt;button id="clearBtn" onclick="clearBtnClicked();"&gt;清空&lt;/button&gt;
&lt;/body&gt;
&lt;/html&gt;
</code></pre>
<ol start="4">
<li>打开<code>main.js</code>文件,编写<code>js</code>代码,重点在初始化函数中,做了以下几件事
<ol>
<li>直接构造一个<code>QWebChannel</code>对象,用回调函数函数接收创建好的<code>QWebChannel对象</code></li>
<li>使用<code>QWebChannel对象</code>获取<code>Qt</code>端注册的对象</li>
<li>给注册对象的信号绑定一个回调函数(槽函数),当<code>Qt</code>端注册的对象此信号发送时,回调函数被调用</li>
<li>调用注册对象的槽函数,给<code>Qt</code>端发送消息,直接使用<code>Qt</code>端注册对象的槽函数名来调用</li>
</ol>
</li>
</ol>
<pre><code class="language-js">// 显示信息
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失败")
    }
}
</code></pre>
<h1 id="三过程中出现的问题">三、过程中出现的问题</h1>
<h2 id="问题一">问题一</h2>
<blockquote>
<ol>
<li>问题描述:运行时,Qt向Js端发送消息没有问题,Js端向Qt端发送消息时失败,会报如下错误</li>
</ol>
<pre><code class="language-cpp">Cannot invoke unknown method of index -1 on object webTransport(0x...)
</code></pre>
<ol start="2">
<li>问题原因及解决办法:<br>
使用<code>Qt 5.11.2</code>编译生成的可执行程序,而网页端用的是<code>Qt 5.14</code>的<code>qwebchannel.js</code>文件,版本不兼容导致的,换成对应的<code>qwebchannel.js</code>文件就好了</li>
</ol>
</blockquote>
<h2 id="问题二">问题二</h2>
<blockquote>
<ul>
<li>在加载本地网页时,为什么是先设置<code>QWebChannel</code>再加载网页?</li>
<li>因为最开始的想法是,如果直接先使用<code>ui.webEngineView-&gt;page()</code>获取<code>QWebEnginePage</code>对象,应该获取到的是个<code>nullptr</code>,这个时候直接设置<code>QWebChannel</code>,程序会崩掉,那我先加载网页,然后<code>QWebEngineView</code>会内部构造一个<code>QWebEnginePage</code>对象,我再通过<code>page()</code>函数获取,这个时候肯定不是<code>nullptr</code>了,再去设置<code>QWebChannel</code>,这个过程简直完美,但是出人意料的是,按照这个过程设置的<code>QWebChannel</code>并没有生效,也不能和<code>js</code>交互</li>
<li>解决办法就是先设置<code>QWebChannel</code>再加载网页,通过查看<code>Qt</code> 源码发现,通过<code>page()</code>函数获取<code>QWebEnginePage</code>对象时,内部做出了判断,如果内部维护的<code>QWebEnginePage</code>对象为空时,会直接构造一个<code>QWebEnginePage</code>对象,并不会返回空指针,设置完<code>QWebChannel</code>后再去调用<code>load()</code>函数加载网页时,内部会直接拿到设置好<code>QWebChannel</code>的<code>QWebEnginePage</code>对象去加载网页</li>
</ul>
<pre><code class="language-cpp">QWebEnginePage* QWebEngineView::page() const
{
                Q_D(const QWebEngineView);
                if (!d-&gt;page) {
          QWebEngineView *that = const_cast&lt;QWebEngineView*&gt;(this);
               that-&gt;setPage(new QWebEnginePage(that));
               }
                return d-&gt;page;
}

void QWebEngineView::load(const QUrl&amp; url)
{
        page()-&gt;load(url);
}
</code></pre>
</blockquote>
<hr style="border: solid; width: 100px; height: 1px" color="#000000" size="1&quot;">
<h1 id="四项目完整源码">四、项目完整源码</h1>
<p>https://gitee.com/doyoung126/qt_-demo.git</p>
<p>下一篇:Qt和JavaScript使用QWebChannel交互二——和浏览器打开的网页交互<font color="red">(还没写,抽时间写)</font></p>
<h1 id="五总结">五、总结</h1>
<p><font color="#999AAA">总结:遇到凡事不要慌,先掏出手机拍个朋网友圈<br>
<img src="https://img-blog.csdnimg.cn/2210e941c1a44ae7a27f13475c8ce7ee.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA5ZK46bG8RG9Zb3VuZw==,size_14,color_FFFFFF,t_70,g_se,x_16"></font></p><font color="#999AAA">
</font></font></font></font>

</div>
<div id="MySignature" role="contentinfo">
    做一条有理想的咸鱼<br><br>
来源:https://www.cnblogs.com/Doyoung/p/15359518.html
頁: [1]
查看完整版本: Qt和JavaScript使用QWebChannel交互一——和Qt内嵌网页交互