程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> 關於C語言 >> Qt學習之路(53): 拖放技術之二

Qt學習之路(53): 拖放技術之二

編輯:關於C語言

很長時間沒有來寫博客了,前段時間一直在幫同學弄一個 spring-mvc 的項目,今天終於做完了,不過公司裡面又要開始做 flex 4,估計還會忙一段時間吧! 接著上次的說,上次說到了拖放技術,今天依然是一個例子,同樣是來自《C++ GUI Programming with Qt 4, 2nd Edition》的。 這次的 demo 還算是比較實用:實現的是兩個 list 之間的數據互拖。在很多項目中,這一需求還是比較常見的吧!下面也就算是拋磚引玉了啊! projectlistwidget.h

  1. #ifndef PROJECTLISTWIDGET_H  
  2. #define PROJECTLISTWIDGET_H  
  3.  
  4. #include <QtGui>  
  5.  
  6. class ProjectListWidget : public QListWidget  
  7. {  
  8.     Q_OBJECT  
  9.  
  10. public:  
  11.     ProjectListWidget(QWidget *parent = 0);  
  12.  
  13. protected:  
  14.     void mousePressEvent(QMouseEvent *event);  
  15.     void mouseMoveEvent(QMouseEvent *event);  
  16.     void dragEnterEvent(QDragEnterEvent *event);  
  17.     void dragMoveEvent(QDragMoveEvent *event);  
  18.     void dropEvent(QDropEvent *event);  
  19.  
  20. private:  
  21.     void performDrag();  
  22.  
  23.     QPoint startPos;  
  24. };  
  25.  
  26. #endif // PROJECTLISTWIDGET_H 
projectlistwidget.cpp
  1. #include "projectlistwidget.h"  
  2.  
  3. ProjectListWidget::ProjectListWidget(QWidget *parent)  
  4.     : QListWidget(parent)  
  5. {  
  6.     setAcceptDrops(true);  
  7. }  
  8.  
  9. void ProjectListWidget::mousePressEvent(QMouseEvent *event)  
  10. {  
  11.     if (event->button() == Qt::LeftButton)  
  12.         startPos = event->pos();  
  13.     QListWidget::mousePressEvent(event);  
  14. }  
  15.  
  16. void ProjectListWidget::mouseMoveEvent(QMouseEvent *event)  
  17. {  
  18.     if (event->buttons() & Qt::LeftButton) {  
  19.         int distance = (event->pos() - startPos).manhattanLength();  
  20.         if (distance >= QApplication::startDragDistance())  
  21.             performDrag();  
  22.     }  
  23.     QListWidget::mouseMoveEvent(event);  
  24. }  
  25.  
  26. void ProjectListWidget::performDrag()  
  27. {  
  28.     QListWidgetItem *item = currentItem();  
  29.     if (item) {  
  30.         QMimeData *mimeData = new QMimeData;  
  31.         mimeData->setText(item->text());  
  32.  
  33.         QDrag *drag = new QDrag(this);  
  34.         drag->setMimeData(mimeData);  
  35.         drag->setPixmap(QPixmap(":/images/person.png"));  
  36.         if (drag->exec(Qt::MoveAction) == Qt::MoveAction)  
  37.             delete item;  
  38.     }  
  39. }  
  40.  
  41. void ProjectListWidget::dragEnterEvent(QDragEnterEvent *event)  
  42. {  
  43.     ProjectListWidget *source =  
  44.             qobject_cast<ProjectListWidget *>(event->source());  
  45.     if (source && source != this) {  
  46.         event->setDropAction(Qt::MoveAction);  
  47.         event->accept();  
  48.     }  
  49. }  
  50.  
  51. void ProjectListWidget::dragMoveEvent(QDragMoveEvent *event)  
  52. {  
  53.     ProjectListWidget *source =  
  54.             qobject_cast<ProjectListWidget *>(event->source());  
  55.     if (source && source != this) {  
  56.         event->setDropAction(Qt::MoveAction);  
  57.         event->accept();  
  58.     }  
  59. }  
  60.  
  61. void ProjectListWidget::dropEvent(QDropEvent *event)  
  62. {  
  63.     ProjectListWidget *source =  
  64.             qobject_cast<ProjectListWidget *>(event->source());  
  65.     if (source && source != this) {  
  66.         addItem(event->mimeData()->text());  
  67.         event->setDropAction(Qt::MoveAction);  
  68.         event->accept();  
  69.     }  
  70. }  
我們從構造函數開始看起。Qt 中很多組件是可以接受拖放的,但是默認動作都是不允許的,因此在構造函數中,我們調用 setAcceptDrops(true); 函數,讓組件能夠接受拖放事件。 在 mousePressEvent() 函數中,我們檢測鼠標左鍵點擊,如果是的話就記錄下當前位置。需要注意的是,這個函數最後需要調用系統自帶的處理函數,以便實現通常的那種操作。這在一些重寫事件的函數中都是需要注意的! 然後我們重寫了 mouseMoveEvent() 事件。下面還是先來看看代碼:
  1. void ProjectListWidget::mouseMoveEvent(QMouseEvent *event)  
  2. {  
  3.     if (event->buttons() & Qt::LeftButton) {  
  4.         int distance = (event->pos() - startPos).manhattanLength();  
  5.         if (distance >= QApplication::startDragDistance())  
  6.             performDrag();  
  7.     }  
  8.     QListWidget::mouseMoveEvent(event);  
在這裡判斷了如果鼠標拖動的時候一直按住左鍵(也就是 if 裡面的內容),那麼就計算一個 manhattanLength() 值。從字面上翻譯,這是個“曼哈頓長度”。這是什麼意思呢?我們看一下 event.pos() - startPos 是什麼。還記得在 mousePressEvent() 函數中,我們將鼠標按下的坐標記錄為 startPos,而 event.pos() 則是鼠標當前的坐標:一個點減去另外一個點,沒錯,這就是向量!其實,所謂曼哈頓距離就是兩點之間的距離(至於為什麼叫這個奇怪的名字,大家查查百科就知道啦!),也就是這個向量的長度。下面又是一個判斷,如果大於 QApplication::startDragDistance(),我們才進行 drag 的操作。當然,最後還是要調用系統默認的鼠標拖動函數。這一判斷的意義在於,防止用戶因為手的抖動等因素造成的鼠標拖動。用戶必須將鼠標拖動一段距離之後,我們才認為他是希望進行拖動操作,而這一距離就是 QApplication::startDragDistance() 提供的,這個值通常是 4px。 performDrag() 開始處理拖放過程。我們創建了一個 QDrag 對象,將 this 作為 parent。QDrag 使用 QMimeData 存儲數據。例如我們使用 QMimeData::setText() 函數將一個字符串存儲為 text/plain 類型的數據。QMimeData 提供了很多函數,用於存儲諸如 URL、顏色等類型的數據。使用 QDrag::setPixmap() 則可以設置拖動發生時鼠標的樣式。QDrag::exec() 會阻塞拖動的操作,直到用戶完成操作或者取消操作。它接受不同類型的動作作為參數,返回值是真正執行的動作。這些動作的類型為 Qt::CopyAction,Qt::MoveAction 和 Qt::LinkAction。返回值會有這三種動作,同時增加一個 Qt::IgnoreAction 用於表示用戶取消了拖放。這些動作取決於拖放源對象允許的類型,目的對象接受的類型以及拖放時按下的鍵盤按鍵。在 exec() 調用之後,Qt 會在拖放對象不需要的時候 delete 掉它。 ProjectListWidget 不僅能夠發出拖動事件,而且能夠接受同一應用程序中的不同 ProjectListWidget 對象的數據。在 dragEnterEvent() 中,我們使用 event->source() 獲取這樣的對象:如果拖放數據來自同一類型的對象,並且來自同一應用程序則返回其指針,否則返回 NULL。我們使用 qobject_cast 宏將指針轉換成 ProjectListWidget* 類型,然後設置接受 Qt::MoveAction 類型的拖動。dragMoveEvent() 則和這個函數具有相同的代碼,因為我們需要重寫拖動移動的代碼。 最後在 dropEvent() 函數中,我們取出 QDrag 中的 mimeData 數據,調用 addItem() 添加到當前的列表中。這樣,一個相對完整的拖放的代碼就完成了。 拖放技術是 Qt 中功能強大的一個技術,但是對於不涉及數據的同一組件中拖動,或許僅僅簡單的實現 mouse event 就足夠了,具體還是要自己斟酌啦!

本文出自 “豆子空間” 博客,請務必保留此出處http://devbean.blog.51cto.com/448512/286796

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved