Qt 拖放技術(shù)之二

2018-10-08 09:56 更新

拖放技術(shù)之二

很長(zhǎng)時(shí)間沒(méi)有來(lái)寫(xiě)博客了,前段時(shí)間一直在幫同學(xué)弄一個(gè) spring-mvc 的項(xiàng)目,今天終于做完了,不過(guò)公司里面又要開(kāi)始做 flex 4,估計(jì)還會(huì)忙一段時(shí)間吧!

接著上次的說(shuō),上次說(shuō)到了拖放技術(shù),今天依然是一個(gè)例子,同樣是來(lái)自《C++ GUI Programming with Qt 4, 2nd Edition》的。

這次的 demo 還算是比較實(shí)用:實(shí)現(xiàn)的是兩個(gè) list 之間的數(shù)據(jù)互拖。在很多項(xiàng)目中,這一需求還是比較常見(jiàn)的吧!下面也就算是拋磚引玉了?。?/p>

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();  
    }  
}

我們從構(gòu)造函數(shù)開(kāi)始看起。Qt 中很多組件是可以接受拖放的,但是默認(rèn)動(dòng)作都是不允許的,因此在構(gòu)造函數(shù)中,我們調(diào)用 setAcceptDrops(true); 函數(shù),讓組件能夠接受拖放事件。

在 mousePressEvent() 函數(shù)中,我們檢測(cè)鼠標(biāo)左鍵點(diǎn)擊,如果是的話(huà)就記錄下當(dāng)前位置。需要注意的是,這個(gè)函數(shù)最后需要調(diào)用系統(tǒng)自帶的處理函數(shù),以便實(shí)現(xiàn)通常的那種操作。這在一些重寫(xiě)事件的函數(shù)中都是需要注意的!

然后我們重寫(xiě)了 mouseMoveEvent() 事件。下面還是先來(lái)看看代碼:


void ProjectListWidget::mouseMoveEvent(QMouseEvent *event)  
{  
    if (event->buttons() & Qt::LeftButton) {  
        int distance = (event->pos() - startPos).manhattanLength();  
        if (distance >= QApplication::startDragDistance())  
            performDrag();  
    }  
    QListWidget::mouseMoveEvent(event);  
} 

在這里判斷了如果鼠標(biāo)拖動(dòng)的時(shí)候一直按住左鍵(也就是 if 里面的內(nèi)容),那么就計(jì)算一個(gè) manhattanLength() 值。從字面上翻譯,這是個(gè)“曼哈頓長(zhǎng)度”。這是什么意思呢?我們看一下 event.pos() - startPos 是什么。還記得在 mousePressEvent() 函數(shù)中,我們將鼠標(biāo)按下的坐標(biāo)記錄為 startPos,而 event.pos() 則是鼠標(biāo)當(dāng)前的坐標(biāo):一個(gè)點(diǎn)減去另外一個(gè)點(diǎn),沒(méi)錯(cuò),這就是向量!其實(shí),所謂曼哈頓距離就是兩點(diǎn)之間的距離(至于為什么叫這個(gè)奇怪的名字,大家查查百科就知道啦!),也就是這個(gè)向量的長(zhǎng)度。下面又是一個(gè)判斷,如果大于 QApplication::startDragDistance(),我們才進(jìn)行 drag 的操作。當(dāng)然,最后還是要調(diào)用系統(tǒng)默認(rèn)的鼠標(biāo)拖動(dòng)函數(shù)。這一判斷的意義在于,防止用戶(hù)因?yàn)槭值亩秳?dòng)等因素造成的鼠標(biāo)拖動(dòng)。用戶(hù)必須將鼠標(biāo)拖動(dòng)一段距離之后,我們才認(rèn)為他是希望進(jìn)行拖動(dòng)操作,而這一距離就是 QApplication::startDragDistance() 提供的,這個(gè)值通常是 4px。

performDrag() 開(kāi)始處理拖放過(guò)程。我們創(chuàng)建了一個(gè) QDrag 對(duì)象,將 this 作為 parent。QDrag 使用 QMimeData 存儲(chǔ)數(shù)據(jù)。例如我們使用 QMimeData::setText() 函數(shù)將一個(gè)字符串存儲(chǔ)為 text/plain 類(lèi)型的數(shù)據(jù)。QMimeData 提供了很多函數(shù),用于存儲(chǔ)諸如 URL、顏色等類(lèi)型的數(shù)據(jù)。使用 QDrag::setPixmap() 則可以設(shè)置拖動(dòng)發(fā)生時(shí)鼠標(biāo)的樣式。QDrag::exec() 會(huì)阻塞拖動(dòng)的操作,直到用戶(hù)完成操作或者取消操作。它接受不同類(lèi)型的動(dòng)作作為參數(shù),返回值是真正執(zhí)行的動(dòng)作。這些動(dòng)作的類(lèi)型為 Qt::CopyAction,Qt::MoveAction 和 Qt::LinkAction。返回值會(huì)有這三種動(dòng)作,同時(shí)增加一個(gè) Qt::IgnoreAction 用于表示用戶(hù)取消了拖放。這些動(dòng)作取決于拖放源對(duì)象允許的類(lèi)型,目的對(duì)象接受的類(lèi)型以及拖放時(shí)按下的鍵盤(pán)按鍵。在 exec() 調(diào)用之后,Qt 會(huì)在拖放對(duì)象不需要的時(shí)候 delete 掉它。

ProjectListWidget 不僅能夠發(fā)出拖動(dòng)事件,而且能夠接受同一應(yīng)用程序中的不同 ProjectListWidget 對(duì)象的數(shù)據(jù)。在 dragEnterEvent() 中,我們使用 event->source() 獲取這樣的對(duì)象:如果拖放數(shù)據(jù)來(lái)自同一類(lèi)型的對(duì)象,并且來(lái)自同一應(yīng)用程序則返回其指針,否則返回 NULL。我們使用 qobject_cast 宏將指針轉(zhuǎn)換成 ProjectListWidget* 類(lèi)型,然后設(shè)置接受 Qt::MoveAction 類(lèi)型的拖動(dòng)。dragMoveEvent() 則和這個(gè)函數(shù)具有相同的代碼,因?yàn)槲覀冃枰貙?xiě)拖動(dòng)移動(dòng)的代碼。

最后在 dropEvent() 函數(shù)中,我們?nèi)〕?QDrag 中的 mimeData 數(shù)據(jù),調(diào)用 addItem() 添加到當(dāng)前的列表中。這樣,一個(gè)相對(duì)完整的拖放的代碼就完成了。

拖放技術(shù)是 Qt 中功能強(qiáng)大的一個(gè)技術(shù),但是對(duì)于不涉及數(shù)據(jù)的同一組件中拖動(dòng),或許僅僅簡(jiǎn)單的實(shí)現(xiàn) mouse event 就足夠了,具體還是要自己斟酌啦!

本文出自 “豆子空間” 博客,請(qǐng)務(wù)必保留此出處 http://devbean.blog.51cto.com/448512/193918

以上內(nèi)容是否對(duì)您有幫助:
在線(xiàn)筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)