一、简述

在图形视图框架中,QGraphicsScene 提供一个快速的接口,用于管理大量 item,QGraphicsItem 是场景中 item 的基类。

图形视图提供了一些典型形状的标准 item,当然,我们也可以自定义 item。除此之外,QGraphicsItem 还支持以下特性:

  • 鼠标按下、移动、释放和双击事件,以及鼠标悬浮事件、滚轮事件和上下文菜单事件
  • 键盘输入焦点和键盘事件
  • 拖放
  • 分组:通过父子关系,或 QGraphicsItemGroup
  • 碰撞检测

下面,一起来看看 QGraphicsScene 对 QGraphicsItem 的管理,主要包括:单击、选择、移动、缩放、删除等。

为了实现以上功能,我们主要实现了 QGraphicsScene 和 QGraphicsItem 对应的事件,通过鼠标和键盘来操作。


操作细节主要包括:

  • 选择:点击左键、按 Shift 键可以单选,按下 Ctrl 可进行多选。
  • 添加:点击左键
  • 删除:点击右键,删除鼠标下的 item;当按下 Ctrl 选择多个 items 时,按下 Backspace 键,将选中的全部删除。
  • 移动:点击左键,选择 item,然后移动鼠标;当按下 Ctrl 选择多个 items 时,可以移动选中的 items。
  • 缩放:按 Alt 键,然后鼠标拖拽 item 的边界。

在对应操作的事件中,我们输出了一些调试信息,以便跟踪。

二、示例

image.png
main.cpp

  1. #include "widget.h"
  2. #include <QApplication>
  3. #include <QGraphicsView>
  4. int main(int argc, char* argv[])
  5. {
  6. QApplication a(argc, argv);
  7. // 创建 item
  8. CustomItem* pItem = new CustomItem();
  9. pItem->setRect(20, 20, 60, 60);
  10. // 将 item 添加至场景中
  11. CustomScene scene;
  12. scene.setSceneRect(0, 0, 400, 300);
  13. scene.addItem(pItem);
  14. // 为视图设置场景
  15. QGraphicsView view;
  16. view.setScene(&scene);
  17. view.show();
  18. return a.exec();
  19. }

widget.h

  1. #ifndef CUSTOM_ITEM_H
  2. #define CUSTOM_ITEM_H
  3. #include <QGraphicsRectItem>
  4. #include <QGraphicsScene>
  5. //QGraphicsScene管理QGraphicsItem(单击/选择/移动/缩放/删除)
  6. // 自定义 Item
  7. class CustomItem : public QGraphicsRectItem {
  8. public:
  9. explicit CustomItem(QGraphicsItem* parent = 0);
  10. protected:
  11. // Shift+左键:进行选择 Alt:准备缩放
  12. void mousePressEvent(QGraphicsSceneMouseEvent* event);
  13. // Alt+拖拽:进行缩放 移动
  14. void mouseMoveEvent(QGraphicsSceneMouseEvent* event);
  15. void mouseReleaseEvent(QGraphicsSceneMouseEvent* event);
  16. // 使item可使用qgraphicsitem_cast
  17. int type() const;
  18. private:
  19. QPointF m_centerPointF;
  20. bool m_bResizing;
  21. };
  22. // 自定义 Scene
  23. class CustomScene : public QGraphicsScene {
  24. protected:
  25. // 左键:添加item 右键:移除item
  26. void mousePressEvent(QGraphicsSceneMouseEvent* event);
  27. void mouseMoveEvent(QGraphicsSceneMouseEvent* event);
  28. // Backspace键移除item
  29. void keyPressEvent(QKeyEvent* event);
  30. };
  31. #endif // CUSTOM_ITEM_H

widget.cpp

  1. #include "widget.h"
  2. #include <QDebug>
  3. #include <QGraphicsSceneMouseEvent>
  4. #include <QKeyEvent>
  5. #include <QtMath>
  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. setPen(p);
  15. // 画刷 - 背景色
  16. setBrush(QColor(247, 160, 57));
  17. // 可选择、可移动
  18. setFlags(QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsMovable);
  19. }
  20. void CustomItem::mousePressEvent(QGraphicsSceneMouseEvent* event)
  21. {
  22. if (event->button() == Qt::LeftButton) {
  23. if (event->modifiers() == Qt::ShiftModifier) {
  24. qDebug() << "Custom item left clicked with shift key.";
  25. // 选中 item
  26. setSelected(true);
  27. } else if (event->modifiers() == Qt::AltModifier) {
  28. qDebug() << "Custom item left clicked with alt key.";
  29. // 重置 item 大小
  30. double radius = boundingRect().width() / 2.0;
  31. QPointF topLeft = boundingRect().topLeft();
  32. m_centerPointF = QPointF(topLeft.x() + pos().x() + radius, topLeft.y() + pos().y() + radius);
  33. QPointF pos = event->scenePos();
  34. qDebug() << boundingRect() << radius << this->pos() << pos << event->pos();
  35. double dist = sqrt(pow(m_centerPointF.x() - pos.x(), 2) + pow(m_centerPointF.y() - pos.y(), 2));
  36. if (dist / radius > 0.8) {
  37. qDebug() << dist << radius << dist / radius;
  38. m_bResizing = true;
  39. } else {
  40. m_bResizing = false;
  41. }
  42. } else {
  43. qDebug() << "Custom item left clicked.";
  44. QGraphicsItem::mousePressEvent(event);
  45. event->accept();
  46. }
  47. } else if (event->button() == Qt::RightButton) {
  48. qDebug() << "Custom item right clicked.";
  49. event->ignore();
  50. }
  51. }
  52. void CustomItem::mouseMoveEvent(QGraphicsSceneMouseEvent* event)
  53. {
  54. if ((event->modifiers() == Qt::AltModifier) && m_bResizing) {
  55. QPointF pos = event->scenePos();
  56. double dist = sqrt(pow(m_centerPointF.x() - pos.x(), 2) + pow(m_centerPointF.y() - pos.y(), 2));
  57. setRect(m_centerPointF.x() - this->pos().x() - dist, m_centerPointF.y() - this->pos().y() - dist, dist * 2, dist * 2);
  58. } else if (event->modifiers() != Qt::AltModifier) {
  59. qDebug() << "Custom item moved.";
  60. QGraphicsItem::mouseMoveEvent(event);
  61. qDebug() << "moved" << pos();
  62. }
  63. }
  64. void CustomItem::mouseReleaseEvent(QGraphicsSceneMouseEvent* event)
  65. {
  66. if ((event->modifiers() == Qt::AltModifier) && m_bResizing) {
  67. m_bResizing = false;
  68. } else {
  69. QGraphicsItem::mouseReleaseEvent(event);
  70. }
  71. }
  72. int CustomItem::type() const
  73. {
  74. return UserType + 1;
  75. }
  76. // 自定义 Scene
  77. void CustomScene::mousePressEvent(QGraphicsSceneMouseEvent* event)
  78. {
  79. qDebug() << "Custom scene clicked.";
  80. QGraphicsScene::mousePressEvent(event);
  81. if (!event->isAccepted()) {
  82. if (event->button() == Qt::LeftButton) {
  83. // 在 Scene 上添加一个自定义 item
  84. QPointF point = event->scenePos();
  85. CustomItem* item = new CustomItem();
  86. item->setRect(point.x() - 25, point.y() - 25, 60, 60);
  87. addItem(item);
  88. } else if (event->button() == Qt::RightButton) {
  89. // 检测光标下是否有 item
  90. QGraphicsItem* itemToRemove = NULL;
  91. foreach (QGraphicsItem* item, items(event->scenePos())) {
  92. if (item->type() == QGraphicsItem::UserType + 1) {
  93. itemToRemove = item;
  94. break;
  95. }
  96. }
  97. // 从 Scene 上移除 item
  98. if (itemToRemove != NULL)
  99. removeItem(itemToRemove);
  100. }
  101. }
  102. }
  103. void CustomScene::mouseMoveEvent(QGraphicsSceneMouseEvent* event)
  104. {
  105. qDebug() << "Custom scene moved.";
  106. QGraphicsScene::mouseMoveEvent(event);
  107. }
  108. void CustomScene::keyPressEvent(QKeyEvent* event)
  109. {
  110. if (event->key() == Qt::Key_Backspace) {
  111. // 移除所有选中的 items
  112. qDebug() << "selected items " << selectedItems().size();
  113. while (!selectedItems().isEmpty()) {
  114. removeItem(selectedItems().front());
  115. }
  116. } else {
  117. QGraphicsScene::keyPressEvent(event);
  118. }
  119. }

Study_QGraphicsSimpleTextItem.zip