DnD = Drag (拖動) + Drop (放下)。Qt 中 QWidget 既可作 Drag Source,也可作 Drop Target,或雙重身份。系統剪貼板交換本質上也是 DnD 的特例。
| 事件 | 觸發時機 | 典型處理 |
|---|---|---|
dragEnterEvent | 拖拽對象進入組件區域 | 檢查 MIME 類型,acceptProposedAction() 允許放置 |
dragMoveEvent | 拖拽對象在組件內移動 | 可選,精細控制視覺反饋 |
dragLeaveEvent | 拖拽對象離開組件區域 | 可選,清理臨時狀態 |
dropEvent | 用戶釋放鼠標完成放置 | 讀取 MIME 數據,執行業務邏輯 |
class MainWindow : public QMainWindow {
Q_OBJECT
public:
MainWindow(QWidget *parent = 0);
protected:
void dragEnterEvent(QDragEnterEvent *event) override;
void dropEvent(QDropEvent *event) override;
private:
bool readFile(const QString &fileName);
QTextEdit *textEdit;
};
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) {
textEdit = new QTextEdit;
setCentralWidget(textEdit);
// 關鍵:禁止 QTextEdit 內建 Drop,由 MainWindow 統一處理
textEdit->setAcceptDrops(false);
setAcceptDrops(true); // MainWindow 接受 Drop
setWindowTitle(tr("Text Editor"));
}
void MainWindow::dragEnterEvent(QDragEnterEvent *event) {
// text/uri-list 標準 MIME:文件路徑列表 (本地文件/URL)
if (event->mimeData()->hasFormat("text/uri-list")) {
event->acceptProposedAction(); // 顯示「可放置」光標
}
}
void MainWindow::dropEvent(QDropEvent *event) {
QList urls = event->mimeData()->urls();
if (urls.isEmpty()) return;
QString fileName = urls.first().toLocalFile(); // 取首個文件轉本地路徑
if (fileName.isEmpty()) return;
if (readFile(fileName)) {
setWindowTitle(tr("%1 - %2").arg(fileName).arg(tr("Drag File")));
}
}
bool MainWindow::readFile(const QString &fileName) {
QFile file(fileName);
if (!file.open(QIODevice::ReadOnly)) return false;
QTextStream in(&file);
textEdit->setText(in.readAll());
return true;
}
text/uri-list 由 IANA 定義,跨平台標準化文件引用傳遞。QMimeData 封裝了平台差異。setAcceptDrops() 精確控制哪個組件響應 Drop,避免父子組件爭搶事件。QUrl::toLocalFile() 自動處理 file:///path 協議轉換,跨平台安全。urls() 返回列表,實現批量拖入只需遍歷處理。QDataStream 序列化)QDrag::setPixmap() 設置拖拽時跟隨游標的圖標Qt::CopyAction / MoveAction / LinkAction 語義區分#include
#include "mainwindow.h"
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
Qt DnD 框架以事件驅動 + MIME 類型協商為核心,實現了跨應用、跨平台的統一拖放體驗。掌握 dragEnterEvent/dropEvent 協作模式,即可輕鬆構建直觀的文件導入、數據重排等交互功能。