При использовании QAbstractTableModel с Qml TableView отображается только 1-й столбец
Я пытаюсь использовать (класс, производный от) QAbstractTableModel с Qml TableView;
Однако отображается только 1-й столбец.
Причина в том, что QVariant MyModel::data(const QModelIndex &index, int role) не вызывается для ненулевых столбцов, но я не понимаю, почему.
QTableView работает хорошо, однако.
Я сделал отдельный, простой проект, который воспроизводит мою проблему:
MyModel.h:
#ifndef MYMODEL_H
#define MYMODEL_H
#include <QObject>
#include <QAbstractTableModel>
#include <QList>
#include <QString>
#include <QDebug>
struct SimpleData
{
QString m_one;
qint32 m_two;
qreal m_three;
};
class MyModel : public QAbstractTableModel
{
Q_OBJECT
public:
explicit MyModel();//MyData *the_data);
int rowCount(const QModelIndex & parent = QModelIndex()) const Q_DECL_OVERRIDE;
int columnCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const Q_DECL_OVERRIDE;
QHash<int, QByteArray> roleNames() const Q_DECL_OVERRIDE;
signals:
public slots:
void theDataChanged();
private:
QList<SimpleData> m_the_data;
};
#endif // MYMODEL_H
mymodel.cpp: #include "mymodel.h"
MyModel::MyModel() : QAbstractTableModel(0)
{
m_the_data << SimpleData{"Alpha", 10, 100.0}
<< SimpleData{"Beta", 20, 200.0}
<< SimpleData{"Gamma", 30, 300.0}
<< SimpleData{"Delta", 40, 400.0};
}
int MyModel::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent)
return m_the_data.size();
}
int MyModel::columnCount(const QModelIndex &parent) const
{
Q_UNUSED(parent)
return 3;
}
QVariant MyModel::data(const QModelIndex &index, int role) const
{
// Check DisplayRole
if(role != Qt::DisplayRole)
{
return QVariant();
}
// Check boudaries
if(index.column() < 0 ||
columnCount() <= index.column() ||
index.row() < 0 ||
rowCount() <= index.row())
{
qDebug() << "Warning: " << index.row() << ", " << index.column();
return QVariant();
}
// Nominal case
qDebug() << "MyModel::data: " << index.column() << "; " << index.row();
switch(index.column())
{
case 0:
return m_the_data[index.row()].m_one;
case 1:
return m_the_data[index.row()].m_two;
case 2:
return m_the_data[index.row()].m_three;
default:
qDebug() << "Not supposed to happen";
return QVariant();
}
}
QHash<int, QByteArray> MyModel::roleNames() const
{
QHash<int, QByteArray> roles;
roles[0] = "one";
roles[1] = "two";
roles[2] = "three";
return roles;
}
void MyModel::theDataChanged()
{
//TODO
}
main.qml:
import QtQuick 2.1
import QtQuick.Controls 1.0
ApplicationWindow {
title: qsTr("Hello World")
width: 640
height: 480
menuBar: MenuBar {
Menu {
title: qsTr("File")
MenuItem {
text: qsTr("Exit")
onTriggered: Qt.quit();
}
}
}
TableView {
anchors.fill: parent
TableViewColumn {title: "1"; role: "one"; width: 70 }
TableViewColumn {title: "2"; role: "two"; width: 70 }
TableViewColumn {title: "3"; role: "three"; width: 70 }
model: theModel
}
}
main.cpp:
#include <QtQml>
#include <QQmlApplicationEngine>
#include <QApplication>
#include <QQuickWindow>
#include <QTableView>
#include "mymodel.h"
int main(int argc, char *argv[])
{
// Application :
QApplication app(argc, argv);
// Data stuff :
//MyData data(&app);
MyModel model;
// UI :
QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty("theModel", &model);
engine.load(QUrl("qrc:/qml/main.qml"));
QList<QObject*> temp = engine.rootObjects();
QObject *topLevel = temp.value(0);
QQuickWindow *window = qobject_cast<QQuickWindow *>(topLevel);
if ( !window ) {
qWarning("Error: Your root item has to be a Window.");
return -1;
}
// Display the main.qml, which show the model:
window->show();
// Same, using a QTableView:
QTableView view;;
view.setModel(&model);
view.show();
return app.exec();
}
Вывод qDebug о TableView (строка, затем столбец):
MyModel::data: 0 ; 0
MyModel::data: 0 ; 0
MyModel::data: 0 ; 1
MyModel::data: 0 ; 1
MyModel::data: 0 ; 2
MyModel::data: 0 ; 2
MyModel::data: 0 ; 3
MyModel::data: 0 ; 3
Вывод qDebug о QTableVierw:
MyModel::data: 0 ; 0
MyModel::data: 0 ; 0
MyModel::data: 0 ; 0
MyModel::data: 1 ; 0
MyModel::data: 2 ; 0
MyModel::data: 0 ; 1
MyModel::data: 1 ; 1
MyModel::data: 2 ; 1
MyModel::data: 0 ; 2
MyModel::data: 1 ; 2
MyModel::data: 2 ; 2
MyModel::data: 0 ; 3
MyModel::data: 1 ; 3
MyModel::data: 2 ; 3
Заметки / вещи, которые я пробовал:
В коде, который я дал, я мог использовать QQmlListProperty; Однако мой фактический код является более сложным
- данные на самом деле запрашиваются,
- У меня нет класса SimpleData
- Я в основном использую QAbstractTableModel в качестве прокси. Как следствие, я не могу переключиться на QQmlListProperty (или эквивалентный)
Был проект для проверки моделей, ModelTest, однако он не работает с Qt 5.2.
тип данных (QString, qreal) не является проблемой: при обмене по-прежнему отображается только 1-й столбец.
Соответствующие ссылки:
http://qt-project.org/doc/qt-5/QAbstractTableModel.html
http://qt-project.org/doc/qt-5/qtquick-modelviewsdata-cppmodels.html
Заранее спасибо!
Спецификации: Windows 7, Qt 5.2, Mingw 4.8, Qt Creator 3.0
1 ответ
API TableViewColumn предполагает, что данные из столбца извлекаются через роли вместо столбцов, то есть "один", "два" и "три", в то время как переданный столбец будет всегда 0. Вы возвращаете QVariant() для всего, кроме Qt::DisplayRole. Qt::DisplayRole равен 0, преобразован в int. В rolenames вы устанавливаете имя для 0 на "one", поэтому вы случайно видите что-то для "one" (DisplayRole).
Таким образом, чтобы это исправить, вы должны вернуть что-то за один, два и три. Я бы предложил определить перечисление пользовательских ролей в заголовке:
//In class MyModel:
enum Role {
OneRole=Qt::UserRole,
TwoRole,
ThreeRole
};
Определите roleNames следующим образом:
QHash<int, QByteArray> MyModel::roleNames() const
{
QHash<int, QByteArray> roles;
roles[OneRole] = "one";
roles[TwoRole] = "two";
roles[ThreeRole] = "three";
return roles;
}
Обратите внимание, что я начал с Qt::UserRole вместо 0. Это позволяет избежать конфликтов с предопределенными ролями, такими как Qt:: DisplayRole.
Затем верните что-нибудь для One, Two и Three в data():
...
switch(role)
{
case OneRole:
return m_the_data[index.row()].m_one;
case TwoRole:
return m_the_data[index.row()].m_two;
case ThreeRole:
return m_the_data[index.row()].m_three;
}
...
Теперь вы должны увидеть данные.
Кажется, что TableView/TableViewColumn из QtQuickControls делает несколько неудачное и запутанное сочетание роли и столбцов: хотя при именовании давайте подумаем о столбцах модели (но на самом деле они ссылаются на столбцы представления здесь), можно получать данные только через разные роли, с столбцом, установленным в 0. (Для меня должно быть другое необязательное свойство "столбец" в TableViewColumn.) Это немного противоречит образу C++ QAbstractItemModel/QTableView, где несколько столбцов являются естественным делом и представления QtQuick, которые все используют только роли для ссылки на данные, часто вообще не поддерживая несколько столбцов (ListView, GridView).