拖放,拖動(dòng)和釋放(Drag and Drop),允許用戶利用鼠標(biāo)在不同組件之間或一個(gè)組件上進(jìn)行數(shù)據(jù)的拖動(dòng)。如果哪個(gè)組件支持拖放操作,就在該組件的設(shè)計(jì)上實(shí)現(xiàn)某些必要的函數(shù)。比如說(shuō),我們可能需要在MainWindow這個(gè)主窗口上實(shí)現(xiàn)拖放,那就在這個(gè)類中實(shí)現(xiàn)一些函數(shù)。如果我們自定義了一個(gè)組件,比如說(shuō)定義了一個(gè)類MyDnDWidget繼承自QWidget,我們就在MyDndWidget類中實(shí)現(xiàn)特定的函數(shù)來(lái)支持拖放。
QMimeData類是Qt內(nèi)部的一個(gè)容器,它記錄著大量擁有Mime Type(元類型)的數(shù)據(jù)。可以把它當(dāng)成是一個(gè)map,每一個(gè)元素都是”Mime Type, Mime Data”這樣的鍵值對(duì),但是使用的時(shí)候不要像map一樣使用,只是用于理解。
拖放操作不只局限在文本和圖像上,任何類型的信息在拖放操作中都能被轉(zhuǎn)換。為了在應(yīng)用之間拖動(dòng)信息,這些應(yīng)用彼此都應(yīng)該知道他們?cè)试S接收哪種數(shù)據(jù)格式,同時(shí)也應(yīng)該知道他們可以產(chǎn)生哪種數(shù)據(jù)格式。這個(gè)是由MIME Types實(shí)現(xiàn)的。
被構(gòu)造的QDrag對(duì)象包含MIME Types的列表,它能使用這些去表現(xiàn)數(shù)據(jù),同時(shí)接收拖動(dòng)數(shù)據(jù)的那個(gè)窗體使用這些MIME Types中的一個(gè)去訪問(wèn)數(shù)據(jù)。Mime Type可以是Qt中提供的,也可以是用戶自定義的,如果需要自定義一個(gè)Mime Type,可以重新實(shí)現(xiàn)mimeTypes()函數(shù):
mimeTypes
QStringList QAbstractItemModel::mimeTypes() const這個(gè)函數(shù)返回允許的Mime Types列表。默認(rèn)情況下,內(nèi)置的models和views使用內(nèi)部的Mime Type,比如text/uri-list。 當(dāng)重新實(shí)現(xiàn)一個(gè)支持拖放操作的model時(shí),如果設(shè)計(jì)者想要返回的數(shù)據(jù)格式不同于內(nèi)部的Mime Type,那就需要重新實(shí)現(xiàn)這個(gè)函數(shù)來(lái)返回自定義的Mime Types列表。通常的實(shí)現(xiàn)形式為:
QStringList types;types << /* the custom mime types */;return types;如果在自定義model中重新實(shí)現(xiàn)了這個(gè)函數(shù),那么同時(shí)也需要重新實(shí)現(xiàn)mimeData()和dropMimeData()函數(shù):
mimeData
QMimeData *QAbstractItemModel::mimeData(const QModelIndexList &indexes) const這個(gè)函數(shù)返回一個(gè)QMimeData *類型的對(duì)象,這個(gè)對(duì)象包含序列化的數(shù)據(jù)元素,而那些數(shù)據(jù)元素存儲(chǔ)在形參indexes中。換句話說(shuō),就是將模型索引列表indexes中的數(shù)據(jù)序列化,然后存儲(chǔ)在一個(gè)QMimeData中,最后返回這個(gè)QMimeData指針。序列化的方法是使用QByteArray和QDataStream兩個(gè)類。通常的實(shí)現(xiàn)格式為:
if(indexes.isEmpty()) return 0;QMimeData *mimeData = new QMimeData;QByteArray encodeData;QDataStream stream(&encodeData, QIODevice::WriteOnly);foreach(QModelIndex index, indexes){ if(index.value()) { /* 將index中存儲(chǔ)的數(shù)據(jù)取出來(lái) */ stream << /* 數(shù)據(jù)1 */ << /* 數(shù)據(jù)2 */ << /* ... */; }}mimeData->setData(/* your custom mime types */, encodeData);return mimeData;需要注意的是,當(dāng)模型索引列表indexes為空時(shí),返回0;
dropMimeData
bool QAbstractItemModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent)當(dāng)Qt檢測(cè)到有Mime Data被釋放到以該模型為model的組件上時(shí),相應(yīng)model的這個(gè)函數(shù)被調(diào)用。設(shè)計(jì)者需要重新實(shí)現(xiàn)這個(gè)函數(shù)來(lái)處理被釋放的數(shù)據(jù),通常是將數(shù)據(jù)存儲(chǔ)到(插入或追加到)model的數(shù)據(jù)結(jié)構(gòu)中。如果這個(gè)釋放動(dòng)作和數(shù)據(jù)被model處理則返回true,否則返回false; 參數(shù)中的row, column, parent指明數(shù)據(jù)釋放的位置。如果row和column為-1,意味著被釋放的數(shù)據(jù)釋放到了parent上,通常是追加到parent的孩子中。如果row和column大于等于0,則被插入到parent孩子的特定位置。 通常的實(shí)現(xiàn)格式為:
if(!data->hasFormats(/* the custom mime types */) return false;if(action == Qt::IgnoreAction) return true;/* 確定插入的位置position */QByteArray encodeData = data->data(/* the custom mime types */);QDataStream stream(&encodeData, Qt::ReadOnly);while(!stream.atEnd()){ stream >> /* 數(shù)據(jù)1 */ >> /* 數(shù)據(jù)2 */ >> /* ... */; beginInsertRows(parent, position, position); /* model內(nèi)部數(shù)據(jù)結(jié)構(gòu)執(zhí)行插入操作 */ endInsertRows(); ++position;}return true;在拖放模型中,用戶既可以拖動(dòng)信息的拷貝(復(fù)制)也可以拖動(dòng)信息本身(剪切)到一個(gè)新地方。但是直到拖放操作完成之后程序才知道用戶是想剪切還是想拷貝。這在拖動(dòng)不同組件之間的信息時(shí)沒(méi)有什么影響,但是在同一個(gè)組件上拖動(dòng)時(shí),檢查使用了哪種drop action是很重要的。
Qt::DropAction
Qt::MoveAction 移動(dòng)數(shù)據(jù)本身(剪切)Qt::CopyAction 復(fù)制數(shù)據(jù)(復(fù)制)Qt::IgnoreAction 忽略數(shù)據(jù)在釋放數(shù)據(jù)的時(shí)候,Qt會(huì)調(diào)用鼠標(biāo)所在位置上組件的dropEvent()函數(shù),設(shè)計(jì)者需要在這個(gè)函數(shù)中告訴拖放操作的數(shù)據(jù)源drop action以便數(shù)據(jù)源確定是復(fù)制還是剪切。
dropEvent
void QWidget::dropEvent(QDropEvent *event)通知數(shù)據(jù)源drop action
event->setDropAction(Qt::MoveAction); /* 通知數(shù)據(jù)源刪除原數(shù)據(jù) */event->accept();或者
event->setDropAction(Qt::CopyAction); /* 通知數(shù)據(jù)源保留原數(shù)據(jù) */event->accept();通常拖動(dòng)操作都是開始于鼠標(biāo)點(diǎn)擊(mousePRessEvent()函數(shù)),而每個(gè)拖動(dòng)操作都是一個(gè)QDrag類,所以在鼠標(biāo)點(diǎn)擊函數(shù)中需要定義一個(gè)QDrag類,然后執(zhí)行拖動(dòng)操作,等待釋放動(dòng)作的反饋。可以把這個(gè)函數(shù)看成數(shù)據(jù)源。
mousePressEvent
void QWidget::mousePressEvent(QMouseEvent *event){ /* ... */ /* 取出點(diǎn)擊位置的數(shù)據(jù),進(jìn)行序列化 */ QDrag *drag = new QDrag(this); drag->setMimeData(mimeData); Qt::DropAction dropAction = drag->exec(Qt::CopyAction | Qt::MoveAction); if(dropAction == Qt::CopyAction || dropAction == IgnoreAction) { /* 不需要?jiǎng)h除原數(shù)據(jù) */ } else if(dropAction == Qt::MoveAction) { /* 刪除原數(shù)據(jù) */ update(/* event->pos()處的范圍矩形 */) }}下面這些類處理拖放和必要的mime type的編碼和解碼: QDrag:為以Mime Data為基礎(chǔ)的拖放轉(zhuǎn)化提供支持。 QDragEnterEvent:這個(gè)類型的事件被發(fā)送到窗口如果有數(shù)據(jù)被拖進(jìn)這個(gè)窗口。 QDragLeaveEvent:這個(gè)類型的事件被發(fā)送到窗口如果有數(shù)據(jù)被拖離這個(gè)窗口。 QDragMoveEvent:當(dāng)正在拖動(dòng)時(shí),這個(gè)類型的事件會(huì)一直被發(fā)出。 QDropEvent:當(dāng)拖放操作完成時(shí),這個(gè)類型的時(shí)候被發(fā)出。
在實(shí)現(xiàn)拖放操作時(shí)就是圍繞這幾個(gè)類來(lái)實(shí)現(xiàn)特定的函數(shù)。
dragEnterEvent
void QWidget::dragEnterEvent(QDragEnterEvent *event);protected作用域的函數(shù)。當(dāng)鼠標(biāo)拖進(jìn)一個(gè)窗體的時(shí)候,該窗體的這個(gè)函數(shù)被調(diào)用,傳遞的事件作為參數(shù)。如果這個(gè)事件被忽略了(ignored),那么這個(gè)窗體不會(huì)接收任何拖動(dòng)事件。換句話說(shuō)這個(gè)函數(shù)就是用于提醒Qt該組件接收的數(shù)據(jù)類型,判斷是accept()還是ignore()這個(gè)事件。通常的實(shí)現(xiàn)格式為:
if(event->mimeData()->hasFormats(/* the custom mime types */) event->accept();else event->ignore();dragLeaveEvent
void QWidget::dragLeaveEvent(QDragLeaveEvent *event);顧名思義,這個(gè)函數(shù)就是當(dāng)拖動(dòng)超出該組件范圍時(shí)調(diào)用的。
dragMoveEvent
void QWidget::dragMoveEvent(QDragMoveEvent *event);當(dāng)正在拖動(dòng)時(shí),會(huì)一直調(diào)用這個(gè)函數(shù)??梢愿淖円恍┳兞勘热缬涗洿藭r(shí)的投影矩形,然后在繪制函數(shù)中繪制。
paintEvent
void QWidget::paintEvent(QPaintEvent *event);這個(gè)函數(shù)就是用于繪制所在類的。它可以被調(diào)用由以下理由: 1.repaint()或者update()函數(shù)被調(diào)用; 2.窗口部件被遮擋并且已經(jīng)被Qt發(fā)現(xiàn); 3.其他的理由
繪制的時(shí)候使用畫刷類QPainter
QPainter painter;painter.begin(this);/* 繪制代碼 */painter.end();新聞熱點(diǎn)
疑難解答
圖片精選
網(wǎng)友關(guān)注