用Qt,QWebChannel 和 QWebEngineView 和 html 交互

环境:vs2019,msvc2017_64。

1.创建项目(注意不要包含中文等特殊字符),勾选 WebEngine Widgets模块。
在这里插入图片描述
2.写一个QWebChannelDemo的界面类,主要负责显示Qt界面和网页界面。看注释。
QWebChannelDemo.h

#pragma once

#include <QtWidgets/QWidget>
#include "ui_QWebChannelDemo.h"

#include <QWebEngineView>
#include <QUrl>
#include<QWebChannel>
#include "WebTransport.h"

class QWebChannelDemo : public QWidget
{
    Q_OBJECT

public:
    QWebChannelDemo(QWidget *parent = Q_NULLPTR);
    void setup();

private:
    Ui::QWebChannelDemoClass ui;
    
	QWebEngineView* map;  //网页视图区域
};

QWebChannelDemo.cpp

#include "QWebChannelDemo.h"
#include <qDebug>

QWebChannelDemo::QWebChannelDemo(QWidget *parent)
    : QWidget(parent)
{
    ui.setupUi(this);
    resize(1000, 900);

    map = new QWebEngineView(this);
    map->move(10, 10);
    map->resize(400, 850);
    
	setup();
}

void QWebChannelDemo::setup()
{
	// 构造一个channel对象
	QWebChannel* channel = new QWebChannel();
	// 向channel对象注册自定义对象 
	//registerObject()参数 1.对象名称必须和js文件的名称对应  
	// 2.获取交互类的实列
	//  webTransport一定要和main.js对象名一致否则会提示,初始化qwebchannel失败。
	channel->registerObject("webTransport", WEB_TRSPT);

	// 使用webview的page设置channel对象
	map->page()->setWebChannel(channel);
	// 最后加载网页        html 资源路径
	map->load(qApp->applicationDirPath() + "/channel/index.html");

	//   Qt按钮点击发送时,通过实列将信息发送给js
	//   实列见下方WebTransport对象
	connect(ui.pushButton, &QPushButton::clicked, [this]() {
		ui.plainTextEdit->appendPlainText(QStringLiteral("发送消息到js:") + ui.lineEdit->text());
		
		QString str = ui.lineEdit->text();
		emit WEB_TRSPT->msgToJs(ui.lineEdit->text());
});
    //接收js发过来的信息,显示在Qt控件上
	connect(WEB_TRSPT, &WebTransport::receviedJsMsg, [this](const QString& msg) {
		ui.plainTextEdit->appendPlainText(QStringLiteral("接收js信息:") + msg);
		});
}

QWebChannelDemo.ui
在这里插入图片描述

WebTransport类负责 通过js文件 达到Qt和html的网页的交互 。
WebTransport.h

#pragma once
#include <QObject>

class WebTransport : public QObject
{
	Q_OBJECT
public:
	WebTransport(QObject *parent = nullptr);
	~WebTransport();
	// 获取实例
	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

WebTransport.cpp

#include "WebTransport.h"
#include <QMutex>
#include <QMutexLocker>

WebTransport::WebTransport(QObject *parent)
	: QObject(parent)
{
}

WebTransport::~WebTransport()
{
}

WebTransport* WebTransport::instance()
{
	static WebTransport* self = nullptr;
	static QMutex lock;
	if (!self)
	{
		QMutexLocker locker(&lock);
		if (!self)
		{
			self = new WebTransport;
		}
	}
	return self;
}

void WebTransport::msgToQt(const QString& msg)
{
	emit receviedJsMsg(msg);
}

资源:style.css (负责网页样式) + main.js (网页和Qt交互的处理函数) + qwebchannel.js(qwebchannel.js文件路径如下:Qt安装路径下的Examples\Qt5.14.1\webchannel\shared\qwebchannel.js)

目录结构
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述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

// 显示信息
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注册的对象,设置到主窗口
            window.webTransport = channel.objects.webTransport;
            // 绑定注册对象的信号
            webTransport.msgToJs.connect(function(msg) {
                outputMsg("接收Qt信息:" + msg);
            });
            webTransport.msgToQt("初始化channel成功!");
        });
    } else {
        alert("初始化qwebchannel失败")
    }
}

style.css

#textArea {
    min-width: 100%;
    min-height: 300px;
    height: 330px;
    background-color: lightskyblue;
    border: 1px solid skyblue;
    /* overflow: auto; */
}

#lineEdit {
    width: 100%;
    height: 30px;
    background-color: lightskyblue;
    border: 1px solid skyblue;
}

button {
    width: 100px;
    height: 30px;
    float: right;
}

qwebchannel.js

/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Copyright (C) 2016 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Milian Wolff <milian.wolff@kdab.com>
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtWebChannel module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/

"use strict";

var QWebChannelMessageTypes = {
    signal: 1,
    propertyUpdate: 2,
    init: 3,
    idle: 4,
    debug: 5,
    invokeMethod: 6,
    connectToSignal: 7,
    disconnectFromSignal: 8,
    setProperty: 9,
    response: 10,
};

var QWebChannel = function(transport, initCallback)
{
    if (typeof transport !== "object" || typeof transport.send !== "function") {
        console.error("The QWebChannel expects a transport object with a send function and onmessage callback property." +
                      " Given is: transport: " + typeof(transport) + ", transport.send: " + typeof(transport.send));
        return;
    }

    var channel = this;
    this.transport = transport;

    this.send = function(data)
    {
        if (typeof(data) !== "string") {
            data = JSON.stringify(data);
        }
        channel.transport.send(data);
    }

    this.transport.onmessage = function(message)
    {
        var data = message.data;
        if (typeof data === "string") {
            data = JSON.parse(data);
        }
        switch (data.type) {
            case QWebChannelMessageTypes.signal:
                channel.handleSignal(data);
                break;
            case QWebChannelMessageTypes.response:
                channel.handleResponse(data);
                break;
            case QWebChannelMessageTypes.propertyUpdate:
                channel.handlePropertyUpdate(data);
                break;
            default:
                console.error("invalid message received:", message.data);
                break;
        }
    }

    this.execCallbacks = {};
    this.execId = 0;
    this.exec = function(data, callback)
    {
        if (!callback) {
            // if no callback is given, send directly
            channel.send(data);
            return;
        }
        if (channel.execId === Number.MAX_VALUE) {
            // wrap
            channel.execId = Number.MIN_VALUE;
        }
        if (data.hasOwnProperty("id")) {
            console.error("Cannot exec message with property id: " + JSON.stringify(data));
            return;
        }
        data.id = channel.execId++;
        channel.execCallbacks[data.id] = callback;
        channel.send(data);
    };

    this.objects = {};

    this.handleSignal = function(message)
    {
        var object = channel.objects[message.object];
        if (object) {
            object.signalEmitted(message.signal, message.args);
        } else {
            console.warn("Unhandled signal: " + message.object + "::" + message.signal);
        }
    }

    this.handleResponse = function(message)
    {
        if (!message.hasOwnProperty("id")) {
            console.error("Invalid response message received: ", JSON.stringify(message));
            return;
        }
        channel.execCallbacks[message.id](message.data);
        delete channel.execCallbacks[message.id];
    }

    this.handlePropertyUpdate = function(message)
    {
        message.data.forEach(data => {
            var object = channel.objects[data.object];
            if (object) {
                object.propertyUpdate(data.signals, data.properties);
            } else {
                console.warn("Unhandled property update: " + data.object + "::" + data.signal);
            }
        });
        channel.exec({type: QWebChannelMessageTypes.idle});
    }

    this.debug = function(message)
    {
        channel.send({type: QWebChannelMessageTypes.debug, data: message});
    };

    channel.exec({type: QWebChannelMessageTypes.init}, function(data) {
        for (const objectName of Object.keys(data)) {
            new QObject(objectName, data[objectName], channel);
        }

        // now unwrap properties, which might reference other registered objects
        for (const objectName of Object.keys(channel.objects)) {
            channel.objects[objectName].unwrapProperties();
        }

        if (initCallback) {
            initCallback(channel);
        }
        channel.exec({type: QWebChannelMessageTypes.idle});
    });
};

function QObject(name, data, webChannel)
{
    this.__id__ = name;
    webChannel.objects[name] = this;

    // List of callbacks that get invoked upon signal emission
    this.__objectSignals__ = {};

    // Cache of all properties, updated when a notify signal is emitted
    this.__propertyCache__ = {};

    var object = this;

    // ----------------------------------------------------------------------

    this.unwrapQObject = function(response)
    {
        if (response instanceof Array) {
            // support list of objects
            return response.map(qobj => object.unwrapQObject(qobj))
        }
        if (!(response instanceof Object))
            return response;

        if (!response["__QObject*__"] || response.id === undefined) {
            var jObj = {};
            for (const propName of Object.keys(response)) {
                jObj[propName] = object.unwrapQObject(response[propName]);
            }
            return jObj;
        }

        var objectId = response.id;
        if (webChannel.objects[objectId])
            return webChannel.objects[objectId];

        if (!response.data) {
            console.error("Cannot unwrap unknown QObject " + objectId + " without data.");
            return;
        }

        var qObject = new QObject( objectId, response.data, webChannel );
        qObject.destroyed.connect(function() {
            if (webChannel.objects[objectId] === qObject) {
                delete webChannel.objects[objectId];
                // reset the now deleted QObject to an empty {} object
                // just assigning {} though would not have the desired effect, but the
                // below also ensures all external references will see the empty map
                // NOTE: this detour is necessary to workaround QTBUG-40021
                Object.keys(qObject).forEach(name => delete qObject[name]);
            }
        });
        // here we are already initialized, and thus must directly unwrap the properties
        qObject.unwrapProperties();
        return qObject;
    }

    this.unwrapProperties = function()
    {
        for (const propertyIdx of Object.keys(object.__propertyCache__)) {
            object.__propertyCache__[propertyIdx] = object.unwrapQObject(object.__propertyCache__[propertyIdx]);
        }
    }

    function addSignal(signalData, isPropertyNotifySignal)
    {
        var signalName = signalData[0];
        var signalIndex = signalData[1];
        object[signalName] = {
            connect: function(callback) {
                if (typeof(callback) !== "function") {
                    console.error("Bad callback given to connect to signal " + signalName);
                    return;
                }

                object.__objectSignals__[signalIndex] = object.__objectSignals__[signalIndex] || [];
                object.__objectSignals__[signalIndex].push(callback);

                // only required for "pure" signals, handled separately for properties in propertyUpdate
                if (isPropertyNotifySignal)
                    return;

                // also note that we always get notified about the destroyed signal
                if (signalName === "destroyed" || signalName === "destroyed()" || signalName === "destroyed(QObject*)")
                    return;

                // and otherwise we only need to be connected only once
                if (object.__objectSignals__[signalIndex].length == 1) {
                    webChannel.exec({
                        type: QWebChannelMessageTypes.connectToSignal,
                        object: object.__id__,
                        signal: signalIndex
                    });
                }
            },
            disconnect: function(callback) {
                if (typeof(callback) !== "function") {
                    console.error("Bad callback given to disconnect from signal " + signalName);
                    return;
                }
                object.__objectSignals__[signalIndex] = object.__objectSignals__[signalIndex] || [];
                var idx = object.__objectSignals__[signalIndex].indexOf(callback);
                if (idx === -1) {
                    console.error("Cannot find connection of signal " + signalName + " to " + callback.name);
                    return;
                }
                object.__objectSignals__[signalIndex].splice(idx, 1);
                if (!isPropertyNotifySignal && object.__objectSignals__[signalIndex].length === 0) {
                    // only required for "pure" signals, handled separately for properties in propertyUpdate
                    webChannel.exec({
                        type: QWebChannelMessageTypes.disconnectFromSignal,
                        object: object.__id__,
                        signal: signalIndex
                    });
                }
            }
        };
    }

    /**
     * Invokes all callbacks for the given signalname. Also works for property notify callbacks.
     */
    function invokeSignalCallbacks(signalName, signalArgs)
    {
        var connections = object.__objectSignals__[signalName];
        if (connections) {
            connections.forEach(function(callback) {
                callback.apply(callback, signalArgs);
            });
        }
    }

    this.propertyUpdate = function(signals, propertyMap)
    {
        // update property cache
        for (const propertyIndex of Object.keys(propertyMap)) {
            var propertyValue = propertyMap[propertyIndex];
            object.__propertyCache__[propertyIndex] = this.unwrapQObject(propertyValue);
        }

        for (const signalName of Object.keys(signals)) {
            // Invoke all callbacks, as signalEmitted() does not. This ensures the
            // property cache is updated before the callbacks are invoked.
            invokeSignalCallbacks(signalName, signals[signalName]);
        }
    }

    this.signalEmitted = function(signalName, signalArgs)
    {
        invokeSignalCallbacks(signalName, this.unwrapQObject(signalArgs));
    }

    function addMethod(methodData)
    {
        var methodName = methodData[0];
        var methodIdx = methodData[1];

        // Fully specified methods are invoked by id, others by name for host-side overload resolution
        var invokedMethod = methodName[methodName.length - 1] === ')' ? methodIdx : methodName

        object[methodName] = function() {
            var args = [];
            var callback;
            var errCallback;
            for (var i = 0; i < arguments.length; ++i) {
                var argument = arguments[i];
                if (typeof argument === "function")
                    callback = argument;
                else if (argument instanceof QObject && webChannel.objects[argument.__id__] !== undefined)
                    args.push({
                        "id": argument.__id__
                    });
                else
                    args.push(argument);
            }

            var result;
            // during test, webChannel.exec synchronously calls the callback
            // therefore, the promise must be constucted before calling
            // webChannel.exec to ensure the callback is set up
            if (!callback && (typeof(Promise) === 'function')) {
              result = new Promise(function(resolve, reject) {
                callback = resolve;
                errCallback = reject;
              });
            }

            webChannel.exec({
                "type": QWebChannelMessageTypes.invokeMethod,
                "object": object.__id__,
                "method": invokedMethod,
                "args": args
            }, function(response) {
                if (response !== undefined) {
                    var result = object.unwrapQObject(response);
                    if (callback) {
                        (callback)(result);
                    }
                } else if (errCallback) {
                  (errCallback)();
                }
            });

            return result;
        };
    }

    function bindGetterSetter(propertyInfo)
    {
        var propertyIndex = propertyInfo[0];
        var propertyName = propertyInfo[1];
        var notifySignalData = propertyInfo[2];
        // initialize property cache with current value
        // NOTE: if this is an object, it is not directly unwrapped as it might
        // reference other QObject that we do not know yet
        object.__propertyCache__[propertyIndex] = propertyInfo[3];

        if (notifySignalData) {
            if (notifySignalData[0] === 1) {
                // signal name is optimized away, reconstruct the actual name
                notifySignalData[0] = propertyName + "Changed";
            }
            addSignal(notifySignalData, true);
        }

        Object.defineProperty(object, propertyName, {
            configurable: true,
            get: function () {
                var propertyValue = object.__propertyCache__[propertyIndex];
                if (propertyValue === undefined) {
                    // This shouldn't happen
                    console.warn("Undefined value in property cache for property \"" + propertyName + "\" in object " + object.__id__);
                }

                return propertyValue;
            },
            set: function(value) {
                if (value === undefined) {
                    console.warn("Property setter for " + propertyName + " called with undefined value!");
                    return;
                }
                object.__propertyCache__[propertyIndex] = value;
                var valueToSend = value;
                if (valueToSend instanceof QObject && webChannel.objects[valueToSend.__id__] !== undefined)
                    valueToSend = { "id": valueToSend.__id__ };
                webChannel.exec({
                    "type": QWebChannelMessageTypes.setProperty,
                    "object": object.__id__,
                    "property": propertyIndex,
                    "value": valueToSend
                });
            }
        });

    }

    // ----------------------------------------------------------------------

    data.methods.forEach(addMethod);

    data.properties.forEach(bindGetterSetter);

    data.signals.forEach(function(signal) { addSignal(signal, false); });

    Object.assign(object, data.enums);
}

//required for use with nodejs
if (typeof module === 'object') {
    module.exports = {
        QWebChannel: QWebChannel
    };
}

交互效果:
在这里插入图片描述

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
QWebChannel是一种用于Qt应用程序和嵌入式Web页面之间进行互操作的通信机制。它允许Qt应用程序向Web页面暴露对象,并允许Web页面访问这些对象的属性和方法。同时,也允许Web页面向Qt应用程序发送消息和调用Qt应用程序的函数。 使用QWebChannel进行Qt和JavaScript之间的交互,需要遵循以下步骤: 1. 在Qt应用程序中创建一个QWebChannel对象,并将其绑定到一个QWebEngineView或QWebEnginePage对象上。 2. 在Qt应用程序中创建一个QObject的子类,该子类将作为Qt对象被暴露给Web页面。 3. 在Qt应用程序中将QObject对象注册到QWebChannel对象中。 4. 将QWebChannel对象传递给Web页面,通过JavaScript代码将其绑定到Web页面中的window对象上。 5. 在JavaScript代码中可以通过window对象访问到Qt应用程序中注册的QObject对象,调用其属性和方法。 6. 在Qt应用程序中可以通过QWebChannel对象接收Web页面发送的消息,以及调用Web页面中的JavaScript函数。 下面是一个简单的例子,演示了如何使用QWebChannel进行Qt和JavaScript之间的交互Qt代码: ```c++ // 创建一个QObject的子类,作为Qt对象被暴露给Web页面 class MyObject : public QObject { Q_OBJECT public: MyObject(QObject *parent = nullptr) : QObject(parent) {} public slots: void showMessage(const QString &message) { qDebug() << "Received message from web page:" << message; } }; int main(int argc, char *argv[]) { QApplication app(argc, argv); // 创建一个QWebChannel对象,并将其绑定到一个QWebEngineView对象上 QWebEngineView view; QWebChannel channel; view.page()->setWebChannel(&channel); // 创建MyObject对象,并将其注册到QWebChannel对象中 MyObject obj; channel.registerObject("myObject", &obj); // 加载Web页面 view.load(QUrl("qrc:/index.html")); view.show(); return app.exec(); } ``` JavaScript代码: ```javascript // 将QWebChannel对象绑定到window对象上 new QWebChannel(qt.webChannelTransport, function(channel) { window.myObject = channel.objects.myObject; }); // 调用MyObject对象的showMessage函数 myObject.showMessage("Hello from web page!"); ``` 在上面的例子中,我们创建了一个名为MyObject的QObject子类,并将其注册到了QWebChannel对象中。在JavaScript代码中,我们通过调用QWebChannel构造函数,将QWebChannel对象绑定到window对象上,并通过window对象访问了MyObject对象的showMessage函数。当Web页面调用showMessage函数时,它将向Qt应用程序发送一个消息,Qt应用程序将通过QWebChannel对象接收到该消息,并调用MyObject对象的showMessage函数。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值