很長時間沒有來寫博客了,前段時間一直在幫同學弄一個 spring-mvc 的項目,今天終於做完了,不過公司裡面又要開始做 flex 4,估計還會忙一段時間吧! 接著上次的說,上次說到了拖放技術,今天依然是一個例子,同樣是來自《C++ GUI Programming with Qt 4, 2nd Edition》的。 這次的 demo 還算是比較實用:實現的是兩個 list 之間的數據互拖。在很多項目中,這一需求還是比較常見的吧!下面也就算是拋磚引玉了啊! projectlistwidget.h
projectlistwidget.cpp
- #ifndef PROJECTLISTWIDGET_H
- #define PROJECTLISTWIDGET_H
- #include <QtGui>
- class ProjectListWidget : public QListWidget
- {
- Q_OBJECT
- public:
- ProjectListWidget(QWidget *parent = 0);
- protected:
- void mousePressEvent(QMouseEvent *event);
- void mouseMoveEvent(QMouseEvent *event);
- void dragEnterEvent(QDragEnterEvent *event);
- void dragMoveEvent(QDragMoveEvent *event);
- void dropEvent(QDropEvent *event);
- private:
- void performDrag();
- QPoint startPos;
- };
- #endif // PROJECTLISTWIDGET_H
我們從構造函數開始看起。Qt 中很多組件是可以接受拖放的,但是默認動作都是不允許的,因此在構造函數中,我們調用 setAcceptDrops(true); 函數,讓組件能夠接受拖放事件。 在 mousePressEvent() 函數中,我們檢測鼠標左鍵點擊,如果是的話就記錄下當前位置。需要注意的是,這個函數最後需要調用系統自帶的處理函數,以便實現通常的那種操作。這在一些重寫事件的函數中都是需要注意的! 然後我們重寫了 mouseMoveEvent() 事件。下面還是先來看看代碼:
- #include "projectlistwidget.h"
- 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())
- performDrag();
- }
- QListWidget::mouseMoveEvent(event);
- }
- void ProjectListWidget::performDrag()
- {
- QListWidgetItem *item = currentItem();
- if (item) {
- 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;
- }
- }
- void ProjectListWidget::dragEnterEvent(QDragEnterEvent *event)
- {
- ProjectListWidget *source =
- qobject_cast<ProjectListWidget *>(event->source());
- if (source && source != this) {
- event->setDropAction(Qt::MoveAction);
- event->accept();
- }
- }
- void ProjectListWidget::dragMoveEvent(QDragMoveEvent *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();
- }
- }
在這裡判斷了如果鼠標拖動的時候一直按住左鍵(也就是 if 裡面的內容),那麼就計算一個 manhattanLength() 值。從字面上翻譯,這是個“曼哈頓長度”。這是什麼意思呢?我們看一下 event.pos() - startPos 是什麼。還記得在 mousePressEvent() 函數中,我們將鼠標按下的坐標記錄為 startPos,而 event.pos() 則是鼠標當前的坐標:一個點減去另外一個點,沒錯,這就是向量!其實,所謂曼哈頓距離就是兩點之間的距離(至於為什麼叫這個奇怪的名字,大家查查百科就知道啦!),也就是這個向量的長度。下面又是一個判斷,如果大於 QApplication::startDragDistance(),我們才進行 drag 的操作。當然,最後還是要調用系統默認的鼠標拖動函數。這一判斷的意義在於,防止用戶因為手的抖動等因素造成的鼠標拖動。用戶必須將鼠標拖動一段距離之後,我們才認為他是希望進行拖動操作,而這一距離就是 QApplication::startDragDistance() 提供的,這個值通常是 4px。 performDrag() 開始處理拖放過程。我們創建了一個 QDrag 對象,將 this 作為 parent。QDrag 使用 QMimeData 存儲數據。例如我們使用 QMimeData::setText() 函數將一個字符串存儲為 text/plain 類型的數據。QMimeData 提供了很多函數,用於存儲諸如 URL、顏色等類型的數據。使用 QDrag::setPixmap() 則可以設置拖動發生時鼠標的樣式。QDrag::exec() 會阻塞拖動的操作,直到用戶完成操作或者取消操作。它接受不同類型的動作作為參數,返回值是真正執行的動作。這些動作的類型為 Qt::CopyAction,Qt::MoveAction 和 Qt::LinkAction。返回值會有這三種動作,同時增加一個 Qt::IgnoreAction 用於表示用戶取消了拖放。這些動作取決於拖放源對象允許的類型,目的對象接受的類型以及拖放時按下的鍵盤按鍵。在 exec() 調用之後,Qt 會在拖放對象不需要的時候 delete 掉它。 ProjectListWidget 不僅能夠發出拖動事件,而且能夠接受同一應用程序中的不同 ProjectListWidget 對象的數據。在 dragEnterEvent() 中,我們使用 event->source() 獲取這樣的對象:如果拖放數據來自同一類型的對象,並且來自同一應用程序則返回其指針,否則返回 NULL。我們使用 qobject_cast 宏將指針轉換成 ProjectListWidget* 類型,然後設置接受 Qt::MoveAction 類型的拖動。dragMoveEvent() 則和這個函數具有相同的代碼,因為我們需要重寫拖動移動的代碼。 最後在 dropEvent() 函數中,我們取出 QDrag 中的 mimeData 數據,調用 addItem() 添加到當前的列表中。這樣,一個相對完整的拖放的代碼就完成了。 拖放技術是 Qt 中功能強大的一個技術,但是對於不涉及數據的同一組件中拖動,或許僅僅簡單的實現 mouse event 就足夠了,具體還是要自己斟酌啦!
- void ProjectListWidget::mouseMoveEvent(QMouseEvent *event)
- {
- if (event->buttons() & Qt::LeftButton) {
- int distance = (event->pos() - startPos).manhattanLength();
- if (distance >= QApplication::startDragDistance())
- performDrag();
- }
- QListWidget::mouseMoveEvent(event);
- }
本文出自 “豆子空間” 博客,請務必保留此出處http://devbean.blog.51cto.com/448512/286796