1. 程式人生 > 實用技巧 >Qt Graphics-View拖拽以及滑鼠指標操作

Qt Graphics-View拖拽以及滑鼠指標操作

因為QGraphicsView繼承自QWidget,它也提供了像QWidget那樣的拖拽功能。

另外,為了方便,Graphics View框架也為場景以及每個item提供拖拽支援。當檢視接收到拖拽事件,它可轉化為QGraphicsSceneDragDropEvent,再發送到場景。場景接管這個事件,把它傳送到游標下接受拖拽的第一個item。 從一個item開始拖拽時,建立一個QDrag物件,傳遞開始拖拽的那個widget的指標。Items可以同時被多個檢視觀察,但只有一個檢視可以開始拖拽。

拖拽在多數情況下是從按下滑鼠或是移動滑鼠開始的,因此,在 mousePressEvent()或mouseMoveEvent()中,你可以從事件中得到那個原始的widget指標,例如:

1 void CustomItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
2 {
3     QMimeData *data = new QMimeData;
4     data->setColor(Qt::green);
5     QDrag *drag = new QDrag(event->widget());
6     drag->setMimeData(data);
7     drag->start();
8 }

為了在場景中獲取拖拽事件,你應重新實現QGraphicsScene::dragEnterEvent()和在QGraphicsItem的子類裡任何與你特定場景需要的事件處理器。items也可以通過呼叫QGraphicsItem::setAcceptDrops()獲得拖拽支援,為了處理將要進行的拖拽,你需要重新實現QGraphicsItem::dragEnterEvent(),QGraphicsItem::dragMoveEvent(),QGraphicsItem::dragLeaveEvent()和QGraphicsItem::dropEvent()。

像QWidget一樣,QGraphicsItem也支援游標(QgraphicsItem::setCursor)與工具提示(QGraphicsItem::setToolTip())。當游標進入到item的區域,游標與工具提示被QGraphicsView啟用(通過呼叫QGraphicsItem::contains()檢測)。你也可以直接在檢視上設定一個預設游標(QGraphicsView::setCursor)。

Qt自帶例程Drag and Drop Robot介紹了這兩方面的內容。

在這個demo中,可以把機器人四周的顏色拖動到機器人的各個部位,比如說頭,臂,身軀等,然後這個部位就會變成相應的顏色,類似於換裝小遊戲。

下圖是經過我的一番操作後的機器人模樣:

以下是我學習這個Demo的一些知識總結,僅供交流學習,如有錯誤,歡迎指正,一起進步:

圓形顏色圖元ColorItem

隨機顏色值:QColor(QRandomGenerator::global()->bounded(256),QRandomGenerator::global()->bounded(256),QRandomGenerator::global()->bounded(256));

Tooltips:setToolTip(QString("QColor(%1,%2,%3)\n%4").arg(color.red()).arg(color.green()).arg(color.blue()).arg("Clickanddragthiscolorontotherobot!"));

滑鼠移入圖元手勢 (展開的小手):setCursor(Qt::OpenHandCursor);

繪製(兩個組合圓):
 1 painter->setPen(Qt::NoPen);
 2 painter->setBrush(Qt::darkGray);
 3 painter->drawEllipse(-12,-12, 30, 30);
 4 painter->setPen(QPen(Qt::black, 1));
 5 painter->setBrush(QBrush(color));
 6 painter->drawEllipse(-15,-15, 30, 30);
 7 
 8 滑鼠左鍵按下:setCursor(Qt::ClosedHandCursor);
 9 
10 滑鼠左鍵釋放:setCursor(Qt::OpenHandCursor);
11 
12 滑鼠左鍵移動過程(主要邏輯):
13 
14 // 需要有一個最小移動距離限制(10px)
15 if(QLineF(event->screenPos(), event->buttonDownScreenPos(Qt::LeftButton))
16   .length()< QApplication::startDragDistance()) {
17   return;
18 }
19 
20  
21 
22 // 建立拖動物件,並繫結一個MIME資料
23 QDrag*drag= new QDrag(event->widget());
24 QMimeData*mime= new QMimeData;
25 drag->setMimeData(mime);

PS:QMimeData is used to describe information that can be stored in theclipboard, and transferred via thedrag and dropmechanism.

QMimeData objects are usually created usingnewand supplied toQDragorQClipboardobjects.

對於從剛初始化後兩次以上的拖動,可能會出現人臉的影象,其餘情況則是對原始圓形圖元的拖動。

設定MIME影象資料:QMimeData::setImageData

設定MIME純文字資料:QMimeData::setText

設定MIME顏色資料:QMimeData::setColorData

設定拖動熱點: QDrag::setHotSpot

設定拖動過程中的影象:QDrag::setPixmap

執行拖動: QDrag::exec();

既然有了拖動(Drag),就得有接收拖動的地方,即釋放拖動的地方(Drop)。在本Demo中,執行Drop操作的主體是中間的搖擺機器人Robot。

首先需要使能Drop:setAcceptDrops(true);

具體需要過載以下三個關於Drag-Drop的虛擬函式:

1 void dragEnterEvent(QGraphicsSceneDragDropEvent*event) override;
2 
3 void dragLeaveEvent(QGraphicsSceneDragDropEvent*event) override;
4 
5 void dropEvent(QGraphicsSceneDragDropEvent*event) override;

其中QGraphicsSceneDragDropEvent類攜帶了拖動的mime資料資訊,通過bool型別成員變數dragOver標識拖動是否移動到機器人身體上,移入則響應dragEnterEvent(顏色加亮顯示),移出則響應dragLeaveEvent,並置位dragOver。

釋放操作則響應dropEvent,將傳遞過來的顏色值賦給當前QPainter畫刷顏色,呼叫update來呼叫paint函式重新繪製。

如果頭像移入頭部則繪製頭像圖片。

所有機器人的各部件(頭、胳膊、軀幹)通過Robot類整體組織起來,並加上了動畫。

Rebort不進行任何paint操作:setFlag(ItemHasNoContents);

PS:The item does not paint anything (i.e., callingpaint()on the item has no effect). You should set this flag on items that do not need to be painted to ensure that Graphics View avoids unnecessary painting preparations.

Robot各部位Item的排序

其中,軀幹是Root Item(所有其他Item是children或軀幹的後代),因此首先繪製(1)。 接下來,繪製頭部(2),因為它是軀幹children列表中的第一個專案。 然後繪製左上臂(3), 由於下臂是上臂的孩子,因此下臂被拉動(4),接著是上臂的下一個兄弟,即右上臂(5),依此類推。

頭部動畫(旋轉和縮放屬性):

1 QPropertyAnimation*headAnimation= new QPropertyAnimation(headItem, "rotation");
2 
3 QPropertyAnimation*headScaleAnimation= new QPropertyAnimation(headItem, "scale");

其餘部位的動畫類似…

所有的QPropertyAnimation通過QParallelAnimationGroup組合在一起並行執行。

1 QParallelAnimationGroup*animation= new QParallelAnimationGroup(this);
2 
3 animation->addAnimation(headAnimation);
4 
5 animation->addAnimation(headScaleAnimation);

最後,讓我們繼續搖擺: