QRangeModel Class

QRangeModel はQAbstractItemModel を実装しています。さらに...

ヘッダ #include <QRangeModel>
CMake: find_package(Qt6 REQUIRED COMPONENTS Core)
target_link_libraries(mytarget PRIVATE Qt6::Core)
qmake: QT += core
以来:Qt 6.10
継承: QAbstractItemModel

注意:このクラスの関数はすべてリエントラントです。

パブリックな型

(since 6.10) struct RowOptions
enum class RowCategory { Default, MultiRoleItem }

プロパティ

パブリック関数

QRangeModel(Range &&range, QObject *parent = nullptr)
QRangeModel(Range &&range, Protocol &&protocol, QObject *parent = nullptr)
virtual ~QRangeModel() override
void resetRoleNames()
void setRoleNames(const QHash<int, QByteArray> &names)

再実装パブリック関数

virtual QModelIndex buddy(const QModelIndex &index) const override
virtual bool canDropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) const override
virtual bool canFetchMore(const QModelIndex &parent) const override
virtual bool clearItemData(const QModelIndex &index) override
virtual int columnCount(const QModelIndex &parent = {}) const override
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override
virtual bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override
virtual void fetchMore(const QModelIndex &parent) override
virtual Qt::ItemFlags flags(const QModelIndex &index) const override
virtual bool hasChildren(const QModelIndex &parent = QModelIndex()) const override
virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override
virtual QModelIndex index(int row, int column, const QModelIndex &parent = {}) const override
virtual bool insertColumns(int column, int count, const QModelIndex &parent = {}) override
virtual bool insertRows(int row, int count, const QModelIndex &parent = {}) override
virtual QMap<int, QVariant> itemData(const QModelIndex &index) const override
virtual QModelIndexList match(const QModelIndex &start, int role, const QVariant &value, int hits, Qt::MatchFlags flags) const override
virtual QMimeData *mimeData(const QModelIndexList &indexes) const override
virtual QStringList mimeTypes() const override
virtual bool moveColumns(const QModelIndex &sourceParent, int sourceColumn, int count, const QModelIndex &destinationParent, int destinationColumn) override
virtual bool moveRows(const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destinationParent, int destinationRow) override
virtual void multiData(const QModelIndex &index, QModelRoleDataSpan roleDataSpan) const override
virtual QModelIndex parent(const QModelIndex &child) const override
virtual bool removeColumns(int column, int count, const QModelIndex &parent = {}) override
virtual bool removeRows(int row, int count, const QModelIndex &parent = {}) override
virtual QHash<int, QByteArray> roleNames() const override
virtual int rowCount(const QModelIndex &parent = {}) const override
virtual bool setData(const QModelIndex &index, const QVariant &data, int role = Qt::EditRole) override
virtual bool setHeaderData(int section, Qt::Orientation orientation, const QVariant &data, int role = Qt::EditRole) override
virtual bool setItemData(const QModelIndex &index, const QMap<int, QVariant> &data) override
virtual QModelIndex sibling(int row, int column, const QModelIndex &index) const override
virtual void sort(int column, Qt::SortOrder order = Qt::AscendingOrder) override
virtual QSize span(const QModelIndex &index) const override
virtual Qt::DropActions supportedDragActions() const override
virtual Qt::DropActions supportedDropActions() const override

シグナル

再実装された保護された関数

virtual bool event(QEvent *event) override
virtual bool eventFilter(QObject *object, QEvent *event) override

保護されたスロット

virtual void resetInternalData() override

詳細説明

QRangeModel は、任意の順次反復可能な C++ 型のデータを Qt のモデル/ビューフレームワークで利用できるようにします。これにより、既存のデータ構造をQt WidgetsQt Quick アイテム・ビューに表示したり、アプリケーションのユーザーがグラフィカル・ユーザー・インターフェースを使用してデータを操作したりすることが容易になります。

QRangeModel を使用するには、C++ の範囲でインスタンス化し、1 つ以上のビューのモデルとして設定します:

std::array<int, 5> numbers = {1, 2, 3, 4, 5};
QRangeModel model(numbers);
listView.setModel(&model);

モデルの構築

範囲は、標準メソッドstd::beginstd::end が実装され、返されるイテレーターの型がstd::forward_iterator を満たす C++ の任意の型にすることができます。std::size が利用可能で、イテレータがstd::random_access_iterator を満たす場合、特定のモデル操作はより良く動作します。

後で範囲を設定するAPIはありませんし、モデルから範囲を取得するAPIもありません。範囲は、値、参照ラッパー、またはポインタで指定することができます。モデルがどのように構築されたかは、モデルAPIを通じた変更が元のデータを変更するかどうかを定義します。

valueで構築された場合、モデルは範囲のコピーを作成し、setData ()やinsertRows ()のようなモデルを修正するQAbstractItemModel APIは、元の範囲に影響を与えません。

QRangeModel model(numbers);

範囲を再取得するAPIがないので、値による範囲からモデルを構築することは、ほとんどの場合、読み取り専用データを表示するためにのみ有用です。データの変更は、dataChanged() のようなモデルが発するシグナルを使ってモニターすることができます。

モデルの変更が元の範囲に影響するようにするには、ポインタで範囲を指定します:

QRangeModel model(&numbers);

または参照ラッパーを通して提供する:

QRangeModel model(std::ref(numbers));

この場合、モデルを修正するQAbstractItemModel APIは、範囲も修正する。insertRows() やremoveColumns() など、範囲の構造を変更するメソッドでは、標準 C++ コンテナ APIresize(),insert(),erase() を使用します。また、データのセットやクリアを行うために、変異イテレータを再参照します。

注意: モデルが構築され、ビューに渡された後は、モデルが操作する範囲を直接変更してはいけません。モデルのビューは変更について知らされませんし、構造的な変更はモデルが保持するQPersistentModelIndex のインスタンスを破損する可能性が高いからです。

呼び出し元は、範囲の寿命がモデルの寿命を超えていることを確認しなければなりません。

スマートポインタを使用して、すべてのクライアントがその範囲を使い終わったときにのみ削除されるようにします。

auto shared_numbers = std::make_shared<std::vector<int>>(numbers);
QRangeModel model(shared_numbers);

QRangeModel は共有ポインタとユニークポインタの両方をサポートしています。

読み取り専用または変更可能

アクセスすると常に定数値が返される const オブジェクトである範囲や、必要なコンテナ API が利用できない場合、QRangeModel は何もせずfalse を返すライトアクセス API を実装します。std::array を使用した例では、C++ 配列のエントリ数は固定であるため、モデルは行を追加したり削除したりすることはできません。しかし、setData ()を使用して値を変更することができ、ユーザはリスト・ビューで値の編集をトリガすることができます。配列をconstにすると、値も読み取り専用になります。

const std::array<int, 5> numbers = {1, 2, 3, 4, 5};

のように要素の型が const の場合も、値は読み取り専用になります。

std::array<const int, 5> numbers = {1, 2, 3, 4, 5};

std::vector を使用した上記の例では、モデルは行を追加または削除することができ、データを変更することができます。範囲を定数参照として渡すと、モデルは読み取り専用になります。

QRangeModel model(std::cref(numbers));

注意: 範囲の値が const の場合、QAbstractItemModel API を使って列や行を削除したり挿入したりすることもできません。より詳細な制御を行うには、the C++ tuple protocol を実装してください。

行と列

範囲内の要素は、モデルの行として解釈されます。これらの行要素のタイプに応じて、QRangeModel は範囲をリスト、テーブル、またはツリーとして公開します。

行要素が単純な値である場合、範囲はリストとして表現されます。

QList<int> numbers = {1, 2, 3, 4, 5};
QRangeModel model(numbers); // columnCount() == 1
QListView listView;
listView.setModel(&model);

行要素の型がベクター、リスト、配列のような反復可能な範囲の場合、範囲はテーブルとして表現されます。

std::vector<std::vector<int>> gridOfNumbers = {
    {1, 2, 3, 4, 5},
    {6, 7, 8, 9, 10},
    {11, 12, 13, 14, 15},
};
QRangeModel model(&gridOfNumbers); // columnCount() == 5
QTableView tableView;
tableView.setModel(&model);

行の型が標準C++コンテナAPIresize(),insert(),erase() を提供する場合、列の追加と削除はinsertColumns() とremoveColumns() で行うことができる。すべての行は、同じ数の列を持つ必要があります。

行としての構造体とガジェット

行の型がthe C++ tuple protocol を実装している場合、範囲は固定数の列を持つテーブルとして表現されます。

using TableRow = std::tuple<int, QString>;
QList<TableRow> numberNames = {
    {1, "one"},
    {2, "two"},
    {3, "three"}
};
QRangeModel model(&numberNames); // columnCount() == 2
QTableView tableView;
tableView.setModel(&model);

C++の型にタプル・プロトコルを実装するよりも簡単で柔軟な方法は、Qtのメタ・オブジェクト・システムを使って プロパティを持つ型を宣言することです。これは、gadget 、またはQObject サブクラスとして宣言される値型になります。

class Book
{
    Q_GADGET
    Q_PROPERTY(QString title READ title)
    Q_PROPERTY(QString author READ author)
    Q_PROPERTY(QString summary MEMBER m_summary)
    Q_PROPERTY(int rating READ rating WRITE setRating)
public:
    Book(const QString &title, const QString &author);

    // C++ rule of 0: destructor, as well as copy/move operations
    // provided by the compiler.

    // read-only properties
    QString title() const { return m_title; }
    QString author() const { return m_author; }

    // read/writable property with input validation
    int rating() const { return m_rating; }
    void setRating(int rating)
    {
        m_rating = qBound(0, rating, 5);
    }

private:
    QString m_title;
    QString m_author;
    QString m_summary;
    int m_rating = 0;
};

QObject サブクラスを使用することで、プロパティをバインド可能にしたり、変更通知シグナルを持つことができます。しかし、QObject インスタンスをアイテムに使用すると、メモリのオーバーヘッドが大きくなります。

Qtのガジェットやオブジェクトを使用する方が便利で、タプルのプロトコルを実装するよりも柔軟です。また、これらの型はQMLから直接アクセス可能です。しかし、プロパティシステムを介したアクセスは実行時のオーバーヘッドを伴います。パフォーマンスが重要なモデルでは、コンパイル時にアクセスコードを生成するためにタプルプロトコルを実装することを検討してください。

マルチロール項目

data()、setData()、clearItemData() などの実装が操作するアイテムのタイプは、上記のgridOfNumbers の例のように、モデル全体で同じにすることができます。しかし、numberNames の例のように、カラムごとに異なるアイテムタイプを持つこともできます。

デフォルトでは、この値はQt::DisplayRoleQt::EditRole のロールに使用されます。ほとんどのビューは、値がconvertible to and from a QString であることを想定しています(しかし、カスタムのデリゲートを使用すると、より柔軟に対応できるかもしれません)。

複数のロールを持つ連想コンテナ

アイテムが連想コンテナで、キー型としてintQt::ItemDataRole 、またはQString を使用し、マップされた型としてQVariant を使用する場合、QRangeModel はそのコンテナを複数のロールのデータの格納場所として解釈します。data() とsetData() 関数はコンテナ内のマッピングされた値を返し、修正します。setItemData() は提供されたすべての値を修正し、itemData() は格納されたすべての値を返し、clearItemData() はコンテナ全体をクリアします。

using ColorEntry = QMap<Qt::ItemDataRole, QVariant>;

const QStringList colorNames = QColor::colorNames();
QList<ColorEntry> colors;
colors.reserve(colorNames.size());
for (const QString &name : colorNames) {
    const QColor color = QColor::fromString(name);
    colors << ColorEntry{{Qt::DisplayRole, name},
                        {Qt::DecorationRole, color},
                        {Qt::ToolTipRole, color.name()}};
}
QRangeModel colorModel(colors);
QListView list;
list.setModel(&colorModel);

キーとして使用するのに最も効率的なデータ型は、Qt::ItemDataRole またはint である。int を使用する場合、itemData() はコンテナをそのまま返し、データのコピーを作成する必要はない。

マルチロールアイテムとしてのガジェットとオブジェクト

ガジェットとQObject タイプもマルチロールアイテムとして表現できます。これらのアイテムのプロパティはname of a role が一致するロールに使用されます。全てのアイテムが同じタイプのガジェットまたはQObject を保持している場合、QRangeModel のroleNames() 実装はそのタイプのプロパティのリストを返します。

class ColorEntry
{
    Q_GADGET
    Q_PROPERTY(QString display MEMBER m_colorName)
    Q_PROPERTY(QColor decoration READ decoration)
    Q_PROPERTY(QString toolTip READ toolTip)
public:
    ColorEntry(const QString &color = {})
        : m_colorName(color)
    {}

    QColor decoration() const
    {
        return QColor::fromString(m_colorName);
    }
    QString toolTip() const
    {
        return QColor::fromString(m_colorName).name();
    }

private:
    QString m_colorName;
};

テーブルで使用される場合、これはガジェットのデフォルト表現です:

QList<QList<ColorEntry>> colorTable;

// ...

QRangeModel colorModel(colorTable);
QTableView table;
table.setModel(&colorModel);

しかし、リストで使用される場合、これらの型はデフォルトで複数列の行として表現され、各プロパティは個別の列として表現されます。ガジェットをリスト内のマルチロールアイテムとして強制的に表現するには、QRoleModel::RowOptionsを特殊化し、static constexpr auto rowCategory メンバ変数をMultiRoleItem に設定することで、ガジェットをマルチロールタイプとして宣言します。

class ColorEntry
{
    Q_GADGET
    Q_PROPERTY(QString display MEMBER m_colorName)
    Q_PROPERTY(QColor decoration READ decoration)
    Q_PROPERTY(QString toolTip READ toolTip)
public:
    ...
};
template <>
struct QRangeModel::RowOptions<ColorEntry>
{
    static constexpr auto rowCategory = QRangeModel::RowCategory::MultiRoleItem;
};

また、このような型を単一要素のタプルにラップし、リストを単一カラムのテーブルにすることもできます:

const QStringList colorNames = QColor::colorNames();
QList<std::tuple<ColorEntry>> colors;

// ...

QRangeModel colorModel(colors);
QListView list;
list.setModel(&colorModel);

この場合、リスト・データの要素に直接アクセスするには、std::get

ColorEntry firstEntry = std::get<0>(colors.at(0));

を使うか、構造化バインディングを使う必要がある:

auto [firstEntry] = colors.at(0);

値またはポインタとしての行

これまでの例では、常に値を保持する範囲に対して QRangeModel を使用してきました。QRangeModel は、スマートポインターを含む、ポインターを保持する範囲でも操作することができます。これにより QRangeModel は、QObject のサブクラスのような、ポリモーフ型のレンジを操作することができます。

class Entry : public QObject
{
    Q_OBJECT
    Q_PROPERTY(QString display READ display WRITE setDisplay NOTIFY displayChanged)
    Q_PROPERTY(QIcon decoration READ decoration WRITE setDecoration NOTIFY decorationChanged)
    Q_PROPERTY(QString toolTip READ toolTip WRITE setToolTip NOTIFY toolTipChanged)

public:
    Entry() = default;

    QString display() const
    {
        return m_display;
    }

    void setDisplay(const QString &display)
    {
        if (m_display == display)
            return;
        m_display = display;
        emit displayChanged(m_display);
    }

signals:
    void displayChanged(const QString &);
    ...
};
std::vector<std::shared_ptr<Entry>> entries = {
    ...
};
QRangeModel model(std::ref(entries));
QListView listView;
listView.setModel(&model);

値の場合と同様に、行のタイプは、範囲がリスト、テーブル、ツリーのどれとして表現されるかを定義します。QObjects である行は、QRangeModel::RowOptions テンプレートがその型をマルチロール項目として宣言するように特化されていない限り、各プロパティを列として表示します。

template <>
struct QRangeModel::RowOptions<Entry>
{
    static constexpr auto rowCategory = QRangeModel::RowCategory::MultiRoleItem;
};
std::vector<std::shared_ptr<Entry>> entries = {
    std::make_shared<Entry>(),
    ...
};

QRangeModel model(std::ref(entries));

注: 範囲が生のポインターを保持している場合、範囲のポインターまたは参照ラッパーからQRangeModelを構築する必要があります。そうしないと、データの所有権が曖昧になり、範囲のコピーが同じ実際の行データを操作することになり、予期せぬ副作用をもたらします。

QRangeModel のサブクラス化

QRangeModel をサブクラス化することで、範囲のデータ型や構造を考慮した便利な API を追加することが可能になります。

class NumbersModel : public QRangeModel
{
    std::vector<int> m_numbers;

public:
    NumbersModel(const std::vector<int> &numbers)
        : QRangeModel(std::ref(m_numbers))
        , m_numbers(numbers)
    {
    }

その際には、範囲をプライベートメンバーとして追加し、そのメンバーへの参照ラッパーまたはポインターを用いて QRangeModel コンストラクタを呼び出します。こうすることで、データを適切にカプセル化し、直接アクセスを避けることができます。

    void setNumber(int idx, int number)
    {
        setData(index(idx, 0), QVariant::fromValue(number));
    }

    int number(int idx) const
    {
        return m_numbers.at(idx);
    }
};

メンバ関数を追加してデータへの型安全アクセスを提供し、QAbstractItemModel API を使用して範囲を変更する操作を実行します。読み取り専用アクセスは、データ構造を直接操作できる。

データのツリー

QRangeModel はデータ構造をツリーモデルとして表現することができます。このようなツリーデータ構造は同型である必要があります。ツリーの全てのレベルにおいて、子行のリストはツリーそのものと全く同じ表現を使用する必要があります。さらに、行の型は静的なサイズである必要があります。ガジェット型またはQObject 型、あるいは {C++ タプルプロトコル} を実装した型のいずれかです。

このようなデータをツリーとして表現するために、QRangeModel はデータ構造をトラバースする必要があります。これらのトラバース関数は、行型を通して暗黙的に提供されることもあれば、明示的なプロトコル型を通して提供されることもあります。

暗黙的な木の走査プロトコル

class TreeRow;

using Tree = std::vector<TreeRow>;

木そのものはTreeRow の値のベクトルである。行に値を使うか項目のポインタを使うかについての考察はTree Rows as pointers or values を参照のこと。

class TreeRow
{
    Q_GADGET
    // properties

    TreeRow *m_parent;
    std::optional<Tree> m_children;

public:
    TreeRow() = default;

    // rule of 0: copy, move, and destructor implicitly defaulted

行クラスは、タプル・プロトコルを実装する型、ガジェット、QObject 。この例では、ガジェットを使用します。

各行アイテムは、親行へのポインタと、子行の範囲(オプション)を保持する必要があります。この範囲は、ツリー自体に使用される範囲構造と同一でなければなりません。

行タイプのデフォルトを構築可能にすることはオプションで、例えばinsertRow() やmoveRows() の実装で、モデルが新しい行データ要素を構築できるようにします。

    // tree traversal protocol implementation
    const TreeRow *parentRow() const { return m_parent; }
    const std::optional<Tree> &childRows() const { return m_children; }

この場合、木の走査プロトコルは、行データ型のメンバ関数として実装することができます。constparentRow() 関数は、const行項目へのポインタを返さなければなりません。また、childRows() 関数は、オプションの子範囲を保持できるconststd::optional への参照を返さなければなりません。

これらの2つの関数は、モデルが読み取り専用のデータ構造としてツリーをナビゲートするのに十分です。ユーザがビューのデータを編集したり、モデルがinsertRows(),removeRows(),moveRows() のような変異モデルAPIを実装したりするためには、書き込みアクセス用の追加関数を実装する必要があります:

    void setParentRow(TreeRow *parent) { m_parent = parent; }
    std::optional<Tree> &childRows() { return m_children; }

モデルは、setParentRow() 関数と mutablechildRows() オーバーロードを呼び出して、既存のツリーブランチに行を移動または挿入し、古い値が無効になった場合に親ポインタを更新します。childRows() の nononst オーバーロードは、さらに行データへの書き込みアクセスを提供する。

注意: このモデルでは、行の親を設定し、その行を古い親から削除し、新しい親の子リストに追加することを別々のステップとして実行します。これにより、プロトコルのインターフェイスを小さく保つことができます。

    ...
    // Helper to assembly a tree of rows, not used by QRangeModel
    template <typename ...Args>
    TreeRow &addChild(Args &&...args)
    {
        if (!m_children)
            m_children.emplace(Tree{});
        auto &child = m_children->emplace_back(std::forward<Args>(args)...);
        child.m_parent = this;
        return child;
    }
};

クラスの残りの実装はモデルには関係ありませんが、addChild() ヘルパーは、ツリーの初期状態を構築する便利な方法を提供してくれます。

Tree tree = {
    {"..."},
    {"..."},
    {"..."},
};

// each toplevel row has three children
tree[0].addChild("...");
tree[0].addChild("...");
tree[0].addChild("...");

tree[1].addChild("...");
tree[1].addChild("...");
tree[1].addChild("...");

tree[2].addChild("...");
tree[2].addChild("...");
tree[2].addChild("...");

このような範囲のインスタンスでインスタンス化された QRangeModel は、データをツリーとして表現します。

// instantiate the model with a pointer to the tree, not a copy!
QRangeModel model(&tree);
QTreeView view;
view.setModel(&model);

ツリーのトラバーサル・プロトコルは別のクラス

ツリー・トラバーサル・プロトコルは別のクラスで実装することもできます。

struct TreeTraversal
{
    TreeRow newRow() const { return TreeRow{}; }
    const TreeRow *parentRow(const TreeRow &row) const { return row.m_parent; }
    void setParentRow(TreeRow &row, TreeRow *parent) const { row.m_parent = parent; }
    const std::optional<Tree> &childRows(const TreeRow &row) const { return row.m_children; }
    std::optional<Tree> &childRows(TreeRow &row) const { return row.m_children; }
};

このプロトコル実装のインスタンスを QRangeModel コンストラクタに渡します:

QRangeModel model(&tree, TreeTraversal{});

ツリーの行をポインタまたは値として渡す

データ範囲の行の型は値かポインタのどちらかになります。上のコードでは、ツリーの行をベクター内の値として使用しています。これは、明示的なメモリ管理を回避するためです。しかし、連続したメモリ・ブロックとしてのベクターは、ストレージの再割り当てや要素の挿入・削除が必要になると、すべてのイテレータや参照が無効になります。これは、ベクター内の親行の位置である親項目へのポインタに影響します。この親項目(およびその中の項目を参照するQPersistentModelIndex インスタンス)が有効であることを確認することは、かなりのパフォーマンス オーバーヘッドを発生させる可能性があります。QRangeModel の実装では、範囲を変更する際に、範囲へのすべての参照が無効になることを想定しなければなりません。

別の方法として、行ポインタの範囲をツリー型として使用することもできます:

struct TreeRow;
using Tree = std::vector<TreeRow *>;

この場合、すべての TreeRow インスタンスをnew という演算子を用いて明示的に確保し、デストラクタを実装して子ベクタのすべての項目をdelete にする必要があります。

structTreeRow { Q_GADGETpublic: TreeRow(constQString&value ={}) : m_value(value) {}~TreeRow() {if(m_children)            qDeleteAll(*m_children);
    }// 移動のみTreeRow(TreeRow&&) = default; TreeRow&operator=(TreeRow&&) = default;// テンプレートへの 入力ヘルパー<typename ...Args>TreeRow*addChild(Args&&...args) {if(m_children) m_children.emplace(Tree{});auto *child =  m_children->emplace_back(newTreeRow(std::forward<Args>(args)...); child->m_parent = this;returnchild; }private:friend structTreeTraversal;    QStringm_value; std::optional<Tree>m_children; TreeRow*m_parent =nullptr; }; Tree tree={newTreeRow("1"), newTreeRow("2"), newTreeRow("3"), newTreeRow("4"),}; tree[0]->addChild("1.1");
tree[1]->addChild("2.1");
tree[2]->addChild("3.1")->addChild("3.1.1");
tree[3]->addChild("4.1");

このデータをツリーとして表現するモデルを構築する前に、ツリーのトラバーサル・プロトコルも実装する必要がある。

struct TreeTraversal
{
    TreeRow *newRow() const { return new TreeRow; }
    void deleteRow(TreeRow *row) { delete row; }

    const TreeRow *parentRow(const TreeRow &row) const { return row.m_parent; }
    void setParentRow(TreeRow &row, TreeRow *parent) { row.m_parent = parent; }
    const std::optional<Tree> &childRows(const TreeRow &row) const { return row.m_children; }
    std::optional<Tree> &childRows(TreeRow &row) { return row.m_children; }
};

ポインタの変更可能な木に対する明示的なプロトコル実装は、newRow()deleteRow(RowType *) という2つの追加メンバ関数を提供しなければならない。

int main(int argc, char **argv)
{
    QApplication app(argc, argv);

    Tree tree = make_tree_of_pointers();

    QRangeModel model(std::move(tree), TreeTraversal{});
    QTreeView treeView;
    treeView.setModel(&model);
    treeView.show();

    return app.exec();
}

モデルは、insertRows()で新しい行を作成するときと、removeRows()で行を削除するときに、これらの関数を呼び出します。さらに、モデルがデータの所有権を持つ場合、破棄時にすべてのトップレベル行を削除します。この例では、ツリーをモデルの中に移動させているため、もはやツリーに対して操作を行う必要がないことに注意してください。QRangeModelは、行ポインタを持つツリーデータをモデル内に移動して構築された場合、データの所有権を持ち、デストラクタで行ポインタを削除します。

行としてポインタを使用する場合、メモリ割り当てと管理のオーバーヘッドが発生します。しかし、行項目への参照は、範囲内を移動しても、範囲が再割り当てされても、安定したままです。これにより、insertRows(),removeRows(),moveRows() を使用する際に、モデルの構造を修正するコストを大幅に削減することができます。

各選択肢には、パフォーマンスとメモリ・オーバーヘッドのトレードオフがあります。最適な選択肢は、使用するケースとデータ構造によって異なります。

C++タプル・プロトコル

上記のnumberNames の例で見たように、行型はタプルにすることができ、実際、タプル・プロトコルを実装する型であれば、どのような型でも可能です。このプロトコルは、std::tuple_sizestd::tuple_element を特殊化し、get 関数をオーバーロードすることで実装されます。既存の構造化データをQtのモデル/ビューフレームワークで利用できるようにするために、カスタムの行型に対してこのようにしてください。

struct Book
{
    QString title;
    QString author;
    QString summary;
    int rating = 0;

    template <size_t I, typename T>
        requires ((I <= 3) && std::is_same_v<std::remove_cvref_t<T>, Book>)
    friend inline decltype(auto) get(T &&book)
    {
        if constexpr (I == 0)
            return std::as_const(book.title);
        else if constexpr (I == 1)
            return std::as_const(book.author);
        else if constexpr (I == 2)
            return std::forward_like<T>(book.summary);
        else if constexpr (I == 3)
            return std::forward_like<T>(book.rating);
    }
};

namespace std {
    template <> struct tuple_size<Book> : std::integral_constant<size_t, 4> {};
    template <size_t I> struct tuple_element<I, Book>
    { using type = decltype(get<I>(std::declval<Book>())); };
}

上記の実装では、Book 型のtitleauthor の値はconst として返されるため、モデルはこれら2つの列の項目を読み取り専用としてフラグを立てます。ユーザは編集をトリガすることができず、setData ()は何もせず、falseを返します。summaryrating については、実装はブックと同じ値のカテゴリを返すので、getBook への変更可能な参照で呼び出されると、それぞれの変数の変更可能な参照が返されます。このモデルは、ユーザーとプログラムによるアクセスの両方に対して、これらの列を編集可能にします。

注: get の実装にはC++23が必要です。

モデル/ビュー・プログラミングも参照してください

メンバ型のドキュメント

enum class QRangeModel::RowCategory

この列挙型は、QRangeModel が構築された範囲の要素をどのように表示するかを記述します。

定数説明
QRangeModel::RowCategory::Default0QRangeModel は行の表示方法を決定します。
QRangeModel::RowCategory::MultiRoleItem1QRangeModel メタオブジェクトを持つ項目は、1次元の範囲で使用される場合にも、マルチロール項目として表示されます。

あなたの型用にRowOptions テンプレートを特殊化し、この列挙型の値の1つを持つパブリック・メンバ変数static constexpr auto rowCategory を追加してください。

RowOptionsも参照してください

プロパティのドキュメント

roleNames : QHash<int, QByteArray>

このプロパティはモデルのロール名を保持します。

範囲内のすべての列が同じ型であり、その型がメタオブジェクトを提供する場合(すなわち、ガジェット、またはQObject サブクラスである場合)、このプロパティはその型のプロパティの名前を保持し、Qt::UserRole 以上のQt::ItemDataRole の値にマッピングされます。さらに、ロール "modelData "は、ガジェットまたはQObject インスタンスへのアクセスを提供します。

このプロパティを明示的に空でないマッピングに設定することで、このデフォルトの動作をオーバーライドします。このプロパティを空のマッピングに設定するか、resetRoleNames() を使用すると、デフォルトの動作が復元されます。

アクセス関数:

virtual QHash<int, QByteArray> roleNames() const override
void setRoleNames(const QHash<int, QByteArray> &names)
void resetRoleNames()

Notifier シグナル:

void roleNamesChanged()

QAbstractItemModel::roleNames()も参照してください

メンバ関数のドキュメント

[explicit] template <typename Range, typename Protocol, int = true> QRangeModel::QRangeModel(Range &&range, Protocol &&protocol, QObject *parent = nullptr)

[explicit] template <typename Range, int = true> QRangeModel::QRangeModel(Range &&range, QObject *parent = nullptr)

[explicit] template <typename Range, int = true> QRangeModel::QRangeModel(Range &&range, QObject *parent = nullptr)

range のデータを操作するQRangeModel インスタンスを構築する。range は、std::beginstd::end が利用可能な連続した範囲でなければならない。protocol が提供された場合、モデルはプロトコル実装を使用して範囲をツリーとして表現します。モデルのインスタンスはparent の子になる。

range は、ポインタまたは参照ラッパーにすることができます。この場合、モデルAPI(setData ()やinsertRow ()など)を変更すると、参照された範囲インスタンスのデータが変更されます。range が値である場合(またはモデルの中に移動した場合)、モデルによって発せられるシグナルに接続し、データの変更に応答します。

QRangeModel が構築されている間は、range にアクセスしません。このため、まだ完全に構築されていない範囲オブジェクトへのポインタや参照をこのコンストラクタに渡すことは合法です。たとえば、subclassing QRangeModel

range がモデルの中に移動された場合、その範囲とその中のすべてのデータはモデルの破棄時に破棄されます。

注意: モデルが範囲オブジェクトの所有権を持つことはありませんが、モデルが構築され、ビューに渡された後は、range を直接変更してはいけません。そのような変更を行うと、モデルのユーザー(他のモデルやビュー)がモデルと同期を保つために必要なシグナルが発せられなくなり、一貫性のない結果や未定義の動作、クラッシュが発生します。

[override virtual noexcept] QRangeModel::~QRangeModel()

QRangeModel を破壊する。

モデルが構築された範囲にはアクセスされず、モデルが移動した範囲から構築された場合にのみ破壊される。

[override virtual] QModelIndex QRangeModel::buddy(const QModelIndex &index) const

再実装:QAbstractItemModel::buddy(const QModelIndex &index) const.

[override virtual] bool QRangeModel::canDropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) const

再実装:QAbstractItemModel::canDropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) const.

[override virtual] bool QRangeModel::canFetchMore(const QModelIndex &parent) const

再実装:QAbstractItemModel::canFetchMore(const QModelIndex &parent) const.

[override virtual] bool QRangeModel::clearItemData(const QModelIndex &index)

再実装:QAbstractItemModel::clearItemData(const QModelIndex &index)。

index で範囲に格納されている値を、デフォルトで構築された値で置き換えます。

読み取り専用の範囲、またはthe C++ tuple protocol を実装する行型の読み取り専用の列で動作するモデルの場合、この実装はfalse を即座に返します。

[override virtual] int QRangeModel::columnCount(const QModelIndex &parent = {}) const

再実装:QAbstractItemModel::columnCount(const QModelIndex &parent) const.

モデルの列数を返します。この関数は、すべてのparent インデックスに対して同じ値を返します。

静的なサイズの行型で動作するモデルの場合、この返される値はモデルのライフタイムを通じて常に同じです。動的なサイズの行型で動作するモデルの場合、モデルは最初の行の項目数を返し、モデルに行がない場合は0を返します。

rowCount およびinsertColumns()も参照してください

[override virtual] QVariant QRangeModel::data(const QModelIndex &index, int role = Qt::DisplayRole) const

再実装:QAbstractItemModel::data(const QModelIndex &index, int role) const.

index で参照される範囲の値について、与えられたrole の下に格納されているデータを返します。

そのインデックスのアイテム・タイプが、intQt::ItemDataRole 、またはQString からQVariant のいずれかにマッピングされる連想コンテナである場合、ロール・データはそのコンテナ内で検索され、返されます。

アイテムがガジェットまたはQObject の場合、実装はroleNames() マッピングのrole エントリにマッチするアイテムのプロパティの値を返す。

そうでない場合、実装は、Qt::DisplayRole またはQt::EditRole に対して、QVariant::fromValue() を介してアイテムから構築されたQVariant を返す。その他のロールの場合、実装は無効な(デフォルトで構築された)QVariant を返します。

Qt::ItemDataRolesetData()、headerData()も参照

[override virtual] bool QRangeModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent)

再実装:QAbstractItemModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent).

[override virtual protected] bool QRangeModel::event(QEvent *event)

再インプリメント:QObject::event(QEvent *e).

[override virtual protected] bool QRangeModel::eventFilter(QObject *object, QEvent *event)

再インプリメント:QObject::eventFilter(QObject *watched, QEvent *event)。

[override virtual] void QRangeModel::fetchMore(const QModelIndex &parent)

再実装:QAbstractItemModel::fetchMore(const QModelIndex &parent).

[override virtual] Qt::ItemFlags QRangeModel::flags(const QModelIndex &index) const

再実装:QAbstractItemModel::flags(const QModelIndex &index) const.

与えられたindex のアイテムフラグを返します。

この実装では、アイテムを有効化するフラグ (ItemIsEnabled) と、アイテムを選択できるようにするフラグ (ItemIsSelectable) の組み合わせを返します。変更可能なデータを持つ範囲で動作するモデルの場合、項目を編集可能にするフラグ (ItemIsEditable) も設定します。

Qt::ItemFlagsも参照して ください。

[override virtual] bool QRangeModel::hasChildren(const QModelIndex &parent = QModelIndex()) const

再実装:QAbstractItemModel::hasChildren(const QModelIndex &parent) const.

[override virtual] QVariant QRangeModel::headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const

再実装:QAbstractItemModel::headerData(int section, Qt::Orientation orientation, int role) const.

指定されたorientation のヘッダ内の、指定されたrolesection のデータを返します。

水平ヘッダの場合、セクション番号は列番号に対応します。同様に、垂直ヘッダーの場合、セクション番号は行番号に対応します。

水平ヘッダーとQt::DisplayRole role 、行の型として配列を使用する範囲を操作するモデルは、section を返す。行の型がタプルの場合、実装はその型の名前をsection で返す。ガジェットまたはQObject 型の行の場合、この関数は、section のインデックスにあるプロパティの名前を返します。

垂直ヘッダの場合、この関数は常にQAbstractItemModel のデフォルト実装の結果を返します。

Qt::ItemDataRolesetHeaderData()、QHeaderViewも参照のこと

[override virtual] QModelIndex QRangeModel::index(int row, int column, const QModelIndex &parent = {}) const

再実装:QAbstractItemModel::index(int row, int column, const QModelIndex &parent) const.

parentrowcolumn にあるモデルアイテムのインデックスを返します。

有効なparentを渡すと、リストやテーブルの範囲を操作するモデルでは無効なインデックスが生成されます。

parent()も参照してください

[override virtual] bool QRangeModel::insertColumns(int column, int count, const QModelIndex &parent = {})

再実装:QAbstractItemModel::insertColumns(int column, int count, const QModelIndex &parent).

parent の範囲のすべての行でcolumn の項目の前にcount の空の列を挿入します。成功すればtrue を返し、そうでなければfalse を返します。

注意: 動的にサイズが変更される行型は、insert(const_iterator, size_t, value_type) メンバ関数を提供する必要があります。

読み取り専用の範囲や、静的なサイズの行型(タプル、配列、構造体など)を持つ範囲で動作するモデルの場合、この実装は何も行わず、すぐにfalse を返します。これは常に木モデルの場合です。

[override virtual] bool QRangeModel::insertRows(int row, int count, const QModelIndex &parent = {})

再実装:QAbstractItemModel::insertRows(int row, int count, const QModelIndex &parent).

parent の範囲に、与えられたrow の前にcount の空の行を挿入します。成功すればtrue を返し、そうでなければfalse を返します。

注意: 範囲は動的にサイズ調整され、insert(const_iterator, size_t, value_type) メンバ関数を提供する必要があります。

読み取り専用または静的なサイズの範囲(配列など)を操作するモデルの場合、この実装は何もせず、直ちにfalse を返します。

注意 : 動的にサイズが変更される列型を持つ範囲の場合、その列はresize(size_t) メンバ関数を提供する必要があります。

[override virtual] QMap<int, QVariant> QRangeModel::itemData(const QModelIndex &index) const

再実装:QAbstractItemModel::itemData(const QModelIndex &index) const.

与えられたindex にあるアイテムのモデル内のすべての定義済みの役割の値を持つマップを返します。

そのindex のアイテム・タイプが、intQt::ItemDataRole 、またはQString からQVariant のいずれかにマッピングされる連想コンテナである場合、そのコンテナからのデータが返されます。

アイテムのタイプがガジェットまたはQObject のサブクラスである場合、role name にマッチするそれらのプロパティの値が返される。

アイテムが連想コンテナ、ガジェット、QObject サブクラスでない場合、基本クラスの実装が呼び出されます。

setItemData(),Qt::ItemDataRole,data()も参照してください

[override virtual] QModelIndexList QRangeModel::match(const QModelIndex &start, int role, const QVariant &value, int hits, Qt::MatchFlags flags) const

再実装:QAbstractItemModel::match(const QModelIndex &start, int role, const QVariant &value, int hits, Qt::MatchFlags flags) const.

[override virtual] QMimeData *QRangeModel::mimeData(const QModelIndexList &indexes) const

再実装:QAbstractItemModel::mimeData(const QModelIndexList &indexes) const.

[override virtual] QStringList QRangeModel::mimeTypes() const

再実装:QAbstractItemModel::mimeTypes() const.

[override virtual] bool QRangeModel::moveColumns(const QModelIndex &sourceParent, int sourceColumn, int count, const QModelIndex &destinationParent, int destinationColumn)

再実装:QAbstractItemModel::moveColumns(const QModelIndex &sourceParent, int sourceColumn, int count, const QModelIndex &destinationParent, int destinationChild)。

sourceParent の下にある、与えられたsourceColumn で始まるcount 列を、親destinationParent の下にあるdestinationColumn 列に移動します。

カラムの移動に成功した場合はtrue を返し、失敗した場合はfalse を返します。

[override virtual] bool QRangeModel::moveRows(const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destinationParent, int destinationRow)

再実装:QAbstractItemModel::moveRows(const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destinationParent, int destinationChild)。

sourceParent の下にある、与えられたsourceRow で始まるcount 行を、親destinationParent の下にあるdestinationRow 行に移動します。

行の移動に成功した場合はtrue を返し、失敗した場合はfalse を返します。

[override virtual] void QRangeModel::multiData(const QModelIndex &index, QModelRoleDataSpan roleDataSpan) const

再実装:QAbstractItemModel::multiData(const QModelIndex &index, QModelRoleDataSpan roleDataSpan) const.

[override virtual] QModelIndex QRangeModel::parent(const QModelIndex &child) const

再実装:QAbstractItemModel::parent(const QModelIndex &index) const.

child インデックスの項目の親を返します。

この関数は、リストやテーブルの範囲を操作するモデルに対しては、常に無効なインデックスを生成します。ツリーを操作するモデルの場合、この関数は、ツリー走査プロトコルの parent() 実装によって返される行項目のインデックスを返します。

index() およびhasChildren()も参照

[override virtual] bool QRangeModel::removeColumns(int column, int count, const QModelIndex &parent = {})

再実装:QAbstractItemModel::removeColumns(int column, int count, const QModelIndex &parent).

column のアイテムからparent の範囲のすべての行のcount 列を削除します。成功すればtrue を返し、そうでなければfalse を返します。

注意: 動的にサイズが変更される行型は、erase(const_iterator, size_t) メンバ関数を提供する必要があります。

読み取り専用の範囲や、静的なサイズの行型(タプル、配列、構造体など)を持つ範囲で動作するモデルの場合、この実装は何も行わず、すぐにfalse を返します。これは常に木モデルの場合です。

[override virtual] bool QRangeModel::removeRows(int row, int count, const QModelIndex &parent = {})

再実装:QAbstractItemModel::removeRows(int row, int count, const QModelIndex &parent)。

与えられたrow から始まるparent の範囲からcount 行を削除します。成功すればtrue を返し、そうでなければfalse を返します。

注意: 範囲は動的にサイズ調整され、erase(const_iterator, size_t) メンバ関数を提供する必要があります。

読み取り専用または静的なサイズの範囲(配列など)を操作するモデルの場合、この実装は何もせず、直ちにfalse を返します。

[override virtual protected slot] void QRangeModel::resetInternalData()

再インプリメント:QAbstractItemModel::resetInternalData().

[override virtual] QHash<int, QByteArray> QRangeModel::roleNames() const

再実装:QAbstractItemModel::roleNames() const.

注意 :QRangeModel のサブクラスでこの関数をオーバーライドすることは可能ですが、プロパティの振る舞いを壊す可能性があります。

注意 : roleNames プロパティのゲッター関数です。

setRoleNames()も参照してください

[override virtual] int QRangeModel::rowCount(const QModelIndex &parent = {}) const

再実装:QAbstractItemModel::rowCount(const QModelIndex &parent) const.

与えられたparent の下にある行の数を返します。これは、無効なparent インデックスのルート範囲の項目数です。

parent インデックスが有効な場合、リストやテーブルの範囲を操作するモデルでは、この関数は常に 0 を返します。ツリーの場合、これは、ツリー・トラバーサル・プロトコルの childRows() 実装によって返される範囲のサイズを返します。

columnCount()、insertRows()、hasChildren()も参照

[override virtual] bool QRangeModel::setData(const QModelIndex &index, const QVariant &data, int role = Qt::EditRole)

再実装:QAbstractItemModel::setData(const QModelIndex &index, const QVariant &value, int role)。

index にあるアイテムのrole データをdata に設定します。

そのindex のアイテム・タイプが、intQt::ItemDataRoleQString のいずれかからQVariant にマッピングされる連想コンテナである場合、role で指定されたキーについて、data がそのコンテナに格納されます。

アイテムがガジェットまたはQObject の場合、roleNames() マッピングのrole エントリに一致するアイテムのプロパティにdata が書き込まれる。この関数は、プロパティが見つかり、data に必要な型に変換できる値が格納されていればtrue を返し、そうでなければfalse を返します。

そうでない場合、この実装は、Qt::DisplayRole およびQt::EditRole の範囲内のindex の項目にdata の値を割り当て、true を返します。その他の役割の場合、この実装はfalse を返す。

読み取り専用の範囲や、the C++ tuple protocol を実装する行型の読み取り専用の列で操作するモデルの場合、この実装は直ちにfalse を返します。

data()も参照してください

[override virtual] bool QRangeModel::setHeaderData(int section, Qt::Orientation orientation, const QVariant &data, int role = Qt::EditRole)

再実装:QAbstractItemModel::setHeaderData(int section, Qt::Orientation orientation, const QVariant &value, int role).

headerData()も参照してください

[override virtual] bool QRangeModel::setItemData(const QModelIndex &index, const QMap<int, QVariant> &data)

再実装:QAbstractItemModel::setItemData(const QModelIndex &index, const QMap<int, QVariant> &roles)。

そのindex の項目タイプが、int またはQt::ItemDataRole からQVariant のいずれかにマッピングされる連想コンテナである場合、data の項目はそのコンテナに格納されます。連想コンテナがQString からQVariant にマッピングされる場合、data の値のうち、role names テーブルにマッピングがあるものだけが格納される。

アイテム・タイプがガジェットまたはQObject サブクラスの場合、role name に一致するプロパティは、data の対応する値に設定されます。

data にエントリがないロールは変更されない。

コピー可能なアイテム・タイプの場合、この実装はトランザクション的であり、data からのすべてのエントリを格納できた場合、true を返します。どの項目も更新できなかった場合、元のコンテナはまったく変更されず、関数は偽を返す。

アイテムが連想コンテナ、ガジェット、QObject サブクラスでない場合、これは基底クラスの実装を呼び出し、data の各エントリに対してsetData() を呼び出す。

itemData()、setData()、Qt::ItemDataRoleも参照のこと

[override virtual] QModelIndex QRangeModel::sibling(int row, int column, const QModelIndex &index) const

再実装:QAbstractItemModel::sibling(int row, int column, const QModelIndex &index) const.

index にあるアイテムのrowcolumn にある兄弟を返すか、その位置に兄弟がない場合は無効なQModelIndex を返します。

この実装は、indexparent() を通過するよりもかなり高速です。

index()、QModelIndex::row()、QModelIndex::column()も参照のこと

[override virtual] void QRangeModel::sort(int column, Qt::SortOrder order = Qt::AscendingOrder)

を再実装しています:QAbstractItemModel::sort(int column, Qt::SortOrder order).

[override virtual] QSize QRangeModel::span(const QModelIndex &index) const

再実装:QAbstractItemModel::span(const QModelIndex &index) const.

[override virtual] Qt::DropActions QRangeModel::supportedDragActions() const

再実装:QAbstractItemModel::supportedDragActions() const.

[override virtual] Qt::DropActions QRangeModel::supportedDropActions() const

再実装:QAbstractItemModel::supportedDropActions() const.

© 2025 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.