工作上用 QT 已經有一陣子了,雖然部門裡只有我一個人在用,但是其它人在用的 Java 和 C# 看起來實在不熟悉,感覺入門又要更久了。學習QT的過程中,也是一直拼拼湊湊,比起純粹的C,原理的確是不求甚解。想說把自己學習 QT 的知識,紀錄下來,這樣下次就不用再拼湊了~ 畢竟這不是工作中的主要程式語言,不常用記不住。
QT 使用版本
本文 QT 使用的版本是 qt-opensource-windows-x86-mingw492-5.6.2.exe,可以直接在網上搜檔名,以後位置如果變更也可以下載的到。安裝的話,一直下一步即可。之前測試的過程中,發現有些例子在不同版本會不相容,請儘量使用此版本,以確保可產生同樣的結果。
導入 Dragdrop 範例
開啟 「Qt Creator」,選擇「範例」,再搜尋中選擇輸入 「drag」,選擇 「Draggable Icons Example」。
畫面會跳出一個說明,直接關掉即可。還有一個 Configure Project 的對話框,直接按下「Configure Project」即可。
接著按下執行,可以看到出現左右兩個區域,裡面的 ICON 在同區域拖拉會移動位置,而跨區則可複製一份。
程式碼說明
這個範例的主體是 class DragWidget,是由 QFrame 所洐生出來的,其包含4個成員函式。
- mousePressEvent: 滑鼠按下,準備開始拖拉
 - dragEnterEvent: 拖拉進入區域
 - dragMoveEvent: 拖拉移動
 - dropEvent: 滑鼠放開 drop
 
main() 中會產生一個 QHBoxLayout ,然後使用 addWidget 來加入兩個 dragWidget 的 instances。dragWidget 的 constructor 會產生三個 icon,這邊的 icon 是以 QLabel 來實現的,其圖示是以 setPixmap() 來指定圖檔,造成 icon 的效果。
| 
					 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25  | 
						DragWidget::DragWidget(QWidget *parent)     : QFrame(parent) {     setMinimumSize(200, 200);     setFrameStyle(QFrame::Sunken | QFrame::StyledPanel);     setAcceptDrops(true);     QLabel *boatIcon = new QLabel(this);     boatIcon->setPixmap(QPixmap(":/images/boat.png"));     boatIcon->move(10, 10);     boatIcon->show();     boatIcon->setAttribute(Qt::WA_DeleteOnClose);     QLabel *carIcon = new QLabel(this);     carIcon->setPixmap(QPixmap(":/images/car.png"));     carIcon->move(100, 10);     carIcon->show();     carIcon->setAttribute(Qt::WA_DeleteOnClose);     QLabel *houseIcon = new QLabel(this);     houseIcon->setPixmap(QPixmap(":/images/house.png"));     houseIcon->move(10, 80);     houseIcon->show();     houseIcon->setAttribute(Qt::WA_DeleteOnClose); }  | 
					
mousePressEvent()
mousePresssEvent() 是在滑鼠按下時觸發的,程式首先透過 childAt(event->pos())取得該位置的QLabel元件,有取到才繼續。透過一些資料的操作,該 Label 的 pixmap 與 pos 被存入了 QDrag 的新物件中,QDrag 的 pixmap 也被設為拖拉的圖示,而原 QLabel 的 pixmap 則被改為網點。
最後呼叫了 drag->exec () 來執行拖拉的行為,當拖拉行為返回時,若動作是 MOVE,則舊的 QLabel 會被 close (刪除)。否則則是恢復其 pixmap,顯示原狀。這也就是一開始講的,同區域就移動跨區域就複製。所以所謂的 MOVE,其實是在新位置複製一份,再刪除舊的。
| 
					 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35  | 
						void DragWidget::mousePressEvent(QMouseEvent *event) {     QLabel *child = static_cast<QLabel*>(childAt(event->pos()));     if (!child)         return;     QPixmap pixmap = *child->pixmap();     QByteArray itemData;     QDataStream dataStream(&itemData, QIODevice::WriteOnly);     dataStream << pixmap << QPoint(event->pos() - child->pos());     QMimeData *mimeData = new QMimeData;     mimeData->setData("application/x-dnditemdata", itemData);     QDrag *drag = new QDrag(this);     drag->setMimeData(mimeData);     drag->setPixmap(pixmap);     drag->setHotSpot(event->pos() - child->pos());     QPixmap tempPixmap = pixmap;     QPainter painter;     painter.begin(&tempPixmap);     painter.fillRect(pixmap.rect(), QColor(127, 127, 127, 127));     painter.end();     child->setPixmap(tempPixmap);     if (drag->exec(Qt::CopyAction | Qt::MoveAction, Qt::CopyAction) == Qt::MoveAction) {         child->close();     } else {         child->show();         child->setPixmap(pixmap);     } }  | 
					
dragEnterEvent() and dragMoveEvent()
這兩個 event 分別是在移到和進入不同區域時呼叫,首先判斷這個 event 的來源是不是 “application/x-dnditemdata”,這是在一開始 drag 時設置的,若有才代表是原先的動作,不是則忽略。
接著判斷來源於是否等於目前的區域,若是同區的,則動作永遠設為 MOVE。不同區則依照系統的,這也是符合同區為移動,跨區為產生新的行為設定。
| 
					 1 2 3 4 5 6 7 8 9 10 11 12 13  | 
						void DragWidget::dragEnterEvent(QDragEnterEvent *event) {     if (event->mimeData()->hasFormat("application/x-dnditemdata")) {         if (event->source() == this) {             event->setDropAction(Qt::MoveAction);             event->accept();         } else {             event->acceptProposedAction();         }     } else {         event->ignore();     } }  | 
					
dropEvent()
最後則是當滑鼠放開的 dropEvent,如同前面,也是先判斷這個 event 的來源是不是 “application/x-dnditemdata”。將之前存入 QDrag 的一些參數,從 QDropEvent 中取出。接著產生一個新的 QLabel,設定其 pixmap 與位置。接著一樣是根據其來源,設定其為 MOVE 或接受系統設定。
| 
					 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26  | 
						void DragWidget::dropEvent(QDropEvent *event) {     if (event->mimeData()->hasFormat("application/x-dnditemdata")) {         QByteArray itemData = event->mimeData()->data("application/x-dnditemdata");         QDataStream dataStream(&itemData, QIODevice::ReadOnly);         QPixmap pixmap;         QPoint offset;         dataStream >> pixmap >> offset;         QLabel *newIcon = new QLabel(this);         newIcon->setPixmap(pixmap);         newIcon->move(event->pos() - offset);         newIcon->show();         newIcon->setAttribute(Qt::WA_DeleteOnClose);         if (event->source() == this) {             event->setDropAction(Qt::MoveAction);             event->accept();         } else {             event->acceptProposedAction();         }     } else {         event->ignore();     } }  | 
					
透過上面這幾個 event 的實現,就可以完成 dragdrop 的行為了。
結語
乍看之下有點不太懂,但仔細花點時間琢磨,還是能懂程式在寫什麼 : )






Latest Comments