Help us understand the problem. What is going on with this article?

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

More than 1 year has passed since last update.

はじめに

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

AI-medical-service
近未来の内視鏡医療を実現する医療ベンチャー
https://www.ai-ms.com/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away