使用webchannel处理c++ Qt和javascript之间的zlib压缩json数据



我想分享我在这个过程中的知识,因为它似乎不是微不足道的,必须花费一些时间来弄清楚。希望这是一本有用的手册。

<标题>

问题:有一个QT应用程序,使用QWebChannel使用PlotlyJS发送大量数据进行绘图。该数据包含约2000万个数据点。经过调查,发现瓶颈是QWebChannel数据传输。我想到的一个可能的解决方案是在打开通道之前压缩数据。我面临的最大问题是如何快速地将膨胀的数据转换回json,以及如何使用js的pako zlib压缩器来膨胀。

我总是收到

Uncaught unknown compression method

修改后出现错误

我想出的解决方案,可以在答案部分看到。

解决方案:

注意:此代码不包含整个web通道的设置,而只包含QT和JS之间的数据处理。

QT c++

重要的是jspako.inflate()不理解qCompress生成的头,因此Unknown compression method error。报头包含四个字节,至少在我的例子中是0- 5。修剪它们可以修复错误。

QString compressJson(const QJsonObject &jsonObj)
{
QJsonDocument doc(jsonObj);
QString strJson(doc.toJson(QJsonDocument::Compact));
QByteArray data = qCompress(strJson.toUtf8());
// Remove the first four bytes, so that the external (js zlib decompressor understands the size header)
// Leaving the leading 4 bytes in place will cause "unknown compression method" on the js side.
data.remove(0, 4);
return QString(data.toBase64());
}
#include <QByteArray>
#include <QJsonObject>
#include <chrono>
using namespace std::chrono;
QJsonObject getPlotDataJson()
{
// generate plotly digestable json data
}
SomeClass::SomeClass(QObject *pParent)
: QObject
{
auto start = high_resolution_clock::now();
m_data = compressJson(getPlotDataJson());
auto stop = high_resolution_clock::now();
auto duration = duration_cast<microseconds>(stop - start);
qDebug() << "SomeClass::compress: " << duration.count() << " usecs";
}

不要忘记将想要与QWebChannel共享的数据设置为Q_PROPERTY

#include <QObject>
#include <QString>
class SomeClass: public QObject{
Q_OBJECT
Q_PROPERTY(QString plotData MEMBER m_data)
public:
explicit SomeClass(QObject *pParent = nullptr);
QString m_data;
};

JS香草

/* Using FileReader to read very large data efficiently */
function largeuint8ArrToString(uint8arr, callback) {
var bb = new Blob([uint8arr]);
var f = new FileReader();
f.onload = function(e) {
callback(e.target.result);
};
f.readAsText(bb);
}
//! Decompresses zlib compressed plot data
function inflateData(data)
{
// 1. Decode from base64
var strData = atob(data);
// 2. Convert to to js bytearray so that pako zlib inflate can understand the data
const charData = strData.split('').map(function(x){return x.charCodeAt(0); });
const bytes = new Uint8Array(charData);
// 3. Decompress
return pako.inflate(bytes)
}
window.addEventListener('DOMContentLoaded', (event) => {
var startTime0 = performance.now()
new QWebChannel(qt.webChannelTransport, function (channel) {
var plotDataInflatedBytes = inflateData(channel.objects.SomeClass.plotData)
largeuint8ArrToString(plotDataInflatedBytes,function(text){
var mapped = JSON.parse(text)
// ... your code to process the data
});
var endTime0 = performance.now()
console.log(`new QWebChannel() exec time ${endTime0 - startTime0} milliseconds`)
});
});

编辑:

以前使用简单循环将二进制数组转换为字符串:

let binArrayToString = function(binArray) {
let str = "";
for (let i = 0; i < binArray.length; i++) {
str += String.fromCharCode(binArray[i]);
}
return str;
}

使用FileReader 将替换为更有效的方法

function largeuint8ArrToString(uint8arr, callback) {
var bb = new Blob([uint8arr]);
var f = new FileReader();
f.onload = function(e) {
callback(e.target.result);
};
f.readAsText(bb);
}
<标题>

结论:除了修复错误,这段代码允许我在大约10秒内绘制数据,而不是30分钟以上。我还注意到,由于binArrayToString映射,如果我将单元数增加一倍,性能仍然会下降很多,所以它仍然是瓶颈。

<标题>

引用:Uint8Array转换为Javascript中的string

如何zlib压缩QByteArray?

如何使用pako.js javascript?Pako没有定义

最新更新