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