10
votes

I have been trying to use a QML TableView to display a QAbstractTableModel. The missing part of the equation seems to be that it is not possible to have a variable number of columns in the TableView, despite overriding QAbstractItemModel::roleNames which should tell Qt the number and name of my columns. I tried testing this out using only QML:

import QtQuick 2.0
import QtQuick.Controls 1.1

Rectangle {
    anchors.fill: parent
    property real showImage: 1.0
    width: 500
    TableView {
        id: myTable
        model: myModel
        //        TableViewColumn {
        //            role: "title"; title: "Name"; width: 200
        //        }
    }

    ListModel {
        id: myModel
        ListElement {
            title: "item one"
        }
        ListElement {
            title: "item two"
        }
    }
}

When run this doesn't show anything despite the TableView's mode containing ListElements with roles defined in them.

However if the above code is uncommented and a TableViewColumn is defined then the column will display data for that role as expected but the table will still not display any other roles. Obviously that will only work for a statically defined number of columns and not my case where the number of columns is not known until run time.

The example given is basically the same as my real life example except that my model is defined in C++.

It seems as if this may have already been asked here but it did not gain any response.

EDIT: I had tried calling a javascript function:

function addColumnToTable(roleName) {
    var columnString = 'import QtQuick 2.3; import QtQuick.Controls 1.2; TableViewColumn {role: "'
            + roleName + '"; title: "' + roleName + '"; width: 40}';
    var column = Qt.createQmlObject(
                columnString
                , myTable
                , "dynamicSnippet1")
    myTable.addColumn(column);
}

From C++:

QVariant roleName = "name";
QObject *root = view->rootObject();
QMetaObject::invokeMethod(root, "addColumnToTable", Q_ARG(QVariant, roleName));

This at least allowed me to dynamically add columns from C++ although not from within the model/view architecture. Yoann's solution is far and away better than this though.

2

2 Answers

14
votes

You could create dynamically as many TableViewColumn as you need, using the resources property of your TableView.

You will have to add a method in your custom model class which will give you the roleNames you want to display.

QML:

Component
{
    id: columnComponent
    TableViewColumn{width: 100 }
}

TableView {
    id: view
    anchors.fill: parent
    resources:
    {
        var roleList = myModel.customRoleNames
        var temp = []
        for(var i=0; i<roleList.length; i++)
        {
            var role  = roleList[i]
            temp.push(columnComponent.createObject(view, { "role": role, "title": role}))
        }
        return temp
    }

    model: myModel

MyModel.h:

class MyModel: public QAbstractListModel
{
    Q_OBJECT
    Q_PROPERTY(QStringList userRoleNames READ userRoleNames CONSTANT)

public:
    explicit MyModel(QObject *parent = 0);

    enum MyModelRoles {
        UserRole1 = Qt::UserRole + 1,
        UserRole2,
        ...
    };

    QStringList userRoleNames();
    int rowCount(const QModelIndex & parent = QModelIndex()) const;
    QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const;
    ...

private:
    QHash<int, QByteArray> roleNames() const;
    ...

};

MyModel.cpp:

...
...

QHash<int, QByteArray> MyModel::roleNames() const {
    QHash<int, QByteArray> roles = QAbstractListModel::roleNames ();
    roles[UserRole1] = "whatever";
    roles[UserRole2] = "youwant";
    return roles;
}

QStringList MyModel::userRoleNames() // Return ordered List of user-defined roles
{
    QMap<int, QString> res;
    QHashIterator<int, QByteArray> i(roleNames());
    while (i.hasNext()) {
        i.next();
        if(i.key() > Qt::UserRole)
            res[i.key()] = i.value();
    }
    return res.values();
}

...
...
3
votes

The solution with resources does not work for me (Qt 5.6). The TableView is not updated.

This works for me:

Component{
    id: columnComponent
    TableViewColumn{width: 30 }
}

TableView {
    id: tableView
    model: listModel
    property var titles: somethingDynamic
    property var curTitles: {
        var t=[]
        for(var i=0;i<columnCount;i++){
            t.push(getColumn(i).title)
        }
        return t
    }
    onTitlesChanged:{
        for(var i=0;i<titles.length;i++){
            if(curTitles.indexOf(titles[i])==-1){
                var column = addColumn(columnComponent) 
                column.title=titles[i]
                column.role=titles[i]                   
            }
        }
        for(var i=curTitles.length-1;i>=0;i--){
            if(titles.indexOf(curTitles[i])==-1){
                removeColumn(i)
            }
        }
    }
}

}

It also correctly updates drag-drop-reordered columns.