はじめに
Qt勉強会@Tokyo 会場係のAtsushi4です。こんにちは。
Qt Advent Calendar 2018の10日目の記事は、cppのモデル(QAbstractItemModelのサブクラス)とQMLの連携に関するお話です。
QMLのListViewでUIを作る時、皆さんはmodelをどう作りますか。
JavaScriptのArrayを使う、QMLのListModelを使う、.qmlファイルでモデルを定義する、などの方法もありますが、私はcppのモデルクラスとして実装するのが割と好きです。
ここでは、私が普段モデルクラスを実装する時に、なんとなく気を付けている事を書きます。
気をつけていること
アイテムクラス
データ1レコードを表すクラスです。なるべくPOD型のように使えて、signal/slotのQueuedConnectionができて、デバッグ出力ができて、QMLでも使えるクラスを目指します。
- SharedDataなクラスにする。
- Q_GADGET, Q_PROPERTYを使い、メンバー変数をプロパティとして実装する。
- メンバー関数をQ_INVOKABLE宣言する。
- swap関数を実装し、Q_DECLARE_SHAREDする。
- Q_DECLARE_METATYPEする。
- qRegisterMetaTypeする。
- QDebugへの出力オペレータを実装する。
コード例
#ifndef ADDRESSITEM_H
#define ADDRESSITEM_H
#include "cppmodel_global.h"
#include <QtCore/QSharedDataPointer>
#include <QtCore/QObject>
#include <QtCore/QDate>
#include <QtCore/QMetaType>
class AddressItemData;
class CPPMODELSHARED_EXPORT AddressItem
{
Q_GADGET
Q_PROPERTY(QString name READ name CONSTANT)
Q_PROPERTY(QString kana READ kana CONSTANT)
Q_PROPERTY(QString email READ email CONSTANT)
Q_PROPERTY(QDate birthday READ birthday CONSTANT)
public:
AddressItem();
AddressItem(const QString &name,
const QString &kana,
const QString &email,
const QDate &birthday);
AddressItem(const AddressItem &);
AddressItem &operator=(const AddressItem &);
~AddressItem();
inline void swap(AddressItem &other) {return data.swap(other.data);}
bool operator==(const AddressItem &other) const;
inline bool operator!=(const AddressItem &other) const {return !(*this == other);}
const QString &name() const;
const QString &kana() const;
const QString &email() const;
const QDate &birthday() const;
private:
QSharedDataPointer<AddressItemData> data;
};
Q_DECLARE_SHARED(AddressItem)
Q_DECLARE_METATYPE(AddressItem)
#ifndef QT_NO_DEBUG_STREAM
class QDebug;
CPPMODELSHARED_EXPORT QDebug operator<<(QDebug debug, const AddressItem &item);
#endif // QT_NO_DEBUG_STREAM
#endif // ADDRESSITEM_H
namespace {
int metaTypeId = qRegisterMetaType<AddressItem>();
}
モデルクラス
ListViewのmodelプロパティに指定するクラスです。QMLプラグインのプロジェクトを作り、qmlRegisterTypeでQML側に公開します。
非同期にデータを読み込み、おおもとのデータを変更することなくソートやフィルタリングができるクラスを目指します。
- QSortFilterProxyModelクラスのサブクラスにする。
- アイテムクラスのリストを持つQAbstractListModelのサブクラスを作り、setSourceModelする。
- roleNamesを実装する。
- dynamicSortFilterをtrueにする。
- アイテムを追加するappendRow(s)関数をQ_INVOKABLE付きで宣言し、実装する。
- アイテムクラスを返すget関数を実装する。
- 64ビット整数型を使用する代わりにQStringを使用する。
- データを読み込むクラスは別のQML Typeとして実装する。
※コード例は省略
おわりに
QML側でモデルを準備するととても簡単に書けるので、私もプロトタイプなどQMLにモデルをべた書きすることが良くあります。それに比べてcpp側でモデルを実装するのはかなり面倒な作業です。
でもQWidgetベースでもQMLでも使えて、うまく作ればListViewで取り回しやすく、QML側の実装がとても楽になります。あとはもう少し簡単にリストモデルが作れると良いんですけどね。
明日は@shin1_okadaさんです。お楽しみに!