QtQuick Controls を試してみてるんだけど、TableView を使ったサンプルに食わせるモデルがなぜか QAbstractListModel なのが気になってた。テーブルビューなんだからテーブルのモデルを使うべきなんじゃない? QAbstractTableModel があるんだからそれ使えばいいんじゃない? とか思ったのでやってみた。
まずは QML 側。今回は TableView だけ定義して、モデル側はC++ で書く。
import QtQuick 2.3
import QtQuick.Controls 1.2
ApplicationWindow {
visible: true
width: 260
height: 120
title: qsTr("TableModel sample")
TableView {
anchors.centerIn: parent
anchors.fill: parent
model: tableModel
TableViewColumn {
role: "foo"
title: "Foo"
width: 80
}
TableViewColumn {
role: "bar"
title: "Bar"
width: 80
}
TableViewColumn {
role: "baz"
title: "Baz"
width: 80
}
}
}
TableView をつくり、 Foo, Bar, Baz というタイトルの列に、それぞれ foo, bar, baz という role をつけて、モデルは tableModel というモデルを見るよと教えてやる。
つぎに C++ 側。
# include <QApplication>
# include <QQmlApplicationEngine>
# include <QQmlContext>
# include "tablemodel.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QQmlApplicationEngine engine;
TableModel *tableModel = new TableModel();
engine.rootContext()->setContextProperty("tableModel", tableModel);
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}
QMLの要求する tableModel
に対し、このあと作る TableModel
クラスのインスタンスをバインドしてやるだけ。
そしてメインディッシュの TableModel
定義&実装。
# ifndef TABLEMODEL_H
# define TABLEMODEL_H
# include <QAbstractTableModel>
class TableModel : public QAbstractTableModel
{
Q_OBJECT
public:
explicit TableModel(QObject *parent = 0);
int rowCount(const QModelIndex &parent) const;
int columnCount(const QModelIndex &parent) const;
QVariant data(const QModelIndex &index, int role) const;
QHash<int, QByteArray> roleNames() const;
private:
enum {
RoleFoo = Qt::UserRole + 1,
RoleBar = Qt::UserRole + 2,
RoleBaz = Qt::UserRole + 3,
};
};
# endif //TABLEMODEL_H
AbstractTableModel
を継承し、rowCount()
、 columnCount()
、data()
、roleNames()
の実装をしてやる。enum については次で説明する。
# include <QtDebug>
# include "tablemodel.h"
TableModel::TableModel(QObject *parent) :
QAbstractTableModel(parent)
{
}
int
TableModel::rowCount(const QModelIndex & /*parent*/) const
{
// 行数を返す。手抜き
qDebug() << "rowCount() called";
return 5;
}
int
TableModel::columnCount(const QModelIndex & /* parent */) const
{
// 列数を返す。手抜き
qDebug() << "columnCount() called";
return 3;
}
QHash<int, QByteArray>
TableModel::roleNames() const
{
// QML側の role と Role* との対応をつける。
qDebug() << "roleNames() called";
QHash<int, QByteArray> rn = QAbstractItemModel::roleNames();
rn[RoleFoo] = "foo";
rn[RoleBar] = "bar";
rn[RoleBaz] = "baz";
return rn;
}
QVariant
TableModel::data(const QModelIndex &index, int role) const
{
// 中身を返す。
QVariant ret = QVariant();
qDebug() << "data() called" << index << role;
if (index.isValid()) {
switch(role) {
case RoleFoo:
ret.setValue(QString("Foo%1").arg(index.row()));
break;
case RoleBar:
ret.setValue(QString("Bar%1").arg(index.row()));
break;
case RoleBaz:
ret.setValue(QString("Baz%1").arg(index.row()));
break;
default:
qDebug() << "data(Invalid Role!)" << index;
break;
}
}
return ret;
}
今回はモデルの中でちゃんとマトリクスを持ってそれを出すみたいなことはせず、手抜き実装とした。なので rowCount()
とか columnCount()
はハードコードした値を返しちゃってる。
roleNames()
が QML 側で定義した role と data()
の第二引数である int role
に渡ってくる値との関連付けとなる。tablemodel.h
で定義していた enum は、これのために作っていた。
で、肝心の data()
。てっきり index.row()
と index.column()
で座標を得ているのかと思ったら、使っているのは index.row()
と role
なのだった。これが最初わかんなくて qDebug()
仕込んだんだけど、いやこれひどいと思うんだ。ちょっとあんまりだよね。
で、こいつをビルドして実行すると、こんなふうになる。
qDebug の出力はこんなかんじ。
rowCount() called
roleNames() called
rowCount() called
columnCount() called
data() called QModelIndex(0,0,0x0,TableModel(0x809a58300) ) 257
rowCount() called
columnCount() called
data() called QModelIndex(0,0,0x0,TableModel(0x809a58300) ) 258
rowCount() called
columnCount() called
data() called QModelIndex(0,0,0x0,TableModel(0x809a58300) ) 259
rowCount() called
columnCount() called
data() called QModelIndex(1,0,0x0,TableModel(0x809a58300) ) 257
rowCount() called
columnCount() called
data() called QModelIndex(1,0,0x0,TableModel(0x809a58300) ) 258
rowCount() called
columnCount() called
data() called QModelIndex(1,0,0x0,TableModel(0x809a58300) ) 259
rowCount() called
columnCount() called
data() called QModelIndex(2,0,0x0,TableModel(0x809a58300) ) 257
rowCount() called
columnCount() called
data() called QModelIndex(2,0,0x0,TableModel(0x809a58300) ) 258
rowCount() called
columnCount() called
data() called QModelIndex(2,0,0x0,TableModel(0x809a58300) ) 259
rowCount() called
columnCount() called
data() called QModelIndex(3,0,0x0,TableModel(0x809a58300) ) 257
rowCount() called
columnCount() called
data() called QModelIndex(3,0,0x0,TableModel(0x809a58300) ) 258
rowCount() called
columnCount() called
data() called QModelIndex(3,0,0x0,TableModel(0x809a58300) ) 259
rowCount() called
columnCount() called
data() called QModelIndex(4,0,0x0,TableModel(0x809a58300) ) 257
rowCount() called
columnCount() called
data() called QModelIndex(4,0,0x0,TableModel(0x809a58300) ) 258
rowCount() called
columnCount() called
data() called QModelIndex(4,0,0x0,TableModel(0x809a58300) ) 259
なんかAbstractTableModel って何のためにあるんだろう、普通に AbstractListModel 使っても変わんないんじゃね?と思う結果だった。ソートとか列の増減とかしたくなったときに嬉しくなったりするのかな?気が向いたら試してみよう。