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
Если у кого-то есть лучшее решение, пожалуйста, напишите!