Qt和JavaScript使用QWebChannel交互一——和Qt內嵌網頁交互
- 2021 年 10 月 1 日
- 筆記
- javascript, Qt, QT相關
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:
// 向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
- 最後,我們到
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);
});
// 構造一個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. 網頁端
- 先創建一個
channel
目錄,放在項目生成目錄,和生成的可執行文件同級
- 使用vsCode打開這個目錄,並創建如下項目結構
- 打開
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>
- 打開
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');
// 調用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失敗")
}
}
三、過程中出現的問題
問題一
- 問題描述:運行時,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); }
四、項目完整源碼
//gitee.com/doyoung126/qt_-demo.git
下一篇:Qt和JavaScript使用QWebChannel交互二——和瀏覽器打開的網頁交互(還沒寫,抽時間寫)
五、總結
總結:遇到凡事不要慌,先掏出手機拍個朋網友圈