【保存版】Qt GUI プログラミング:ドラッグ&ドロップの基本テクニックから QDragLeaveEvent を活用した応用テクニックまでを網羅


QDragLeaveEvent::QDragLeaveEvent()は、ドラッグとドロップ操作がウィジェットから離れたときに送信されるイベントを構築します。このイベントは、ドラッグとドロップ操作の処理において重要な役割を果たし、ウィジェットがドラッグされたデータを受け入れるかどうかを決定するために使用されます。

詳細

QDragLeaveEvent は、以下の情報を提供します。

  • ドラッグとドロップ操作のドロップ動作
  • マウスカーソルの位置
  • ドラッグされたデータの種類

このイベントは、QDragEnterEvent と一連の QDragMoveEvent の後に常に送信されます。ただし、QDropEvent が送信された場合は送信されません。

使用方法

QDragLeaveEvent を処理するには、以下の手順に従います。

  1. QDragLeaveEvent のシグナルスロットに接続します。
  2. シグナルスロット内で、イベントの情報を取得します。
  3. ドラッグされたデータを受け入れるかどうかを決定します。
  4. accept() または ignore() メソッドを使用して、決定を Qt に伝えます。

void MyWidget::dragLeaveEvent(QDragLeaveEvent *event)
{
    if (event->mimeData()->hasFormat("application/x-qt/image")) {
        event->accept();
    } else {
        event->ignore();
    }
}

この例では、MyWidget ウィジェットが application/x-qt/image 形式のデータをドラッグされた場合にのみドラッグされたデータを受け入れるようにします。

  • QDragLeaveEvent は、ドラッグとドロップ操作がウィジェットから離れたことを示すためだけに使用されます。ドラッグされたデータの処理には、QDropEvent を使用する必要があります。


#include <QtWidgets/QApplication>
#include <QtWidgets/QLabel>
#include <QtWidgets/QMimeData>
#include <QtCore/QDrag>

class MyWidget : public QLabel {
public:
    MyWidget(QWidget *parent = nullptr);

protected:
    void dragEnterEvent(QDragEnterEvent *event) override;
    void dragMoveEvent(QDragMoveEvent *event) override;
    void dragLeaveEvent(QDragLeaveEvent *event) override;
    void dropEvent(QDropEvent *event) override;
};

MyWidget::MyWidget(QWidget *parent) : QLabel(parent) {
    setAcceptDrops(true);
}

void MyWidget::dragEnterEvent(QDragEnterEvent *event) {
    if (event->mimeData()->hasFormat("application/x-qt/image")) {
        event->acceptProposedAction(Qt::CopyAction | Qt::MoveAction);
    } else {
        event->ignore();
    }
}

void MyWidget::dragMoveEvent(QDragMoveEvent *event) {
    if (event->mimeData()->hasFormat("application/x-qt/image")) {
        event->acceptProposedAction(Qt::CopyAction | Qt::MoveAction);
    } else {
        event->ignore();
    }
}

void MyWidget::dragLeaveEvent(QDragLeaveEvent *event) {
    if (event->mimeData()->hasFormat("application/x-qt/image")) {
        event->accept();
    } else {
        event->ignore();
    }
}

void MyWidget::dropEvent(QDropEvent *event) {
    if (event->mimeData()->hasFormat("application/x-qt/image")) {
        const QImage image = QImage::fromMimeData(event->mimeData());
        if (!image.isNull()) {
            setPixmap(image.scaled(size(), Qt::KeepAspectRatio));
            event->acceptProposedAction(Qt::CopyAction | Qt::MoveAction);
        } else {
            event->ignore();
        }
    } else {
        event->ignore();
    }
}

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

    MyWidget widget;
    widget.show();

    return app.exec();
}

このコードでは、MyWidget というラベルウィジェットを作成し、dragEnterEvent(), dragMoveEvent(), dragLeaveEvent(), dropEvent() メソッドをオーバーライドして、ドラッグとドロップ操作を処理します。

  • dropEvent() メソッドは、ドラッグされたデータがウィジェットにドロップされたときに呼び出されます。このメソッドでは、ドラッグされたデータを取得し、処理することができます。
  • dragLeaveEvent() メソッドは、ドラッグされたデータがウィジェットから離れたときに呼び出されます。このメソッドでは、ウィジェットの表示を元に戻したり、ドラッグされたデータの処理を終了したりすることができます。
  • dragMoveEvent() メソッドは、ドラッグされたデータがウィジェットの上を移動するときに呼び出されます。このメソッドでは、ドラッグされたデータの位置を確認し、ウィジェットの表示を更新することができます。
  • dragEnterEvent() メソッドは、ドラッグされたデータがウィジェットの上に初めてドラッグされたときに呼び出されます。このメソッドでは、ドラッグされたデータの種類を確認し、ウィジェットがドラッグされたデータを受け入れるかどうかを決定します。


しかし、QDragLeaveEvent を使用せずにドラッグとドロップ操作を処理する方法もあります。以下に、いくつかの代替方法を紹介します。

QDragMoveEvent を使用する

QDragMoveEvent は、ドラッグされたデータがウィジェットの上を移動するときに送信されます。このイベントでは、マウスカーソルの位置を確認し、ドラッグされたデータがウィジェットの境界線に近づいたかどうかを判断することができます。

void MyWidget::dragMoveEvent(QDragMoveEvent *event)
{
    if (event->mimeData()->hasFormat("application/x-qt/image")) {
        QPoint pos = event->pos();
        QRect rect = this->rect();

        if (pos.x() < rect.left() || pos.x() > rect.right() ||
            pos.y() < rect.top() || pos.y() > rect.bottom()) {
            event->ignore();
        } else {
            event->acceptProposedAction(Qt::CopyAction | Qt::MoveAction);
        }
    } else {
        event->ignore();
    }
}

この例では、MyWidget ウィジェットが application/x-qt/image 形式のデータをドラッグされた場合にのみ処理します。ドラッグされたデータがウィジェットの境界線から離れた場合は、イベントを無視します。

QDropEvent を使用する

QDropEvent は、ドラッグされたデータがウィジェットにドロップされたときに送信されます。このイベントでは、ドラッグされたデータを取得し、処理することができます。

void MyWidget::dropEvent(QDropEvent *event)
{
    if (event->mimeData()->hasFormat("application/x-qt/image")) {
        const QImage image = QImage::fromMimeData(event->mimeData());
        if (!image.isNull()) {
            setPixmap(image.scaled(size(), Qt::KeepAspectRatio));
            event->acceptProposedAction(Qt::CopyAction | Qt::MoveAction);
        } else {
            event->ignore();
        }
    } else {
        event->ignore();
    }
}

この例では、MyWidget ウィジェットが application/x-qt/image 形式のデータをドラッグされた場合にのみ処理します。ドラッグされたデータがウィジェットにドロップされた場合は、データを取得してウィジェットに表示します。

独自のイベントを作成し、ドラッグとドロップ操作の処理に使用することもできます。

class MyDragLeaveEvent : public QEvent
{
public:
    MyDragLeaveEvent(const QPoint &pos);

    QPoint pos() const { return m_pos; }

private:
    QPoint m_pos;
};

MyWidget::MyWidget(QWidget *parent) : QLabel(parent) {
    setAcceptDrops(true);
}

void MyWidget::dragLeaveEvent(QDragLeaveEvent *event) {
    if (event->mimeData()->hasFormat("application/x-qt/image")) {
        MyDragLeaveEvent customEvent(event->pos());
        QApplication::sendEvent(this, &customEvent);
    } else {
        event->ignore();
    }
}

bool MyWidget::event(QEvent *event)
{
    if (event->type() == MyDragLeaveEvent::Type) {
        MyDragLeaveEvent *customEvent = static_cast<MyDragLeaveEvent *>(event);
        QPoint pos = customEvent->pos();
        QRect rect = this->rect();

        if (pos.x() < rect.left() || pos.x() > rect.right() ||
            pos.y() < rect.top() || pos.y() > rect.bottom()) {
            // ドラッグされたデータがウィジェットから離れた処理
        } else {
            // ドラッグされたデータがウィジェット内にいる処理
        }

        return true;
    }

    return QLabel::event(event);
}