在图形视图框架中,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 }