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