Qt 拖放技術之二

2018-10-08 09:56 更新

拖放技術之二

很長時間沒有來寫博客了,前段時間一直在幫同學弄一個 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

以上內容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號