このページでは

ドラッグ&ドロップ

ドラッグ&ドロップは、ユーザーがアプリケーション間やアプリケーション内で情報を転送するために利用できる、シンプルな視覚的な仕組みです。ドラッグ&ドロップの機能は、クリップボードの「切り取り」や「貼り付け」の仕組みと似ています。

このドキュメントでは、基本的なドラッグ&ドロップの仕組みについて説明し、カスタムコントロールでこれを有効にするためのアプローチの概要を説明します。 ドラッグ&ドロップ操作は、アイテムビューやグラフィックスビューフレームワーク、Qt Widgets およびQt Quick の編集コントロールなど、Qt の多くのコントロールでもサポートされています。アイテムビューおよびグラフィックスビューの詳細については、「アイテムビューおよび グラフィックスビューフレームワーク でのドラッグ&ドロップの使用」を参照してください。

ドラッグ&ドロップ関連クラス

これらのクラスは、ドラッグ&ドロップおよびそれに必要なMIMEタイプのエンコードとデコードを扱います。

QDrag

MIME ベースのドラッグ&ドロップデータ転送のサポート

QDragEnterEvent

ドラッグ&ドロップ操作がウィジェットに入った際に、そのウィジェットに送信されるイベント

QDragLeaveEvent

ドラッグ&ドロップ操作がウィジェットから離れた際にウィジェットに送信されるイベント

QDragMoveEvent

ドラッグ&ドロップ操作が進行中に送信されるイベント

QDropEvent

ドラッグ&ドロップ操作が完了したときにウィジェットに送信されるイベント

QUtiMimeConverter

MIMEタイプとUniform Type Identifier(UTI)形式間の変換

設定

QStyleHints オブジェクトには、ドラッグ&ドロップ操作に関連するいくつかのプロパティが用意されています。

  • QStyleHints::startDragTime() は、ドラッグが開始されるまでに、ユーザーがオブジェクト上でマウスボタンを押し続けなければならない時間をミリ秒単位で表します。
  • QStyleHints::startDragDistance() は、マウスのボタンを押したまま移動した距離のうち、どの程度以上であればその移動がドラッグとして認識されるかを示します。
  • QStyleHints::startDragVelocity() は、ドラッグを開始するためにユーザーがマウスをどれだけの速度(ピクセル/秒)で動かす必要があるかを示します。0 という値は、そのような制限がないことを意味します。

これらの値は、コントロールでドラッグ&ドロップ機能をサポートする場合に利用できる、基盤となるウィンドウシステムに準拠した適切なデフォルト値です。

xml-ph-0000@deepl.internal でのドラッグ&ドロップQt Quick

このドキュメントの残りの部分では、主に C++ でのドラッグ&ドロップの実装方法に焦点を当てています。Qt Quick シーン内でドラッグ&ドロップを使用するには、Qt Quick Drag 、DragEvent 、およびDropArea の各項目に関するドキュメント、ならびにQt Quick のドラッグ&ドロップのサンプルを参照してください。

ドラッグ

ドラッグを開始するには、QDrag オブジェクトを作成し、そのexec()関数を呼び出します。 ほとんどのアプリケーションでは、マウスボタンが押され、カーソルが一定の距離移動してからドラッグ&ドロップ操作を開始するのが望ましいです。しかし、ウィジェットからのドラッグを有効にする最も簡単な方法は、ウィジェットのmousePressEvent()を再実装し、ドラッグ&ドロップ操作を開始することです:

void MainWindow::mousePressEvent(QMouseEvent *event)
{
    if (event->button() == Qt::LeftButton
        && iconLabel->geometry().contains(event->pos())) {

        QDrag *drag = new QDrag(this);
        QMimeData *mimeData = new QMimeData;

        mimeData->setText(commentEdit->toPlainText());
        drag->setMimeData(mimeData);
        drag->setPixmap(iconPixmap);

        Qt::DropAction dropAction = drag->exec();
        ...
    }
}

ユーザーがドラッグ操作を完了するまでには多少時間がかかる場合がありますが、アプリケーションの観点からは、exec() 関数はブロックする関数であり、one of several values を返します。これらは操作がどのように終了したかを示しており、詳細は後述します。

なお、exec() 関数はメインのイベントループをブロックしません。

マウスクリックとドラッグを区別する必要があるウィジェットの場合、ドラッグの開始位置を記録するために、ウィジェットのmousePressEvent() 関数を再実装すると便利です:

void DragWidget::mousePressEvent(QMouseEvent *event)
{
    if (event->button() == Qt::LeftButton)
        dragStartPosition = event->pos();
}

その後、mouseMoveEvent() 内で、ドラッグを開始すべきかどうかを判断し、その操作を処理するためのドラッグオブジェクトを作成できます:

void DragWidget::mouseMoveEvent(QMouseEvent *event)
{
    if (!(event->buttons() & Qt::LeftButton))
        return;
    if ((event->pos() - dragStartPosition).manhattanLength()
         < QApplication::startDragDistance())
        return;

    QDrag *drag = new QDrag(this);
    QMimeData *mimeData = new QMimeData;

    mimeData->setData(mimeType, data);
    drag->setMimeData(mimeData);

    Qt::DropAction dropAction = drag->exec(Qt::CopyAction | Qt::MoveAction);
    ...
}

この特定のアプローチでは、QPoint::manhattanLength() 関数を使用して、マウスクリックが発生した位置と現在のカーソル位置との間の距離を大まかに推定します。この関数は精度を犠牲にして速度を優先しており、通常はこの目的に適しています。

ドロップ

ウィジェット上にドロップされたメディアを受け取るには、そのウィジェットに対してsetAcceptDrops(true)を呼び出し、dragEnterEvent()およびdropEvent()というイベントハンドラ関数を再実装します。

たとえば、次のコードは `QWidget ` のサブクラスのコンストラクタでドロップイベントを有効にし、ドロップイベントハンドラを実用的に実装できるようにしています:

Window::Window(QWidget *parent)
    : QWidget(parent)
{
    ...
    setAcceptDrops(true);
}

dragEnterEvent() 関数は通常、ウィジェットが受け入れるデータのタイプを Qt に通知するために使用されます。dragMoveEvent() およびdropEvent() の再実装において、QDragMoveEvent またはQDropEvent のいずれかを受け取りたい場合は、この関数を再実装する必要があります。

以下のコードは、dragEnterEvent() を再実装して、ドラッグ&ドロップシステムに対してプレーンテキストのみを処理できることを伝える方法を示しています。

void Window::dragEnterEvent(QDragEnterEvent *event)
{
    if (event->mimeData()->hasFormat("text/plain"))
        event->acceptProposedAction();
}

dropEvent() は、ドロップされたデータを展開し、アプリケーションに適した方法で処理するために使用されます。

以下のコードでは、イベントで提供されたテキストが `QTextBrowser ` に渡され、データを記述するために使用される MIME タイプのリストが `QComboBox ` に格納されます:

void Window::dropEvent(QDropEvent *event)
{
    textBrowser->setPlainText(event->mimeData()->text());
    mimeTypeCombo->clear();
    mimeTypeCombo->addItems(event->mimeData()->formats());

    event->acceptProposedAction();
}

この例では、提案されたアクションの内容を確認せずに受け入れています。実際のアプリケーションでは、アクションが適切でない場合、dropEvent() 関数から、提案されたアクションを受け入れず、データの処理も行わずに戻る必要がある場合があります。例えば、アプリケーションで外部ソースへのリンクをサポートしていない場合は、Qt::LinkAction のアクションを無視するように選択できるでしょう。

提案されたアクションのオーバーライド

また、提案されたアクションを無視し、データに対して別のアクションを実行することも可能です。これを行うには、accept() を呼び出す前に、イベントオブジェクトのsetDropAction() を呼び出し、Qt::DropAction から優先するアクションを指定します。これにより、提案されたアクションの代わりに、置き換えられたドロップアクションが確実に使用されます。

より高度なアプリケーションでは、dragMoveEvent() およびdragLeaveEvent() を再実装することで、ウィジェットの特定の部分をドロップイベントに反応させ、アプリケーション内のドラッグ&ドロップをより細かく制御できるようになります。

複雑なウィジェットのサブクラス化

一部の標準的な Qt ウィジェットは、独自のドラッグ&ドロップ機能を備えています。これらのウィジェットをサブクラス化する際は、dragEnterEvent() やdropEvent() に加えてdragMoveEvent() を再実装する必要がある場合があります。これにより、基底クラスが提供するデフォルトのドラッグ&ドロップ処理を無効にし、必要な特別なケースを処理できるようになります。

ドラッグ&ドロップのアクション

最も単純なケースでは、ドラッグ&ドロップ操作のターゲットはドラッグされているデータのコピーを受け取り、ソース側が元のデータを削除するかどうかを決定します。これは、CopyAction アクションによって定義されます。 ターゲットは、他のアクション、具体的にはMoveAction およびLinkAction アクションを処理することもできます。ソースがQDrag::exec()を呼び出し、それがMoveAction を返した場合、ソースは、必要に応じて元のデータを削除する責任を負います。 ソースウィジェットによって作成された `QMimeData ` および `QDrag ` オブジェクトは削除してはなりません。これらは Qt によって破棄されます。ターゲットは、ドラッグ&ドロップ操作で送信されたデータの所有権を引き受ける責任があります。これは通常、データへの参照を保持することで行われます。

ターゲットが `LinkAction ` アクションを理解している場合、元の情報への独自の参照を保存する必要があります。ソースはデータに対してそれ以上の処理を行う必要はありません。ドラッグ&ドロップアクションの最も一般的な用途は、同じウィジェット内での移動を行う場合です。この機能の詳細については、「ドロップアクション」のセクションを参照してください。

ドラッグアクションのもう一つの主な用途は、text/uri-list などの参照型を使用する場合であり、この場合、ドラッグされたデータは実際にはファイルやオブジェクトへの参照となります。

新しいドラッグ&ドロップタイプの追加

ドラッグ&ドロップは、テキストや画像に限定されません。あらゆる種類の情報をドラッグ&ドロップ操作で転送することができます。アプリケーション間で情報をドラッグするには、各アプリケーションが、どのデータ形式を受け入れられ、どの形式を出力できるかを互いに示すことができなければなりません。これはMIME タイプを使用して実現されます。 送信元によって生成されるQDrag オブジェクトには、データを表現するために使用されるMIMEタイプのリスト(最も適切なものから順に並んでいる)が含まれており、ドロップ先はこのリストの中から適切なものを選択してデータにアクセスします。 一般的なデータ型の場合、便利関数が使用されるMIMEタイプを透過的に処理しますが、カスタムデータ型の場合は、それらを明示的に指定する必要があります。

QDrag の便利関数でカバーされていない種類の情報に対してドラッグ&ドロップ操作を実装するには、まず第一に、適切な既存のフォーマットを探すことが最も重要です。インターネット番号割り当て機関 (IANA) は、情報科学研究所 (ISI) にて、MIME メディアタイプの階層リストを提供しています。 標準の MIME タイプを使用することで、現在および将来にわたって、アプリケーションと他のソフトウェアとの相互運用性を最大限に高めることができます。

追加のメディアタイプをサポートするには、setData() 関数を使用してQMimeData オブジェクトのデータを設定し、完全な MIME タイプと、適切な形式のデータを含むQByteArray を指定するだけです。次のコードは、ラベルからピクマップを取得し、それをQMimeData オブジェクト内の Portable Network Graphics (PNG) ファイルとして保存します。

    QByteArray output;
    QBuffer outputBuffer(&output);
    outputBuffer.open(QIODevice::WriteOnly);
    imageLabel->pixmap().toImage().save(&outputBuffer, "PNG");
    mimeData->setData("image/png", output);

もちろん、このケースでは、代わりにsetImageData()を使用するだけで、さまざまな形式の画像データを指定することも可能です:

    mimeData->setImageData(QVariant(*imageLabel->pixmap()));

しかし、このケースでもQByteArray のアプローチは有用です。なぜなら、QMimeData オブジェクトに格納されるデータ量をより細かく制御できるからです。

なお、アイテムビューで使用されるカスタムデータ型はmeta objects として宣言する必要があり、それらに対応するストリーム演算子を実装する必要があります。

ドロップ操作

クリップボードモデルでは、ユーザーはソース情報を切り取りまたは コピーし、後で貼り付けることができます。同様に、ドラッグアンドドロップモデルでも、ユーザーは情報のコピーをドラッグすることも、情報そのものを新しい場所にドラッグ(移動)することもできます。 ドラッグ&ドロップモデルでは、プログラマーにとってさらに複雑な問題が生じます。操作が完了するまで、プログラムはユーザーが情報を切り取りたいのかコピーしたいのかを判別できないのです。アプリケーション間で情報をドラッグする場合、この違いは通常問題になりませんが、アプリケーション内では、どのドロップアクションが使用されたかを確認することが重要です。

ウィジェットの `mouseMoveEvent()` を再実装し、考えられるドロップ操作の組み合わせに基づいてドラッグ&ドロップ操作を開始することができます。例えば、ドラッグ操作によってウィジェット内のオブジェクトが常に移動するようにしたい場合があります:

void DragWidget::mouseMoveEvent(QMouseEvent *event)
{
    if (!(event->buttons() & Qt::LeftButton))
        return;
    if ((event->pos() - dragStartPosition).manhattanLength()
         < QApplication::startDragDistance())
        return;

    QDrag *drag = new QDrag(this);
    QMimeData *mimeData = new QMimeData;

    mimeData->setData(mimeType, data);
    drag->setMimeData(mimeData);

    Qt::DropAction dropAction = drag->exec(Qt::CopyAction | Qt::MoveAction);
    ...
}

exec() 関数が返すアクションは、情報が別のアプリケーションにドロップされた場合、デフォルトで `CopyAction ` となる可能性がありますが、同じアプリケーション内の別のウィジェットにドロップされた場合は、異なるドロップアクションが返される可能性があります。

提案されたドロップアクションは、ウィジェットの dragMoveEvent() 関数でフィルタリングできます。ただし、dragEnterEvent() ですべての提案されたアクションを受け入れ、後でユーザーにどのアクションを受け入れるかを決定させることも可能です:

void DragWidget::dragEnterEvent(QDragEnterEvent *event)
{
    event->acceptProposedAction();
}

ウィジェット内でドロップが発生すると、dropEvent() ハンドラ関数が呼び出され、考えられる各アクションを順番に処理できます。まず、同じウィジェット内でのドラッグ&ドロップ操作を処理します:

void DragWidget::dropEvent(QDropEvent *event)
{
    if (event->source() == this && event->possibleActions() & Qt::MoveAction)
        return;

この場合、移動操作の処理は行いません。受け入れる各種類のドロップアクションを検証し、それに応じて処理を行います:

    if (event->proposedAction() == Qt::MoveAction) {
        event->acceptProposedAction();
        // Process the data from the event.
    } else if (event->proposedAction() == Qt::CopyAction) {
        event->acceptProposedAction();
        // Process the data from the event.
    } else {
        // Ignore the drop.
        return;
    }
    ...
}

上記のコードでは個々のドロップアクションをチェックしている点に注意してください。「提案されたアクションのオーバーライド」のセクションで前述したように、提案されたドロップアクションをオーバーライドし、可能なドロップアクションの選択肢から別のものを選択する必要がある場合があります。 これを行うには、イベントのpossibleActions()によって渡された値に各アクションが含まれているかを確認し、setDropAction()でドロップアクションを設定し、accept()を呼び出す必要があります。

ドロップ矩形

ウィジェットの `dragMoveEvent()` を使用すると、カーソルが特定の領域内にある場合にのみ提案されたドロップアクションを受け入れることで、ドロップをウィジェットの特定の部分に制限できます。たとえば、次のコードは、カーソルが子ウィジェット上にある場合に、提案されたドロップアクションをすべて受け入れます(`dropFrame`):

void Window::dragMoveEvent(QDragMoveEvent *event)
{
    if (event->mimeData()->hasFormat("text/plain")
        && event->answerRect().intersects(dropFrame->geometry()))

        event->acceptProposedAction();
}

また、ドラッグ&ドロップ操作中に視覚的なフィードバックを表示したり、ウィンドウをスクロールさせたり、その他の適切な処理を行ったりする必要がある場合にも、dragMoveEvent() を使用できます。

クリップボード

アプリケーションは、クリップボードにデータを格納することで相互に通信することもできます。これを利用するには、QApplication オブジェクトからQClipboard オブジェクトを取得する必要があります。

QMimeData クラスは、クリップボードとの間でやり取りされるデータを表すために使用されます。データをクリップボードに格納するには、一般的なデータ型向けのsetText()、setImage()、setPixmap()といった利便性向上のための関数を使用できます。 これらの関数は、QMimeData クラスにある関数と似ていますが、データの保存先を制御する追加の引数を取る点が異なります。Clipboard が指定された場合、データはクリップボードに格納され、Selection が指定された場合、データはマウス選択領域に格納されます(X11のみ)。デフォルトでは、データはクリップボードに格納されます。

たとえば、次のコードを使用すると、QLineEdit の内容をクリップボードにコピーできます。

QGuiApplication::clipboard()->setText(lineEdit->text(), QClipboard::Clipboard);

異なるMIMEタイプのデータもクリップボードにコピーできます。前のセクションで説明した方法でQMimeData オブジェクトを作成し、setData()関数でデータを設定します。その後、setMimeData()関数を使用して、このオブジェクトをクリップボードにコピーできます。

QClipboard クラスは、dataChanged()シグナルを介して、そのデータに変更があったことをアプリケーションに通知できます。たとえば、このシグナルをウィジェットのスロットに接続することで、クリップボードを監視することができます:

    connect(clipboard, &QClipboard::dataChanged,
            this, &ClipWindow::updateClipboard);

このシグナルに接続されたスロットは、クリップボード上のデータを表すために使用できるMIMEタイプのいずれかを使用して、そのデータを読み取ることができます:

void ClipWindow::updateClipboard()
{
    mimeTypeCombo->clear();

    QStringList formats = clipboard->mimeData()->formats();
    if (formats.isEmpty())
        return;

    for (const auto &format : formats) {
        QByteArray data = clipboard->mimeData()->data(format);
        // ...
    }

X11環境では、selectionChanged()シグナルを使用してマウスの選択範囲を監視することができます。

例

他のアプリケーションとの連携

X11 では公開XDND プロトコルが使用されますが、Windows では Qt は OLE 標準を使用し、macOS 向けの Qt は Cocoa Drag Manager を使用します。X11 では XDND が MIME を使用するため、変換は必要ありません。Qt API はプラットフォームに関係なく同じです。 Windowsでは、MIMEに対応したアプリケーションは、MIMEタイプであるクリップボード形式名を使用して通信できます。一部のWindowsアプリケーションでは、すでにクリップボード形式にMIMEの命名規則を採用しています。

独自のクリップボード形式を変換するためのカスタムクラスは、WindowsではQWindowsMimeConverter を、macOSではQUtiMimeConverter を再実装することで登録できます。

© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.