简体   繁体   English

从Cpp到QML的结构内部写入数组

[英]Write array inside of a struct from Cpp to QML

I need to write an array (from external C code) from Cpp to QML. 我需要从Cpp到QML编写一个数组(从外部C代码)。 As it was mentioned that this may be an XY-problem, now I first describe my basic intention. 如前所述,这可能是一个XY问题,现在我首先描述我的基本意图。 I use an external c-program that is given as it is and that I can't change. 我使用的外部c程序按原样提供,并且我无法更改。 This c-program provides the following struct and the function mx_get to get the data from a certain position: 该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);
}

I need to use this c-program in my QML app, that is 99% QML and 1% Cpp. 我需要在我的QML应用中使用此C程序,即99%QML和1%Cpp。 The cpp-part is a class to connect the QML to the c-program. cpp-part是将QML连接到c程序的类。 I use qmlRegisterType<MyTerminal> to use it inside QML. 我使用qmlRegisterType<MyTerminal>在QML中使用它。

MyTerminal.h: 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 */
};

In the QML part, I want to display the content of Tmatrix->data. 在QML部分中,我想显示Tmatrix-> data的内容。 Therefore I currently use Text inside a Repeater : 因此,我目前在Repeater使用Text

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)
    }
}

I did it element by element, but this now takes about 20 secs as the array size increased to 19*19. 我逐个元素地完成了此操作,但是随着数组大小增加到19 * 19,现在大约需要20秒。 (In the following code samples, Panel->Q is of type Tmatrix .) (在以下代码示例中, Panel->Q的类型为Tmatrix 。)

MyTerminal.cpp: 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: 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;
    }
}

So I am now trying to set the whole array with one call in cpp: 因此,我现在尝试使用cpp中的一个调用来设置整个数组:

QQmlProperty::write(this, "quality", QVariant::fromValue(Panel->Q->data));

Therefore I think I need to add this: 因此,我认为我需要添加以下内容:

Q_DECLARE_METATYPE(Tmatrix)

But I still get the error "static assertion failed: Type is not registered, please use the Q_DECLARE_METATYPE macro to make it known to Qt's meta-object system". 但是我仍然收到错误“静态断言失败:类型未注册,请使用Q_DECLARE_METATYPE宏使Qt的元对象系统知道它”。 Probably because the double pointer is inside the struct? 可能是因为双指针位于结构内部?

What is the best (most performant) way to send the pointer content to QML? 将指针内容发送到QML的最佳方式(性能最高)是什么?

I also tried again with QMetaObject::invokeMethod passing Panel->Q (instead of passing Panel->Q->data to QQmlProperty::write), but I didn't find help how to do this correctly neither. 我还尝试了QMetaObject :: invokeMethod传递Panel->Q (而不是将Panel->Q->data传递给QQmlProperty :: write),但是我也没有找到如何正确执行此操作的帮助。

Edit: Or can I declare my array quality inside my Cpp-class which inherits QObject and still access it from QML? 编辑:或者我可以在继承QObject并仍从QML访问它的Cpp类中声明我的数组quality吗? Maybe that's easier. 也许更容易。

What is the best (most performant) way to send the pointer content to QML? 将指针内容发送到QML的最佳方式(性能最高)是什么?

You can't send the pointer itself directly. 您不能直接发送指针本身。 You'll need to pack the data into a QVariantList or a QVariantMap . 您需要将数据打包到QVariantListQVariantMap (Since you're using doubles, you could also store it as a QList<qreal> and pass it to QML). (由于您使用的是double,因此您也可以将其存储为QList<qreal>并将其传递给QML)。

You could make your TMatrix object into a QList<QList<qreal>> by 可以通过以下方式将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}
};

This makes it easier to throw your object around from and to C++/QML. 这样可以更轻松地将对象与C ++ / QML相互抛出。

More info: Data Type Conversion between QML and C++ 更多信息: QML和C ++之间的数据类型转换

Or can I declare my array quality inside my Cpp-class which inherits QObject and still access it from QML? 还是可以在继承QObject并仍从QML访问它的Cpp类中声明数组质量? Maybe that's easier. 也许更容易。

Yes, that may be more appropriate, as your data is large. 是的,因为您的数据很大,所以这可能更合适。 But note that, your data still needs to be convertible to a QML data type. 但是请注意,您的数据仍然需要转换为QML数据类型。 (Again, I suggest the QVariantMap for your object, since that's eventually how JS handles objects.) (再次,我建议为您的对象使用QVariantMap,因为这最终是JS处理对象的方式。)

This may come in the form of a member variable of your C++ class, eg 这可能以C ++类的成员变量的形式出现,例如

private:
    QList<QList<qreal>> tMatrix = { /*...*/ };

You can then fill this matrix in the constructor with your data values. 然后,您可以使用数据值在构造函数中填充此矩阵。 (And it doesn't necessarily have to be a QList , it could be a double or another QList<> , but that depends on what your TMatrix is for, so it's up to you. Just make sure it's a valid type .) (并且不必一定是QList ,它可以是double或另一个QList<> ,但这取决于您的TMatrix的用途,所以这取决于您。只需确保它是有效类型即可 。)

Then have a public slot or invokable method which will be called from QML/JS, eg 然后有一个从QML / JS调用的公共插槽或可调用方法,例如

public:
    Q_INVOKABLE qreal getObj(int x, int y);

And define it like so 像这样定义它

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];
}

This is only the getter, if you want a setter, you could define it likewise, with void return type and qreal arguments (which allows you to then pass JS objects into it). 这只是吸气剂,如果您想要一个setter,也可以使用void返回类型和qreal参数(允许您随后将JS对象传递到其中)来同样地对其进行定义。

Remember to expose your class to QML (I believe you've done so already), then you can access tMatrix in QML/JS using... 请记住, 将您的类暴露给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))            
    }
}

More info: Integrating QML and C++ 更多信息: 集成QML和C ++

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM