最終更新日:2021/08/31 原本2019-02-21

PyQt5とpython3によるGUIプログラミング[3]

ここの和訳です。画像すっ飛んでますが。。。
http://doc.qt.io/qt-5/model-view-programming.html

Model/View Programming Introduction to Model/View Programming

Qtはアイテムビュークラスのセットが含まれています、それは、データとそれがユーザに提示される方法との間の関係を管理するために、モデル/ビューアーキテクチャを使用します。このアーキテクチャによって導入された機能の分離は、開発者にアイテムの表示をカスタマイズするためのより大きな柔軟性を提供し、データソースの広い範囲が既存のアイテムビューで使用できるようにするための標準モデルインタフェースを提供します。このドキュメントでは、関連する概念の概要を説明し、アイテムビューシステムのアーキテクチャを説明し、モデル/ビューパラダイムを簡単に紹介します。
アーキテクチャ内の各コンポーネントを説明します、そして提供されるクラスを使用する方法を示す例を挙げています。

The model/view architecture

モデル・ビュー・コントローラ(MVC)は、ユーザインタフェースを構築する際によく使用されるSmalltalk由来のデザインパターンです。
Design Patternsでガンマらが書いています。

MVCは、3種類のオブジェクトで構成されています。モデルは、アプリケーションオブジェクトで、ビューはその画面表示であり、コントローラは、ユーザインタフェースに、ユーザ入力に反応する方法を定義します。 MVCの前は、ユーザインタフェース設計は、これらのオブジェクトを一緒にひとかたまりにする傾向がありました。 MVCは、柔軟性と再利用を高めるために、それらを分離します。
ビューとコントローラオブジェクトを組み合わせるなら、結果は、モデル/ビューアーキテクチャです。
これはやはり、そのデータがユーザに提示される方法から格納されている方法を分離します、しかし、同じ原理に基づいたシンプルなフレームワークを提供します。

この分離により、複数の異なるビューで同一のデータを表示するために、基礎となるデータ構造を変更することなく、ビューの新しい型を実装することができます。
ユーザー入力の柔軟な取り扱いを可能にするために、我々は、デリゲートの概念を導入します。
このフレームワークでデリゲートを持つことの利点は、データのアイテムがレンダリングされ、編集される方法がカスタマイズされるのを許可するということです。

The model/view architecture

モデルは、アーキテクチャ内の他のコンポーネントのためのインタフェースを提供し、データのソースと通信します。通信の性質は、データ・ソースのタイプ、およびモデルの実装方法に依存します。
ビューは、モデルからモデルのインデックスを取得します:これらは、データのアイテムへの参照です。モデルへのモデルインデックスを供給することにより、ビューは、データのアイテムをデータ・ソースから取得することができます。
標準ビューでは、デリゲートは、データのアイテムをレンダリングします。アイテムを編集すると、デリゲートは、モデルと直接モデルインデックスを使用して通信します。
1.png

モデル、ビュー、およびデリゲート:一般的に、モデル/ビュークラスは、上記の三つのグループに分けることができます。これらのコンポーネントの各々は、いくつかのケースでは、共通のインターフェースを提供し、抽象クラス、関数のデフォルトの実装によって定義されます。抽象クラスは、他のコンポーネントが予想される機能の完全なセットを提供するためにサブクラス化されることを意味します。これはまた、特殊な構成要素が書き込まれることを可能にします。モデル、ビュー、およびデリゲートはシグナルとスロットを使用して相互に通信します。
• モデルからの信号は、データ・ソースによって保持されたデータへの変更についてのビューを知らせます。
• アイテムが表示されているとのビューからの信号は、ユーザの相互作用に関する情報を提供します。
• デリゲートからの信号は、編集の間、エディタの状態についてモデルとビューに命令するのに用いられます。

Models

すべてのアイテムモデルはQAbstractItemModelクラスに基づいています。このクラスは、データにアクセスするためのビューとデリゲートによって使用されるインターフェイスを定義します。データ自体は、モデル内に格納される必要はありません。それは別のクラス、ファイル、データベース、または他のアプリケーションコンポーネントが提供するデータ構造またはリポジトリ内に保持することができます。
モデルの周囲の基本的な概念は、モデル・クラス上のセクションに示されています。
QAbstractItemModelは、テーブル、リスト、ツリーの形でデータを表すビューを処理するのに十分な柔軟性があり、データへのインタフェースを提供します。しかし、リストやテーブルのようなデータ構造のための新しいモデルを実装するとき、彼らは一般的な機能の適切なデフォルト実装例を提​​供するため、QAbstractListModelとQAbstractTableModelクラスは、より良い出発点です。これらの各クラスは、リストやテーブルの特殊な種類をサポートしているモデルを提供するためにサブクラス化することができます。サブクラス化モデルのプロセスは、新しいモデルの作成に関するセクションで説明されています。Qtは、データアイテムを処理するために使用できるいくつかの既製のモデルを提供します。
• QStringListModelはQStringの項目の単純なリストを格納するために使用されます。
• QStandardItemModelは、任意のデータを含むことができ、それぞれがアイテムのより複雑なツリー構造を管理します。
• QFileSystemModelは、ローカルファイリングシステム内のファイルやディレクトリに関する情報を提供します。
• QSqlQueryModel、QSqlTableModel、およびQSqlRelationalTableModelは、モデル/ビュー規則を使用してデータベースにアクセスするために使用されています。

これらの標準モデルが要件に合わない場合は、独自のカスタムモデルを作成するためにQAbstractItemModel、QAbstractListModel、またはQAbstractTableModelをサブクラス化することができます。

Views

完全な実装は、ビューの異なる種類のために提供されています。
QListViewは、項目のリストを表示し、QTableViewは、テーブル内のモデルからのデータを表示します、
そして、QTreeViewは階層リスト内のデータのモデルアイテムを表示します。これらのクラスのそれぞれはQAbstractItemView抽象基本クラスに基づいています。これらのクラスは、すぐに使用できる実装であるが、それらはまた、カスタマイズされたビューを提供するためにサブクラス化することができます。
使用可能なビューは、ビュークラスのセクションで説明されています。

Delegates

QAbstractItemDelegateは、モデル/ビューフレームワークにおけるデリゲートのための抽象基本クラスです。デフォルトのデリゲートの実装はQStyledItemDelegateによって提供され、これはQtの標準ビューによって、デフォルトのデリゲートとして使用されています。しかし、QStyledItemDelegateとQItemDelegateはペイントやビュー内のアイテムのエディタを提供する独立した選択肢があります。両者の差はQStyledItemDelegateは、そのアイテムをペイントする現在のスタイルを使用していることです。
したがって、基本クラスとしてカスタムデリゲートを実装するとき、またはQtのスタイルシートで作業するとき、我々はQStyledItemDelegateを使用することをお勧めします。
デリゲートは、デリゲートクラス上のセクションで説明されています。

Sorting

モデル/ビューアーキテクチャでソートに近づく二つの方法があります。そのアプローチを選択することは、あなたの配下のモデルによって異なります。
あなたのモデルは、ソート可能であるならば、それはQAbstractItemModel::sort()関数を再実装する場合、例えば、QTableViewとQTreeViewの両方を使用すると、プログラムモデルのデータをソートすることを可能にするAPIを提供しています。
また、あなたは、インタラクティブソートを有効にすることができます(つまり、ユーザーがビューのヘッダをクリックすることで、データをソートすることができます)、QHeaderView::sortIndicatorChanged()シグナルを接続するQTableView::sortByColumn()スロットまたはQTreeView::sortByColumn()スロット、それぞれによります。

別のアプローチは、あなたのモデルが必要なインターフェイスを持っていない場合、または、あなたのデータを提示するために、リストビューを使用する場合は、ビュー内のデータを提示する前に、モデルの構造を変換するために、プロキシモデルを使用することです。これは、プロキシモデルのセクションで詳細にカバーされています。

Convenience classes

コンビニエンスクラスのいくつかは、Qtのアイテムベースのビューとテーブルクラスに依存するアプリケーションの利益のために、標準ビュークラスから派生しています。これらはサブクラス化することを意図するものではありません。
そのようなクラスの例としては、QListWidget、QTreeWidget、およびQTableWidgetが含まれます。
これらのクラスが、ビュークラスほど柔軟でなくて、任意のモデルで使うことができません。私たちは、あなたが強くクラスのアイテムベースのセットを必要としない限り、あなたはアイテムビューのデータを扱うためのモデル/ビューのアプローチを使用することをお勧めします。
あなたはまだアイテムベースのインタフェースを使用しながら、モデル/ビューアプローチによって提供される機能を利用したい場合は、このようなQStandardItemModelとQListView、QTableView、およびQTreeViewとしてビュークラスを、使用することを検討してください。

Using Models and Views

次のセクションでは、Qtの中で、モデル/ビューパターンを使用する方法について説明します。各セクションには例が含まれており、新しいコンポーネントを作成する方法を示す断面図が続いています。

Two models included in Qt

Qtの提供する標準モデルの二つはQStandardItemModelとQFileSystemModelです。
QStandardItemModelは、リスト、テーブル、ツリービューで必要な様々なデータ構造を表現するために使用することができる多目的モデルです。
このモデルは、データの項目を保持します。 QFileSystemModelは、ディレクトリの内容に関する情報を保持したモデルです。
その結果、データ自体の任意の項目を保持するが、単にローカルファイリングシステム上のファイルやディレクトリを表していません。
QFileSystemModelはで実験するすぐに使用できるモデルを提供し、容易に既存のデータを使用するように構成することができます。
このモデルを使用して、我々は、既製のビューで使用するためのモデルを設定し、モデルのインデックスを使用してデータを操作する方法を探求する方法を示すことができます。

Using views with an existing model

QListViewとQTreeViewクラスはQFileSystemModelで使用する最も適した図です。以下に示す例では、次のリストビュー内の同じ情報をツリービューでディレクトリの内容を表示します。選択した項目が両方のビューで強調表示されるように、ビューは、ユーザの選択を共有しています。

2.png

それを使用する準備ができている、とディレクトリの内容を表示するには、いくつかのビューを作成するように私たちはQFileSystemModelを設定します。これは、モデルを使用する最も簡単な方法を示しています。モデルの構築および使用は、単一のmain()関数内から実行されます。

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    QSplitter *splitter = new QSplitter;

    QFileSystemModel *model = new QFileSystemModel;
    model->setRootPath(QDir::currentPath());

モデルは、特定のファイルシステムからのデータを使用するように設定されています。 setRootPath()の呼び出しは、ビューに公開するために、ファイルシステム上のドライブモデルに命令します。我々は2つの異なる方法でモデルに保持されたアイテムを調べることができるように、我々は2つのビューを作成します。

    QTreeView *tree = new QTreeView(splitter);
    tree->setModel(model);
    tree->setRootIndex(model->index(QDir::currentPath()));

    QListView *list = new QListView(splitter);
    list->setModel(model);
    list->setRootIndex(model->index(QDir::currentPath()));

ビューは、他のウィジェットと同様に構成されています。モデル内のアイテムを表示するビューを設定することは、単に、引数としてディレクトリモデルでそのsetModel()関数を呼び出すことです。私たちは、現在のディレクトリのファイルシステムモデルから適切なモデルインデックスを渡し、各ビューにsetRootIndex()関数を呼び出すことによって、モデルによって供給されたデータをフィルタリングします。この場合に用いるindex()関数は、QFileSystemModelにユニークです。私たちは、ディレクトリとそれを供給し、それがモデルインデックスを返します。モデルインデックスは、モデル・クラスで説明されています。関数の残りの部分は、ちょうどスプリッタウィジェット内のビューを表示し、アプリケーションのイベントループを実行します。

    splitter->setWindowTitle("Two views onto the same file system model");
    splitter->show();
    return app.exec();
}

上記の例では、アイテムの選択を処理する方法について述べることを省きました。この主題は、アイテムのビューで選択の処理に関するセクションで詳しく説明されています。

Model Classes

選択がどのように扱われるかを調べる前に、あなたは、モデル/ビューフレームワークで使われる概念を調べることが役に立つとわかるでしょう。

Basic concepts

モデル/ビューアーキテクチャでは、モデルはビューとデリゲートがデータにアクセスするために使用する標準インタフェースを提供します。 Qtでは、標準的なインタフェースがQAbstractItemModelクラスによって定義されます。

たとえデータのアイテムが任意の基になるデータ構造にどのように格納されていても、
QAbstractItemModelのすべてのサブクラスは、アイテムのテーブルを含む階層構造としてデータを表します。

ビューは、モデル内のデータアイテムにアクセスするには、この規則を使用しますが、それらはこの情報をユーザに提示するように限定されるものではありません。

3.png

モデルはまた、シグナルとスロット機構を介してデータの変更に関する接続されているすべてのビューを通知します。
このセクションでは、モデルクラスを介して他のコンポーネントによってアクセスされるデータのアイテムの方法の中心となるいくつかの基本的な概念を説明します。高度な概念は、後のセクションで説明します。

Model indexes

データの表現がそれにアクセスする方法とは別に保持されていることを確実にするために、モデルインデックスの概念が導入されています。モデルを介して得られる各情報は、モデルインデックスにより表されます。ビューとデリゲートは、表示するデータのアイテムを要求するために、これらのインデックスを使用します。
結果として、モデルでは、データを取得する方法を知っている必要があるだけで、モデルによって管理されるデータのタイプは、かなり一般的に定義することができます。モデルインデックスは、それらを作成したモデルへのポインタが含まれており、複数のモデルで作業する場合、これは混乱を防ぐことができます。

QAbstractItemModel *model = index.model();

モデルインデックスは、情報の部分の一時的な参照を提供します、モデルを介してデータを検索、および変更するために使用することができます。
モデルは随時、その内部構造を再編成することがあるので、モデルのインデックスが無効になることがあり、保存されているべきではありません。情報片に対する長期参照が必要な場合は、永続的なモデルのインデックスが作成されなければなりません。
これは、モデルが最新の保持情報への参照を提供します。一時的なモデルインデックスはQModelIndexクラスによって提供され、永続的なモデルインデックスはQPersistentModelIndexクラスによって提供されています。
行番号、列番号、および親アイテムのモデルインデックス:データのアイテムに対応するモデルインデックスを取得するには、3つのプロパティをモデルに指定する必要があります。
次のセクションでは、詳細にこれらの特性を説明します。

Rows and columns

その最も基本的なフォームでは、モデルは、単純なテーブルとしてアクセスすることができます、
ここでのアイテムは、その行と列の番号によって配置されています。これはデータの基本にある部分が配列構造に保存されることを意味しません。行と列の番号の使用は、構成要素が互いに通信できるようにする唯一の慣行です。私たちは、モデルに、その行と列の番号を指定することで、任意のアイテムに関する情報を取得することができ、我々はアイテムを表すインデックスを受け取ります。

QModelIndex index = model->index(row, column, ...);

リストやテーブルのような単純な、単一レベルのデータ構造へのインターフェイスを提供するモデルは、提供される他の情報を必要としませんが、上記のコードが示すように、我々はモデルインデックスを取得するとき、より多くの情報を提供する必要があります。

Rows and columns

図は、基本的なテーブルモデルの表現を示します、ここで、各アイテムは、行番号と列番号のペアで配置されています。私たちは、モデルに該当する行と列の番号を渡すことによって、データのアイテムを参照するモデルインデックスを取得します。

QModelIndex indexA = model->index(0, 0, QModelIndex());
QModelIndex indexB = model->index(1, 1, QModelIndex());
QModelIndex indexC = model->index(2, 1, QModelIndex());

モデルのトップレベルのアイテムは、常にその親アイテムとしてQmodelIndex()を指定することによって参照されています。これは次のセクションで説明されています。

Parents of items

テーブルまたはリストビューでデータを使用した場合、モデルによって提供されているアイテムデータへのテーブルのようなインターフェイスが理想的です。行と列の番号システムは、ビューはアイテムを表示する方法に正確にマッピングされます。しかしながら、このようなツリービューのような構造は、内のアイテムに、より柔軟なインターフェイスを公開するためのモデルを必要とします。結果として、各アイテムは、ツリービューのトップレベルのアイテムがアイテムの別のリストを含むことができることとほぼ同じ方法で、アイテムの別のテーブルの親になることができます。
モデルアイテムのインデックスを要求する場合、我々は、アイテムの親に関するいくつかの情報を提供しなければなりません。モデル以外では、アイテムを参照する唯一の方法は、モデルインデックスを通して行われるため、親モデルインデックスも与えなければなりません。

QModelIndex index = model->index(row, column, parent);

Parents, rows, and columns

図は、各アイテムが親、行番号及び列番号によって参照されるツリーモデルの表現を示します。
アイテム「A」と「C」は、モデル内のトップレベルの兄弟のように表されます。

QModelIndex indexA = model->index(0, 0, QModelIndex());
QModelIndex indexC = model->index(2, 1, QModelIndex());

アイテム「A」は、子どもの番号を持っています。
アイテム「B」のモデルインデックスは、次のコードで取得されています。

QModelIndex indexB = model->index(1, 0, indexA);

Item roles

モデル内のアイテムは、異なる状況のために供給されるデータの種類を可能にする、他のコンポーネントのための様々なロールを実行することができます。たとえば、Qt::DisplayRoleビュー内のテキストとして表示することができる文字列にアクセスするために使用されます。一般的に、アイテムは異なるロールの数値のデータが含まれており、標準的なロールは、Qt::ItemDataRoleによって定義されています。
モデルにアイテムに対応するモデルインデックスを渡すことによって、そして、欲しいデータのタイプを得るためにロールを指定することによって、我々はモデルにアイテムのデータを求めることができます:

QVariant value = model->data(index, role);

Item roles

ロールは、データのタイプが参照されるモデルに示します。ビューは、さまざまな方法でロールを表示することができますので、それぞれのロールに適切な情報を提供することが重要です。
Creating New Modelsのセクションでは、より詳細にロールのいくつかの特定の用途をカバーしています。

アイテムデータのための最も一般的な用途は、Qt::ItemDataRoleで定義された標準ロールによってカバーされています。各ロールに適切なアイテムのデータを供給することにより、モデルはアイテムがユーザに提示されるべきかについてのビューとデリゲートへのヒントを提供することができます。
異なる種類のビューは必要に応じてこの情報を受け取りまたは無視する自由を持っています。アプリケーション特有の目的のための追加のロールを定義することも可能です。

Summary

•モデルインデックスは、任意の基礎となるデータ構造に依存しない方法でモデルにより提供されるアイテムの場所に関するビューやデリゲートの情報を与えます。
•アイテムは、その行と列の番号によって、そして、その親アイテムのモデルインデックスによって参照されます。
•モデルインデックスは、このようなビューやデリゲートなどの他のコンポーネントの要求に応じてモデルによって構成されています。
•インデックスがindex()を使用して要求された場合に、有効なモデルのインデックスが親アイテムに指定されている場合は、返されるインデックスは、モデル内のその親アイテムの下のアイテムを指します。つまり得られたインデックスは、そのアイテムの子を指します。
•インデックスがindex()を使用して要求されたときに、無効なモデルインデックスが親アイテムに指定されている場合は、インデックスが返され、モデルの最上位のアイテムが表示されます。
•ロールは、アイテムに関連するデータの異なる種類を区別します。

Using model indexes

データは、モデルのインデックスを使用して、モデルから取り出すことができる方法を示すために、我々は、ビューなしでQFileSystemModelを設定し、ウィジェット内でファイルやディレクトリの名前を表示させます。
これは、モデルを使用する通常の方法を示していませんが、それは、モデルインデックスを扱うときのモデルで使用される規則を示しています。
私たちは、次のようにファイル・システム・モデルを構築します:

    QFileSystemModel *model = new QFileSystemModel;
    QModelIndex parentIndex = model->index(QDir::currentPath());
    int numRows = model->rowCount(parentIndex);

このケースでは、我々はデフォルトQFileSystemModelを設定しました、そのモデルにより提供されるindex()の特定の実装を使用して、親のインデックスを取得します、そして、我々はrowCount()関数を使用して、モデルの行数をカウントします。
簡単にするために、我々は、モデルの最初の列のアイテムにのみ関心があります。私たちは、各行の最初のアイテムのためのモデルインデックスを取得し、順番に各行を調べ、モデルでそのアイテムに格納されたデータを読み込みます。

    for (int row = 0; row < numRows; ++row) {
        QModelIndex index = model->index(row, 0, parentIndex);

モデルインデックスを得るために、我々は、行番号、列番号(最初の列の場合はゼロ)、そして私たちが望むすべてのアイテムの親のための適切なモデルインデックスを指定します。
各アイテムに格納されているテキストは、モデルのdata()関数を使用して検索されます。
私たちは、文字列の形式でアイテムのデータを得るためのモデルインデックスとDisplayRoleを指定します。

        QString text = model->data(index, Qt::DisplayRole).toString();
        // Display the text in a widget.

    }

上記の例では、モデルからデータを取得するために使用される基本的な原則を示しています。
• モデルの大きさは、rowCount()およびcolumnCount()を使用して求めることができます。これらの関数は一般的に、親モデルのインデックスの指定を要求します。
• モデルインデックスは、モデル内のアイテムにアクセスするために使用されます。行、列、及び親モデルインデックスは、アイテムを指定するのに必要とされます。
• モデルでトップレベルのアイテムにアクセスするために、QModelIndex()による親インデックスとして、ヌルモデルインデックスを指定します。
• アイテムは、異なるロールのためのデータが含まれています。特定のロールのためのデータを取得するには、モデルインデックスとロールの両方がモデルに供給されなければなりません。

Further reading

新モデルはQAbstractItemModelが提供する標準インタフェースを実装することによって作成することができます。
作成新モデルのセクションでは、文字列のリストを保持するための便利な、すぐに使用できるモデルを作成することによってこれを実証します。

View Classes

Concepts
モデル/ビューアーキテクチャでは、ビューはモデルからのデータアイテムを取得し、それらをユーザに提示します。

データが提示される方法は、モデルにより提供されるデータの表現に似ている必要がなくて、データのアイテムを保存するのに用いられる基礎となるデータ構造とは、完全に異なる場合があります。

コンテンツとプレゼンテーションの分離がQAbstractItemModel、QAbstractItemViewによって提供される標準のビューインタフェースが提供する標準モデルのインタフェース、および一般的な方法でデータのアイテムを表すモデルのインデックスを使用することによって達成されます。

ビューは通常、モデルから得られたデータの全体的なレイアウトを管理します。彼らは、自分自身のデータの個々のアイテムをレンダリングする、またはその両方のレンダリングと編集機能を処理するために、デリゲートを使用することができます。
同様にデータを提示するように、ビューはアイテム、およびアイテム選択のいくつかの側面間のナビゲーションを処理します。ビューはまた、このようなコンテキストメニューとドラッグアンドドロップなどの基本的なユーザーインターフェイス機能を実装します。ビューには、アイテムのデフォルトの編集機能を提供することができ、またはそれは、カスタムエディタを提供するために、デリゲートでも動作します。
ビューは、モデルなしで構築することができるが、それは有用な情報を表示する前にモデルが提供されなければなりません。

ビューは、ユーザーがビューごとに別々に維持、または複数のビュー間で共有することができる選択の使用を介して選択したアイテムを追跡します。
このようなQTableViewやQTreeViewなどの一部のビューは、ヘッダだけでなく、アイテムが表示されます。これらは、ビュークラス、QHeaderViewによって実装されます。ヘッダーは通常、それらを含むビューと同じモデルにアクセスします。彼らはQAbstractItemModel :: headerData()関数を使用してモデルからデータを取得し、通常はラベルの形式でヘッダー情報を表示します。新しいヘッダーは、ビューのために、より専門的なラベルを提供するために、QHeaderViewクラスからサブクラス化することができます。

Using an existing view

qtは、大部分のユーザーになじみがある方向でモデルからデータを提示する3つのすぐに使える表示クラスを提供します。
QListViewは、単純なリストとして、または古典的なアイコン表示の形で、モデルからアイテムを表示することができます。
QTreeViewは、深くネストされた構造がコンパクトに表現することができるように、リストの階層としてモデルからアイテムを表示します。
QTableViewはずっとスプレッドシートアプリケーションのレイアウトのように、テーブルの形式のモデルからアイテムを提示します。

上に示した標準ビューのデフォルトの動作は、ほとんどのアプリケーションで十分なはずです。これらは、基本的な編集機能を提供し、より専門的なユーザインターフェイスのニーズに合わせてカスタマイズすることができます。

Using a model

我々は例のモデルとして作成された文字列のリストモデルを取り、いくつかのデータでそれを設定し、モデルの内容を表示するビューを構築します。これはすべて、単一の関数内で実行することができます。

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

// Unindented for quoting purposes:
QStringList numbers;
numbers << "One" << "Two" << "Three" << "Four" << "Five";

QAbstractItemModel *model = new StringListModel(numbers);

QStringListModelがQAbstractItemModelとして宣言されていることに注意してください。

これは、私たちがモデルに抽象インタフェースを使うことを可能にし、たとえ私たちが、ストリングリストモデルを、違うモデルと交換しても、コードが動作することが保証されます。

QListViewにより提供されたリストビューは、ストリングリストモデルのアイテムを示すのに十分です。私たちは、ビューを構築し、次のコード行を使用してモデルを設定します。

QListView *view = new QListView;
view->setModel(model);

ビューは通常の方法で示されています:

    view->show();
    return app.exec();
}

ビューは、モデルのインターフェースを介してデータにアクセスし、モデルのコンテンツをレンダリングします。ユーザーがアイテムを編集しようとすると、ビューは、エディタウィジェットを提供するために、デフォルトのデリゲートを使用しています。

上記のイメージは、QListViewがストリングリストモデルのデータを表している方法を示しています。

モデルが編集可能なので、ビューは、リストの個々のアイテムを、デフォルトのデリゲートを使用して編集することを自動的に可能にします。

Using multiple views of a model

同じモデルに複数のビューを提供することは、単に各ビューに同じモデルを設定することですです。次のコードでは、我々は、この例のために作成した同じ単純なテーブルモデルを使用して、2つのテーブルビュー、それぞれを作成します。

    QTableView *firstTableView = new QTableView;
    QTableView *secondTableView = new QTableView;

    firstTableView->setModel(model);
    secondTableView->setModel(model);

モデル/ビューアーキテクチャにおけるシグナルとスロットの使用は、モデルへの変更が接続されているすべてのビューに伝播することができることを意味します、それは我々が、使用されるビューに関係なく、常に同じデータにアクセスできることを保証します。

上の画像は、それぞれが選択したアイテムの番号を含む、同じモデル上の2つの異なるビューを示しています。モデルからのデータが一貫してビュー全体に示されているが、各ビューは、独自の内部選択モデルを維持します。これは、特定の状況において有用であり得るが、多くの用途のために、共有の選択モデルが望ましいです。

Handling selections of items

ビュー内のアイテムの選択を処理するためのメカニズムがQItemSelectionModelクラスによって提供されています。
標準ビューのすべては、デフォルトでは、自分の選択モデルを構築し、通常の方法でそれらと対話します。
ビューによって使用されている選択モデルがselectionModel()関数を介して得ることができ、交換の選択モデルはsetSelectionMode()で指定することができます。
私たちは、同じモデルのデータに複数の一貫性のあるビューを提供したい場合に、ビューが使用する選択モデルを制御する能力は便利です。
あなたは、モデルやビューをサブクラス化されない限り、一般的に、あなたが直接選択の内容を操作する必要はありません。
しかし、選択モデルへのインタフェースは、必要に応じて、アクセスすることができ、これはHandling Selections in Item Views.で説明されています。

Sharing selections among views

それは便利ですがビュークラスは、デフォルトでは、自分の選択モデルを提供することを、私たちは、同じモデルに複数のビューを使用する際には、モデルのデータとユーザの選択の両方が、すべてのビューで一貫して示されていることが望ましいことが多いです。
ビュークラスは、その内部の選択モデルを交換することを可能にするので、我々は次の行を持つビュー間の統一された選択を達成することができます。

    secondTableView->setSelectionModel(firstTableView->selectionModel());

第2のビューは、最初のビューの選択モデルが与えられます。両方のビューは、現在のデータと同期して、選択した項目の両方を維持し、同じ選択モデルで動作します。

上記の例では、同じタイプの2つの図は、同じモデルのデータを表示するために使用しました。しかし、二つの異なるタイプのビューを使用した場合は、選択されたアイテムは非常に異なって、各ビューで表すことができます。例えば、テーブルビュー内の連続した選択は、ツリービューで強調表示された項目の断片化された集合として表すことができます。

Delegate Classes

Concepts
Model-View-Controllerパターンとは異なり、モデル/ビューの設計は、ユーザとの対話を管理するための完全に独立したコンポーネントが含まれていません。
一般的に、ビューは、ユーザに、ユーザ入力を処理するためのモデルデータを提示する責任があります。
この入力が得られる方法で、ある程度の柔軟性を可能にするために、相互作用は、デリゲートによって行われます。
これらのコンポーネントは、入力機能を提供し、また、いくつかのビューで個々のアイテムをレンダリングする責任があります。
デリゲートを制御するための標準的なインタフェースがQAbstractItemDelegateクラスで定義されます。
デリゲートは、paint()とsizeHint()関数を実装することによって、その内容自体をレンダリングすることができると期待されます。
しかし、シンプルなウィジェットベースのデリゲートではなくQItemDelegateの代わりにQAbstractItemDelegateをサブクラス化することができます、これらの機能のデフォルトの実装を利用します。
デリゲートのためのエディタは、編集処理を管理するためのウィジェットを使用するか、イベントを直接処理することによってのいずれかで実施することができます。
最初のアプローチは、このセクションの後半でカバーされており、それはまた、スピンボックスのデリゲートの例に示されています。
Pixelatorの例では、テーブルビューのための特殊なレンダリングを実行するカスタムデリゲートを作成する方法を示しています。

Using an existing delegate

Qtので提供される標準ビューは、編集機能を提供するためにQItemDelegateのインスタンスを使用します。
QListView、QTableView、およびQTreeView:デリゲートインターフェースのデフォルト実装は、標準ビューのそれぞれについて、いつものスタイルでアイテムをレンダリングします。
すべての標準ロールは、標準ビューで使用される既定のデリゲートによって処理されます。
これらが解釈される方法は、QItemDelegateのマニュアルに記載されています。
ビューによって使用されるデリゲートは、itemDelegate()関数によって返されます。setItemDelegate()関数を使用すると、標準のビューのカスタムデリゲートをインストールすることができ、そしてカスタムビューのデリゲートを設定するときに、この機能を使用する必要があります。

A simple delegate

ここで実装デリゲートは、編集機能を提供するために、スピン・ボックスを使用し、主に整数を表示するモデルで使用するためのものです。我々は、この目的のためにカスタム整数ベーステーブルモデルを設定しますが、カスタムデリゲートは、データ入力を制御するので、我々は簡単に、代わりにQStandardItemModelを使用することもできました。
私たちは、モデルのコンテンツを表示するためのテーブルビューを構築します、これは編集のためのカスタムデリゲートを使用します。

私たちは、カスタム表示機能を書きたくないので、私たちはQItemDelegateからデリゲートをサブクラス化します。
しかし、まだ、エディタウィジェットを管理するための機能を、我々は提供する必要があります。

class SpinBoxDelegate : public QStyledItemDelegate
{
    Q_OBJECT

public:
    SpinBoxDelegate(QObject *parent = 0);

    QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option,
                          const QModelIndex &index) const Q_DECL_OVERRIDE;

    void setEditorData(QWidget *editor, const QModelIndex &index) const Q_DECL_OVERRIDE;
    void setModelData(QWidget *editor, QAbstractItemModel *model,
                      const QModelIndex &index) const Q_DECL_OVERRIDE;

    void updateEditorGeometry(QWidget *editor,
        const QStyleOptionViewItem &option, const QModelIndex &index) const Q_DECL_OVERRIDE;
};

デリゲートを構築するときに、エディタウィジェットが設定されていないことに注意してください。それが必要とされるときに私たちは、エディタウィジェットを構築します。

Providing an editor

この例では、テーブルビューがエディタを提供する必要がある場合、それはアイテムが変更されるために適切であるエディタ・ウィジェットを提供するために、デリゲートを要求します。

createEditor()関数は、デリゲートが、適切なウィジェットを設定することができる必要があるすべてのものが供給されています:

QWidget *SpinBoxDelegate::createEditor(QWidget *parent,
    const QStyleOptionViewItem &/* option */,
    const QModelIndex &/* index */) const
{
    QSpinBox *editor = new QSpinBox(parent);
    editor->setFrame(false);
    editor->setMinimum(0);
    editor->setMaximum(100);

    return editor;
}

ビューは、それが不要になったとき、それを破壊するための責任を取るので、私たちはエディタ・ウィジェットへのポインタを保持する必要がないことに注意してください。
我々は、ユーザーが期待する標準的な編集のショートカットを提供することを確実にするために、エディタ上で、デリゲートのデフォルトのイベントフィルタをインストールします。
追加のショートカットがより洗練された動作を可能にするために、エディタに追加することができます。これらは、編集のヒント上のセクションで説明されています。

我々が後でこれらの目的のために定義する関数を呼び出すことにより、エディタのデータとジオメトリが正しく設定されていることを、ビューは保証します。

私たちは、ビューによって供給されたモデルインデックスに応じて異なるエディタを作成することができます。

たとえば、整数の列と文字列の列を持っているならば、我々は列が編集されているかに応じてQSpinBoxかQLineEditを返すことができます。
デリゲートは、エディタにモデルデータをコピーする機能を提供しなければなりません。この例では、表示ロールに格納されたデータを読み込み、それに応じてスピンボックスに値を設定します。

void SpinBoxDelegate::setEditorData(QWidget *editor,
                                    const QModelIndex &index) const
{
    int value = index.model()->data(index, Qt::EditRole).toInt();

    QSpinBox *spinBox = static_cast<QSpinBox*>(editor);
    spinBox->setValue(value);
}

この例では、私たちは、エディタ・ウィジェットがスピンボックスであることを知っていますが、モデル内のデータの種類ごとに異なるエディタを提供している可能性があります、その場合には、我々は、そのメンバ関数にアクセスする前に適切な型にウィジェットをキャストする必要があります。

Submitting data to the model

ユーザーがスピンボックスの値の編集が終了したとき、ビューがsetModelData()関数を呼び出すことによって、モデルで編集した値を格納するために、デリゲートを要求します。

void SpinBoxDelegate::setModelData(QWidget *editor, QAbstractItemModel *model,
                                   const QModelIndex &index) const
{
    QSpinBox *spinBox = static_cast<QSpinBox*>(editor);
    spinBox->interpretText();
    int value = spinBox->value();

    model->setData(index, value, Qt::EditRole);
}

ビューは、デリゲートのエディタウィジェットを管理しているので、我々は供給されるエディタの内容でモデルを更新する必要があるだけです。
ここでは、スピンボックスが最新であることを確認し、それが指定されたインデックスを使用して含まれている値を使用してモデルを更新します。
標準QItemDelegateクラスは、それがcloseEditor()シグナルを放出することによって編集を終了したビューに通知します。
ビューは、エディタウィジェットが閉じられ、破壊されることを保証します。この例では、我々は単に簡単な編集機能を提供するので、私たちは、このシグナルを発する必要は決してありません。
データに対するすべての操作はQAbstractItemModelが提供するインターフェースを介して行われています。
これは、操作するデータのタイプからデリゲートはほとんど依存しなくなりますが、いくつかの想定は、エディタウィジェットの特定の種類を使用するためになされなければなりません。
この例では、モデルが常に整数値が含まれていることを想定しているが、QVariantは予想外のデータについて、より適切なデフォルト値を提供するため、我々はまだモデルの異なる種類で、このデリゲートを使用することができます。

Updating the editor's geometry

エディタのジオメトリを管理するのはデリゲートの責任です。
エディタが作成されるとき、そして、ビューのアイテムのサイズまたは位置が変わるとき、ジオメトリ(形状)はセットされなければなりません。
幸いなことに、ビューは、ビューのオプションオブジェクト内のすべての必要なジオメトリ情報を提供します。

void SpinBoxDelegate::updateEditorGeometry(QWidget *editor,
    const QStyleOptionViewItem &option, const QModelIndex &/* index */) const
{
    editor->setGeometry(option.rect);
}

この場合、私たちはアイテムの四角形の中に表示オプションが提供するジオメトリ情報を使用しています。いくつかの要素でアイテムをレンダリングするデリゲートは、直接アイテムの四角形を使用することはありません。これは、アイテム内の他の要素に関連して、エディタを配置します。

Editing hints

編集した後、デリゲートは、編集プロセスの結果について他のコンポーネントにヒントを提供し、それ以降の編集操作を支援するヒントを提供しなければなりません。これは、適切なヒントとthecloseEditor()シグナルを放射することによって達成されます。

それが造られたとき、我々が、スピンボックスにインストールしたデフォルトのQItemDelegateイベントフィルタによってこれは考慮されます。

スピンボックスの動作は、よりユーザーフレンドリーにするために調整することができます。
ユーザーがスピンボックスで自分の選択を確認するためにリターンを打つ場合QItemDelegateによって供給されたデフォルトのイベントフィルタでは、デリゲートは、モデルに値をコミットし、スピンボックスを閉じます。
私たちは、スピンボックスに、我々独自のイベントフィルタをインストールすることによってこの動作を変更し、私たちのニーズに合わせた編集のヒントを提供することができます。

例えば、我々は自動的にビュー内の次のアイテムを編集し始めるために、EditNextItemヒントでemitcloseEditor()にそうするかもしれません。
イベントフィルタの使用を必要としない別のアプローチは、我々独自のエディター・ウィジェットを提供することにあります、おそらく便宜上QSpinBoxをサブクラス化します。
この別のアプローチは、私たちに、エディタ・ウィジェットに追加のコードを書くコストでどう動作するかというより詳細な制御を与えるだろう。(つまり面倒)
あなたが標準のQtエディタ・ウィジェットの動作をカスタマイズする必要がある場合は、デリゲートでイベントフィルタをインストールする方が簡単です。

デリゲートはこれらのヒントを放出する必要はありませんが、そうでないものは、アプリケーションに小さく統合され、一般的な編集アクションをサポートするためのヒントを発するものよりも使用可能ではありません。

Handling Selections in Item Views

Concepts

アイテムビュークラスで使用される選択モデルは、モデル/ビュー​​アーキテクチャの施設に基づく選択の一般的な説明を提供します。
選択を操作するための標準クラスが提供されているアイテムビューのために十分であるが、選択モデルは、独自のアイテム・モデルとビューの要件に適合するように特殊な選択モデルを作成することができます。
ビューで選択されたアイテムに関する情報はQItemSelectionModelクラスのインスタンスに格納されます。
これは、単一モデル内のアイテムのためのモデルインデックスを維持し、任意のビューとは無関係です。
モデルに多くのビューが存在し得るので、アプリケーションは、一貫した方法で複数のビューを表示することができ、ビュー間の選択を共有することが可能です。
選択は、選択範囲で構成されています。

選択されたアイテムの各レンジごとに開始と終了のモデル・インデックスだけを記録することによって、これらはアイテムの大規模な選択についての情報を維持します。
アイテムの非連続的な選択は、選択を記述するために、複数の選択範囲を使用して構築されます。
選択は、選択モデルに保持されたモデルのインデックスの集合に適用されます。

適用されるアイテムの最新の選択は、現在の選択として知られています。
この選択の影響があっても、選択コマンドの特定のタイプを使用することにより、その適用後に修正することができます。
これらは、このセクションで後述します。

Current item and selected items

ビューでは、常に現在のアイテムと選択したアイテムの2つの独立した状態があります。アイテムは、現在のアイテムと同時に選択することができます。ビューは常にキーボードナビゲーションなど、現在のアイテムがあることを保証する責任があり、例えば、現在のアイテムが必要です。
以下の表は、現在のアイテムと選択したアイテム間の違いを強調しています。

Current Item

Selected Items
1つの現在のアイテムが、存在することができるだけです。
複数の選択されたアイテムが存在することができます。
現在のアイテムがキーナビゲーションやマウスボタンクリックで変更されます。
ユーザーがアイテムと対話するとき - 例えば、単一選択、複数選択、など - のアイテムの選択状態は、いくつかの事前定義されたモードに応じて、設定または設定解除されています。
編集キー(F2)が押される、または、アイテムがダブルクリックされる(編集が許可される)ならば、現在のアイテムは編集されます。
現在の項目を選択または選択解除されるべき範囲(または2つの組み合わせ)を指定するアンカーと一緒に使用することができます。
現在のアイテムがフォーカス矩形で示されています。
選択されたアイテムが選択矩形で示されています。

選択を操作する際には、アイテムのモデル内のすべてのアイテムの選択状態の記録としてQitemSelectionModelを考えると便利です。
選択モデルが設定されると、アイテムのコレクションは、選択・選択解除することができ、またはそれらの選択状態が既に選択されたアイテムなのかを知る必要なしに切り替えることができます。
すべての選択されたアイテムのインデックスは、いつでも取り出すことができます、そしてその他のコンポーネントは、シグナルとスロット機構を介して選択モデルへの変更を通知することができます。

Using a selection model

標準ビュークラスは、ほとんどのアプリケーションで使用可能なデフォルト選択モデルを提供します。
一つのビューに属する選択モデルは、ビューのselectionModel()関数を使用して得ることができそして、setSelectionMode()で多くのビュー間で共有するので、新しい選択モデルの構築は、一般的に必要とされていません。
選択は、モデル、およびQItemSelectionにモデルインデックスのペアを指定して作成されています。
これは、指定されたモデル内のアイテムを参照するためにインデックスを使用して、選択したアイテムのブロックで、左上と右下のアイテムとしてそれらを解釈します。
モデル内のアイテムに選択を適用するには、選択モデルに提出する選択が必要です。
これは多くの方法で達成することができ、それぞれは、選択モデルに既に存在する選択に異なる効果をもたらします。

Selecting items

選択の主な機能のいくつかを実証するために、我々は、全部で32アイテムとカスタム・テーブル・モデルのインスタンスを作成し、そのデータにテーブルビューを開きます。

    TableModel *model = new TableModel(8, 4, &app);

    QTableView *table = new QTableView(0);
    table->setModel(model);
    QItemSelectionModel *selectionModel = table->selectionModel();

テーブルビューのデフォルトの選択モデルは、後で使用するために取得されます。
我々はモデル内の任意のアイテムを修正するのではなく、ビューがテーブルの左上に表示されるいくつかのアイテムを選択します。これを行うために、我々は、選択される領域で、左上と右下の項目に対応するモデルのインデックスを取得する必要があります。

    QModelIndex topLeft;
    QModelIndex bottomRight;

    topLeft = model->index(0, 0, QModelIndex());
    bottomRight = model->index(5, 2, QModelIndex());

モデルでこれらのアイテムを選択し、テーブルビューの対応する変化を確認するために、我々はその後、選択オブジェクトを構築した選択モデルに適用する必要があります。

    QItemSelection selection(topLeft, bottomRight);
    selectionModel->select(selection, QItemSelectionModel::Select);

選択は、選択フラグの組み合わせによって定義されたコマンドを使用して、選択モデルに適用されます。
この場合、使用されるフラグは、選択オブジェクトに記録されたアイテムに関係なく、以前の状態の、選択モデルに含まれることになり。得られた選択は、図で示されています。

アイテムの選択は、選択フラグによって定義される様々な操作を使用して変更することができます。これらの操作に起因する選択は、複雑な構造を有していてもよいが、それは、選択モデルによって効率的に表現されます。私たちが選択を更新する方法を調べる際に、選択したアイテムを操作するための別の選択フラグの使用が記述されています。

Reading the selection state

選択モデルに格納されたモデルのインデックスはselectedIndexes()関数を使用して読み取ることができます。

彼らがどのモデルのためにいるかについてわかっている限り、我々が反復処理することができるモデルインデックスのソートされていないリストをこれは返します:

    QModelIndexList indexes = selectionModel->selectedIndexes();
    QModelIndex index;

    foreach(index, indexes) {
        QString text = QString("(%1,%2)").arg(index.row()).arg(index.column());
        model->setData(index, text);
    }

上記のコードは、Qtの便利なforeachキーワードを使用して、選択モデルで返されるインデックスと一致しているアイテムを反復処理し、それを修正しています。

選択モデルは、選択の変更を示すためにシグナルを発します。これらは、全体としての選択や項目モデル内で、現在フォーカスがあるアイテムの両方への変更について、他のコンポーネントに通知します。
私たちは、スロットにselectionChanged()シグナルを接続し、モデル内のアイテムが選択または選択解除された場合、選択範囲の変更を調べることができます。

スロットは、2つのQItemSelectionオブジェクトで呼ばれます。

1つは、新たに項目を選択するために対応する索引のリストが含まれています;
他は、新たに選択解除項目に対応するインデックスが含まれています。

次のコードでは、私たちは、selectionChanged()シグナルを受信するスロットを提供します
それは、文字列で選択した項目に記入し、選択を解除したアイテムの内容をクリアします。

void MainWindow::updateSelection(const QItemSelection &selected,
    const QItemSelection &deselected)
{
    QModelIndex index;
    QModelIndexList items = selected.indexes();

    foreach (index, items) {
        QString text = QString("(%1,%2)").arg(index.row()).arg(index.column());
        model->setData(index, text);
    }

    items = deselected.indexes();

    foreach (index, items)
        model->setData(index, "");
}

currentChanged()シグナルを2つのモデルのインデックスで呼ばれるスロットに接続することによって、我々は現在フォーカスされている項目を追跡することができます。

これらは、以前にフォーカスしたアイテム、および現在フォーカスしているアイテムに対応しています。

次のコードでは、我々はcurrentChanged()シグナルを受信し、QMainWindowのステータスバーを更新するために提供された情報を使用するスロットを提供します。

void MainWindow::changeCurrent(const QModelIndex &current,
    const QModelIndex &previous)
{
    statusBar()->showMessage(
        tr("Moved from (%1,%2) to (%3,%4)")
            .arg(previous.row()).arg(previous.column())
            .arg(current.row()).arg(current.column()));
}

ユーザーによりされた選択を監視することは、これらのシグナルのため直接的であるけれども、我々はまた、直接選択モデルを更新することができます。

Updating a selection

選択コマンドはQItemSelectionModel::SelectionFlag によって定義された選択フラグの組み合わせによって提供されています。
各選択フラグは、select()関数のいずれかが呼び出されたときに選択した項目の内部のレコードを更新する方法を選択モデル伝えます。
最も一般的に使用されるフラグが選択フラグです、これは、選択されたとして、指定した項目を記録するための選択モデルを指示します。
トグルフラグは、与えられた任意の選択解除のアイテムを選択し、任意の現在選択されているアイテムの選択を解除し、指定したアイテムの状態を反転させるために選択モデルを引き起こします。
選択を解除フラグが指定されたすべてのアイテムの選択を解除します。
選択モデル内の個々のアイテムは、アイテムの選択を作成し、選択モデルに適用することによって更新されます。
å次のコードでは、指定されたアイテムの選択状態を反転させるトグルコマンドを使用して、上に示したテーブルモデルへのアイテムの第二の選択を適用します。

    QItemSelection toggleSelection;

    topLeft = model->index(2, 1, QModelIndex());
    bottomRight = model->index(7, 3, QModelIndex());
    toggleSelection.select(topLeft, bottomRight);

    selectionModel->select(toggleSelection, QItemSelectionModel::Toggle);

この操作の結果は、我々が達成したものを可視化する便利な方法を提供し、テーブルビューに表示されます。

デフォルトでは、選択コマンドは、唯一のモデルのインデックスで指定された個々のアイテムで動作します。
しかし、選択コマンドを記述するために使用されるフラグは、全体の行と列を変更するための追加のフラグと組み合わせることができます。

たとえば、あなたが選択し、行との組み合わせであるコマンドを使用しますが、一つだけのインデックスでselect()を呼び出した場合、参照されたアイテムを含んでいる行全体が選択されます。

次のコードは、行と列のフラグの使用する方法を示しています。

    QItemSelection columnSelection;

    topLeft = model->index(0, 1, QModelIndex());
    bottomRight = model->index(0, 2, QModelIndex());

    columnSelection.select(topLeft, bottomRight);

    selectionModel->select(columnSelection,
        QItemSelectionModel::Select | QItemSelectionModel::Columns);

    QItemSelection rowSelection;

    topLeft = model->index(0, 0, QModelIndex());
    bottomRight = model->index(1, 0, QModelIndex());

    rowSelection.select(topLeft, bottomRight);

    selectionModel->select(rowSelection,
        QItemSelectionModel::Select | QItemSelectionModel::Rows);

4つだけインデックスが選択モデルに供給されていますが、列と行選択フラグの使用は、2つの列と2つの行が選択されていることを意味します。
以下の画像は、これらの2つの選択の結果を示しています。

例えば、モデル上で実行するコマンドは、すべてのモデル内の項目の選択を累積し関与しています。
選択をクリアするか、新しいものと現在の選択範囲を置き換えることも可能です。

現在の選択を新しい選択と入れ替えるために、他の選択フラグとカレントフラグを結びつけます。

このフラグを使っているコマンドは、select()を呼ぶことで選択モデルに、モデルのインデックスの現在のコレクションを置き換えるために指示します。

あなたが新しいものの追加を開始する前に、すべての選択をクリアするには、クリアフラグを指定して、他の選択フラグを結合します。これは、モデルインデックスの選択モデルのコレクションをリセットする効果を有します。

Selecting all items in a model

モデル内のすべてのアイテムを選択するには、そのレベルのすべてのアイテムをカバーするモデルの各レベルのための選択を作成する必要があります。
私たちは、指定された親のインデックスを持つ、左上と右下のアイテムに対応するインデックスを取得して次の操作を行います。

    QModelIndex topLeft = model->index(0, 0, parent);
    QModelIndex bottomRight = model->index(model->rowCount(parent)-1,
        model->columnCount(parent)-1, parent);

選択は、これらのインデックスとモデルで構成されています。
対応するアイテムは、その後、選択モデルで選択されています。

    QItemSelection selection(topLeft, bottomRight);
    selectionModel->select(selection, QItemSelectionModel::Select);

これは、モデル内のすべてのレベルに対して実行される必要があります。トップレベルのアイテムについては、我々は通常の方法で親インデックスを定義します。

    QModelIndex parent = QModelIndex();

階層型モデルの場合、hasChildren()関数は、任意の所与のアイテムがアイテムの別のレベルの親であるかどうかを決定するために使用されます。

Creating New Models

モデル/ビューコンポーネント間の機能の分離は、作成されるモデル は既存のビューを利用することができます。

このアプローチは、我々に標準的なグラフィカル・ユーザ・インタフェース・コンポーネント(例えばQListView、QTableViewとQTreeView)を使用して、さまざまなソースから、現行データを提示させます。

QAbstractItemModelクラスは、データの挿入、削除、修正、または何らかの方法でソートされる可能性を考慮して、階層構造内の情報を配置するデータソースをサポートするのに十分な柔軟性があるインターフェイスを提供します。
また、ドラッグ&ドロップ操作のサポートを提供します。
QAbstractListModelとQAbstractTableModelクラスは、単純な非階層データ構造へのインタフェースのサポートを提供し、単純なリストやテーブルモデルの出発点として使用するのが容易です。
このセクションでは、モデル/ビューアーキテクチャの基本原理を探求するための簡単な読み取り専用のモデルを作成します。
アイテムは、ユーザによって変更することができるように、このセクションでは、この単純なモデルを適合させます。
より複雑なモデルの例としては、Simple Tree Modelの例を参照してください。
QAbstractItemModelサブクラスの要件は、モデルサブクラス参考文献に詳細に記載されています。

Designing a model

既存のデータ構造のための新しいモデルを作成する場合、データにインターフェイスを提供するために使用されるべきモデルのタイプを考慮することが重要です。データ構造は、アイテムのリストまたはテーブルとして表すことができる場合は、これらのクラスは、多くの機能に適したデフォルトの実装を提供するので、あなたはQAbstractListModelまたはQAbstractTableModelをサブクラス化することができます。

しかし、基礎となるデータ構造が階層ツリー構造で表すことができるだけであるならば、QAbstractItemModelをサブクラス化することが必要です。
このアプローチは、Simple Tree Model の例で取り上げられます。

QAbstractListModelが構築するための理想的な基本クラスを提供しますので、このセクションでは、我々は、文字列のリストに基づいて、単純なモデルを実装しています。

根底にあるデータ構造がたとえどんな形をとっても、根底にあるデータ構造へのより自然なアクセスを許す専門モデルで標準的なQAbstractItemModel APIを補うことは通常良い考えです。

これはデータでモデルを取り付けることをより簡単にするが、まだ他の一般的なモデル/ビュー構成要素が標準的なAPIを用いたそれと相互作用するのを可能にします。
以下に記すモデルは、カスタム・コンストラクタをちょうどこの目的に提供します。

これは、簡単にデータを持つモデルを取り込むことができ、それでもそれは、標準的なAPIを使用して対話するために、他の一般的なモデル/ビューコンポーネントを有効にします。以下で説明するモデルは、この目的のためだけにカスタムコンストラクタを提供します。

A read-only example model

ここで実装モデルは、標準的なQStringListModelクラスに基づく単純な、非階層、読み取り専用のデータモデルです。それは、その内部のデータソースとしてQStringListを有し、機能モデルを作るために必要なもののみを実装します。
実装を容易にするために、それはリストモデルの賢明なデフォルトの動作を定義するため、我々はQAbstractListModelをサブクラス化し、それがQAbstractItemModelクラスよりも単純なインタフェースを公開します。
モデルを実装する場合、それはQAbstractItemModelは、任意のデータ自体を格納しないことを覚えておくことが重要ですが、それは単にビューがデータにアクセスするために使用するインターフェースを提供します。
最小の読み取り専用モデルのために、インタフェースのほとんどのデフォルトの実装があるので、いくつかの機能を実現するだけでよいです。

クラス宣言は以下の通りです。

class StringListModel : public QAbstractListModel
{
    Q_OBJECT

public:
    StringListModel(const QStringList &strings, QObject *parent = 0)
        : QAbstractListModel(parent), stringList(strings) {}

    int rowCount(const QModelIndex &parent = QModelIndex()) const;
    QVariant data(const QModelIndex &index, int role) const;
    QVariant headerData(int section, Qt::Orientation orientation,
                        int role = Qt::DisplayRole) const;

private:
    QStringList stringList;
};

別にモデルのコンストラクタから、我々は2つの機能を実装する必要があります。
rowCount()はモデルで列の数を返します、そして、data()は指定された典型的なインデックスと一致しているデータのアイテムを返します。

行儀が良いモデルは、ヘッダーに任意のものを表示するツリーとテーブルビューを与えるためにheaderData()を実装します。

これは、非階層モデルであることに注意してください、私たちは、親子関係を心配する必要はありません。
我々のモデルは、階層型だった場合、我々はまた、index()とparent()関数を実装する必要があります。
文字列のリストは、stringList のプライベートメンバ変数で内部に格納されています。
Dimensions of the model
私たちは、モデルの行数は、文字列リスト内の文字列の数と同じにしたいです。
私たちは、このことを念頭に置いてrowCount()関数を実装します。

int StringListModel::rowCount(const QModelIndex &parent) const
{
    return stringList.count();
}

モデルは非階層であるため、我々は安全に親アイテムに対応するモデルインデックスを無視することができます。デフォルトでは、QAbstractListModel由来のモデルは1列のみが含まれているので、私たちは columnCount()関数を再実装する必要はありません。

Model headers and data

ビューのアイテムのために、我々はストリング・リストで文字列を返したいです。
data()関数は、インデックス引数と一致するデータのアイテムを返す役割を果たします:

QVariant StringListModel::data(const QModelIndex &index, int role) const
{
    if (!index.isValid())
        return QVariant();

    if (index.row() >= stringList.size())
        return QVariant();

    if (role == Qt::DisplayRole)
        return stringList.at(index.row());
    else
        return QVariant();
}

付属のモデルインデックスが有効であれば、我々は単に有効なQVariantを返し、行番号が文字列リスト内のアイテムの範囲内です、そして、要求されたロールは、私たちがサポートしているものです。
このようにQTreeViewとQTableViewなどの一部のビューは、アイテムデータと共にヘッダを表示することができます。
モデルが、ヘッダを持つビューに表示された場合、我々はヘッダーに行と列の番号を表示したいと思います。

我々はheaderData()関数をサブクラス化することによって、ヘッダの情報を提供することができます。

QVariant StringListModel::headerData(int section, Qt::Orientation orientation,
                                     int role) const
{
    if (role != Qt::DisplayRole)
        return QVariant();

    if (orientation == Qt::Horizontal)
        return QString("Column %1").arg(section);
    else
        return QString("Row %1").arg(section);
}

ここでも、ロールは、我々がサポートしているものである場合にのみ有効なQVariantを返します。
正確なデータ判定が戻るようにするとき、ヘッダーの向きも考慮されます。

すべてのビューがアイテム・データでヘッダを表示するというわけではありません、そして、それらを隠すように構成することもできます。

それにもかかわらず、あなたがモデルによって提供されるデータについての関連情報を提供するためにheaderData()関数を実装することをお勧めします。

アイテムにはいくつかのロールを持つことができます。そして、指定されるロールに従い異なるデータを伝えます。

我々のモデル内のアイテムは、1つだけのロール(DisplayRole)、を持っているので、我々は、指定されたロールに関係なく、アイテムのデータを返します。
しかし、我々は、そのようなビューがツールチップ内のアイテムに関する情報を表示するために使用できるツールチップのロールとして他のロールにDisplayRoleを提供するデータを再利用することができます。

An editable model

読み取り専用のモデルは、しかし、ユーザに提示することができる方法の簡単な選択肢を示し
多くのアプリケーションのために、編集可能なリストモデルは、はるかに便利です。

アイテムを読出し専用のために実装したdata()関数を変えることによって、そして、2つの追加機能を実装することによって編集可能にするために、我々はリードオンリー・モデルを修正することができます:flags()メソッドとsetData()。

次の関数の宣言は、クラス定義に追加されます。

    Qt::ItemFlags flags(const QModelIndex &index) const;
    bool setData(const QModelIndex &index, const QVariant &value,
                 int role = Qt::EditRole);

Making the model editable

デリゲートは、アイテムエディタを作成する前に編集可能であるかどうかをチェックします。モデルは、デリゲートに、そのアイテムが編集可能であることを知らせる必要があります。私たちは、モデル内の各アイテムの正しいフラグを返すことによってこれを行います。このケースでは、我々はすべてのアイテムを有効にして、それらを選択し、編集可能の両方を行います。

Qt::ItemFlags StringListModel::flags(const QModelIndex &index) const
{
    if (!index.isValid())
        return Qt::ItemIsEnabled;

    return QAbstractItemModel::flags(index) | Qt::ItemIsEditable;
}

我々は、デリゲートが、実際の編集処理を行う方法を知っている必要はありません。
我々は、ただデリゲートに、モデル内のデータを設定する方法を提供する必要があります。
これは、setData()関数によって達成されます:

bool StringListModel::setData(const QModelIndex &index,
                              const QVariant &value, int role)
{
    if (index.isValid() && role == Qt::EditRole) {

        stringList.replace(index.row(), value.toString());
        emit dataChanged(index, index);
        return true;
    }
    return false;
}

このモデルでは、モデルのインデックスに対応する文字列リスト内のアイテムは、提供された値に置き換えられます。
しかし、私たちは、文字列のリストを変更する前に、インデックスが有効であること、アイテムが正しいタイプのものであり、ロールがサポートされていることを確認する必要があります。
慣例により、私たちは、これが標準のアイテムデリゲートが使用するロールがあるので、ロールがEditRoleであることを主張しています。
しかし、ブール値については、あなたはQt::CheckStateRoleを使用するとQt::ItemIsUserCheckableフラグを設定することができます。
チェックボックスは、値を編集するために使用されます。
このモデルでの基礎となるデータは、すべてのロールのために同じであるので、この詳細は、ちょうどそれが簡単に標準的なコンポーネントでモデルを統合することができます。
データが設定されている場合、モデルビューは、いくつかのデータが変更されたことを知らせなければなりません。これはdataChanged()シグナルを放出することによって行われます。データの一つのアイテムだけが変更されているので、シグナルで指定されたアイテムの範囲は、ちょうど1つのモデルインデックスに限定されています。
また、data()関数は、Qt::EditRoleテストを追加するように変更する必要があります。

QVariant StringListModel::data(const QModelIndex &index, int role) const
{
    if (!index.isValid())
        return QVariant();

    if (index.row() >= stringList.size())
        return QVariant();

    if (role == Qt::DisplayRole || role == Qt::EditRole)
        return stringList.at(index.row());
    else
        return QVariant();
}

Inserting and removing rows

モデル内の行と列の数を変更することが可能です。
文字列リストのモデルでは、それが単に行数を変更することは理にかなっています、したがって
私たちは挿入した行を除去するための関数のみを再実装します。これらは、クラス定義で宣言されています。

    bool insertRows(int position, int rows, const QModelIndex &index = QModelIndex());
    bool removeRows(int position, int rows, const QModelIndex &index = QModelIndex());

このモデルの行がリスト内の文字列に対応しているので、insertRows()関数は、指定された位置より前の文字列リストに空の文字列の番号を挿入します。
挿入された文字列の数は、指定された行数に相当します。
親インデックスは、通常モデルの行が追加されるべき場所を決定するために使用されます。
ここでは、文字列のみの単一のトップレベルのリストを持っているので、我々は単にそのリストに空の文字列を挿入します。

bool StringListModel::insertRows(int position, int rows, const QModelIndex &parent)
{
    beginInsertRows(QModelIndex(), position, position+rows-1);

    for (int row = 0; row < rows; ++row) {
        stringList.insert(position, "");
    }

    endInsertRows();
    return true;
}

モデルは、最初の行数が変更されたことを他のコンポーネントに通知するbeginInsertRows()関数を呼び出します。
関数は、最初と最後の新しい行の行番号を挿入すると、その親アイテムのモデルインデックスを指定します。
文字列のリストを変更した後、それは操作を完了し、モデルの寸法が変更されていることを他のコンポーネントに知らせるためにendInsertRows()を呼びます。そして成功したことを示すためにtrueを返します。
モデルから行を削除する機能も書くことは簡単です。
モデルから取り除かれる列は、ポジションおよび与えられた列の数により指定します。
我々は、我々の実装を簡素化するために親インデックスを無視して、単なる文字列のリストから該当するアイテムを削除します。

bool StringListModel::removeRows(int position, int rows, const QModelIndex &parent)
{
    beginRemoveRows(QModelIndex(), position, position+rows-1);

    for (int row = 0; row < rows; ++row) {
        stringList.removeAt(position);
    }

    endRemoveRows();
    return true;
}

beginRemoveRows()関数は、常に任意の基礎となるデータが削除される前に呼び出され、最初と最後の行が削除されるように指定されています。
これは、使用できなくなる前に他のコンポーネントがデータにアクセスすることを可能にします。

行が削除された後、操作を終了し、他のコンポーネントに、モデルの寸法が変更されたことを知らせるために、モデルはendRemoveRows()を発します。

Next steps

我々は、垂直方向のリストの形でモデルのアイテムを提示するQListViewクラスを使用して、このモデル、または他の任意のモデルで提供されたデータを表示することができます。
文字列リストモデルの場合、このビューにはアイテムを操作することができるように、デフォルトのエディタを提供します。
私たちは、ビュークラスが標準ビュークラスによって使用可能になる可能性を検討します。
モデルサブクラス参考文書は、より詳細にQAbstractItemModelサブクラスの要件について説明し、様々なタイプのモデルでさまざまな機能を有効にするために実装しなければならない仮想関数へのガイドを提供します。

Item View Convenience Classes

アイテムベースのウィジェットは、その用途を反映する名前を持つ:QListWidgetは、アイテムのリストを提供し、QTreeWidgetは、マルチレベルのツリー構造が表示され、QTableWidgetは、セルのアイテムのテーブルを提供します。
各クラスには、アイテムを選択すると、ヘッダ管理のための一般的な動作を実装するQAbstractItemViewクラスの振る舞いを継承します。

List widgets

アイテムの単一レベルのリストは、一般的にQListWidgetとQListWidgetItemの数字を使用して表示されます。
リスト・ウィジェットは、他のウィジェットと同様に構成されています。

    QListWidget *listWidget = new QListWidget(this);

それらが構成されているときに、リストのアイテムがリストウィジェットに直接添加することができます。

    new QListWidgetItem(tr("Sycamore"), listWidget);
    new QListWidgetItem(tr("Chestnut"), listWidget);
    new QListWidgetItem(tr("Mahogany"), listWidget);

彼らはまた、親リストウィジェットなしで構築され、いくつかの後の時点でリストに追加できます。

    QListWidgetItem *newItem = new QListWidgetItem;
    newItem->setText(itemText);
    listWidget->insertItem(row, newItem);

リストの各項アイテムは、テキストラベルとアイコンを表示することができます。テキストの描画に使用される色とフォントは、アイテムのためにカスタマイズされた外観を提供するために変更することができます。
「これは何?」ツールチップ、ステータスのヒント、ヘルプは、すべて簡単にリストが正しくアプリケーションに統合されていることを確認するように設定されています。

    newItem->setToolTip(toolTipText);
    newItem->setStatusTip(toolTipText);
    newItem->setWhatsThis(whatsThisText);

デフォルトでは、リスト内のアイテムは、その作成順に提示されています。
アイテムのリストは前方にソートまたはアルファベット順を逆にされているアイテムのリストを生成するためにはQt:: SORTORDERに与えられた基準に従ってソートすることができます。

    listWidget->sortItems(Qt::AscendingOrder);
    listWidget->sortItems(Qt::DescendingOrder);
Tree widgets

ツリーやアイテムの階層リストはQTreeWidgetとQTreeWidgetItemクラスによって提供されています。
ツリーウィジェットの各項アイテムは、独自の子アイテムを持つことができ、情報の列数を表示することができます。ツリーウィジェットは、他のウィジェットのように作成されます。

    QTreeWidget *treeWidget = new QTreeWidget(this);

アイテムはツリーウィジェットに追加する前に、列の数を設定する必要があります。
例えば、我々は二つの列を定義し、各列の最上部にラベルを提供するために、ヘッダーを作成することができます。

    treeWidget->setColumnCount(2);
    QStringList headers;
    headers << tr("Subject") << tr("Default");
    treeWidget->setHeaderLabels(headers);

各セクションのラベルを設定する最も簡単な方法は、文字列のリストを提供することです。
より洗練されたヘッダについて、あなたは、ツリーアイテムを構築し、あなたが望むようにそれを飾る、ツリーウィジェットヘッダーとしてそれを使用することができます。
ツリーウィジェットのトップレベルのアイテムは、その親ウィジェットとしてツリーウィジェットで構成されています。
これらは、任意の順序で挿入することができます。また、各項アイテムを構築する前のアイテムを指定することによって、それらが特定の順序で表示されていることを確認することができます。

    QTreeWidgetItem *cities = new QTreeWidgetItem(treeWidget);
    cities->setText(0, tr("Cities"));
    QTreeWidgetItem *osloItem = new QTreeWidgetItem(cities);
    osloItem->setText(0, tr("Oslo"));
    osloItem->setText(1, tr("Yes"));

    QTreeWidgetItem *planets = new QTreeWidgetItem(treeWidget, cities);

ツリーウィジェットは、ツリー内のより深いから他のアイテムには少し異なるトップレベルのアイテムを扱います。アイテムは、ツリーウィジェットのtakeTopLevelItem()関数を呼び出すことで、ツリーの最上位レベルから除去することができますが、低いレベルからのアイテムは、その親アイテムのtakeChild()関数を呼び出すことによって除去されます。
アイテムは、インサートTopLevelItem()関数を使用して、ツリーの最上位レベルに挿入されています。
ツリーの下位レベルでは、親アイテムのinsertChild()関数が使用されます。
ツリーの最上位と下位のレベルの間のアイテムを移動するのは簡単です。
私達はちょうどアイテムはトップレベルのアイテムであるか否かを確認する必要があり、この情報は、アイテムのparent()関数によって供給されています。

たとえば、その場所に関係なく、我々はツリーウィジェットで現在のアイテムを削除することができます:

    QTreeWidgetItem *parent = currentItem->parent();
    int index;

    if (parent) {
        index = parent->indexOfChild(treeWidget->currentItem());
        delete parent->takeChild(index);
    } else {
        index = treeWidget->indexOfTopLevelItem(treeWidget->currentItem());
        delete treeWidget->takeTopLevelItem(index);
    }

ツリーウィジェットにアイテムをどこかに挿入することは、同じパターンに従います:

    QTreeWidgetItem *parent = currentItem->parent();
    QTreeWidgetItem *newItem;
    if (parent)
        newItem = new QTreeWidgetItem(parent, treeWidget->currentItem());
    else
        newItem = new QTreeWidgetItem(treeWidget, treeWidget->currentItem());

Table widgets

スプレッドシートアプリケーションに見られるものと同様のアイテムのテーブルはQTableWidgetとQTableWidgetItemで構成されています。
これらは、その中に使用するしているスクロールテーブルのウィジェットにヘッダとアイテムを提供します。
テーブルは、行と列のセット番号を使用して作成することができます、あるいは、必要に応じてこれらの寸法に合っていないテーブルに追加することができます。

    QTableWidget *tableWidget;
    tableWidget = new QTableWidget(12, 3, this);

アイテムは、必要な場所のテーブルに追加される前に、テーブルの外で構築されています。

    QTableWidgetItem *newItem = new QTableWidgetItem(tr("%1").arg(
        pow(row, column+1)));
    tableWidget->setItem(row, column, newItem);

水平方向と垂直方向のヘッダーは、テーブルの外にアイテムを構築し、ヘッダーとしてそれらを使用してテーブルに追加することができます。

    QTableWidgetItem *valuesHeaderItem = new QTableWidgetItem(tr("Values"));
    tableWidget->setHorizontalHeaderItem(0, valuesHeaderItem);

テーブルの行と列がゼロから始まることに注意してください。

Common features

各クラスで同じインタフェースを介して利用可能で便利なクラスのそれぞれに共通するアイテムベースの機能がいくつかあります。
我々は、さまざまなウィジェットのためのいくつかの例を、次のセクションでこれらを提示します。
使用される各機能の使用の詳細については各ウィジェットのためのモデル/ビュークラスのリストを見てください。

Hidden items

彼らを削除するよりはむしろ、アイテムビューウィジェットでアイテムを隠すことができることは、時々役に立ちます。上記のウィジェットのすべてのためのアイテムが隠され、後で再表示することができます。
あなたはアイテムがisItemHidden()関数を呼び出すことによって隠されているかどうかを判断することができ、そしてアイテムがsetItemHidden()関数で隠して非表示にすることができます。
この操作は、アイテムベースであるため、同様の機能は、すべての3つの便利なクラスで利用可能です。

Selections

アイテムが選ばれる方法は、ウィジェットの選択モードによって制御されます(QAbstractItemView::SelectionMode)。
このプロパティは、ユーザーが選択アイテムの連続した範囲でなければならないかどうか、多くのアイテムの選択における一つまたは多くのアイテムを、選択することができるかどうかを制御します。
選択モードは、上記のウィジェットのすべてで同じように動作します。

Single item selections:

ユーザーがウィジェットからの単一のアイテムを選択する必要がある場合には、デフォルトの単一選択モードが最も適しています。
このモードでは、現在のアイテムと選択したアイテムは同じです。

Multi-item selections:

このモードでは、既存の選択(非常に非排他的なチェックボックスが独立して切り換えられることができる方法のような)を変えることなく、ユーザーはウィジェットでどんなアイテムの選択状態でも切り換えることができます。

Extended selections:

選ばれる(例えばスプレッドシートで見つかるそれら)ことを、多くの隣接したアイテムにしばしば要求するウィジェットは、ExtendedSelectionモードを必要とします。
このモードでは、ウィジェット内のアイテムの連続的な範囲は、マウスとキーボードの両方で選択することができます。
修飾キーが使用される場合、ウィジェットで選択されたアイテムに隣接していない多くのアイテムを含む複雑な選択を作成することもできます。ユーザは、修飾キーを使用せずに、アイテムを選択した場合、既存の選択がクリアされます。

ウィジェットの選択されたアイテムがselectedItems()関数を使用して読み込まれます、そして繰り返し処理することができ、関連するアイテムのリストを提供します。たとえば、我々は次のコードで選択したアイテムのリスト内のすべての数値の合計を見つけることができます。

    QList<QTableWidgetItem *> selected = tableWidget->selectedItems();
    QTableWidgetItem *item;
    int number = 0;
    double total = 0;

    foreach (item, selected) {
        bool ok;
        double value = item->text().toDouble(&ok);

        if (ok && !item->text().isEmpty()) {
            total += value;
            number++;
        }
    }

単一選択モードについては、現在のアイテムが選択中であることに注意してください。マルチ選択と拡張選択モードでは、ユーザーが選択を作った方法に従い、現在のアイテムが、選択範囲内に存在しない場合があります。

Searching

開発者として、あるいはユーザに提示するサービスのいずれかとして、アイテムビューウィジェットでアイテムを見つけることができるのは便利です。
すべての3つのアイテムビューの便利なクラスは、可能な限り、首尾一貫してシンプルにするためにfindItems()共通関数を提供しています。

アイテムは、彼らはQt::MatchFlagsから値を選択することにより、指定された基準に従って、含まれるテキストを検索されています。
私たちはfindItems()関数でアイテムとマッチングするリストを取得することができます。

    QTreeWidgetItem *item;
    QList<QTreeWidgetItem *> found = treeWidget->findItems(
        itemText, Qt::MatchWildcard);

    foreach (item, found) {
        treeWidget->setItemSelected(item, true);
        // Show the item->text(0) for each item.
    }

上記のコードは、彼らに検索文字列に指定されたテキストが含まれている場合、ツリーウィジェット内のアイテムが選択されます。
このパターンは、リスト、テーブルウィジェットで使用することができます。

Using Drag and Drop with Item Views

Qtのドラッグ&ドロップのインフラは完全にモデル/ビューフレームワークでサポートされています。
リスト、テーブル内のアイテム、およびツリーがビュー内でドラッグすることができ、データは、MIMEエンコードされたデータとしてインポートおよびエクスポートすることができます。
標準ビューは、内部のドラッグ&ドロップをサポートし、アイテムが動かされている場合、自動的にそれらが表示される順序を変更します。
彼らは最も簡単な、最も一般的な使用のために設定されているので、デフォルトでは、ドラッグ・アンド・ドロップは、これらのビューのために有効になっていません。
アイテムの周りにドラッグすることができるようにするには、ビューの必要性の特定のプロパティを有効にします、そして、アイテム自体もドラッグ可能にする必要があります。
アイテムがビューからインポートさせられるだけであり、そして、データがそれへドロップを許さないモデルの必要条件は、完全に使用可能にされたドラッグ&ドロップ・モデルに対するものより少ないです。
新しいモデルでドラッグ&ドロップ・サポートを可能にすることに関する詳細は、Model Subclassing Referenceを参照してください。

Using convenience views

QListWidget、QTableWidget、およびQTreeWidgetで使用するアイテムの種類のそれぞれは、デフォルトでは、フラグの異なるセットを使用するように構成されています。
たとえば、各々のQListWidgetItemまたはQTreeWidgetItemが、まず最初に、チェック可能で、選択可能が有効化されて、ドラッグ&ドロップoperatiodragEnablednの供給源として使われることができます;
各QTableWidgetItemも編集そしてドラッグ&ドロップ操作の対象として使用することができます。
あなたは、通常、組み込みのドラッグ&ドロップのサポートを利用するために、ビュー自体にさまざまなプロパティを設定する必要があります:
• アイテムのドラッグを有効にするには、trueにビューのプロパティを設定します。
• ユーザーがビュー内の内部または外部のいずれかのアイテムをドロップできるようにするには、ビューのviewport()のacceptDropsプロパティをtrueに設定します。
• カレントのドラッグしたアイテムがドロップされるところをユーザーに示すためには、ビューのshowDropIndicatorプロパティを設定します。これは、継続的にビュー内のアイテムの配置についての情報の更新をユーザに提供します。
例えば、我々は、ドラッグを有効にすることができますし、次のコード行を持つリストウィジェットにドロップします。

QListWidget *listWidget = new QListWidget(this);
listWidget->setSelectionMode(QAbstractItemView::SingleSelection);
listWidget->setDragEnabled(true);
listWidget->viewport()->setAcceptDrops(true);
listWidget->setDropIndicatorShown(true);

結果はリストウィジェットです、これは、アイテムがビュー内で周りにコピーすることができます、
さらには同じタイプのデータを含むビューの間にユーザーがドラッグアイテムをすることができます。
両方の状況では、アイテムがコピーというより移動されます。

ビュー内の項目を移動することをユーザに有効にするには、我々はリストウィジェットのドラッグドロップモードを設定する必要があります。

listWidget->setDragDropMode(QAbstractItemView::InternalMove);

Using model/view classes

ドラッグ&ドロップのためのビューを設定すると便利なビューで使用したのと同じパターンに従います。
例えば、QListViewはQListWidgetと同じ方法で設定することができます。

QListView *listView = new QListView(this);
listView->setSelectionMode(QAbstractItemView::ExtendedSelection);
listView->setDragEnabled(true);
listView->setAcceptDrops(true);
listView->setDropIndicatorShown(true);

ビューによって表示されたデータへのアクセスは、モデルによって制御されるため、使用されるモデルは、ドラッグ&ドロップ操作のサポートを提供しなければなりません。

モデルでサポートされているアクションは、QAbstractItemModel::supportedDropActions()関数を再実装することで指定できます。
たとえば、コピーして移動する操作は、次のコードで可能になっています。

Qt::DropActions DragDropListModel::supportedDropActions() const
{
    return Qt::CopyAction | Qt::MoveAction;
}

Qt::DropActionsの値の任意の組み合わせを挙げることができるが、このモデルは、それらをサポートするために書き込まれる必要があります。
たとえば、Qt::MoveActionが正しくリストモデルで使用可能にするために、モデルは、直接またはその基底クラスから実装を継承することによって、QAbstractItemModel::removeRows()の実装を提供する必要があります。

Enabling drag and drop for items

モデルは、アイテムをドラッグすることができるビューに知らせます、そして適切なフラグを提供するためにQAbstractItemModel::flags()関数を再実装することにより、ドロップを受け入れます。例えば、QAbstractListModelに基づく単純なリストを提供したモデルは、ドラッグを有効にすることができますし、返されるフラグは、Qt::ItemIsDragEnabledとQt::ItemIsDropEnabledの値が含まれていることを保証することにより、アイテムごとに、ドロップします。

Qt::ItemFlags DragDropListModel::flags(const QModelIndex &index) const
{
    Qt::ItemFlags defaultFlags = QStringListModel::flags(index);

    if (index.isValid())
        return Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | defaultFlags;
    else
        return Qt::ItemIsDropEnabled | defaultFlags;
}

アイテムは、モデルの最上位レベルにドロップことができることに注意してください、しかしドラッグは、単に妥当なアイテムが有効になっています。
モデルがQStringListModelから派生しているので上記のコードでは、我々は、flags()関数の実装を呼び出すことにより、フラグのデフォルト設定を得ます。

Encoding exported data

データのアイテムをドラッグ・アンド・ドロップ操作でモデルからエクスポートされた場合、それらは、一つ以上のMIMEタイプに応じた適切なフォーマットにエンコードされます。
モデルは、彼らが標準のMIMEタイプのリストを返すQAbstractItemModel::mimeTypes()関数を、実装し直すことでアイテムを供給するために使用できるMIMEタイプを宣言します。
たとえば、プレーンテキストのみを提供していたモデルは、以下の実装を提供します:

QStringList DragDropListModel::mimeTypes() const
{
    QStringList types;
    types << "application/vnd.text.list";
    return types;
}

モデルはまた、アドバタイズ形式でデータをエンコードするためにコードを提供しなければなりません。これは他のドラッグのように、QMimeDataオブジェクトを提供し、操作をドロップするようにQAbstractItemModel::mimeData()関数を実装することによって達成されます。
次のコードでは、インデックスの特定のリストに対応するデータの各アイテムは、プレーンテキストとしてエンコードとQMimeDataオブジェクトに格納されている方法を示しています。

QMimeData *DragDropListModel::mimeData(const QModelIndexList &indexes) const
{
    QMimeData *mimeData = new QMimeData();
    QByteArray encodedData;

    QDataStream stream(&encodedData, QIODevice::WriteOnly);

    foreach (const QModelIndex &index, indexes) {
        if (index.isValid()) {
            QString text = data(index, Qt::DisplayRole).toString();
            stream << text;
        }
    }

    mimeData->setData("application/vnd.text.list", encodedData);
    return mimeData;
}

モデルインデックスのリストが関数に供給されるため、このアプローチは十分に一般的な階層的および非階層的なモデルの両方で使用されます。
なお、カスタムデータ型は、メタオブジェクトとして宣言する必要があり、そのストリーム演算子は、彼らのために実装する必要があります。
詳細については、QMetaObjectクラスの説明を参照してください。

Inserting dropped data into a model

その方法は、任意の与えられたモデルが扱えるドロップされたデータのタイプ(リスト、テーブル、またはツリー)とその内容がユーザに提示される可能性のある方法の両方に依存します。
一般的に、ドロップされたデータを適応させるために取られたアプローチは、ほとんどのモデルの基礎となるデータの格納に合ったものでなければなりません。
異なるタイプのモデルは、異なる方法でドロップされたデータを処理する傾向があります。
リストテーブルモデルは、データのアイテムが格納されたフラットな構造を提供します。結果として、データがビュー内の既存のアイテムにドロップされたとき、彼らは新しい行(および列)を挿入することができる、またはそれらが供給されたデータの一部を使用してモデルのアイテムの内容を上書きすることができます。
ツリーモデルは、多くの場合、それらの基礎となるデータ・ストアに新しいデータを含む子アイテムを追加することができますので、ユーザーに関する限りより予想通り機能します。
ドロップされたデータは、QAbstractItemModel::dropMimeData()でモデルの再実装によって処理されます。
例えば、単純な文字列のリストを処理するモデルは、モデル(無効なアイテムにすなわち、)の最上位レベルにドロップしたデータが別々に、既存のアイテムの上にドロップ処理する実装を提供することができます。
モデルはQAbstractItemModel::canDropMimeData()を再実装することによって、特定のアイテムにドロップする、またはドロップされたデータに応じて禁止することができます。
モデルは最初の操作が作用されるべきであることを確認する必要があり、供給されたデータを使用することができる形式であり、モデル内でのその先が有効であること:

bool DragDropListModel::canDropMimeData(const QMimeData *data,
    Qt::DropAction action, int row, int column, const QModelIndex &parent)
{
    Q_UNUSED(action);
    Q_UNUSED(row);
    Q_UNUSED(parent);

    if (!data->hasFormat("application/vnd.text.list"))
        return false;

    if (column > 0)
        return false;

    return true;
}
bool DragDropListModel::dropMimeData(const QMimeData *data,
    Qt::DropAction action, int row, int column, const QModelIndex &parent)
{
    if (!canDropMimeData(data, action, row, column, parent))
        return false;

    if (action == Qt::IgnoreAction)
        return true;

供給されたデータは、プレーンテキストでない場合、またはドロップに指定された列番号が無効である場合、単純な1つの列のストリング・リスト・モデルは、失敗を示すことができます。
それが既存のアイテムの上にドロップされるかどうかに応じて、モデルに挿入されるデータは、違って扱われます。
この単純な例では、リストの最初のアイテムの前に、最後のアイテムの後に、既存のアイテム間のドロップを許可します。
ドロップが発生するとき、親アイテムに対応するモデルインデックスは、いずれかのドロップアイテムに発生したことを示す、有効であるか、またはそれは、ドロップがモデルの最上位レベルに対応するビューのどこかで発生したことを示す、無効になります。

    int beginRow;

    if (row != -1)
        beginRow = row;

親インデックスが有効かどうかに関係なく、アイテムをモデルに挿入するためにそれを使うことができるかどうか見るために供給される行番号を、我々はまず最初に調べます。

    else if (parent.isValid())
        beginRow = parent.row();

親モデルのインデックスが有効である場合は、ドロップがアイテムに発生します。
この単純なリストのモデルでは、我々は、アイテムの行番号を確認し、モデルの最上位レベルにドロップアイテムを挿入するために、その値を使用します。

    else
        beginRow = rowCount(QModelIndex());

ドロップがビュー内の他の場所で発生し、行番号が使用不可能であるとき、私たちはモデルの最上位レベルにアイテムを追加します。
階層モデルでは、ドロップアイテムで発生したときに、そのアイテムの子としてモデルに新しいアイテムを挿入することがよいでしょう。
ここに示した単純な例では、モデルが一つだけのレベルを持っています、従って、このアプローチは適切でありません。

Decoding imported data

dropMimeData()の各実装は、データを復号化し、モデルの基礎となるデータ構造に挿入する必要があります。
単純な文字列のリストモデルについては、エンコードされたアイテムは、デコード化され、QStringListにストリーミングすることができます。

    QByteArray encodedData = data->data("application/vnd.text.list");
    QDataStream stream(&encodedData, QIODevice::ReadOnly);
    QStringList newItems;
    int rows = 0;

    while (!stream.atEnd()) {
        QString text;
        stream >> text;
        newItems << text;
        ++rows;
    }

ストリングは、その後、基礎となるデータ・ストアに挿入することができます。
一貫性を保つため、このモデル独自のインターフェイスを介して行うことができます。

    insertRows(beginRow, rows, QModelIndex());
    foreach (const QString &text, newItems) {
        QModelIndex idx = index(beginRow, 0, QModelIndex());
        setData(idx, text);
        beginRow++;
    }

    return true;
}

モデルは通常、QAbstractItemModel::insertRows()とQAbstractItemModel::setData()関数の実装を提供する必要があることに注意してください。

Proxy Models

モデル/ビュー​​フレームワークでは、単一のモデルが提供するデータのアイテムは、ビューの任意の数で共有することができ、これらのそれぞれは、場合によっては全く異なる方法で同一の情報を表すことができます。カスタムビューとデリゲートは、同じデータの根本的に異なる表現を提供するための効果的な方法です。
しかし、アプリケーションは多くの場合、同じデータ(例えばアイテムのリストの上の違って分類されたビュー)の処理されたバージョンの上へ従来のビューを提供する必要があります。
それは、ソートやビューの内部機能などの操作をフィルタリング実行するために適切と思われます、しかしこのアプローチは、複数のビューは、このような潜在的に高価な操作の結果を共有することはできません。
別のアプローチは、モデル自体の中に並べ替えを含み、各ビューは、最新の処理動作に応じて編成されたデータのアイテムを表示する必要があり、同様の問題につながります。

この問題を解決するために、モデル/ビューフレームワークは、個々のモデルとビューの間で供給された情報を管理するためのプロキシモデルを使用しています。
プロキシモデルは、ビューの観点から普通のモデルのようにふるまうコンポーネントで、そのビューのためのソースモデルからデータにアクセスします。
多くのプロキシモデルは、モデル/ビューフレームワークによって使用されるシグナルとスロットを確保し、各ビューに関係なく、適切に更新されることを確実とし、それ自体と、ソース・モデルとの間に配置されています。

Using proxy models

プロキシモデルは、既存のモデルとビューの任意の数の間に挿入することができます。
Qtは、通常、インスタンス化され、直接使用される標準的なプロキシモデル、QSortFilterProxyModelが供給されていますが、カスタムのフィルタリングやソートの動作を提供するためにサブクラス化することができます。

QSortFilterProxyModelクラスは、以下のように使用することができます。

    QSortFilterProxyModel *filterModel = new QSortFilterProxyModel(parent);
    filterModel->setSourceModel(stringListModel);

    QListView *filteredView = new QListView;
    filteredView->setModel(filterModel);

プロキシモデルはQAbstractItemModelから継承されるので、それらは、ビューの任意の種類に接続することができ、ビュー間で共有することができます。
それらはまた、パイプライン構成で他のプロキシモデルから得られた情報を処理するために使用することができます。
QSortFilterProxyModelクラスは、インスタンス化の用途に直接使用するように設計されています。
もっと専門的なプロキシモデルは、このクラスをサブクラス化し、必要な比較演算を実装することによって作成することができます。

Customizing proxy models

一般的に、プロキシモデルで使用される処理の種類は、プロキシモデル内の別の場所のいずれかにソースモデルで元の場所からのマッピングにデータの各アイテムを含んでいます。
一部のモデルでは、いくつかのアイテムは、プロキシモデルには対応する位置を持っていないかもしれません。これらのモデルは、プロキシモデルをフィルタリングしています。

ビューは、プロキシモデルによって提供されるモデルインデックスを使用してアイテムにアクセスし、これらは、ソースモデルまたはそのモデルの元のアイテムの場所に関する情報が含まれていません。
QSortFilterProxyModelは、ソースモデルからのデータがビューに供給される前にフィルターに通されるのを可能にして、そのうえソースモデルの内容を予めソートされたデータとしてビューに供給することができます。

Custom filtering models

QSortFilterProxyModelクラスはかなり汎用性のあるフィルタリングモデルを提供し、これは、一般的なさまざまな状況で使用することができます。
高度なユーザーの場合、QSortFilterProxyModelをサブクラス化することができ、カスタムフィルタを可能にするメカニズムを提供することが実現されます。

QSortFilterProxyModelのサブクラスは、2つの仮想関数を再実装することができます、それは
プロキシモデルからモデルインデックスを要求または使用されているときはいつでもが呼び出されます。
filterAcceptsColumn()はソースモデルの一部から特定の列をフィルタリングするために使用されます。

QSortFilterProxyModelにおける上記の機能のデフォルトの実装では、すべてのアイテムがビューに渡されることを保証するためにtrueを返します。これらの関数の実装では、個々の行と列をフィルタする場合はfalseを返す必要があります。

Custom sorting models

QSortFilterProxyModelインスタンスは、ソース・モデルの構造を変更することなく、ビューに露出するアイテムのソートされた階層構造を可能にする、ソースモデル内のアイテムとプロキシモデルにおけるそれらの間のマッピングを設定するにはQtの組み込みのqStableSort()関数を使用します。
カスタムのソート動作を提供するために、カスタム比較を実行するためにlessThan()関数を再実装します。

Model Subclassing Reference

モデルサブクラスはQAbstractItemModelベースクラスで定義された仮想関数の多くの実装を提供する必要があります。
ビューにアイテムの単純なリストか、テーブルか複雑な階層を供給するかどうか、実装される必要があるこれらの機能の数は、モデルのタイプによって異なります 。
QAbstractListModelとQAbstractTableModelから継承するモデルは、それらのクラスによって提供される機能のデフォルトの実装を利用することができます。

ツリー状の構造内のデータのアイテムを公開するモデルは、QAbstractItemModelにおける仮想関数の多くの実装を提供する必要があります。
モデルのサブクラスで実装される必要がある機能は、三つのグループに分けることができます。

すべてのモデルは、モデルの寸法を照会するビューとデリゲートを可能にするための機能を実装する必要があります、そしてアイテムを調べ、データを取得します。
ナビゲーションとインデックスの作成:階層モデルは、ビューが公開されている構造のようなツリーをナビゲートするために呼び出すことができる機能を提供する必要があります、そしてアイテムのモデルインデックスを取得します。

ドラッグアンドドロップのサポートとMIMEタイプの取り扱い:
モデルは、内部と外部のドラッグアンドドロップ操作が実行される方法を制御する機能を継承します。
これらの関数は、データ項目がMIMEタイプの観点から説明されることを可能にします、それは
そのほかのコンポーネントとアプリケーションが理解することができます。

Item data handling

モデルは、アクセスの様々なレベルを変化させてデータに提供することができます:
彼らは、単純な読み取り専用のコンポーネントとすることができる、いくつかのモデルは、サイズ変更操作サポートすることができ、その他のアイテムを編集することを可能にすることができます。

Read-Only access

モデルによって提供されるデータへの読み取り専用アクセスを提供するために、以下の機能は、モデルのサブクラスで実装する必要があります。

flags()

モデルによって提供される各項目についての情報を得るために、他のコンポーネントによって使用されます。
多くのモデルでは、フラグの組み合わせは、Qt::ItemIsEnabled と Qt::ItemIsSelectable を含める必要があります。

data()

ビューとデリゲートにアイテムデータを供給するために使用します。
ビューとデリゲートにアイテムデータを供給するために使用します。一般的に、モデルは、Qt::DisplayRoleとアプリケーション固有のユーザーのロールにデータを提供する必要があるだけです、しかしQt::ToolTipRole、Qt::AccessibleTextRole、そしてQt::AccessibleDescriptionRoleのためのデータを提供することも重要です。
各ロールに関連付けられているタイプについては、Qt::ItemDataRoleの列挙型のドキュメントを参照してください。

headerData()

そのヘッダに表示するための情報を持つビューを提供します。
情報は、ヘッダ情報のみを表示できるビューによって取得されます。

rowCount()

モデルによって公開されているデータの行数を提供します。

これらの4つの関数は、リストモデル(QAbstractListModelサブクラス)とテーブルモデル(QAbstractTableModelサブクラス)を含むモデルのすべての種類、で実装する必要があります。
また、以下の機能をQAbstractTableModelとQAbstractItemModelの直接のサブクラスで実装する必要があります。

columnCount()

モデルによって公開されているデータの列の数を提供します。これはすでにQAbstractListModelに実装されているため、リストのモデルは、この機能を提供していません。
Editable items
編集可能なモデルは、データのアイテムを変更可能にし、また、行および列を挿入し、除去することができる機能を提供します。編集を有効にするには、以下の機能を正しく実装する必要があります。

flags()

各アイテムにフラグの適切な組み合わせを返す必要があります。具体的には、この関数によって返される値は、読み取り専用モデル内のアイテムに適用される値に加えて、Qt::ItemIsEditable を含める必要があります。

setData()

指定されたモデルインデックスに関連するデータのアイテムを変更するために使用します。ユーザインタフェース要素によって提供されるユーザ入力を受け入れることができるようにするには、この関数はQt::EditRoleに関連付けられたデータを処理する必要があります。
また、実装はQt::ItemDataRoleによって指定されたロールの多くの異なる種類に関連付けられたデータを受け入れることができます。データのアイテムを変更した後、モデルは、変更を他のコンポーネントに通知するdataChanged()シグナルを発する必要があります。

setHeaderData()

水平方向と垂直方向のヘッダー情報を変更するために使用します。データのアイテムを変更した後、モデルは変更を他のコンポーネントに通知するheaderDataChanged()シグナルを発する必要があります。

Resizable models

モデルのすべての種類は、行の挿入および削除をサポートすることができます。テーブルモデルおよび階層モデルも、列の挿入と削除をサポートすることができます。

モデルの寸法の変更が起こった前後両方を、他のコンポーネントに通知することが重要です。

結果として、以下の関数は、モデルのサイズが変更されるように実施することができます、しかし実装は、適切な関数を取り付けビューやデリゲートに通知するために呼び出されていることを確認する必要があります。

insertRows()

モデルのすべての種類に新しい行とデータの項目を追加するために使用します。実装は任意の基礎となるデータ構造に新しい行を挿入する前にbeginInsertRows()を呼び出し、その直後endInsertRows()を呼び出す必要があります。

removeRows()

彼らがさまざまなモデルのすべてのタイプから含まれるデータの列とアイテムを削除するために使用します。
行が任意の基礎となるデータ構造から削除される前に実装は、beginRemoveRows()を呼び出し、その直後にendRemoveRows()を呼び出す必要があります。

insertColumns()

テーブルモデルと階層モデルに新たな列とデータのアイテムを追加するために使用します。
実装は任意の基礎となるデータ構造に新しい列を挿入する前にbeginInsertColumns()を呼び出し、その直後にendInsertColumns()を呼び出す必要があります。

removeColumns()

彼らはテーブルモデルおよび階層モデルから含まれているデータの列とアイテムを削除するために使用します。
呼び出し列が任意の基になるデータ構造から削除する前に実装は、beginRemoveColumns()を呼び出し、その直後にendRemoveColumns()を呼び出す必要があります。

操作が成功した場合、一般的に、これらの関数はtrueを返す必要があります。しかしながら、操作は部分的にしか成功しなかった場合があるかもしれません。例えば、指定された行数を挿入するよりも少ない場合です。
このような場合には、モデルは、状況に対処するために取り付けられているすべてのコンポーネントに失敗を有効に示すためにfalseを返します。
リサイズAPIの実装で呼び出された関数によって放出されるシグナルは、コンポーネントのすべてのデータが使用できなくなる前に行動を取るチャンスを与えます。
インサートと削除の操作のカプセル化は、開始と終了機能も正しく永続的なモデルのインデックスを管理するためのモデルを可能にします。
通常、開始および終了関数は、モデルの基礎となる構造の変更に関する他のコンポーネントに通知することが可能です。
モデルの構造への、より複雑な変更、おそらく内部の再編成を伴うか、データの並べ替えのために、取り付けられているすべてのビューが更新されるようにするにはのlayoutChanged()シグナルを発することが必要です。

Lazy population of model data

Lazy population of model dataは効果的にそれが実際のビューで必要とされるまで、モデルに関する情報の要求を延期することができます。
一部のモデルでは、リモート・ソースからデータを取得する必要があり、またはデータが編成される方法についての情報を得るために時間のかかる操作を実行する必要があります。ビューは、一般に正確にモデルデータを表示するためにできるだけ多くの情報を要求するので、データの不必要なフォローアップ要求を低減するために、それらに返される情報の量を制限するのに有用であり得ます。
与えられたアイテムの子の数を見つけることは高価な操作である階層的なモデルでは、モデルのrowCount()を実装が必要なときのみ呼び出されることを確実にするために有用です。
このような場合には、hasChildren()関数は、ビューの子供の存在を確認するための安価な方法を提供するために実装することができます、QTreeViewの場合には、その親アイテムに適切な装飾を描画します。

hasChildren()の再実装が真実であるか間違って戻るかどうかにかかわらず、見解が何人の子供たちがいるかについて知るためにrowCount()を呼ぶことが必要でない場合があります。

hasChildren()の再実装がtrueまたはfalseを返すかどうかにかかわらず、ビューが何人の子供たちがいるかについて知るためにrowCount()を呼ぶことが必要でない場合があります。

例えば、もしそれらを示すために、親アイテムが拡張されなかったならば、QTreeViewは、いくつの子供がいるかを知っている必要はありません。
多くのアイテムが子を持っていることがわかっている場合は、hasChildren()が無条件にtrueを返すように再実装することは時々取る有用なアプローチです。

これは、可能な限り高速にイニシャルpopulation of model dataを作りながら、各アイテムは後で子供たちのために検査することができるようになります。

唯一の欠点は、ユーザーが存在しない子アイテムを表示しようとして、子供のいないアイテムがいくつかのビューで間違って表示してしまうかもしれないことです。
Navigation and model index creation
階層モデルは、ビューに、彼らが公開ツリー状の構造をナビゲートするために呼び出すと、アイテムのモデルインデックスを取得することができる機能を提供する必要があります。

Parents and children

ビューに公開された構造は、基礎となるデータ構造によって決定されるので、以下の機能の実装を提供することにより、独自のモデルインデックスを作成するのは、各モデルのサブクラスまでです。

index()

親アイテムのモデルインデックスを指定すると、この機能で、ビューとデリゲートは、そのアイテムの子にアクセスすることができます。
指定された行、列、及び親モデルインデックスに対応する有効な子アイテムが見つからない場合、QModelIndex()関数は、無効なモデルインデックスを返す必要があります。

parent()

任意の子アイテムの親に対応するモデルインデックスを提供します。
指定されたモデルのインデックスが、モデル内の最上位のアイテムに対応する場合、またはモデルに有効な親アイテムがない場合は、QModelIndex()関数で作成された空のコンストラクタの無効なモデルインデックスを返す必要があります。

上記の両方の機能を使用するcreateIndex()ファクトリ関数は、他のコンポーネントが使用するインデックスを生成します。
モデルインデックスは、後に、それに対応するアイテムと再関連付けることができることを確実にするために、モデルはこの機能にいくつかのユニークな識別子を供給するのは普通です。

Drag and drop support and MIME type handling

モデル/ビュークラスは、多くのアプリケーションのために十分であるデフォルトの動作を提供し、ドラッグアンドドロップ操作をサポートしています。

しかし、それらがコピーされるか、またはデフォルトで移動されて、どのようにそれらが既存のモデルに挿入されるかどうかにかかわらず、また、ドラッグ&ドロップ操作の間にアイテムがエンコードされる方法をカスタマイズすることが可能です。

また、便利なビュークラスは密接にすることを、既存の開発者が期待従うべき特殊な動作を実装します。コンビニエンスビューセクションは、この動作の概要を説明します。

MIME data

デフォルトでは、内蔵のモデルとビューは、モデルのインデックスに関する情報を周囲に渡すために内部のMIMEタイプ(アプリケーション/ X-qabstractitemmodelのデータリスト)を使用します。
これはアイテムのリストのデータを指定します、そして、各々のアイテムの行と列数と各々のアイテムがサポートしているロールに関する情報を含みます。
このMIMEタイプを使用してエンコードされたデータは、順番に並べられるアイテムを含んでいるQModelIndexListでQAbstractItemModel::mimeData()を呼ぶことによって得られることができます。
ドラッグ・ドロップをサポートするカスタムモデルを実装するするとき、以下の機能を実装し直すことで、特殊な形式のデータのアイテムをエクスポートすることが可能です。

mimeData()

この機能は、デフォルトのアプリケーション/ X-qabstractitemmodelデータリスト内のMIMEタイプ以外の形式でデータを返すように実装することができます。
サブクラスは、基本クラスからデフォルトQMimeDataオブジェクトを取得し、追加のフォーマットでそれにデータを追加することができます。

多くのモデルのために、text/plainでかつimage/ pngなどのMIMEタイプに代表される一般的な形式でアイテムの内容を提供することが便利です。
画像や、色やHTML文書を簡単にQMimeData::setImageData()、QMimeData::setColorData()、およびQMimeData::setHtml()関数に追加することができることに注目してください。

Accepting dropped data

ドラッグ&ドロップ操作がビュー上で実行されると、基礎となるモデルは、それがサポートしており、それが受け入れることができるMIMEタイプ操作のタイプを決定するために照会されます。
この情報はQAbstractItemModel::supportedDropActions()とQAbstractItemModel::mimeTypes()関数によって提供されています。
QAbstractItemModelのサポートによって提供される実装がオーバーライドしないモデルは、操作やアイテムのデフォルトの内部MIMEタイプをコピーします。
順番に並べられたアイテムのデータがビュー上にドロップされると、データはQAbstractItemModel::dropMimeData()を使用して、現在のモデルに挿入されています。
この関数のデフォルトの実装では、モデル内の任意のデータを上書きすることはありません。
その代わりに、それは、データのいずれかのアイテムの兄弟として、またはそのアイテムの子としてアイテムを挿入しようとします。
MIMEタイプが内蔵されたQAbstractItemModelの既定の実装を利用するには、新しいモデルは以下の機能の実装を提供する必要があります:

insertRows()

これらの機能は自動的にQAbstractItemModel::dropMimeData()が提供する既存の実装を使用して、モデルに新しいデータを挿入することを可能にします。
insertColumns()

setData()

新しい行と列を、アイテムに追加できるようにします。
setItemData()
この関数は、新しいアイテムを追加するためのより効率的なサポートを提供します。

他の形式のデータを受け入れるには、これらの機能を実装する必要があります。

supportedDropActions()

モデルが受け入れるドラッグ&ドロップ操作のタイプを示して、ドロップアクションの組み合わせを返すために使用されます。

mimeTypes()

デコードされてモデルで扱うことができるMIMEタイプのリストを返すために使用します。
一般的に、モデルへの入力でサポートされているMIMEタイプは、外部コンポーネントで使用するためにデータをエンコードするときに使用できるものと同じです。

dropMimeData()

ドラッグ&ドロップ操作で転送されたデータの実際のでコーディングを行います、モデルでそれが設定される場所を決定し、そして、必要な場合に新しい行と列を挿入します。

各々のモデルで公開されるデータの必要条件に、この関数がサブクラスで実装される方法は、依存します。

列またはコラムを付け加えるか、削除することによってdropMimeData()関数の実装がモデルの寸法を変えるならば、または、データのアイテムが修正されるならば、気遣いは関連するすべてのシグナルが放出されるように注意する必要があります。
モデルが一貫して機能することを確実とするために、単にサブクラス(例えばssetData()、insertRows()とinsertColumns())で他の機能の再実装を呼び出すことは、役に立ちます。
ドラッグ操作がきちんと働くことを確実とするために、モデルからデータを削除する以下の機能を再実装することが重要です。
• removeRows()
• removeRow()
• removeColumns()
• removeColumn()

アイテムビューのドラッグ&ドロップの詳細については、Using drag and drop with item viewsを参照してください。

Convenience views

コンビニエンスビュー(QListWidget、QTableWidget、およびQTreeWidget)は、デフォルトのドラッグを無効にして、多くのアプリケーションに適した柔軟性に劣るが、より自然な動作を提供するための機能をドロップします。
それは、データが転送されると、既存の内容を置き換え、QTableWidgetのセルにデータをドロップする方が一般的ですので、例えば、基礎となるモデルは、対象項目のデータを設定しますではなく、モデルに新しい行と列を挿入します。
ドラッグ・アンド・コンビニエンス・ビューのドロップの詳細については、あなたは、ドラッグの使用を参照してくださいすることができ、アイテムビューでドロップします。

Performance optimization for large amounts of data

canFetchMore()関数のチェックは親が利用できるより多くのデータを持っているし、それに応じて、trueまたはfalseを返す場合。

fetchMore()関数は、指定した親に基づいてデータをフェッチします。

両方のこれらの機能はQAbstractItemModelに取り込むインクリメンタルデータを含むデータベースクエリ、例えば、組み合わせることができます。

私達は、取り出されるより多くのデータがあるかどうかを示すcanFetchMore()、および必要であるようにモデルを取り込むfetchMore()を再実装します。

もう一つの例はダイナミックに取り付けられた木モデルです、そこで、我々は木モデルの枝が拡大されるfetchMore()を再実装します。

fetchMore()の再実装が行をモデルに加えるならば、あなたはbeginInsertRows()とendInsertRows()を呼ぶ必要があります。

また、canFetchMore()とfetchMore()の両方とも、彼らのデフォルトの実装はfalseを返し、何もしないように実装する必要があります。

The Model/View Classes

これらのクラスは(モデル内の)基礎となるデータは、(ビューの)データがユーザによって提示され、操作されている方法とは別に保持されたモデル/ビューデザインパターンを使用しています。

QAbstractItemDelegate

モデルから編集データアイテムを表示するために使用

QAbstractItemModel

アイテムモデルクラスのための抽象インタフェース

QAbstractItemView

アイテムビュークラスの基本的な機能

QAbstractListModel

サブクラス化できる抽象モデルは1次元リストのモデルを作成します

QAbstractProxyModel

プロキシアイテムの並べ替えを行うことができ機種、フィルタリングまたは他のデータ処理タスクの基本クラス

QAbstractTableModel

テーブルモデルを作成するためにサブクラス化できる抽象モデル

QColumnView

カラム表示のモデル/ビューの実装

QDataWidgetMapper

ウィジェットへのデータモデルのセクションとの間のマッピング

QFileSystemModel

ローカルファイルシステムのデータモデル

QHeaderView

アイテムビューのヘッダ行やヘッダ列

QIdentityProxyModel

そのソースモデルが変更されていないプロキシ処理

QItemDelegate

モデルからのデータアイテムの表示と編集機能

QItemEditorCreator

QItemEditorCreatorBaseをサブクラス化せずにアイテムエディタの作成者・ベースを作成することが可能になります

QItemEditorCreatorBase

新しいアイテムエディタのクリエイターを実装するときにサブクラス化されなければならない抽象基底クラス

QItemEditorFactory

ビューとデリゲートで編集アイテムデータ用のウィジェット

QItemSelection

モデルの選択したアイテムについての情報を管理します

QItemSelectionModel

ビューの選択したアイテムを追跡します

QItemSelectionRange

モデル内の選択されたアイテムの範囲についての情報を管理します

QListView

モデルへのリストまたはアイコン表示

QListWidget

アイテムベースリストウィジェット

QListWidgetItem

QListWidgetアイテムビュークラスで使用するためのアイテム

QModelIndex

データモデル内のデータを検索するために使用されます

QPersistentModelIndex

データモデル内のデータを検索するために使用されます

QSortFilterProxyModel

別のモデルとビューの間で受け渡されるデータのソートやフィルタリングをサポート

QStandardItem

QStandardItemModelクラスで使用するためのアイテム

QStandardItemEditorCreator

可能性はQItemEditorCreatorBaseをサブクラス化することなく、ウィジェットを登録します

QStandardItemModel

カスタムデータを格納するための一般的なモデル

QStringListModel

ビューに文字列を供給するモデル

QStyledItemDelegate

モデルからのデータ項目の表示と編集機能

QTableView

デフォルトモデル/テーブルビューのビューの実装

QTableWidget

デフォルトのモデルとアイテムベーステーブルビュー

QTableWidgetItem

QTableWidgetクラスで使用するためのアイテム

QTableWidgetSelectionRange

モデルのインデックスと選択モデルを使用せずに、モデルで選択して対話する方法

QTreeView

デフォルトモデル/ツリービューのビューの実装

QTreeWidget

事前に定義されたツリーモデルを使用する、ツリービュー

QTreeWidgetItem

QTreeWidgetの便利なクラスで使用するためのアイテム

QTreeWidgetItemIterator

QTreeWidgetインスタンス内のアイテムを反復処理する方法
• Related Examples
• Dir View
• Spin Box Delegate
• Pixelator
• Simple Tree Model
• Chart
See also Item Views Puzzle Example.

/div>