QWebChannel 向 JavaScript 发送空的 QVariant POD 结构
Posted
技术标签:
【中文标题】QWebChannel 向 JavaScript 发送空的 QVariant POD 结构【英文标题】:QWebChannel sends null QVariant POD structs to JavaScript 【发布时间】:2018-10-27 03:34:19 【问题描述】:我正在编写一个 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 是基于 QT Widget 的 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
, mpWorkerThreadnullptr
, mpWorkerObjectnullptr
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 对象从 C++ 端向 JavaScript 端发送自定义 POD 结构。
为了在 QVariant 中使用自定义数据结构 - 或者在我的情况下是 QVariantList 的 - 我们需要使用 QT 注册自定义类型 - 作为一种元 rtti 注册。如果没有这个,javascript 槽函数 var updateMap 将无法从 POD 的字段中解码类型信息。
【问题讨论】:
@eyllanesc 完成! @eyllanesc ty 很好的答案,我还没有完全为我工作。我有点困惑 FlightRoute 对象如何神奇地通知 javascript 中的插槽 - 我的代码使用 Json 数组调用发出 routesChanged(array) - 但我的函数不再在 JavaScript 中被调用 - 注意我删除了我的 FlightRoute 的 qRegisterMetaType<..> 和 Q_DECLARE_METATYPE(...)。我的 javascript 理论上应该使用 dataSource.routesChanged.connect(updateMap) 建立连接(它曾经) 我的测试代码在以下链接中:github.com/eyllanesc/***/tree/master/53018492 执行并打开链接:127.0.0.1:9000,你会看到印象。如果你有问题,我可以帮助你,如果你通过 github 与我分享你的代码,我可以更正它。 @eyllanesc 谢谢 - 天哪,你真的很有条理!惊人的工作流程。我在那里看到了大量的 sn-p 项目。顺便说一句,我终于让我的代码工作了,在您的示例中,routeIPC 的生命周期仍然存在,因为它存在于 main 中,在我的情况下,我需要在 mainwindow 中有一个它的实例,我只有一个 WebViewEngine 成员。因此,为了从(为了发布到它)访问 IPC,我需要通过以下方式检索 ipc 对象: auto pFlightRouteIPC = mpWebView->page()->webChannel()->registeredObjects().find(" routeIPC").value() 然后通过 pFlightRouteIPC->setRoutes(routes) 发布。 @eyllanesc 顺便说一句 QWebChannel 只支持 Json 对象?它真的没有写在任何地方,引擎是否需要具有本机 json 转换的 QVariants 或 QStrings 才能工作? - 只是好奇。顺便说一句,我需要做的下一件事(现在我在 javascript 级别有纬度/经度值是用线将这些点连接在一起构建一个地图。它可能有点牵强,但你碰巧有这样一个例子: )。我将调整我的 html 以调用类似 developers.google.com/maps/documentation/javascript/shapes 的内容 【参考方案1】:有一个很常见的错误,很多人认为通过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
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();
【讨论】:
以上是关于QWebChannel 向 JavaScript 发送空的 QVariant POD 结构的主要内容,如果未能解决你的问题,请参考以下文章
实现QObject与JavaScript通讯(基于QWebEngine + QWebChannel)
未捕获的 ReferenceError:未定义 QWebChannel
QWebChannel:在 QtCreator 之外运行时构造崩溃