Skip to content

Instantly share code, notes, and snippets.

@propella
Created July 3, 2011 06:11
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save propella/1061993 to your computer and use it in GitHub Desktop.
Save propella/1061993 to your computer and use it in GitHub Desktop.
Qt drag and drop test
* Qt その4 ドラッグアンドドロップ
- いろいろな色の名前が書いてあるリストがある。
- ドラッグドロップによって順番を変える。
こういうのを作ります。Qt 標準の QStringListModel を使えば数行で出来てしまうのですが、Qt の設計を味わうためにわざわざモデルを作り直してみます。
メインプログラムはこんな感じ。これからモデルの MyModel を追加します。
>|c|
// main.cpp
#include "MyModel.h"
int main(int argc, char **argv)
{
QApplication app(argc, argv);
// 色の名前の文字列リストを作り、MyModel に入れる。
QStringList colors;
colors << "Red" << "Orange" << "Yellow" << "Green" << "Blue";
MyModel model(colors);
// リストと、参考までにテーブルを作って QSplitter の中に並べる。
QSplitter widget;
QListView list(&widget);
QTableView table(&widget);
// リストとテーブルにモデルを登録する。
list.setModel(&model);
table.setModel(&model);
// リストをドラッグアンドドロップ可能にする。
list.setSelectionMode(QAbstractItemView::ExtendedSelection);
list.setDragEnabled(true);
list.setAcceptDrops(true);
list.setDropIndicatorShown(true);
list.setDragDropMode(QAbstractItemView::InternalMove);
// 表示
widget.show();
return app.exec();
}
||<
MyModel に実装するメソッドは次のヘッダファイルのようになります。
>|c|
// MyModel.h
#include <QtGui>
// QAbstractListModel のサブクラスを定義する。
class MyModel : public QAbstractListModel
{
// おまじない: Qt 拡張のシグナルやスロットが使えるようになる。
Q_OBJECT
public:
// コンストラクタは QStringList を受け取る。
MyModel(const QStringList &strings, QObject *parent = 0);
// 表示に必要なメソッド。
int rowCount(const QModelIndex &parent = QModelIndex()) const;
QVariant data(const QModelIndex &index, int role) const;
// ドラッグアンドドロップに必要なメソッド。
Qt::ItemFlags flags(const QModelIndex &index) const;
bool setData(const QModelIndex &index, const QVariant &value,
int role = Qt::EditRole);
bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex());
bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex());
private:
QStringList _list;
};
||<
特に、ドラッグアンドドロップに最低限必要なのは flags(), setData(), insertRows(), removeRows() の四つです。マニュアルには dropMimeData() を実装する事と書いてあるのですが、アプリ内でデータを動かすのには大げさです。Qt のソースを読むと、デフォルトの実装でドラッグ時にモデルの removeRows()、ドロップ時に insertRows() と setData() が呼ばれるようになっているので、アプリ内ドラッグアンドドロップにはこれらのメソッドを定義すると良いと分かります。
QAbstractListModel の実装を読むと、
- insertRows() と setData() は QAbstractItemModel::decodeData() の中で、
- removeRows() は QAbstractItemView::startDrag() の中で
呼ばれています。decodeData() から setData() を読む実装が面白くて、
>|c|
bool QAbstractItemModel::setItemData(const QModelIndex &index, const QMap<int, QVariant> &roles)
{
bool b = true;
for (QMap<int, QVariant>::ConstIterator it = roles.begin(); it != roles.end(); ++it)
b = b && setData(index, it.value(), it.key());
return b;
}
||<
のようにモデルの中に存在する role を全部舐めてセットするようになっています。ここで、role というのはリスト要素の名前やアイコンなどの属性を表すEnum で、モデルに rule とインデックスを渡す事で属性の値を取って来れるようになっています。ここで例えば Javascript なんかだとオブジェクト自体を辞書として使えるので、このようにモデルによってあったり無かったりする属性を表現するのに最適なのですが、あいにく C++ のオブジェクトは柔軟ではないので Enum をキーとして使っています。
>|c|
#include "MyModel.h"
MyModel::MyModel(const QStringList &strings, QObject *parent)
: QAbstractListModel(parent)
{
_list = strings;
}
// columnCount はデフォルトの実装があるので rowCount のみ実装します。
int MyModel::rowCount(const QModelIndex &) const
{
return _list.count();
}
// DecorationRole(アイコン) EditRole(編集) DisplayRole(表示) の属性を返す。
QVariant MyModel::data(const QModelIndex &index, int role) const
{
QString str = _list.at(index.row());
QColor color = QColor(str);
if (index.column() == 0 && role == Qt::DecorationRole) return color;
if (index.column() == 0 && role == Qt::EditRole) return str;
if (index.column() == 0 && role == Qt::DisplayRole) return str;
return QVariant();
}
// 編集可能かどうかを答える。
Qt::ItemFlags MyModel::flags(const QModelIndex &index) const
{
return QAbstractItemModel::flags(index)
| Qt::ItemIsEditable
| Qt::ItemIsDragEnabled
| Qt::ItemIsDropEnabled;
}
// Qt::EditRole か Qt::DisplayRole の時にリスト更新
bool MyModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
qDebug() << role << ":" << value;
if (role == Qt::EditRole || role == Qt::DisplayRole) {
_list.replace(index.row(), value.toString());
emit dataChanged(index, index);
return true;
}
return true;
}
bool MyModel::insertRows(int row, int count, const QModelIndex &parent)
{
beginInsertRows(QModelIndex(), row, row + count - 1);
for (int r = 0; r < count; ++r) _list.insert(row, QString());
endInsertRows();
return true;
}
bool MyModel::removeRows(int row, int count, const QModelIndex &parent)
{
beginRemoveRows(QModelIndex(), row, row + count - 1);
for (int r = 0; r < count; ++r) _list.removeAt(row);
endRemoveRows();
return true;
}
||<
Qt は web 上でソースが読めるので大変便利です。
- QAbstractListModel の実装
-- http://qt.gitorious.org/qt/qt/blobs/4.7/src/corelib/kernel/qabstractitemmodel.cpp
- QAbstractItemView の実装
-- http://qt.gitorious.org/qt/qt/blobs/4.7/src/gui/itemviews/qabstractitemview.cpp
SOURCES = MyModel.cpp main.cpp
HEADERS = MyModel.h
// main.cpp
#include "MyModel.h"
int main(int argc, char **argv)
{
QApplication app(argc, argv);
// 色の名前の文字列リストを作り、MyModel に入れる。
QStringList colors;
colors << "Red" << "Orange" << "Yellow" << "Green" << "Blue";
MyModel model(colors);
// リストと、参考までにテーブルを作って QSplitter の中に並べる。
QSplitter widget;
QListView list(&widget);
QTableView table(&widget);
// リストとテーブルにモデルを登録する。
list.setModel(&model);
table.setModel(&model);
// リストをドラッグアンドドロップ可能にする。
list.setSelectionMode(QAbstractItemView::ExtendedSelection);
list.setDragEnabled(true);
list.setAcceptDrops(true);
list.setDropIndicatorShown(true);
list.setDragDropMode(QAbstractItemView::InternalMove);
// 表示
widget.show();
return app.exec();
}
#include "MyModel.h"
MyModel::MyModel(const QStringList &strings, QObject *parent)
: QAbstractListModel(parent)
{
_list = strings;
}
// columnCount はデフォルトの実装があるので rowCount のみ実装します。
int MyModel::rowCount(const QModelIndex &) const
{
return _list.count();
}
// DecorationRole(アイコン) EditRole(編集) DisplayRole(表示) の属性を返す。
QVariant MyModel::data(const QModelIndex &index, int role) const
{
QString str = _list.at(index.row());
QColor color = QColor(str);
if (index.column() == 0 && role == Qt::DecorationRole) return color;
if (index.column() == 0 && role == Qt::EditRole) return str;
if (index.column() == 0 && role == Qt::DisplayRole) return str;
return QVariant();
}
// 編集可能かどうかを答える。
Qt::ItemFlags MyModel::flags(const QModelIndex &index) const
{
return QAbstractItemModel::flags(index)
| Qt::ItemIsEditable
| Qt::ItemIsDragEnabled
| Qt::ItemIsDropEnabled;
}
// Qt::EditRole か Qt::DisplayRole の時にリスト更新
bool MyModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
qDebug() << role << ":" << value;
if (role == Qt::EditRole || role == Qt::DisplayRole) {
_list.replace(index.row(), value.toString());
emit dataChanged(index, index);
return true;
}
return true;
}
bool MyModel::insertRows(int row, int count, const QModelIndex &parent)
{
beginInsertRows(QModelIndex(), row, row + count - 1);
for (int r = 0; r < count; ++r) _list.insert(row, QString());
endInsertRows();
return true;
}
bool MyModel::removeRows(int row, int count, const QModelIndex &parent)
{
beginRemoveRows(QModelIndex(), row, row + count - 1);
for (int r = 0; r < count; ++r) _list.removeAt(row);
endRemoveRows();
return true;
}
// MyModel.h
#include <QtGui>
// QAbstractListModel のサブクラスを定義する。
class MyModel : public QAbstractListModel
{
// おまじない: Qt 拡張のシグナルやスロットが使えるようになる。
Q_OBJECT
public:
// コンストラクタは QStringList を受け取る。
MyModel(const QStringList &strings, QObject *parent = 0);
// 表示に必要なメソッド。
int rowCount(const QModelIndex &parent = QModelIndex()) const;
QVariant data(const QModelIndex &index, int role) const;
// ドラッグアンドドロップに必要なメソッド。
Qt::ItemFlags flags(const QModelIndex &index) const;
bool setData(const QModelIndex &index, const QVariant &value,
int role = Qt::EditRole);
bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex());
bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex());
private:
QStringList _list;
};
@DaikiMaekawa
Copy link

@propella Cool! I'll try to implement my widget take your codes into account. 😄
BTW, What the license is this for? I hope that's not copyleft license.

Thanks for your reply.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment