QML TableView с динамическим числом столбцов

Я пытался использовать QML TableView отображать QAbstractTableModel, Недостающая часть уравнения, кажется, состоит в том, что невозможно иметь переменное число столбцов в TableView несмотря на переопределение QAbstractItemModel::roleNames который должен сказать Qt количество и имя моих столбцов. Я попытался проверить это, используя только 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"
        }
    }
}

При запуске это ничего не показывает, несмотря на TableView режим, содержащий ListElement с ролями, определенными в них.

Однако если приведенный выше код не прокомментирован и TableViewColumn определяется, тогда столбец будет отображать данные для этой роли, как и ожидалось, но таблица все равно не будет отображать другие роли. Очевидно, что это будет работать только для статически определенного числа столбцов, а не в моем случае, когда количество столбцов не известно до времени выполнения.

Данный пример в основном такой же, как в реальной жизни, за исключением того, что моя модель определена в C++.

Кажется, что это, возможно, уже спрашивалось здесь, но это не получило никакого ответа.

РЕДАКТИРОВАТЬ: я пытался вызвать функцию JavaScript:

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

Из C++:

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

Это, по крайней мере, позволило мне динамически добавлять столбцы из C++, но не из архитектуры модель / представление. Решение Йоанна намного лучше, чем это, хотя.

3 ответа

Решение

Вы можете создать динамически столько TableViewColumn по мере необходимости, используя resources собственность вашего TableView,

Вам нужно будет добавить метод в свой класс модели, который даст вам имена ролей, которые вы хотите отобразить.

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

...
...

Решение с ресурсами у меня не работает (Qt 5.6). TableView не обновляется.

Это работает для меня:

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

}

Также корректно обновляются столбцы с перетаскиванием.

Другой способ сделать это с помощью Instantiator:

      TableView{
  id: view
  model: tableViewModel
  Instantiator{
    model: someStringList
    onObjectAdded: view.addColumn(object)
    onObjectRemoved: view.removeColumn(object)
    delegate: TableViewColumn{
      width: 100
      title: modelData
      role: modelData
    }
  }
}

Этот код (и код от @palfi) вызывают некоторое предупреждение в консоли: Для каждого созданного столбца есть:

      qml: TableView::insertColumn(): you cannot add a column to multiple views

И затем, когда столбец удален, он выдает это предупреждение:

       file:///usr/lib/qt/qml/QtQuick/Controls/Private/BasicTableView.qml:297: Error: Invalid attempt to destroy() an indestructible object

Если у кого-то есть лучшее решение, пожалуйста, напишите!

Другие вопросы по тегам