当前位置: 首页 > news >正文

Qt-Web混合开发-QtWebChannel实现Qt与Web通信交互-进阶功能(6)

Qt-Web混合开发-QtWebChannel实现Qt与Web通信交互-进阶功能🥬

文章目录

  • Qt-Web混合开发-QtWebChannel实现Qt与Web通信交互-进阶功能🥬
    • 1、概述🌽
    • 2、实现效果🍆
    • 3、实现功能🍒
    • 4、关键代码🥝
    • 5、源代码🥔

更多精彩内容
👉个人内容分类汇总 👈
👉Qt - Web混合开发👈

1、概述🌽

  • Qt版本:V5.12.5

  • 注意:windows下webenginewidgets只支持MSVC编译器,不支持MinGW(mingw好像需要自己编译);

  • 当使用QWebEngineView实现Qt + html混合开发时经常遇见的问题就是Qt自身有信号槽,但是和web网页怎么通信,web里又没有信号槽,第一时间想到的是socket通信,但是Qt其实封装了更加简单便捷的通信方式,可使用QtWebChannelqwebchannel.js实现Qt和web通信;

  1. QWebChannel支持的功能:

    1. 将JavaScript函数【绑定到Qt的信号】(类似Qt信号槽);
    2. 在JavaScript中异步调用QWebChannel::registerObject()注册的对象对象的【槽函数】(必须是public slots:声明的);
    3. 在JavaScript中调用QWebChannel::registerObject()注册的对象中带【返回值的槽函数】,并通过回调函数获取Qt槽函数的返回值
    4. 在JavaScript中指定读取/修改QWebChannel::registerObject()注册的对象中使用Q_PROPERTY定义的【属性值】;
    5. 在JavaScript中指定读取QWebChannel::registerObject()注册的对象中使用Q_ENUM标记的【枚举】。
  2. 前面讲了Qt使用 QWebChannel实现与Web中的javascript通信的简单示例,但是如果需要传递复杂数据该怎么实现呢,总不能所有数据都用字符串吧;

  3. 其实可以转换为JSON的数据类型QWebChannel都支持,而JSON支持下列6种数据类型,对应到Qt中可以使用5种类型,其它各种类型的数据,例如QByteArray,QImage、自定义结构体等数据类型都需要自己转换为JSON数据格式再通过QWebChannel传递;

    JSON支持类型Qt中对应的数据类型
    逻辑值(true 或 false)bool
    数字(整数或浮点数)int、int64、double等数字类型都可以
    字符串(在双引号中)QString
    数组(在中括号中)QJsonArray
    对象(在大括号中)QJsonObject
    null

2、实现效果🍆

在这里插入图片描述

3、实现功能🍒

  1. 构建后将html、css、js文件自动拷贝到可执行程序路径下;
  2. web界面和qt界面实现双向通信;
  3. 由于QWebChannel传递数据只有可以转换为【JSON的数据类型】才可以传递,其它类型无法传递,例如QByteArray这些JSON不支持的数据类型,这里演示了可以传递的所有数据类型的使用方式;
  4. Web界面中javascript直接读取Qt中注册对象使用Q_PROPERTY定义的【属性值】;
  5. Web界面中javascript直接读取Qt中注册对象使用使用Q_ENUM标记的【枚举】;
  6. 定义一个带有返回值的槽函数,javascript调用该函数后可以获取【返回值】;

4、关键代码🥝

  • pro文件:INSTALLS 用法
QT += webenginewidgets webchannel   # 使用QWebEngineView和QWebchannel需要加载模块

# 程序编译后需要使用nmake install(msvc)或make install (linux)将web2文件夹拷贝到当前路径下,或者自己手动拷贝
webFile.path = $$path
webFile.files = $$PWD/web2
INSTALLS += webFile      # 将web文件夹拷贝到path路径下,需要配置Custom Process Step: nmake install才生效
  • webClient.html
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Web客户端</title>
<!--  qwebchannel.js文件一般在Qt安装路径下 D:\Qt\Qt5.12.5\Examples\Qt-5.12.5\webchannel\shared\qwebchannel.js-->
    <script src="qwebchannel.js"></script>
    <script src="main.js"></script>
    <link rel="stylesheet" type="text/css" href="main.css">
</head>
<body>
    <h1 align="center">Web客户端程序 </h1>
    <div align="center">
        <textarea id="textAreaId" name="textArea"></textarea> </br>

        <button id="but_bool" class="button" onclick="butClick_bool()">bool类型</button>
        <button id="but_double" class="button" onclick="butClick_double()">double类型</button>
        <button id="but_str" class="button" onclick="butClick_str()">string类型</button>
        <button id="but_arr" class="button" onclick="butClick_arr()">array类型</button>
        <button id="but_obj" class="button" onclick="butClick_obj()">object类型</button> </br>
        
        <button id="but_Property" class="button" onclick="butClick_Property()">读取并修改Qt对象中的属性</button>
        <button id="but_enum" class="button" onclick="butClick_enum()">读取Q_ENUM标记的枚举</button>
        <button id="but_return" class="button" onclick="butClick_return()">调用带返回值的Qt函数</button>
    </div>
</body>
</html>

  • main.js
/**
 * 程序启动立即初始化
 */
window.onload = function()
{
    if(typeof qt != "undefined")
    {
        window.channel = new QWebChannel(qt.webChannelTransport, function(channel)
        {
            // 获取Qt注册的对象,Qt中registerObject注册的字符串
            window.core = channel.objects.CoreId;

            // 将函数showText和Qt信号toWeb***()绑定
            core.toWebBool.connect(function(msg)
            {
                showText(msg);
            });
            core.toWebDouble.connect(function(msg)
            {
                showText(msg);
            });
            core.toWebString.connect(function(msg)
            {
                showText(msg);
            });
            core.toWebJsonArray.connect(function(msg)
            {
                showText(msg);
            });
            core.toWebJsonObject.connect(function(msg)
            {
                showText(msg);
            });
        });
    }
    else
    {
        alert("qt对象未获取到!");
    }
}

/**
 * 显示Qt发给Web的信息
 * @param {*} msg 
 */
function showText(msg)
{
    var textEdit = document.getElementById("textAreaId");
    if(textEdit)
    {
        // 由于typeof 不能分辨出数组和对象,所以这里使用构造器来判断
        if(msg.constructor === Array)         // 数组类型
        {
            textEdit.value = textEdit.value + "Array:" + msg+ '\n';                    // 追加信息
        }
        else if(msg.constructor === Object)  // 对象类型
        {
            var str = msg.key1 + " " + msg.key2+ " " + msg.key3;
            textEdit.value = textEdit.value + "Object:" + str+ '\n';                   // 追加信息
        }
        else
        {
            textEdit.value = textEdit.value + typeof msg  + ":" + msg+ '\n';           // 追加信息
        }
        
        textEdit.scrollTop = textEdit.scrollHeight;             // 滚动条一直再最下方
    }
}

/**
 * html中按键点击时调用这个函数将信号发送给Qt
 */
function butClick_bool()
{
    core.on_toQtBool(true);    
}
function butClick_double()
{
    core.on_toQtDouble(123.321);    
}
function butClick_str()
{
    core.on_toQtString("Web 按键点击");    
}
function butClick_arr()
{
    var arr = [1, 2, "123"];
    core.on_toQtJsonArray(arr);   
}
function butClick_obj()
{
    var obj = {
        key1 : 123,
        key2 : 321.1,
        key3 : "abc"
    };
    core.on_toQtJsonObject(obj);   
}

/**
 * 点击按键后直接读取Qt中注册对象Core中使用Q_PROPERTY定义的属性值并修改
 */
function butClick_Property()
{
    var textEdit = document.getElementById("textAreaId");
    if(textEdit)
    {
        textEdit.value = textEdit.value + "Qt对象属性:" + core.value + '\n';              // 读取Qt中使用Q_PROPERTY定义的属性值
        textEdit.scrollTop = textEdit.scrollHeight;             // 滚动条一直再最下方
        core.value++;                                                                     // 修改Qt中使用Q_PROPERTY定义的属性值
    }
}

/**
 * 直接访问Qt中使用Q_ENUM标记的枚举
 */
function butClick_enum()
{
    var textEdit = document.getElementById("textAreaId");
    if(textEdit)
    {
        textEdit.value = textEdit.value + "Qt枚举值:" + core.CoreEnum.Value1 + '\n';  
        textEdit.scrollTop = textEdit.scrollHeight;             // 滚动条一直再最下方
    }
}

/**
 * 在javascript中调用Qt函数后【获取返回值】
 */
function butClick_return()
{
    core.on_returnValue(123, function(returnValue)  // 这里使用回调函数获取返回值
    {
        var textEdit = document.getElementById("textAreaId");
        if(textEdit)
        {
            textEdit.value = textEdit.value + "Qt函数返回值:" + returnValue + '\n';  
            textEdit.scrollTop = textEdit.scrollHeight;             // 滚动条一直再最下方
        }
    });
}
  • widget.h:注意,这里的 Core 类是Qt和Js通信的关键;
#ifndef WIDGET_H
#define WIDGET_H

#include <QJsonArray>
#include <QJsonObject>
#include <QWidget>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

private:
    void showText(QString str);

private slots:
    void on_toQtBool(bool value)             ;
    void on_toQtDouble(double value)         ;
    void on_toQtString(QString value)        ;
    void on_toQtJsonArray(QJsonArray value)  ;
    void on_toQtJsonObject(QJsonObject value);

    void on_but_bool_clicked();

    void on_but_double_clicked();

    void on_but_str_clicked();

    void on_but_array_clicked();

    void on_but_object_clicked();

private:
    Ui::Widget *ui;
};

/**
 * @brief  Qt和Web端交互的中介单例类
 */
class Core : public QObject
{
    Q_OBJECT
    Q_PROPERTY(int value READ value WRITE setValue NOTIFY valueChanged)  // 定义一个属性,javascript可以读取属性值

public:
    enum CoreEnum
    {
        Value1 = 100,
        Value2
    };
    Q_ENUM(CoreEnum)    // 使用Q_ENUM标记的枚举,javascript可以直接访问

public:
    static Core* getInstance()
    {
        static Core core;
        return &core;
    }

    int value() {return m_value;}
    void setValue(int v) {m_value = v;}

signals:
    void valueChanged();
    /**
     * @brief     Qt发送给Web的信号
     * @param str
     */
    void toWebBool(bool value);
    void toWebDouble(double value);
    void toWebString(QString value);
    void toWebJsonArray(QJsonArray value);
    void toWebJsonObject(QJsonObject value);

    /**
     * @brief     Web发送给Qt的信号
     * @param str
     */
    void toQtBool(bool value);
    void toQtDouble(double value);
    void toQtString(QString value);
    void toQtJsonArray(QJsonArray value);
    void toQtJsonObject(QJsonObject value);

public slots:
    /**
     * @brief     Web端需要调用Qt槽函数来传递,必须声明为public slots,否则web找不到
     * @param str
     */
    void on_toQtBool(bool value)              {emit toQtBool(value);}
    void on_toQtDouble(double value)          {emit toQtDouble(value);}
    void on_toQtString(QString value)         {emit toQtString(value);}
    void on_toQtJsonArray(QJsonArray value)   {emit toQtJsonArray(value);}
    void on_toQtJsonObject(QJsonObject value) {emit toQtJsonObject(value);}

    /**
     * @brief        定义一个带有返回值的槽函数,javascript调用该函数后可以获取返回值
     * @param value
     * @return
     */
    QString on_returnValue(int value);
private:
    int m_value = 10;
};

#endif // WIDGET_H

  • widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include <QJsonDocument>
#include <QTime>
#include <qdir.h>
#include <qwebchannel.h>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    this->setWindowTitle(QString("使用QtWebChannel实现Qt与Web通信交互(2),进阶功能 - V%1").arg(APP_VERSION));  // 设置窗口标题

    QWebChannel* channel = new QWebChannel(this);
    channel->registerObject("CoreId", Core::getInstance());  // 向QWebChannel注册用于Qt和Web交互的对象。

    ui->webEngineView->page()->setWebChannel(channel);       // 将与webEngineView要使用的web通道实例设置为channel
    ui->webEngineView->setUrl(QDir("./web2/webClient.html").absolutePath());

    // 绑定槽函数,接收web界面中javascript传递过来的信号
    connect(Core::getInstance(), &Core::toQtBool, this, &Widget::on_toQtBool);
    connect(Core::getInstance(), &Core::toQtDouble, this, &Widget::on_toQtDouble);
    connect(Core::getInstance(), &Core::toQtString, this, &Widget::on_toQtString);
    connect(Core::getInstance(), &Core::toQtJsonArray, this, &Widget::on_toQtJsonArray);
    connect(Core::getInstance(), &Core::toQtJsonObject, this, &Widget::on_toQtJsonObject);
}

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

/**
 * @brief       显示发送的数据
 * @param str
 */
void Widget::showText(QString str)
{
    ui->textEdit->append(QString("发送:%1").arg(str));
}

/**
 * @brief       显示Web发送给Qt的数据
 * @param value
 */
void Widget::on_toQtBool(bool value)
{
    ui->textEdit->append(QString("Bool类型数据:%1").arg(value ? "true" : "false"));
}

void Widget::on_toQtDouble(double value)
{
    ui->textEdit->append(QString("double类型数据:%1").arg(value));
}

void Widget::on_toQtString(QString value)
{
    ui->textEdit->append(QString("QString类型数据:%1").arg(value));
}

void Widget::on_toQtJsonArray(QJsonArray value)
{
    QJsonDocument doc;
    doc.setArray(value);
    ui->textEdit->append(QString("QJsonArray类型数据:%1").arg(doc.toJson().data()));
}

void Widget::on_toQtJsonObject(QJsonObject value)
{
    QJsonDocument doc;
    doc.setObject(value);
    ui->textEdit->append(QString("QJsonArray类型数据:%1").arg(doc.toJson().data()));
}


/**
 * @brief 发送Bool类型数据
 */
void Widget::on_but_bool_clicked()
{
    static bool value = true;
    value = !value;
    emit Core::getInstance()->toWebBool(value);

    showText(QString("%1").arg(value ? "true" : "false"));
}

/**
 * @brief 发送double类型数据
 */
void Widget::on_but_double_clicked()
{
    double value = QTime::currentTime().msec() / 50.0;
    emit Core::getInstance()->toWebDouble(value);

    showText(QString("%1").arg(value));
}

/**
 * @brief 发送字符串类型数据
 */
void Widget::on_but_str_clicked()
{
    double value = QTime::currentTime().msec() / 50.0;
    emit Core::getInstance()->toWebString(QString("%1").arg(value));

    showText(QString("%1").arg(value));
}

/**
 * @brief 发送Json数组类型数据
 */
void Widget::on_but_array_clicked()
{
    double value = QTime::currentTime().msec() / 50.0;
    QJsonArray array = {value, value / 10.0, QString("%1").arg(value / 20.0)};
    emit Core::getInstance()->toWebJsonArray(array);

    QJsonDocument doc;
    doc.setArray(array);
    showText(QString("%1").arg(doc.toJson().data()));
}

/**
 * @brief 发送json对象类型数据
 */
void Widget::on_but_object_clicked()
{
    double value = QTime::currentTime().msec() / 50.0;
    QJsonObject obj;
    obj.insert("key1", value);
    obj.insert("key2", value / 30.0);
    obj.insert("key3", QString("%1").arg(value / 40.0));
    emit Core::getInstance()->toWebJsonObject(obj);

    QJsonDocument doc;
    doc.setObject(obj);
    showText(QString("%1").arg(doc.toJson().data()));
}

/**
 * @brief        带返回值的槽函数,将返回值传递给javascript
 * @param value
 * @return
 */
QString Core::on_returnValue(int value)
{
    qDebug() << "调用Qt槽函数,并返回值";
    return QString("调用成功:%1").arg(value);
}

5、源代码🥔

  • gitee
  • github

🌙`、、`ヽ`ヽ`、、ヽヽ、`、ヽ`ヽ`ヽヽ`
ヽ`、`ヽ`、ヽ``、ヽ`ヽ`、ヽヽ`ヽ、ヽ
`ヽ、ヽヽ`ヽ`、``ヽ`ヽ、ヽ、ヽ`ヽ`ヽ
、ヽ`ヽ`、ヽヽ``、ヽ`、ヽヽ 🚶‍♀ ヽ``ヽ`

相关文章:

  • SpringMVC:SpringMVC五种类型参数传递(4)
  • 微信小程序 | 小程序WXSS-WXML-WXS
  • 蓝桥杯入门即劝退(十六)查找元素范围(双解法)
  • Servlet转发与重定向
  • 【C语言进阶】参加面试怎能不会结构体?进来学,手把手教会你结构体的原理与使用
  • JSP运动会信息网站
  • 大数据呀大数据
  • C#编程基础(万字详解,这一篇就够了)
  • 滑动窗口的最大值【滑动窗口问题】
  • MYSQL 8.0 -- 事务中删除不存在的记录导致死锁
  • [1181]linux两台服务器之间传输文件和文件夹
  • 题:付账问题
  • python中的字典详解
  • C++11标准模板(STL)- 算法(std::minmax)
  • STM32F4 | PWM输出实验
  • @angular/forms 源码解析之双向绑定
  • 《网管员必读——网络组建》(第2版)电子课件下载
  • 【译】React性能工程(下) -- 深入研究React性能调试
  • ABAP的include关键字,Java的import, C的include和C4C ABSL 的import比较
  • android 一些 utils
  • JavaScript实现分页效果
  • JavaScript异步流程控制的前世今生
  • Vue2 SSR 的优化之旅
  • windows下使用nginx调试简介
  • 免费小说阅读小程序
  • 七牛云 DV OV EV SSL 证书上线,限时折扣低至 6.75 折!
  • 前言-如何学习区块链
  • 日剧·日综资源集合(建议收藏)
  • 体验javascript之美-第五课 匿名函数自执行和闭包是一回事儿吗?
  • 正则学习笔记
  • PostgreSQL之连接数修改
  • 关于Kubernetes Dashboard漏洞CVE-2018-18264的修复公告
  • 容器镜像
  • ​LeetCode解法汇总2182. 构造限制重复的字符串
  • ​LeetCode解法汇总2304. 网格中的最小路径代价
  • ​secrets --- 生成管理密码的安全随机数​
  • #mysql 8.0 踩坑日记
  • #单片机(TB6600驱动42步进电机)
  • $(selector).each()和$.each()的区别
  • (bean配置类的注解开发)学习Spring的第十三天
  • (ctrl.obj) : error LNK2038: 检测到“RuntimeLibrary”的不匹配项: 值“MDd_DynamicDebug”不匹配值“
  • (ISPRS,2023)深度语义-视觉对齐用于zero-shot遥感图像场景分类
  • (JSP)EL——优化登录界面,获取对象,获取数据
  • (Redis使用系列) Springboot 使用redis实现接口幂等性拦截 十一
  • (博弈 sg入门)kiki's game -- hdu -- 2147
  • (分类)KNN算法- 参数调优
  • (附源码)spring boot建达集团公司平台 毕业设计 141538
  • (每日持续更新)jdk api之FileReader基础、应用、实战
  • (十八)三元表达式和列表解析
  • (淘宝无限适配)手机端rem布局详解(转载非原创)
  • (一)硬件制作--从零开始自制linux掌上电脑(F1C200S) <嵌入式项目>
  • (正则)提取页面里的img标签
  • (转)全文检索技术学习(三)——Lucene支持中文分词
  • (转载)Linux 多线程条件变量同步
  • (转载)VS2010/MFC编程入门之三十四(菜单:VS2010菜单资源详解)