Collectives™ on Stack Overflow

Find centralized, trusted content and collaborate around the technologies you use most.

Learn more about Collectives

Teams

Q&A for work

Connect and share knowledge within a single location that is structured and easy to search.

Learn more about Teams

I'm trying to pass a 2d QList as a Q_PROPERTY into QML, however, inside QML and i am unable to actually access any of the information.

some code:

the q_property get populated by a q_invokable function in the constructor:

void Class::createNewGameArray(){
QList<QList<QString>> testArray;
for( int i = 0; i < _intervals.size(); ++i) {
    QList<QString> innerArray;
    testArray.append(innerArray);
        testArray[i].append(_intervals[i]);
        testArray[i].append("Audio");
for( int i = 0; i < _intervals.size(); ++i) {
    QList<QString> innerArray;
    testArray.append(innerArray);
        testArray[i+12].append(_intervals[i]);
        testArray[i+12].append("Text");
 std::random_shuffle(testArray.begin(),testArray.end());
Class::setGameArray(testArray);
emit gameArrayChanged(_newGameArray);

which returns this:

(("M7", "Text"), ("M3", "Text"), ("m3", "Text"), ("M6", "Audio"), ("TT", "Audio"), ("P4", "Text"), ("m7", "Audio"), ("m2", "Text"), ("m6", "Audio"), ("m6", "Text"), ("M7", "Audio"), ("P5", "Text"), ("P4", "Audio"), ("m2", "Audio"), ("M2", "Audio"), ("M3", "Audio"), ("P5", "Audio"), ("m3", "Audio"), ("M6", "Text"), ("TT", "Text"), ("m7", "Text"), ("Oct", "Audio"), ("Oct", "Text"), ("M2", "Text"))

exactly what i want.

i set the rootContext like so in main.cpp:

Class object;
QQmlApplicationEngine engine;
QQmlContext* context = engine.rootContext();
context->setContextProperty("object", &object);
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));

however, inside qml i only get

qml: QVariant(QList<QList<QString> >)

and am unable to actually do anything with it.

My goal, ideally, would be to be able to access the 2d qlist from qml in this manner:

object.gameArray[0][1] // return "Text"

I'm able to do this with regular QLists (without the 2d). Any help would be greatly appreciated!

The cleanest approach is probably to encapsulate the list in a QAbstractItemModel (QAbstractTableModel). The easiest approach is to use QQmlListProperty. – m7913d Aug 21, 2017 at 20:49

QML does not inherently understand QLists, so in general it is not possible to pass in a QList of any type T and have QML able to access the items inside the list.

However, the QML engine does have built in support for a few specific types of QList:

  • QList<QObject *>
  • QList<QVariant>
  • QStringList - (not QList<QString>!!!)
  • Therefore if you can construct your list of lists using any combination of the 3 types above, then you can have a working solution. In your use case I would suggest the following construction:

    QList<QVariant(QStringList)>

    A final note before we try it... Just because this will work, it does not necessarily mean that it is a good idea. The QList contents are copied to Javascript arrays at runtime, and therefore any minor updates to any of the lists from the C++ will cause the entire list to be reconstructed as a new Javascript array, which could be expensive.

    Now, let's try it...

    myclass.h

    #ifndef MYCLASS_H
    #define MYCLASS_H
    #include <QStringList>
    #include <QVariant>
    class MyClass : public QObject
        Q_OBJECT
        Q_PROPERTY(QList<QVariant> variantList READ variantList NOTIFY variantListChanged)
    public:
        explicit MyClass(QObject *parent = nullptr) : QObject(parent),
            m_variantList({
                          QStringList({ "apple", "banana", "coconut" }),
                          QStringList({ "alice", "bob", "charlie" }),
                          QStringList({ "alpha", "beta", "gamma" })
            }) { }
        QList<QVariant> variantList() const { return m_variantList; }
    signals:
        void variantListChanged();
    public slots:
    private:
        QList<QVariant> m_variantList;
    #endif // MYCLASS_H
    

    main.qml

    import QtQuick 2.7
    import QtQuick.Controls 2.0
    ApplicationWindow {
        visible: true
        width: 640
        height: 480
        Column {
            id: column
            // will add the strings here from the handler below
        Component.onCompleted: {
            console.log("variantList length %1".arg(myClass.variantList.length))
            for (var i = 0; i < myClass.variantList.length; i++) {
                console.log("stringList %1 length %2".arg(i).arg(myClass.variantList[i].length))
                for (var j = 0; j < myClass.variantList[i].length; j++) {
                    // print strings to the console
                    console.log("variantList i(%1), j(%2) = %3".arg(i).arg(j).arg(myClass.variantList[i][j]))
                    // add the strings to a visual list so we can see them in the user interface
                    Qt.createQmlObject('import QtQuick 2.7; Text { text: "i(%1), j(%2) = %3" }'.arg(i).arg(j).arg(myClass.variantList[i][j]), column)
    

    main.cpp

    #include <QGuiApplication>
    #include <QQmlApplicationEngine>
    #include <QQmlContext>
    #include "myclass.h"
    int main(int argc, char *argv[])
        QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
        QGuiApplication app(argc, argv);
        QQmlApplicationEngine engine;
        MyClass myClass;
        engine.rootContext()->setContextProperty("myClass", &myClass);
        engine.load(QUrl(QLatin1String("qrc:/main.qml")));
        if (engine.rootObjects().isEmpty())
            return -1;
        return app.exec();
    

    Runtime output

    qml: variantList length 3
    qml: stringList 0 length 3
    qml: variantList i(0), j(0) = apple
    qml: variantList i(0), j(1) = banana
    qml: variantList i(0), j(2) = coconut
    qml: stringList 1 length 3
    qml: variantList i(1), j(0) = alice
    qml: variantList i(1), j(1) = bob
    qml: variantList i(1), j(2) = charlie
    qml: stringList 2 length 3
    qml: variantList i(2), j(0) = alpha
    qml: variantList i(2), j(1) = beta
    qml: variantList i(2), j(2) = gamma
    

    Automatic conversions would only work for several specific types of containers, and that's it. Just because conversion A works, and conversion B works, doesn't mean that conversion A will work too.

    You can pretty much forget about using the [] operator in all the cases where automatic conversion doesn't work.

    A variant list of variant lists however might just work. I haven't tested it myself but there is a slim hope. However, you will have to manually do the conversion before you pass that stuff to QML.

    An approach that will most definitely work is to create accessor functions, like for example QString Class::get(int row, int col) or you can have separate accessors to select a row, and then pass that result to another function to select a column to give you the string.

    You could also do a model but that's not really necessary unless you are dealing with views on the QML side. – dtech Aug 21, 2017 at 21:17

    Thanks for contributing an answer to Stack Overflow!

    • Please be sure to answer the question. Provide details and share your research!

    But avoid

    • Asking for help, clarification, or responding to other answers.
    • Making statements based on opinion; back them up with references or personal experience.

    To learn more, see our tips on writing great answers.