Qt学習の道(53):ドラッグアンドドロップ技術の二


長い間ブログを書いていませんでしたが、この間ずっと同級生にspring-mvcのプロジェクトを手伝っていました.今日はやっと終わりましたが、会社の中でflex 4を始めなければなりません.まだ忙しいでしょう.
前回に続いて、前回ドラッグアンドドロップ技術についてお話ししましたが、今日も例です.同じく『C++GUI Programming with Qt 4,2 nd Edition』から来ています.
今回のdemoは比較的実用的である:実現したのは2つのlist間のデータの相互ドラッグである.多くのプロジェクトでは、このニーズはよくあるのではないでしょうか.次はレンガを投げて玉を引くことにしましょう.
projectlistwidget.h
   
   
   
   
  1. #ifndef PROJECTLISTWIDGET_H  
  2. #define PROJECTLISTWIDGET_H  
  3.  
  4. #include   
  5.  
  6. class ProjectListWidget : public QListWidget  
  7. {  
  8.     Q_OBJECT  
  9.  
  10. public:  
  11.     ProjectListWidget(QWidget *parent = 0);  
  12.  
  13. protected:  
  14.     void mousePressEvent(QMouseEvent *event);  
  15.     void mouseMoveEvent(QMouseEvent *event);  
  16.     void dragEnterEvent(QDragEnterEvent *event);  
  17.     void dragMoveEvent(QDragMoveEvent *event);  
  18.     void dropEvent(QDropEvent *event);  
  19.  
  20. private:  
  21.     void performDrag();  
  22.  
  23.     QPoint startPos;  
  24. };  
  25.  
  26. #endif // PROJECTLISTWIDGET_H 

projectlistwidget.cpp
   
   
   
   
  1. #include "projectlistwidget.h"  
  2.  
  3. ProjectListWidget::ProjectListWidget(QWidget *parent)  
  4.     : QListWidget(parent)  
  5. {  
  6.     setAcceptDrops(true);  
  7. }  
  8.  
  9. void ProjectListWidget::mousePressEvent(QMouseEvent *event)  
  10. {  
  11.     if (event->button() == Qt::LeftButton)  
  12.         startPos = event->pos();  
  13.     QListWidget::mousePressEvent(event);  
  14. }  
  15.  
  16. void ProjectListWidget::mouseMoveEvent(QMouseEvent *event)  
  17. {  
  18.     if (event->buttons() & Qt::LeftButton) {  
  19.         int distance = (event->pos() - startPos).manhattanLength();  
  20.         if (distance >= QApplication::startDragDistance())  
  21.             performDrag();  
  22.     }  
  23.     QListWidget::mouseMoveEvent(event);  
  24. }  
  25.  
  26. void ProjectListWidget::performDrag()  
  27. {  
  28.     QListWidgetItem *item = currentItem();  
  29.     if (item) {  
  30.         QMimeData *mimeData = new QMimeData;  
  31.         mimeData->setText(item->text());  
  32.  
  33.         QDrag *drag = new QDrag(this);  
  34.         drag->setMimeData(mimeData);  
  35.         drag->setPixmap(QPixmap(":/p_w_picpaths/person.png"));  
  36.         if (drag->exec(Qt::MoveAction) == Qt::MoveAction)  
  37.             delete item;  
  38.     }  
  39. }  
  40.  
  41. void ProjectListWidget::dragEnterEvent(QDragEnterEvent *event)  
  42. {  
  43.     ProjectListWidget *source =  
  44.             qobject_cast(event->source());  
  45.     if (source && source != this) {  
  46.         event->setDropAction(Qt::MoveAction);  
  47.         event->accept();  
  48.     }  
  49. }  
  50.  
  51. void ProjectListWidget::dragMoveEvent(QDragMoveEvent *event)  
  52. {  
  53.     ProjectListWidget *source =  
  54.             qobject_cast(event->source());  
  55.     if (source && source != this) {  
  56.         event->setDropAction(Qt::MoveAction);  
  57.         event->accept();  
  58.     }  
  59. }  
  60.  
  61. void ProjectListWidget::dropEvent(QDropEvent *event)  
  62. {  
  63.     ProjectListWidget *source =  
  64.             qobject_cast(event->source());  
  65.     if (source && source != this) {  
  66.         addItem(event->mimeData()->text());  
  67.         event->setDropAction(Qt::MoveAction);  
  68.         event->accept();  
  69.     }  
  70. }  

構造関数から見始めます.Qtの多くのコンポーネントはドラッグ&ドロップを受け入れることができますが、デフォルトの動作は許可されていません.そのため、コンストラクション関数ではsetAcceptDrops(true)を呼び出します.で行ないます.
mousePressEvent()関数では、マウスの左クリックを検出し、そうであれば現在の位置を記録します.この関数は、通常の動作を実現するために、最後にシステムが持参した処理関数を呼び出す必要があることに注意されたい.これは、いくつかの書き換えイベントの関数で注意する必要があります.
そしてmouseMoveEvent()イベントを書き換えました.コードを見てみましょう.
   
   
   
   
  1. void ProjectListWidget::mouseMoveEvent(QMouseEvent *event)  
  2. {  
  3.     if (event->buttons() & Qt::LeftButton) {  
  4.         int distance = (event->pos() - startPos).manhattanLength();  
  5.         if (distance >= QApplication::startDragDistance())  
  6.             performDrag();  
  7.     }  
  8.     QListWidget::mouseMoveEvent(event);  

ここで、マウスをドラッグしながら左ボタン(ifの中身)を押し続けると、manhattanLength()の値が計算されると判断します.文字通り、これは「マンハッタンの長さ」です.これはどういう意味ですか.見てみましょうpos()-startPosとは何ですか.MousePressEvent()関数では、マウスで押した座標をstartPosとして記録し、event.pos()はマウスの現在の座標です.1つの点から別の点を減算します.間違いありません.これがベクトルです.実は、マンハッタンの距離とは2点の間の距離です(なぜこの奇妙な名前を呼んだのか、百科事典を調べてみればわかります!)、つまりこのベクトルの長さです.次の判断は、QLPplication::startDragDistance()より大きい場合、dragの操作を行います.もちろん、最後にシステムのデフォルトのマウスドラッグ関数を呼び出します.この判断の意味は、手の振れなどによるマウスのドラッグを防止することにある.ユーザーはマウスを一定の距離ドラッグしなければならない後、ドラッグ操作を望んでいると考えられます.この距離はQAPplication::startDragDistance()が提供し、この値は通常4 pxです.
performDrag()はドラッグアンドドロップ処理を開始します.これをparentとするQdragオブジェクトを作成しました.QdragはQMimeDataを用いてデータを格納する.たとえば、QMimeData::setText()関数を使用して、文字列をtext/plainタイプのデータとして格納します.QMimeDataには、URL、色などのデータを格納するための多くの関数が用意されています.Qdrag::setPixmap()を使用すると、ドラッグが発生したときのマウスのスタイルを設定できます.Qdrag::exec()は、ユーザが操作を完了するかキャンセルするまでドラッグ操作をブロックします.パラメータとして異なるタイプの動作を受け入れ、戻り値は実際に実行される動作です.これらの動作のタイプはQt::CopyAction,Qt::MoveAction,Qt::LinkActionである.戻り値には、Qt::IgnoreActionを追加して、ユーザーがドラッグアンドドロップをキャンセルしたことを示す3つのアクションがあります.これらのアクションは、ドラッグ&ドロップ元のオブジェクトが許可するタイプ、目的のオブジェクトが受け入れるタイプ、ドラッグ&ドロップ時に押すキーボードキーに依存します.exec()呼び出し後、Qtはドラッグ&ドロップオブジェクトが不要なときにdeleteを削除します.
ProjectListWidgetは、ドラッグイベントを発行するだけでなく、同じアプリケーション内の異なるProjectListWidgetオブジェクトのデータを受け入れることができます.dragEnterEvent()では、event->source()を使用して、ドラッグ&ドロップデータが同じタイプのオブジェクトから来て、同じアプリケーションから来た場合はポインタを返し、そうでない場合はNULLを返します.qobject_を使用しますcastマクロはポインタをProjectListWidget*タイプに変換し、Qt::MoveActionタイプのドラッグを受け入れるように設定します.
dragMoveEvent()はこの関数と同じコードを持っています.ドラッグ移動のコードを書き換える必要があるからです.
最後にdropEvent()関数では、QdragのmimeDataデータを取り出し、addItem()を現在のリストに追加するように呼び出します.
これにより、比較的完全なドラッグアンドドロップのコードが完了します.
ドラッグアンドドロップ技術はQtの強力な技術ですが、データに関係のない同じコンポーネントでドラッグするには、mouse eventを簡単に実現するだけで十分かもしれません.具体的には自分で考えてみましょう.