在圖形視圖框架中,QGraphicsScene 提供一個快速的接口,用於管理大量 item,QGraphicsItem 是場景中 item 的基類。
圖形視圖提供了一些典型形狀的標准 item,當然,我們也可以自定義 item。除此之外,QGraphicsItem 還支持以下特性:
- 鼠標按下、移動、釋放和雙擊事件,以及鼠標懸浮事件、滾輪事件和上下文菜單事件
- 鍵盤輸入焦點和鍵盤事件
- 拖放
- 分組:通過父子關系,或 QGraphicsItemGroup
- 碰撞檢測
下面,一起來看看 QGraphicsScene 對 QGraphicsItem 的管理,主要包括:單擊、選擇、移動、縮放、刪除等。
為了實現以上功能,我們主要實現了 QGraphicsScene 和 QGraphicsItem 對應的事件,通過鼠標和鍵盤來操作。
操作細節主要包括:
- 選擇:點擊左鍵、按 Shift 鍵可以單選,按下 Ctrl 可進行多選。
- 添加:點擊左鍵
- 刪除:點擊右鍵,刪除鼠標下的 item;當按下 Ctrl 選擇多個 items 時,按下 Backspace 鍵,將選中的全部刪除。
- 移動:點擊左鍵,選擇 item,然后移動鼠標;當按下 Ctrl 選擇多個 items 時,可以移動選中的 items。
- 縮放:按 Alt 鍵,然后鼠標拖拽 item 的邊界。
在對應操作的事件中,我們輸出了一些調試信息,以便跟蹤。

源碼
custom_item.h:
1 #ifndef CUSTOM_ITEM_H 2 #define CUSTOM_ITEM_H
3
4 #include <QGraphicsRectItem>
5 #include <QGraphicsScene>
6
7 //QGraphicsScene管理QGraphicsItem(單擊/選擇/移動/縮放/刪除) 8 // 自定義 Item
9 class CustomItem : public QGraphicsRectItem 10 { 11 public: 12 explicit CustomItem(QGraphicsItem *parent = 0); 13 protected: 14 // Shift+左鍵:進行選擇 Alt:准備縮放
15 void mousePressEvent(QGraphicsSceneMouseEvent *event); 16 // Alt+拖拽:進行縮放 移動
17 void mouseMoveEvent(QGraphicsSceneMouseEvent *event); 18 void mouseReleaseEvent(QGraphicsSceneMouseEvent *event); 19 // 使item可使用qgraphicsitem_cast
20 int type() const; 21 private: 22 QPointF m_centerPointF; 23 bool m_bResizing; 24 }; 25
26 // 自定義 Scene
27 class CustomScene : public QGraphicsScene 28 { 29 protected: 30 // 左鍵:添加item 右鍵:移除item
31 void mousePressEvent(QGraphicsSceneMouseEvent *event); 32 void mouseMoveEvent(QGraphicsSceneMouseEvent *event); 33 // Backspace鍵移除item
34 void keyPressEvent(QKeyEvent *event); 35 }; 36
37 #endif // CUSTOM_ITEM_H
custom_item.cpp:
1 #include <QKeyEvent>
2 #include <QGraphicsSceneMouseEvent>
3 #include <QDebug>
4 #include "custom_item.h"
5
6 // 自定義 Item
7 CustomItem::CustomItem(QGraphicsItem *parent) 8 : QGraphicsRectItem(parent) 9 { 10 // 畫筆 - 邊框色
11 QPen p = pen(); 12 p.setWidth(2); 13 p.setColor(QColor(0, 160, 230)); 14
15 setPen(p); 16 // 畫刷 - 背景色
17 setBrush(QColor(247, 160, 57)); 18
19 // 可選擇、可移動
20 setFlags(QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsMovable); 21 } 22
23 void CustomItem::mousePressEvent(QGraphicsSceneMouseEvent *event) 24 { 25 if (event->button() == Qt::LeftButton) { 26 if (event->modifiers() == Qt::ShiftModifier) { 27 qDebug() << "Custom item left clicked with shift key."; 28 // 選中 item
29 setSelected(true); 30 } else if (event->modifiers() == Qt::AltModifier) { 31 qDebug() << "Custom item left clicked with alt key."; 32 // 重置 item 大小
33 double radius = boundingRect().width() / 2.0; 34 QPointF topLeft = boundingRect().topLeft(); 35 m_centerPointF = QPointF(topLeft.x() + pos().x() + radius, topLeft.y() + pos().y() + radius); 36 QPointF pos = event->scenePos(); 37 qDebug() << boundingRect() << radius << this->pos() << pos << event->pos(); 38 double dist = sqrt(pow(m_centerPointF.x()-pos.x(), 2) + pow(m_centerPointF.y()-pos.y(), 2)); 39 if (dist / radius > 0.8) { 40 qDebug() << dist << radius << dist / radius; 41 m_bResizing = true; 42 } else { 43 m_bResizing = false; 44 } 45 } else { 46 qDebug() << "Custom item left clicked."; 47 QGraphicsItem::mousePressEvent(event); 48 event->accept(); 49 } 50 } else if (event->button() == Qt::RightButton) { 51 qDebug() << "Custom item right clicked."; 52 event->ignore(); 53 } 54 } 55
56 void CustomItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event) 57 { 58 if ((event->modifiers() == Qt::AltModifier) && m_bResizing) { 59 QPointF pos = event->scenePos(); 60 double dist = sqrt(pow(m_centerPointF.x()-pos.x(), 2) + pow(m_centerPointF.y()-pos.y(), 2)); 61 setRect(m_centerPointF.x()-this->pos().x()-dist, m_centerPointF.y()-this->pos().y()-dist, dist*2, dist*2); 62 } else if(event->modifiers() != Qt::AltModifier) { 63 qDebug() << "Custom item moved."; 64 QGraphicsItem::mouseMoveEvent(event); 65 qDebug() << "moved" << pos(); 66 } 67 } 68
69 void CustomItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) 70 { 71 if ((event->modifiers() == Qt::AltModifier) && m_bResizing) { 72 m_bResizing = false; 73 } else { 74 QGraphicsItem::mouseReleaseEvent(event); 75 } 76 } 77
78 int CustomItem::type() const
79 { 80 return UserType + 1; 81 } 82
83 // 自定義 Scene
84 void CustomScene::mousePressEvent(QGraphicsSceneMouseEvent *event) 85 { 86 qDebug() << "Custom scene clicked."; 87 QGraphicsScene::mousePressEvent(event); 88 if (!event->isAccepted()) { 89 if (event->button() == Qt::LeftButton) { 90 // 在 Scene 上添加一個自定義 item
91 QPointF point = event->scenePos(); 92 CustomItem *item = new CustomItem(); 93 item->setRect(point.x()-25, point.y()-25, 60, 60); 94 addItem(item); 95 } else if (event->button() == Qt::RightButton) { 96 // 檢測光標下是否有 item
97 QGraphicsItem *itemToRemove = NULL; 98 foreach (QGraphicsItem *item, items(event->scenePos())) { 99 if (item->type() == QGraphicsItem::UserType+1) { 100 itemToRemove = item; 101 break; 102 } 103 } 104 // 從 Scene 上移除 item
105 if (itemToRemove != NULL) 106 removeItem(itemToRemove); 107 } 108 } 109 } 110
111 void CustomScene::mouseMoveEvent(QGraphicsSceneMouseEvent *event) 112 { 113 qDebug() << "Custom scene moved."; 114 QGraphicsScene::mouseMoveEvent(event); 115 } 116
117 void CustomScene::keyPressEvent(QKeyEvent *event) { 118 if (event->key() == Qt::Key_Backspace) { 119 // 移除所有選中的 items
120 qDebug() << "selected items " << selectedItems().size(); 121 while (!selectedItems().isEmpty()) { 122 removeItem(selectedItems().front()); 123 } 124 } else { 125 QGraphicsScene::keyPressEvent(event); 126 } 127 }
使用很簡單,將 item 添加至 scene 中,通過 view 顯示即可。
1 #include <QApplication>
2 #include <QGraphicsView>
3 #include "custom_item.h"
4
5 int main(int argc, char *argv[]) 6 { 7 QApplication a(argc, argv); 8
9 // 創建 item
10 CustomItem *pItem = new CustomItem(); 11 pItem->setRect(20, 20, 60, 60); 12
13 // 將 item 添加至場景中
14 CustomScene scene; 15 scene.setSceneRect(0, 0, 400, 300); 16 scene.addItem(pItem); 17
18 // 為視圖設置場景
19 QGraphicsView view; 20 view.setScene(&scene); 21 view.show(); 22
23 return a.exec(); 24 }
