我需要从Cpp到QML编写一个数组(从外部C代码)。如前所述,这可能是一个XY问题,现在我首先描述我的基本意图。我使用的外部c程序按原样提供,并且我无法更改。该c程序提供以下结构和函数mx_get,以从特定位置获取数据:
typedef struct Tmatrix
{
size_t rows;
size_t cols;
double *data;
} Tmatrix;
double mx_get(const Tmatrix *matrix, const size_t i, const size_t j)
{
return *(matrix->data + i * matrix->cols + j);
}
我需要在我的QML应用程序中使用此C程序,即99%QML和1%Cpp。 cpp-part是将QML连接到c程序的类。我使用qmlRegisterType<MyTerminal>
在QML中使用它。
MyTerminal.h:
#include <QObject>
extern "C"
{
#include "external-c-program.h"
}
Q_DECLARE_METATYPE(Tmatrix)
class MyTerminal: public QObject
{
Q_OBJECT
public:
MyTerminal();
Q_INVOKABLE void getAllPartitionQualities();
/* and other stuff not relevant here... */
private:
TPanel* Panel; /* this contains the Tmatrix at Panel->Q */
};
在QML部分,我想显示Tmatrix-> data的内容。因此,我目前在Text
中使用Repeater
:
Repeater {
model: nRows*nColumns
Text {
text: myTerminal.quality[indexY][indexX].toFixed(2)
property int indexX: index - parseInt(index/nColumns)*nColumns
property int indexY: parseInt(index/nRows)
}
}
我逐个元素地完成了此操作,但是随着数组大小增加到19 * 19,现在大约需要20秒。 (在以下代码示例中,Panel->Q
的类型为Tmatrix
。)
MyTerminal.cpp:
for (int y=0; y<PARTITION_ROWS; y++)
{
for (int x=0; x<PARTITION_COLUMNS; x++)
{
QMetaObject::invokeMethod(this, "setQuality",
Q_ARG(QVariant, x),
Q_ARG(QVariant, y),
Q_ARG(QVariant, mx_get(Panel->Q, x, y)));
}
}
QML:
MyTerminal {
id: myTerminal
property int nRows: 19
property int nColumns: 19
property var quality: [[]]
Component.onCompleted: {
// Initialize quality as 2d array with size nRows*nColumns
var i, j;
var temp = new Array(nRows);
for (i=0; i<nRows; i++) {
temp[i] = new Array(nColumns);
for (j=0; j<nColumns; j++) {
temp[i][j] = 0.0;
}
}
quality = temp;
}
function setQuality(x, y, q) {
quality[y][x] = q;
// Assign to itself to trigger an update of the texts
quality = quality;
}
}
所以我现在正在尝试通过cpp中的一个调用来设置整个数组:
QQmlProperty::write(this, "quality", QVariant::fromValue(Panel->Q->data));
因此,我认为我需要添加以下内容:
Q_DECLARE_METATYPE(Tmatrix)
但是我仍然收到错误“静态断言失败:类型未注册,请使用Q_DECLARE_METATYPE宏使Qt的元对象系统知道它”。 可能是因为双指针位于结构内部?
将指针内容发送到QML的最佳方式(性能最高)是什么?
我还尝试使用QMetaObject :: invokeMethod传递Panel->Q
(而不是将Panel->Q->data
传递给QQmlProperty :: write),但是我也没有找到如何正确执行此操作的帮助。
编辑:还是可以在继承QObject并仍从QML访问它的Cpp类中声明数组quality
?也许更容易。
答案 0 :(得分:0)
将指针内容发送到QML的最佳方式(性能最高)是什么?
您不能直接发送指针本身。您需要将数据打包到QVariantList
或QVariantMap
中。 (由于您使用的是双精度,因此也可以将其存储为QList<qreal>
并将其传递给QML。)
您可以通过
将您的TMatrix
对象变成QList<QList<qreal>>
QList<QList<qreal>> tMatrix = {
{0.0, 1.0, 2.0, 3.0},
{4.0, 5.0, 6.0, 7.0},
{8.0, 9.0, 10.0, 11.0}
};
这使将对象与C ++ / QML之间的相互抛出变得更容易。
更多信息:Data Type Conversion between QML and C++
还是可以在继承QObject并仍从QML访问它的Cpp类中声明数组质量?也许更容易。
是的,因为您的数据很大,所以可能更合适。但是请注意,您的数据 still 需要可转换为QML数据类型。 (再次,我建议为您的对象使用QVariantMap,因为这最终是JS处理对象的方式。)
这可能以C ++类的成员变量的形式出现,例如
private:
QList<QList<qreal>> tMatrix = { /*...*/ };
然后可以使用数据值在构造函数中填充此矩阵。 (而且不一定是QList
,也可以是double
或另一个QList<>
,但这取决于您的TMatrix
的用途,因此由您决定。只需确保它是valid type。)
然后有一个公共插槽或可调用的方法,例如,可以从QML / JS中调用
public:
Q_INVOKABLE qreal getObj(int x, int y);
像这样定义它
qreal MyClass::getObj(int x, int y)
{
if (y <= 0 || reals.size() <= y)
return qreal();
if (x <= 0 || vMaps[y].size() <= x)
return qreal();
return tMatrix[y][x];
}
这只是吸气剂,如果您想要一个setter,则可以使用void
返回类型和qreal
自变量(允许您随后将JS对象传递到其中)来同样地定义它。 / p>
请记住expose your class to QML(我相信您已经这样做了),然后您可以使用...在QML / JS中访问tMatrix
MyClass // depends on how you registered your type
{
id: myClass
Component.onCompleted
{
var obj = getObj(2, 1) // calls the function from your class
// use an id if needed (e.g. myClass.getObj())
console.debug("Retrieved Object at (2, 1):")
console.debug(JSON.stringify(obj))
}
}