QAbstractTableModel + TableView サンプル

QtQuick Controls を試してみてるんだけど、TableView を使ったサンプルに食わせるモデルがなぜか QAbstractListModel なのが気になってた。テーブルビューなんだからテーブルのモデルを使うべきなんじゃない？ QAbstractTableModel があるんだからそれ使えばいいんじゃない？ とか思ったのでやってみた。

まずは QML 側。今回は TableView だけ定義して、モデル側はC++ で書く。

```qml:main.qml
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++ 側。

```cpp:main.cpp
#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` 定義＆実装。

```cpp:tablemodel.h
#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 については次で説明する。

```cpp:tablemodel.cpp
#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()` 仕込んだんだけど、いやこれひどいと思うんだ。ちょっとあんまりだよね。

で、こいつをビルドして実行すると、こんなふうになる。

![screenshot.jpg](https://qiita-image-store.s3.amazonaws.com/0/57111/a1eccdd8-f91a-c995-7e23-29e9aa683998.jpeg)

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 使っても変わんないんじゃね？と思う結果だった。ソートとか列の増減とかしたくなったときに嬉しくなったりするのかな？気が向いたら試してみよう。