Qt学習の道(52):ドラッグアンドドロップ技術の一つ


ドラッグアンドドロップDrag and Dropは、DnDとも呼ばれることもあり、現代のソフトウェア開発に欠かせない技術です.これにより、アプリケーション内部、さらにはアプリケーション間で情報交換が可能となるメカニズムが提供され、オペレーティングシステムとアプリケーション間でクリップボードのコンテンツ交換が行われ、DnDの一部と考えられてもよい.
DnDは実は2つの部分から構成されています:DragとDrop.Dragはドラッグアンドドロップされるオブジェクトを「ドラッグ」し、Dropはドラッグアンドドロップされるオブジェクトを「ドロップ」し、前者は一般的にマウスを押すプロセスであり、後者はマウスを離すプロセスであり、両者の間でマウスはずっと押されている.もちろん、これは通常の状況にすぎませんが、他の状況はアプリケーションの具体的な実装によって異なります.Qtの場合、widgetはdragオブジェクトとしてもdropオブジェクトとしても、どちらでもよい.
次の例はC++GUI Programming with Qt 4,2 nd Editionから来ている.この例では、システム内のテキストファイルをドラッグ&ドロップし、ウィンドウでコンテンツを読み込むプログラムを作成します.
mainwindow.h

  
  
  
  
  1. #ifndef MAINWINDOW_H  
  2. #define MAINWINDOW_H  
  3.  
  4. #include <QtGui>  
  5.  
  6. class MainWindow : public QMainWindow  
  7. {  
  8.     Q_OBJECT  
  9.  
  10. public:  
  11.     MainWindow(QWidget *parent = 0);  
  12.     ~MainWindow();  
  13.  
  14. protected:  
  15.     void dragEnterEvent(QDragEnterEvent *event);  
  16.     void dropEvent(QDropEvent *event);  
  17.  
  18. private:  
  19.     bool readFile(const QString &fileName);  
  20.     QTextEdit *textEdit;  
  21. };  
  22.  
  23. #endif // MAINWINDOW_H 

mainwindow.cpp

  
  
  
  
  1. #include "mainwindow.h"  
  2.  
  3. MainWindow::MainWindow(QWidget *parent)  
  4.     : QMainWindow(parent)  
  5. {  
  6.     textEdit = new QTextEdit;  
  7.     setCentralWidget(textEdit);  
  8.  
  9.     textEdit->setAcceptDrops(false);  
  10.     setAcceptDrops(true);  
  11.  
  12.     setWindowTitle(tr("Text Editor"));  
  13. }  
  14.  
  15. MainWindow::~MainWindow()  
  16. {  
  17. }  
  18.  
  19. void MainWindow::dragEnterEvent(QDragEnterEvent *event)  
  20. {  
  21.     if (event->mimeData()->hasFormat("text/uri-list")) {  
  22.         event->acceptProposedAction();  
  23.     }  
  24. }  
  25.  
  26. void MainWindow::dropEvent(QDropEvent *event)  
  27. {  
  28.     QList<QUrl> urls = event->mimeData()->urls();  
  29.     if (urls.isEmpty()) {  
  30.         return;  
  31.     }  
  32.  
  33.     QString fileName = urls.first().toLocalFile();  
  34.     if (fileName.isEmpty()) {  
  35.         return;  
  36.     }  
  37.  
  38.     if (readFile(fileName)) {  
  39.         setWindowTitle(tr("%1 - %2").arg(fileName, tr("Drag File")));  
  40.     }  
  41. }  
  42.  
  43. bool MainWindow::readFile(const QString &fileName)  
  44. {  
  45.     bool r = false;  
  46.     QFile file(fileName);  
  47.     QTextStream in(&file);  
  48.     QString content;  
  49.     if(file.open(QIODevice::ReadOnly)) {  
  50.         in >> content;  
  51.         r = true;  
  52.     }  
  53.     textEdit->setText(content);  
  54.     return r;  

main.cpp

  
  
  
  
  1. #include <QtGui/QApplication>  
  2. #include "mainwindow.h"  
  3.  
  4. int main(int argc, char *argv[])  
  5. {  
  6.     QApplication a(argc, argv);  
  7.     MainWindow w;  
  8.     w.show();  
  9.     return a.exec();  

ここのコードは複雑ではありません.MainWindowでは、ウィンドウの中央にあるwidgetとしてQTextEditが使用されます.このクラスには2つのprotectedの関数があります.dragEnterEvent()とdropEvent()です.この2つの関数はQWidgetから継承されています.名前を見るとsignalだけでなく、2つのイベントであることがわかります.
コンストラクション関数ではQTextEditのオブジェクトを作成しました.デフォルトでは、QTextEditは他のアプリケーションからドラッグ&ドロップしたテキストタイプの情報を受け入れることができます.ユーザーがファイルをここにドラッグすると、ファイル名がテキストの現在の位置に挿入されます.ただし、ファイル名を挿入するだけでなく、MainWindowでdropイベントを処理したいので、QTextEditのsetAcceptDrops()関数をfalseに設定し、MainWindowのsetAcceptDrops()をtrueに設定して、MainWindowでdropイベントを処理します.
ユーザーがコンポーネントの上にオブジェクトをドラッグすると、dragEnterEvent()関数がコールバックされます.イベント処理コードでacceptProposeAction()関数を呼び出すと、ドラッグしたオブジェクトをこのコンポーネントに置くことができることをユーザーに示すことができます.既定では、コンポーネントはドラッグ&ドロップを受け入れません.このような関数を呼び出すと、Qtは自動的にカーソルでコンポーネントにオブジェクトを置くことができるかどうかをユーザーに提示します.ここでは、ウィンドウがドラッグ&ドロップを受け入れることができることをユーザーに伝えたいと思います.そこで、まずドラッグアンドドロップのMIMEタイプをチェックします.MIMEタイプはtext/uri-listであり、通常、URIのリストを記述するために使用される.これらのURIは、ファイル名であってもよいし、URLであってもよいし、他のリソース記述子であってもよい.MIMEタイプはInternet Assigned Numbers Authority(IANA)によって定義され、Qtのドラッグ・アンド・ドロップ・イベントはMIMEタイプを使用してドラッグ・アンド・ドロップ・オブジェクトのタイプを判断します.MIMEタイプの詳細については、「http://www.iana.org/assignments/media-types/.
ユーザーがコンポーネントの上にオブジェクトを解放すると、dropEvent()関数がコールバックされます.QMimeData::urls()またはQurlのリストを使用します.通常、このドラッグは1つのファイルだけを使用する必要がありますが、複数のファイルを一緒にドラッグすることも排除されません.したがって、このlistが空であるかどうかを確認し、空でない場合は最初のリストを取り出します.成立しない場合は、すぐに戻ります.最後にreadFile()関数を呼び出してファイルの内容を読み込みます.読み出し操作については後述するが,ここでは省略する.
では、これで私たちの小さなプログラムの説明が終わりました.実行して効果を見てみましょう.
Qtは、ドラッグおよび離脱についても同様の関数を提供します.dragMoveEvent()とdragLeaveEvent()ですが、ほとんどのアプリケーションでは、この2つの関数の使用率はずっと小さいです.