드래그 앤 드롭
끌어서 놓기는 사용자가 애플리케이션 간 또는 애플리케이션 내에서 정보를 전송하는 데 사용할 수 있는 간단한 시각적 메커니즘을 제공합니다. 끌어서 놓기는 클립보드의 잘라내기 및 붙여넣기 메커니즘과 기능이 유사합니다.
이 문서에서는 기본적인 끌어서 놓기 메커니즘에 대해 설명하고 사용자 지정 컨트롤에서 이를 활성화하는 데 사용되는 접근 방식을 간략하게 설명합니다. 드래그 앤 드롭 작업은 아이템 뷰와 그래픽 뷰 프레임워크, Qt Widgets 및 Qt Quick 의 편집 컨트롤과 같은 Qt의 많은 컨트롤에서도 지원됩니다. 항목 보기와 그래픽 보기에 대한 자세한 정보는 항목 보기 및 그래픽 보기 프레임워크에서 드래그 앤 드롭 사용하기를 참조하십시오.
드래그 앤 드롭 클래스
이 클래스는 드래그 앤 드롭과 필요한 마임 유형 인코딩 및 디코딩을 다룹니다.
MIME 기반 드래그 앤 드롭 데이터 전송 지원 | |
드래그 앤 드롭 동작이 위젯에 들어갈 때 위젯으로 전송되는 이벤트 | |
드래그 앤 드롭 동작이 위젯을 떠날 때 위젯으로 전송되는 이벤트 | |
드래그 앤 드롭 동작이 진행 중인 동안 전송되는 이벤트 | |
드래그 앤 드롭 동작이 완료될 때 전송되는 이벤트 | |
MIME 유형과 UTI(Uniform Type Identifier) 형식 간 변환 |
구성
QStyleHints 객체는 드래그 앤 드롭 작업과 관련된 몇 가지 속성을 제공합니다:
- QStyleHints::startDragTime()는 드래그가 시작되기 전에 사용자가 개체 위에 마우스 버튼을 누르고 있어야 하는 시간(밀리초)을 나타냅니다.
- QStyleHints::startDragDistance()는 사용자가 마우스 버튼을 누른 상태에서 마우스를 얼마나 멀리 움직여야 드래그로 해석되는지를 나타냅니다.
- QStyleHints::startDragVelocity()는 드래그를 시작하기 위해 사용자가 마우스를 움직여야 하는 속도(픽셀/초)를 나타냅니다.
0
값은 이러한 제한이 없음을 의미합니다.
이 수치는 컨트롤에서 드래그 앤 드롭을 지원하는 경우 기본 창 시스템을 준수하는 합리적인 기본값을 제공합니다.
드래그 앤 드롭 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(); ... } }
사용자가 드래그 작업을 완료하는 데 시간이 다소 걸릴 수 있지만 애플리케이션에 관한 한 실행() 함수는 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 서브클래스의 생성자에서 드롭 이벤트를 활성화하여 드롭 이벤트 핸들러를 유용하게 구현할 수 있도록 합니다:
dragEnterEvent() 함수는 일반적으로 위젯이 허용하는 데이터의 유형을 Qt에 알리는 데 사용됩니다. dragMoveEvent () 및 dropEvent()의 재구현에서 QDragMoveEvent 또는 QDropEvent 을 수신하려면 이 함수를 다시 구현해야 합니다.
다음 코드는 드래그 앤 드롭 시스템에 일반 텍스트만 처리할 수 있음을 알리기 위해 dragEnterEvent()를 다시 구현하는 방법을 보여줍니다:
void Window::dragEnterEvent(QDragEnterEvent *event) { if (event->mimeData()->hasFormat("text/plain")) event->acceptProposedAction(); }
dropEvent()는 드래그 앤 드롭한 데이터를 압축을 풀고 애플리케이션에 적합한 방식으로 처리하는 데 사용됩니다.
다음 코드에서는 이벤트에 제공된 텍스트가 QTextBrowser 으로 전달되고 QComboBox 은 데이터를 설명하는 데 사용되는 MIME 유형 목록으로 채워집니다:
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 위젯은 드래그 앤 드롭에 대한 자체적인 지원을 제공합니다. 이러한 위젯을 서브클래싱할 때는 기본 클래스가 기본 드래그 앤 드롭 처리를 제공하지 않도록 하고 원하는 특수한 경우를 처리하기 위해 dragEnterEvent() 및 dropEvent() 외에 dragMoveEvent()를 다시 구현해야 할 수도 있습니다.
드래그 앤 드롭 동작
가장 간단한 경우, 드래그 앤 드롭 액션의 대상은 드래그되는 데이터의 복사본을 받고 원본을 삭제할지 여부는 소스에서 결정합니다. 이는 CopyAction
동작으로 설명됩니다. 대상은 다른 작업, 특히 MoveAction
및 LinkAction
작업을 처리하도록 선택할 수도 있습니다. 소스에서 QDrag::exec()를 호출하고 MoveAction
을 반환하는 경우 원본 데이터를 삭제하기로 선택한 경우 소스에서 삭제할 책임이 있습니다. 소스 위젯에 의해 생성된 QMimeData 및 QDrag 객체는 삭제해서는 안 되며, Qt에 의해 소멸됩니다. 대상은 드래그 앤 드롭 작업으로 전송된 데이터의 소유권을 가질 책임이 있으며, 이는 일반적으로 데이터에 대한 참조를 유지함으로써 수행됩니다.
대상이 LinkAction
동작을 이해하면 원본 정보에 대한 자체 참조를 저장해야 하며, 소스에서는 데이터에 대한 추가 처리를 수행할 필요가 없습니다. 드래그 앤 드롭 동작의 가장 일반적인 용도는 동일한 위젯 내에서 이동을 수행할 때입니다. 이 기능에 대한 자세한 내용은 드롭 동작 섹션을 참조하세요.
드래그 동작의 다른 주요 용도는 드래그한 데이터가 실제로 파일이나 개체에 대한 참조인 텍스트/URI 목록과 같은 참조 유형을 사용할 때입니다.
새로운 드래그 앤 드롭 유형 추가하기
드래그 앤 드롭은 텍스트와 이미지에만 국한되지 않습니다. 모든 유형의 정보를 끌어서 놓기 작업으로 전송할 수 있습니다. 애플리케이션 간에 정보를 드래그하려면 애플리케이션이 허용할 수 있는 데이터 형식과 생성할 수 있는 데이터 형식을 서로에게 표시할 수 있어야 합니다. 이는 MIME 형식을 사용하여 이루어집니다. 소스에서 구성한 QDrag 객체에는 데이터를 표현하는 데 사용하는 MIME 유형 목록(가장 적합한 것부터 가장 적합하지 않은 것 순으로 정렬)이 포함되어 있으며, 드래그 대상은 이 중 하나를 사용하여 데이터에 액세스합니다. 일반적인 데이터 유형의 경우 편의 함수는 사용되는 MIME 유형을 투명하게 처리하지만, 사용자 정의 데이터 유형의 경우 명시적으로 명시해야 합니다.
QDrag 편의 함수에 포함되지 않는 정보 유형에 대한 드래그 앤 드롭 작업을 구현하려면 가장 중요한 첫 번째 단계는 적절한 기존 형식을 찾는 것입니다: 인터넷 할당 번호 기관(IANA)은 정보 과학 연구소(ISI)에서 MIME 미디어 유형의 계층적 목록을 제공합니다. 표준 MIME 유형을 사용하면 현재와 미래의 다른 소프트웨어와 애플리케이션의 상호 운용성을 극대화할 수 있습니다.
추가 미디어 유형을 지원하려면 setData() 함수를 사용하여 QMimeData 객체의 데이터를 설정하고 전체 MIME 유형과 적절한 형식의 데이터가 포함된 QByteArray 을 제공하면 됩니다. 다음 코드는 레이블에서 픽셀맵을 가져와 QMimeData 객체에 PNG(Portable Network Graphics) 파일로 저장합니다:
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 로 선언해야 하며, 이에 대한 스트림 연산자를 구현해야 한다는 점에 유의하세요.
드롭 동작
클립보드 모델에서 사용자는 소스 정보를 잘라내거나 복사한 다음 나중에 붙여넣을 수 있습니다. 드래그 앤 드롭 모델에서도 마찬가지로 사용자는 정보의 복사본을 드래그하거나 정보 자체를 새 위치로 드래그(이동) 할 수 있습니다. 드래그 앤 드롭 모델에는 프로그래머에게 추가적인 복잡성이 있습니다: 프로그램은 작업이 완료될 때까지 사용자가 정보를 잘라낼 것인지 복사할 것인지 알 수 없습니다. 애플리케이션 간에 정보를 드래그할 때는 별 차이가 없지만, 애플리케이션 내에서는 어떤 드래그 동작이 사용되었는지 확인하는 것이 중요합니다.
위젯에 마우스MoveEvent()를 다시 구현하고 가능한 드래그 동작의 조합으로 드래그 앤 드롭 작업을 시작할 수 있습니다. 예를 들어 드래그하면 항상 위젯의 객체가 이동하도록 할 수 있습니다:
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); ... }
실행() 함수가 반환하는 동작은 정보가 다른 애플리케이션에 놓인 경우 기본적으로 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 에서 재구현하여 등록할 수 있습니다.
© 2025 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.