QWebChannel 将空 QVariant POD 结构发送到 JavaScript
QWebChannel sends null QVariant POD structs to JavaScript
我正在编写一个 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);
作为通知者使用的IPCclass如下:
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 I found to what I am trying to achieve is a QT Widget based JavaScript 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++ 端,我们从 C++ 端的 QWebEngineView to a Gui layout that displays the web content. These need to be initialized ahead of time the signal to slot flows asynchronously through the established QWebChannel. In my case, I only care about sending custom POD structs via QVariant 对象到 JavaScript 端设置一个页面。
为了将自定义数据结构与 QVariant 一起使用——或者在我的例子中是 QVariantList 的——我们需要向 QT 注册自定义类型——作为一种元 rtti 注册。如果没有这个,当 javascript 槽函数 var updateMap 将无法从 POD 的字段中解码类型信息。
有一个很常见的错误,很多人认为通过QWebChannel传输数据就是使用QMetatype,而且文档不是很清楚,也没有说明可以传输什么样的数据,但是report clearly states that it can be transported: json or items that can be packaged in a json . And that can be checked if the implementation is reviewed, for example in a 为 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
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();
}
我正在编写一个 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);
作为通知者使用的IPCclass如下:
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 I found to what I am trying to achieve is a QT Widget based JavaScript 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++ 端,我们从 C++ 端的 QWebEngineView to a Gui layout that displays the web content. These need to be initialized ahead of time the signal to slot flows asynchronously through the established QWebChannel. In my case, I only care about sending custom POD structs via QVariant 对象到 JavaScript 端设置一个页面。
为了将自定义数据结构与 QVariant 一起使用——或者在我的例子中是 QVariantList 的——我们需要向 QT 注册自定义类型——作为一种元 rtti 注册。如果没有这个,当 javascript 槽函数 var updateMap 将无法从 POD 的字段中解码类型信息。
有一个很常见的错误,很多人认为通过QWebChannel传输数据就是使用QMetatype,而且文档不是很清楚,也没有说明可以传输什么样的数据,但是report clearly states that it can be transported: json or items that can be packaged in a json . And that can be checked if the implementation is reviewed, for example in a
所以在任何需要通过 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();
}