이 페이지에서

드래그 앤 드롭

드래그 앤 드롭은 사용자가 애플리케이션 간 및 애플리케이션 내에서 정보를 전송할 수 있는 간단한 시각적 메커니즘을 제공합니다. 드래그 앤 드롭은 클립보드의 잘라내기 및 붙여넣기 메커니즘과 기능이 유사합니다.

이 문서는 기본적인 드래그 앤 드롭 메커니즘을 설명하고, 사용자 정의 컨트롤에서 이를 구현하기 위해 사용되는 접근 방식을 개괄적으로 다룹니다. 드래그 앤 드롭 작업은 항목 뷰(item views) 및 그래픽 뷰 프레임워크(graphics view framework)는 물론, Qt Widgets 및 Qt Quick 용 편집 컨트롤 등 Qt의 많은 컨트롤에서도 지원됩니다. 항목 뷰 및 그래픽 뷰에 대한 자세한 내용은 항목 뷰 및 그래픽 뷰 프레임워크에서 드래그 앤 드롭 사용에서 확인할 수 있습니다.

드래그 앤 드롭 클래스

이 클래스들은 드래그 앤 드롭과 이에 필요한 MIME 유형 인코딩 및 디코딩을 처리합니다.

QDrag

MIME 기반 드래그 앤 드롭 데이터 전송 지원

QDragEnterEvent

드래그 앤 드롭 작업이 위젯에 진입할 때 위젯으로 전송되는 이벤트

QDragLeaveEvent

드래그 앤 드롭 작업이 위젯을 떠날 때 위젯으로 전송되는 이벤트

QDragMoveEvent

드래그 앤 드롭 작업이 진행 중일 때 전송되는 이벤트

QDropEvent

드래그 앤 드롭 작업이 완료될 때 전송되는 이벤트

QUtiMimeConverter

MIME 유형과 UTI(Uniform Type Identifier) 형식 간 변환

구성

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()을 호출하기 전에, Qt::DropAction 에서 지정한 선호 동작을 전달하여 이벤트 객체의 setDropAction()을 호출해야 합니다. 이렇게 하면 제안된 동작 대신 대체 드롭 동작이 사용됩니다.

보다 정교한 애플리케이션을 구현하려면, ` dragMoveEvent()` 및 ` dragLeaveEvent()`를 재구현함으로써 위젯의 특정 부분이 드롭 이벤트에 반응하도록 만들고, 애플리케이션 내 드래그 앤 드롭 기능을 더욱 세밀하게 제어할 수 있습니다.

복잡한 위젯의 서브클래스 생성

일부 표준 Qt Widgets는 드래그 앤 드롭 기능을 자체적으로 지원합니다. 이러한 Widgets를 상속할 때는, 기저 클래스가 기본 드래그 앤 드롭 처리를 제공하는 것을 방지하고, 관심 있는 특수한 경우를 처리하기 위해 dragEnterEvent() 및 dropEvent() 외에도 dragMoveEvent()을 재구현해야 할 수 있습니다.

드래그 앤 드롭 동작

가장 간단한 경우, 드래그 앤 드롭 작업의 대상은 드래그되는 데이터의 복사본을 수신하고, 소스는 원본을 삭제할지 여부를 결정합니다. 이는 CopyAction 작업으로 설명됩니다. 대상 위젯은 다른 동작, 특히 ` MoveAction ` 및 ` LinkAction ` 동작을 처리하도록 선택할 수도 있습니다. 소스 위젯이 ` QDrag::exec()`를 호출하고, 이 함수가 ` MoveAction`를 반환하는 경우, 소스 위젯은 원본 데이터를 삭제할지 여부를 결정할 책임이 있습니다. 소스 위젯이 생성한 QMimeData 및 QDrag 객체는 삭제해서는 안 됩니다. 이 객체들은 Qt에 의해 자동으로 소멸됩니다. 대상은 드래그 앤 드롭 작업에서 전송된 데이터에 대한 소유권을 인수할 책임이 있으며, 이는 일반적으로 데이터에 대한 참조를 유지함으로써 이루어집니다.

대상(target)이 ‘ LinkAction ’ 동작을 인식하는 경우, 원본 정보에 대한 자체 참조를 저장해야 하며, 소스(source)는 데이터에 대해 추가 처리를 수행할 필요가 없습니다. 드래그 앤 드롭 동작의 가장 일반적인 용도는 동일한 위젯 내에서 ‘Move’를 수행할 때입니다. 이 기능에 대한 자세한 내용은 ‘Drop Actions’ 섹션을 참조하십시오.

드래그 동작의 또 다른 주요 용도는 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);
        // ...
    }

selectionChanged() 신호는 X11 환경에서 마우스 선택 영역을 모니터링하는 데 사용할 수 있습니다.

예제

다른 애플리케이션과의 상호 운용

X11에서는 공개 XDND 프로토콜이 사용되는 반면, Windows에서는 Qt가 OLE 표준을 사용하고, macOS용 Qt는 Cocoa 드래그 관리자를 사용합니다. 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.