[SOLVED] Drag and Drop の動作,実装方法など



  • 特にQGraphicsView周りのD&Dイベント処理がややこしかったので覚書。
    Qt4.7.2,Windows7Homeで確認。その他の環境は未確認。
    acceptする,しないはevent->accept()をコールする,しないの意味。
    実装方法は,各クラスを継承した自作クラスで(一つのクラスのみで)D&Dを実装する方法。

    ※※※ 筆者は残念なプログラマなので信憑性もそれに順ずる。

    • QWidget:
      acceptDrops既定値はfalse。acceptDropsをtrueにしないとハンドラが呼ばれない。
      TopLevelWidgetかつ子WidgetにacceptDropsがtrueのWidgetを持つ場合は
      Window表示時に自動でsetAcceptDrops(true)される。
      dragEnterEventでacceptしないとその他のハンドラが呼ばれない。
      dragMoveEventは処理しなくても良い。
       * 実装方法:
       dragEnterEventでaccept,
       dropEventでdrop時の処理を実装して,acceptDropsをtrueにする。
    • QGraphicsView:
      acceptDrops既定値はtrue。acceptDropsをfalseにすればハンドラは呼ばれない。
      Sceneがセットされていない時はQWidgetと同じ。Sceneがセットされると以下の動作をする。
      dragEnterEventでacceptしなくてもdragMoveEventは呼ばれる。
      各ハンドラではイベントをScene用にマッピングしてSceneに渡す。
      dragMoveEventではSceneの処理結果でacceptを上書き(setAccept)する。
      結果,dragEnterEventでacceptしてもdragMoveEventで上書きされてしまう。
       * 実装方法1(ViewのみでD&Dを受付け,Sceneにイベントを渡さない):
       dragEnterEvent,dragMoveEvent,dragLeaveEvent,dropEventを,基底クラスの関数を呼ばないように上書きする。
       あとはQWidgetと同じ。
       * 実装方法2(Viewの全域でD&Dを受け付けるがSceneにもイベントを渡す):
       dragMoveEventで基底クラスの関数を呼んだ後にacceptする。
       dropEventではdrop時の処理を実装し,基底クラスの関数を呼ぶ。

    • QGraphicsScene
      各ハンドラはViewを経由して呼び出される。
      Sceneにポジションマッピング済みのイベントを受け取るので,Scene上の位置が必要ならこちらが便利。
      dragMoveEventではItemのacceptDropsがtrueの場合のみ
      ItemのdragEnterEvent,dragMoveEvent,dragLeaveEventを呼び出す。
      有効なItemが無ければeventはisAcceptedはfalseかつIgnoreActionをセットされる。
       * 実装方法1(SceneのみでD&Dを受付け,Itemにイベントを渡さない):
       dragMoveEventでacceptし,基底クラスの関数を呼び出さない。
       dropEventではdrop時の処理を実装する(基底クラスの関数は呼んでもItemにイベントは渡らない)。
       * 実装方法2(Scene全域でD&Dを受付けるがItemにもイベントを渡す):
       dragMoveEventで基底クラスの関数を呼んだ後に
       event->accept()とevent->setDropAction(Qt::DropAction)を呼ぶ。
       DropActionはCopyActionなら+付き,LinkActionなら矢印付き,MoveActionなら何もつかない四角形の
       アイコン(マウスカーソル)になる(Windows7の場合)。
       ※ Item上ではItem固有のアイコンにする場合は,dropActionがIgnoreActionの場合のみsetDropActionする。

    • QGraphicsItem
      acceptDrops既定値はfalse。
      acceptDropsをtrueにしておくと,dragEnterEvent又はdragMoveEventでevent->ignoreしない限りdropEventは呼ばれる。
       * 実装方法:
       dropEventにdrop時の処理を実装して,acceptDropsをtrueにする。

    その他:
     QGraphicsViewやQTextEditなど,acceptDropsの既定値がtrueのWidgetを置くと
     親WidgetでD&Dイベントを受け取れなくなる。
     親WidgetでD&Dイベントを処理するならこれらのWidgetにsetAcceptDrops(false)するか,
     必要に応じてevent->ignore()するように再定義した派生クラスに変える必要がある。(ignoreすると親Widgetにイベントが渡る)

    なんとなくまとめ:
     QGraphicsView関連では,特定のItemのみD&Dを受付ける場合はItemに,
     Scene上のポジションマッピングを使う場合はSceneに,
     Sceneごとゴッソリ入れ替える(特にSceneの型を変える)場合はViewに実装するのがいいのかな,と思いました(まる)



  • 動作確認しつつちょっと修正。
    調べれば調べる程ヤヤコシイ。



  • QWidgetで動作確認できるサンプル書いてみました。
    コード行が少なくなるように書いてるので,コーディングマナーはアレですが。
    .proは
    SOURCES += d_d_widget.cpp
    だけで動くはず。

    d_d_widget.cpp
    @
    #include <QtGui>

    class Widget : public QWidget
    {
    Q_OBJECT
    public:
    Widget()
    {
    setAcceptDrops(true);
    setLayout(new QVBoxLayout);
    layout()->addWidget(new QLabel(".cppファイルを受付けます。複数ファイルもOK。"));
    layout()->addWidget(editor = new QTextEdit);
    editor->setAcceptDrops(false);
    }
    protected:
    void dragEnterEvent(QDragEnterEvent *event)
    {
    if (event->mimeData()->hasUrls())
    foreach (QUrl url, event->mimeData()->urls())
    if (QFileInfo(url.toLocalFile()).suffix() == "cpp")
    {
    event->accept();
    break;
    }
    }
    void dropEvent(QDropEvent *event)
    {
    editor->clear();
    foreach (QUrl url, event->mimeData()->urls())
    if (QFileInfo(url.toLocalFile()).suffix() == "cpp")
    open(url);
    else
    error(url);
    }
    private:
    void open(QUrl url) {editor->append("○ " + QFileInfo(url.toLocalFile()).fileName());}
    void error(QUrl url) {editor->append("× ..." + url.path().right(10));}
    QTextEdit *editor;
    };

    int main(int argc, char **argv)
    {
    QApplication app(argc, argv);
    QTextCodec::setCodecForCStrings(QTextCodec::codecForLocale());
    Widget w;
    w.show();
    return app.exec();
    }

    #include "d_d_widget.moc"

    @



  • WIKIにしたらDrag and Dropだけで1カテゴリできちゃいそうですなぁ。
    とてもまとめる能力が無い。。。


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.