javascript - QWebChannel 将空 QVariant POD 结构发送到 JavaScript

标签 javascript c++ qt qt5 qtwebchannel

我正在编写一个 C++ QWidget 应用程序,该应用程序与在资源托管网页中运行的 JavaScript 进行通信。我需要找到一种方法将以下 POD 结构的数组发送到网页中托管的 JavaScript 函数,但不幸的是数据总是以空数组的形式结束。我认为问题类似于this question - 但这也没有答案。

自定义 POD 结构(我需要发送其中的列表 (QVariantList))是:

using FPlanWPT = struct FPlanWPT {
    //std::string name;
    double longitude;
    double latitude;
    double bearing;
};

// register custom type with the QT type system
Q_DECLARE_METATYPE(FPlanWPT);

用作通知程序的IPC类如下:

class FlightRoute : public QObject {
    Q_OBJECT
    Q_PROPERTY(QVariantList data READ data NOTIFY dataChanged)
public:
    explicit FlightRoute(QObject* parent = nullptr)
        : QObject(parent)
    {}

    //! Flight route data getter.
    QVariantList data() const {
        return valueList;
    }
public slots:
    void updateRouteData(const QVariantList& data);

signals:
    void dataChanged(const QVariantList& data);

private:
    QVariantList valueList;
};

上面的想法是,当

最接近的example我发现我想要实现的是基于 JavaScript 的 QT Widget Chart.js生成随机图表列并更新网页中运行的图表的应用程序。

在 QT C++ Widget 应用程序和 JavaScript 之间进行 IPC 通信的关键是初始化 QWebChannel两端。

C++方面,这本质上是:

class mainwindow : public QMainWindow
{
    Q_OBJECT
public:
    explicit mainwindow(QWidget *parent = Q_NULLPTR);
    ~mainwindow();
    ...
private:
    ...
    std::unique_ptr<QWebEngineView> mpWebView;
}

构造函数如下:

//! Constructor.
mainwindow::mainwindow(QWidget *parent)
    : QMainWindow(parent)
    , mUI(new Ui::mainwindow())
    . . .
    , mpWebView(std::make_unique<QWebEngineView>())
    , mRecordBuffer{}
    , mpWorkerThread{nullptr}
    , mpWorkerObject{nullptr}
{
    static auto& gLogger = gpLogger->getLoggerRef(
        gUseSysLog ? Logger::LogDest::SysLog :
        Logger::LogDest::EventLog);

    // JavaScript integration - allow remote debugging with mpWebView
    qputenv("QTWEBENGINE_REMOTE_DEBUGGING", "1234");

    // register custom types for serialization with signals/slots
    qRegisterMetaType<FPlanWPT>("FPlanWPT");

    // initialize the form
    mUI->setupUi(this);    

    // load the web page containing the google map javascript
    mpWebView->page()->load(QUrl("qrc:///html/test.html"));

    // initialize the link to the HTML web page content
    auto webChannel = new QWebChannel(this);
    const auto routeIPC = new FlightRoute(this);
    // register IPC object with the QWebChannel
    connect(mpWorkerObject, &Worker::fooSignal, routeIPC, &FlightRoute::updateRouteData, Qt::DirectConnection);
    webChannel->registerObject(QStringLiteral("routeIPC"), routeIPC);
    mpWebView->page()->setWebChannel(webChannel);

    // Insert the html page at the top of the grid layout
    mUI->flightTrackGridLayout->addWidget(mpWebView.get(), 0, 0, 1, 3);
    . . .
}

JavaScript 方面(这是我的整个 HTML 文件)

<html>
<head>
    <script type="text/javascript" src="qrc:///qtwebchannel/qwebchannel.js"></script>
</head>
<body>
    <div style="margin:auto; width:100%;">
        <canvas id="canvas"></canvas>
    </div>
    <script>
        // function to update the google
        var updateMap = function (waypointData) {
            // waypoint data is always a list of nulls
            console.log(waypointData);
        }

        // script called once web page loaded
        window.onload = function () {
            new QWebChannel(qt.webChannelTransport,
                function (channel) {
                    // whenever the route data changes, invoke updateMap slot
                    var dataSource = channel.objects.routeIPC;
                    dataSource.dataChanged.connect(updateMap);
                }
            );
        }
    </script>
</body>
</html>

此外,在 C++ 端,我们设置了 QWebEngineView 中的页面。到显示 Web 内容的 Gui 布局。这些需要提前初始化,信号槽通过已建立的 QWebChannel 异步流动。就我而言,我只关心通过 QVariant 发送自定义 POD 结构从 C++ 端到 JavaScript 端的对象。

为了将自定义数据结构与 QVariant(或者在我的例子中是 QVariantList)一起使用,我们需要向 QT 注册自定义类型 - 作为一种元 rtti 注册。如果没有这个,JavaScript 槽函数 var updateMap 将无法从 POD 字段中解码类型信息。

最佳答案

有一个很常见的错误,很多人认为通过QWebChannel传输数据就是使用QMetatype,而且文档也不是很清楚,没有说明可以传输什么样的数据,而是一个report明确指出它可以传输:json 或可以打包在 json 中的项目。如果对实现进行审查,则可以检查这一点,例如在 answer 中。为了实现WebView的QWebChannel,我的主要任务是进行从QString到QJsonObject的转换,反之亦然。

因此,在任何需要通过 QWebChannel 发送数据的情况下,您都必须将其转换为 QJsonValue、QJsonObject 或 QJsonArray。当您希望发送数据数组时,我将使用 QJsonArray:

flightroute.h

#ifndef FLIGHTROUTE_H
#define FLIGHTROUTE_H

#include <QJsonArray>
#include <QJsonObject>
#include <QObject>

struct FPlanWPT {
    QString name;
    double longitude;
    double latitude;
    double bearing;
    QJsonObject toObject() const{
        QJsonObject obj;
        obj["name"] = name;
        obj["longitude"] = longitude;
        obj["latitude"] = latitude;
        obj["bearing"] = bearing;
        return obj;
    }
};

class FlightRoute : public QObject
{
    Q_OBJECT
public:
    using QObject::QObject;
    void setRoutes(const QList<FPlanWPT> &routes){
        QJsonArray array;
        for(const FPlanWPT & route: routes){
            array.append(route.toObject());
        }
        emit routesChanged(array);
    }
signals:
    void routesChanged(const QJsonArray &);
};

#endif // FLIGHTROUTE_H

ma​​in.cpp

#include "flightroute.h"

#include <QApplication>
#include <QTimer>
#include <QWebChannel>
#include <QWebEngineView>
#include <random>

int main(int argc, char *argv[])
{
    qputenv("QTWEBENGINE_REMOTE_DEBUGGING", "9000");
    QApplication a(argc, argv);
    QWebEngineView view;

    FlightRoute routeIPC;
    QWebChannel webChannel;
    webChannel.registerObject(QStringLiteral("routeIPC"), &routeIPC);
    view.page()->setWebChannel(&webChannel);

    std::random_device rd;
    std::mt19937 gen(rd());
    std::uniform_real_distribution<> dist(0, 100);

    QTimer timer;
    QObject::connect(&timer, &QTimer::timeout, [&](){
        QList<FPlanWPT> routes;
        for(int i=0; i<10; i++){
            routes << FPlanWPT{"name1", dist(gen), dist(gen), dist(gen)};
        }
        routeIPC.setRoutes(routes);
    });
    timer.start(1000);

    view.load(QUrl(QStringLiteral("qrc:/index.html")));
    view.resize(640, 480);
    view.show();
    return a.exec();
}

关于javascript - QWebChannel 将空 QVariant POD 结构发送到 JavaScript,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53018492/

相关文章:

javascript - 当复选框名称来自php中的数据库时,如何访问多个复选框名称

c++ - 从方法内部调用自析构函数

c++ - 在构造函数的可变参数中使用其他模板化类执行模板化类的初始化

qt - 在 Qt 中如何对 QModelIndex 的直接子索引进行排序

linux - 使用 Qt(在 X11 上)检测窗口标题栏中的控制按钮位置

javascript - 检测子元素动画

javascript - 在 jsxgraph 中,为什么不在同一平面上的五个点绘制抛物线?

javascript - 为什么单选按钮没有传递 asp 控件的 clientID

c++ - #include <CEGUI/RendererModules/Ogre/CEGUIOgreRenderer.h> 没有正确包含 ogre 头文件

c - Qt 原子操作使用我的 MIPS 24k 核心中不存在的条件代码寄存器