Discuz! Board

 找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
查看: 1080|回复: 2
打印 上一主题 下一主题

Qt QWebChannel web端js与C++交互

[复制链接]

1228

主题

1997

帖子

7582

积分

认证用户组

Rank: 5Rank: 5

积分
7582
跳转到指定楼层
楼主
发表于 2022-12-3 17:29:55 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
文章目录
1. QWebChannel
2. 使用QWebEngine的示例
2.1 C++端代码
2.1.1 mainwindow.cpp
2.1.2 mytestclass.h
2.1.3 mytestclass.cpp
2.2 Web端
2.3 运行
3. 使用WebSocket的示例
3.1 C++端代码
3.1.1 mainwindow.cpp
3.2 Web端代码
3.3 运行
3.4 问题
4. 代码
1. QWebChannel
QWebChannel类的作用是向远端HTML客户端暴露 QObject。

QWebChannel填补了C++应用程序和HTML/JavaScript 应用程序之间的空白。通过将QObject派生对象发布到QWebChannel并在HTML中引入qwebchannel.js脚本可从此处获取。在HTML端,可以透明地访问QObject的属性、公共槽和方法。不需要手动消息传递和数据序列化,C++方面的属性更新和信号发射将自动传输到可能远程运行的HTML客户机。在客户端,将为任何发布的C++ QObject创建JavaScript对象。它反映了C++对象的API,因此可以直观地使用。

但是,Web端与C++之间怎么进行通信能? 有两个方法:

QWebEngine 提供一个 web 引擎,用于在 Qt 应用中嵌入任意的网页内容。Qt WebEngine 是基于 Chromium 项目实现的,提供了一个 js 的宿主环境,内部实现了js调用C++的环境;
Websocket C++端建立websocket server,Web端连接,qwebchannel.js会获取到C++端所有的属性、槽函数等。
2. 使用QWebEngine的示例
2.1 C++端代码
2.1.1 mainwindow.cpp
// 要导出的类,此类供js调用
m_myTestClass = new MyTestClass(this);

// 创建QWebChannel,把创建的类注册到QWebChannel中,js才能调用此类的方法
m_webChannel = new QWebChannel(this);
m_webChannel->registerObject("mytestclass", m_myTestClass);

// js与C++通信方式一、使用QWebEngineView加载网页,web端js与C++之间的通信
QString strHtml = QApplication::applicationDirPath() + "/../../testWeb/test.html";
// QWebEngineView 基于 Chromium 的 web 引擎
m_webEngineView = new QWebEngineView(this);
m_webEngineView->load(QUrl::fromLocalFile(strHtml));
m_webEngineView->page()->setWebChannel(m_webChannel);
ui->verticalLayout->addWidget(m_webEngineView);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
导出类 MyTestClass,供js调用

2.1.2 mytestclass.h
#ifndef MYTESTCLASS_H
#define MYTESTCLASS_H

#include <QObject>
#include <QThread>

class MyTestClass : public QObject
{
    Q_OBJECT

    // 导出的属性
    Q_PROPERTY(QString navStatus MEMBER m_navStatus NOTIFY navStatusChanged)

public:
    explicit MyTestClass(QObject *parent = nullptr);

signals:
    // 导出的事件
    void navStatusChanged(const QString& navStatus);

public slots:
    // 导出的槽函数
    void function1(const QString& str);


private:
    void setNavStatus(const QString& status);

    QString m_navStatus;

};

#endif // MYTESTCLASS_H

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
2.1.3 mytestclass.cpp
#include "mytestclass.h"

#include <QMessageBox>
#include <QVariant>
#include <QDebug>

MyTestClass::MyTestClass(QObject *parent) : QObject(parent),
    m_navStatus("hello")
{
}

void MyTestClass::function1(const QString& str)
{
    setNavStatus(str);
    qDebug() << __FUNCTION__ << str;
}

void MyTestClass::setNavStatus(const QString &status)
{
    m_navStatus = status;
    emit navStatusChanged(m_navStatus);
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2.2 Web端
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <button id="callcpp">callcpp</button>
    <button>getValue</button>

    <script src="qwebchannel.js"></script>

    <script type="text/javascript">
        var webObj;

        // 创建Webchannel,与C++端建立连接
        new QWebChannel(qt.webChannelTransport, function (channel) {

                // 获取类的对象
                webObj = channel.objects.mytestclass;

                // 类的事件
                webObj.navStatusChanged.connect(function(arg){
                    alert("navStatusChanged: " + arg);
                });
            });

        function callcpp(){

            // 类的方法
            webObj.function1('this is a test');
        }

        function getValue(){

            // 类的属性值
            var status = webObj.navStatus;

            alert(status);
        }


    </script>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
其中,qwebchannel.js 取自 Qt安装目录的 Qt5.12.0\Examples\Qt-5.12.0\webchannel\shared 目录。

2.3 运行

QT UI加载了html页面,显示了其中的元素。

(1)Web端的getValue获取C++对象的属性navStatus,属性初始值为 “hello”

(2)Web端的 callcpp 调用 C++端的函数 function1,C++端function1函数中打出

(3)Web端响应 navStatusChanged 事件



3. 使用WebSocket的示例
3.1 C++端代码
3.1.1 mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    // 要导出的类,此类供js调用
    m_myTestClass = new MyTestClass(this);

    // 创建QWebChannel,把创建的类注册到QWebChannel中,js才能调用此类的方法
    m_webChannel = new QWebChannel(this);
    m_webChannel->registerObject("mytestclass", m_myTestClass);

    // js与C++通信方式二、创建QWebsocketServer,web端与之建立连接
    startServer();

}

MainWindow::~MainWindow()
{
    delete ui;
}

// 建立WebSocket服务
void MainWindow::startServer()
{
    m_websocketServer = new QWebSocketServer("testWebchannel", QWebSocketServer::NonSecureMode, this);

    if(!m_websocketServer->listen(QHostAddress::Any, 12345))
    {
        qDebug() << "websocket server listen failed, error: " << m_websocketServer->errorString();
        return;
    }

    connect(m_websocketServer, &QWebSocketServer::newConnection, this, &MainWindow:nNewConnection);
    qDebug() << "startServer";
}

void MainWindow:nNewConnection()
{
    QWebSocket* client = m_websocketServer->nextPendingConnection();
    qDebug() << (QString("Homay robot server has new connection from %1.%2").arg(client->peerAddress().toString()).arg(client->localPort()));

    auto pTransport = new WebSocketTransport(client);
    // 可以不需要,这里只是为了调试打印js端的qwebchannel.js是怎么和C++端通信的,通信协议是什么样的
    connect(pTransport, &WebSocketTransport::messageReceived, this, &MainWindow:nTransportMessageReceived);

    m_webChannel->connectTo(pTransport);
}

void MainWindow:nTransportMessageReceived(const QJsonObject &message, QWebChannelAbstractTransport *transport)
{
    qDebug() << "onTransportMessageReceived: " << message;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
导出类 MyTestClass,供js调用。
代码中 WebSocketTransport 类代码,取自 Qt安装目录的 Qt5.12.0\Examples\Qt-5.12.0\webchannel\shared 目录下的 websockettransport.h websockettransport.cpp。
此处省略。

3.2 Web端代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <button id="callcpp">callcpp</button>
    <button>getValue</button>

    <script src="qwebchannel.js"></script>

    <script type="text/javascript">
        var webObj;

        // 连接c++端的 Websocket
                var socket = new WebSocket('ws://127.0.0.1:12345');

        // 连接成功后
        socket.onopen = function(){
            alert("onopen");

            // 创建Webchannel
            new QWebChannel(socket, function (channel) {

                // 获取类的对象
                webObj = channel.objects.mytestclass;

                // 类的事件
                webObj.navStatusChanged.connect(function(arg){
                    alert("navStatusChanged: " + arg);
                });
            });
        }

        function callcpp(){
            // 类的方法
            webObj.function1('this is a test');
        }

        function getValue(){
            // 类的属性值
            var status = webObj.navStatus;

            alert(status);
        }



    </script>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54


与2.2中的Web端代码相比,此时,需要先创建WebSocket连接,把socket传入QWebChannel中。

3.3 运行
(1)运行C++, C++代码没有加载html, 此时为空界面;
(2)使用Chrome浏览器,或者Edge浏览器打开Web测试网页,一打开就与C++端的websocket连接上了

(3)接下来与2.3的演示一样。

3.4 问题
我这里端口用的12345,可能在有些电脑上此端口被占用了,会报错:



改一下端口号就好了, 比如我改成:



4. 代码
所有代码详见:QWebChannel
————————————————
版权声明:本文为CSDN博主「J_Xio」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/Jay_Xio/article/details/122662697

回复

使用道具 举报

1228

主题

1997

帖子

7582

积分

认证用户组

Rank: 5Rank: 5

积分
7582
沙发
 楼主| 发表于 2022-12-3 18:29:48 | 只看该作者
  1. H5
  2. if (/macintosh|mac os x/i.test(navigator.userAgent)) {
  3.         window.isMac = true
  4.         new QWebChannel(qt.webChannelTransport,  (channel) => {
  5.             // qt 返回的通信管道 。  通过该函数绑定回调
  6.           window.bridge = channel.objects.bridge;
  7.           window.noteBridge = channel.objects.notebridge;
  8.           window.bridge.addEventListener = function(cmd, fn) {
  9.                 window.bridge[cmd].connect(fn);
  10.           };
  11.           window.noteBridge.addEventListener = function(cmd, fn) {
  12.             //    用于区分笔记和云盘的通信管道
  13.                 window.noteBridge[cmd].connect(fn);
  14.           };
  15.           window.bridge.mouseDown=function () {}
  16.           resolve()
  17.         });
  18.     }
  19. C++
  20. WebEngineView *wev = qobject_cast<WebEngineView *>(m_cloudPage);
  21. m_jsChannel = new QWebChannel(this);
  22. m_jsChannel->registerObject("bridge", (QObject*)CCloudBridgeJs::instance());
  23. m_jsChannel->registerObject("notebridge", (QObject*)CNoteBridgeJs::instance());
  24. wev->page()->setWebChannel(m_jsChannel);
复制代码
回复 支持 反对

使用道具 举报

1228

主题

1997

帖子

7582

积分

认证用户组

Rank: 5Rank: 5

积分
7582
板凳
 楼主| 发表于 2022-12-3 18:40:52 | 只看该作者
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|Archiver|手机版|小黑屋|firemail ( 粤ICP备15085507号-1 )

GMT+8, 2024-5-3 11:16 , Processed in 0.056814 second(s), 18 queries .

Powered by Discuz! X3

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表