在很多項目中,需要在兩個列表之間通過拖放移動數據。本文基於《C++ GUI Programming with Qt 4》示例,實現一個自定義 ProjectListWidget,支持列表項在兩個實例間雙向拖放移動。
class ProjectListWidget : public QListWidget {
Q_OBJECT
public:
explicit ProjectListWidget(QWidget *parent = nullptr);
protected:
void mousePressEvent(QMouseEvent *event) override;
void mouseMoveEvent(QMouseEvent *event) override;
void dragEnterEvent(QDragEnterEvent *event) override;
void dragMoveEvent(QDragMoveEvent *event) override;
void dropEvent(QDropEvent *event) override;
private:
void performDrag();
QPoint startPos;
};
ProjectListWidget::ProjectListWidget(QWidget *parent)
: QListWidget(parent) {
setAcceptDrops(true); // 關鍵:默認不接受拖放事件
}
void ProjectListWidget::mousePressEvent(QMouseEvent *event) {
if (event->button() == Qt::LeftButton)
startPos = event->pos();
QListWidget::mousePressEvent(event); // 必須調用基類實現
}
void ProjectListWidget::mouseMoveEvent(QMouseEvent *event) {
if (event->buttons() & Qt::LeftButton) {
int distance = (event->pos() - startPos).manhattanLength();
if (distance >= QApplication::startDragDistance()) // 通常 4px
performDrag();
}
QListWidget::mouseMoveEvent(event);
}
曼哈頓距離:兩點坐標差向量的 L1 范數,這裡用於過濾手抖導致的微小移動。
void ProjectListWidget::performDrag() {
QListWidgetItem *item = currentItem();
if (!item) return;
QMimeData *mimeData = new QMimeData;
mimeData->setText(item->text()); // 存儲文本數據
QDrag *drag = new QDrag(this);
drag->setMimeData(mimeData);
drag->setPixmap(QPixmap(":/images/person.png")); // 拖動時的鼠標圖標
// 執行移動操作,成功則刪除源項目
if (drag->exec(Qt::MoveAction) == Qt::MoveAction)
delete item;
}
QDrag::exec() 會阻塞直到拖放結束,返回實際執行的動作(MoveAction/CopyAction/LinkAction/IgnoreAction)。
核心邏輯:只接受來自同類型、非自身的組件數據,並設置為移動動作。
// dragEnterEvent / dragMoveEvent 邏輯相同
void ProjectListWidget::dragEnterEvent(QDragEnterEvent *event) {
ProjectListWidget *source = qobject_cast<ProjectListWidget*>(event->source());
if (source && source != this) {
event->setDropAction(Qt::MoveAction);
event->accept();
}
}
void ProjectListWidget::dropEvent(QDropEvent *event) {
ProjectListWidget *source = qobject_cast<ProjectListWidget*>(event->source());
if (source && source != this) {
addItem(event->mimeData()->text()); // 取出文本添加到當前列表
event->setDropAction(Qt::MoveAction);
event->accept();
}
}
通過重寫鼠標與拖放事件,結合 QMimeData 傳遞數據、QDrag 管理拖放會話,實現了列表項雙向移動。對於同一組件內部排序等不涉及跨組件數據傳遞的場景,僅處理鼠標事件可能更輕量。
本文出自 “豆子空間” 博客,原文地址:https://devbean.blog.51cto.com/448512/286796