binding QStandardItemModel from C++ with QML



  • Hi all,

    I was wondering why can't I show check boxes in qml's listview when I use QStandardItemModel. Let me give you an example:
    c++ side

    ...
    QStandardItemModel model;
     model.setColumnCount(1);
     const int N = 25;
     model.setRowCount(N);
    for (int r = 0; r < N; ++r)
    {
        QStandardItem* item = new QStandardItem(QString("item %1").arg(r));
        item->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled);
        if (r % 2 == 0)
            item->setData(Qt::Unchecked, Qt::CheckStateRole);
        else
            item->setData(Qt::Checked, Qt::CheckStateRole
    
        model.setItem(r, 0, item);
     }
    QQmlApplicationEngine engine;
    engine.rootContext()->setContextProperty("myModel", &model);
    ...
    

    while on the qml's side:

    ListView {
        ...
        model: myModel
        delegate: RowLayout {
            width:  parent.width
                CheckBox {
                    checked: /*what goes in here?*/
                }
                Text {
                    width: 100; height: 32
                    text: model.display
                    Layout.fillWidth: true
                }
        }
    }
    

    I'm perfectly aware that checkboxes can be set via custom roles, that is:

    QStandardItemModel model;
    model.setColumnCount(1);
    const int N = 25;
    model.setRowCount(N);
    // new roles
    enum
    {
        checkedRole = Qt::UserRole,
        nameRole
    };
    QHash<int, QByteArray> names;
    names[checkedRole] = "checked";
    names[nameRole] = "name";
    model.setItemRoleNames(names);
    
    for (int r = 0; r < N; ++r)
    {
        QStandardItem* item = new QStandardItem(QString("item %1").arg(r));
        item->setData(QString("item %1").arg(r), nameRole);
    
        // item->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled); don't need that anymore
        if (r % 2 == 0)
            item->setData(Qt::Unchecked, checkedRole); // instead of Qt::CheckStateRole
        else
            item->setData(Qt::Checked, checkedRole); // instead of Qt::CheckStateRole
    
        model.setItem(r, 0, item);
    }
    ...
    

    and on the qml's side, i can write this:

    ListView {
        ...
        model: myModel
        delegate: RowLayout {
            width:  parent.width
            CheckBox {
                checked: model.checked
            }
            Text {
                width: 100; height: 32
                text: model.name
                Layout.fillWidth: true
            }
        }
    }
    

    where the name is my custom role instead of Qt::DisplayRole (qml property is display, if I'm not mistaken), and since I can't find qml property for Qt::CheckStateRole, I need to put my custom role, checked.

    Finally, the questions.
    Is it safe to conclude that the Qt::DisplayRole is exposed to QML via display property, while the Qt::CheckStateRole isn't? Why is it? Is there any rational explanation behind this behaviour?
    On the downside, I can't use the same QStandardItemModel code for both QWidgets and QML, since I need to create custom roles for the QML side. Which means, I need to have two different models, one for QWidgets, and one for QML. Yes, I'm aware that I really don't need two instances, but nevertheless, this seems like waste of time and resources. And what's more, the big chunk of the code is practically the same.
    Ideally, I'd like to be able to write model code (now it's the QStandardItemModel, developed on the c++ side, nothing fancy), and use it in the QML, or in QWidgets.
    Maybe I'm missing something, but I couldn't find anything in the docs about that.



  • QML Views expose data do the delegate based on the role names of the model.

    The default role names for a QStandardItemModel are:

    {
        Qt::DisplayRole: "display",
        Qt::DecorationRole: "decoration",
        Qt::EditRole: "edit",
        Qt::ToolTipRole: "toolTip", 
        Qt::StatusTipRole, "statusTip"
        Qt::WhatsThisRole, "whatsThis"
    }
    

    As you can see there's no mention of Qt::CheckStateRole.
    What you could do is just add it like that :

    QHash<int, QByteArray> roleNames = model.roleNames();
    roleNames[Qt::CheckStateRole] = "checkState";
    model.setRoleNames(roleNames);
    

    Then you access it in your QML delegate like this: checked: model.checkState, and continue using it normally in QWidgets since you still use the same roles.



  • Thanks for the feedback.
    I have a follow up question though.

    • Where are QML's default role names documented? I couldn't see the list. It would save me a lot of time. EDIT Here they are: link.


Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.