QWebChannel отправляет нулевые структуры QVariant POD в JavaScript

Я пишу приложение на C ++ QWidget, которое взаимодействует с JavaScript, работающим на веб-странице, размещенной на ресурсе. Мне нужно найти способ отправки массива следующих структур POD в функцию JavaScript, размещенную на веб-странице, но, к сожалению, данные всегда заканчиваются массивами нулей. Я думаю, что проблема похожа на этот вопрос — но на это тоже нет ответа.

Пользовательская структура 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;
};

Идея выше заключается в том, что когда

Ближайший пример Я нашел то, что я пытаюсь достичь, это JavaScript на основе QT Widget Chart.js приложение, которое генерирует случайные столбцы диаграммы и обновляет диаграмму, работающую на веб-странице.

Ключом к получению IPC-связи между приложением QT C ++ Widget и JavaScript является инициализация 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 в макет Gui, который отображает веб-контент. Их необходимо инициализировать заблаговременно, сигнал в слот проходит асинхронно через установленный QWebChannel. В моем случае меня интересует только отправка пользовательских структур POD через QVariant объекты со стороны C ++ на сторону JavaScript.

Чтобы использовать пользовательские структуры данных с QVariant — или в моем случае с QVariantList — нам нужно зарегистрировать пользовательский тип с помощью QT — в качестве своего рода регистрации мета-тегов. Без этого, когда функция слота javascript var updateMap не сможет декодировать информацию о типе из полей POD.

1

Решение

Существует очень распространенная ошибка, многие думают, что для передачи данных через QWebChannel используется QMetatype, и документация не очень ясна и не указывает, какие данные можно транспортировать, но доклад четко говорится, что его можно транспортировать: JSON или предметы, которые могут быть упакованы в JSON . И это может быть проверено, если реализация рассматривается, например, в ответ для реализации QWebChannel для WebView моей главной задачей было сделать преобразование из 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

main.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();
}
1

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]