C++
Qt
QtQuick
adventcalendar2018
QtDay 10

QMLで使うモデルをC++で実装する


はじめに

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への出力オペレータを実装する。


コード例


addressitem.h

#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



addressitem.cpp(抜粋)

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さんです。お楽しみに!