一、简述
在图形视图框架中,QGraphicsScene 提供一个快速的接口,用于管理大量 item,QGraphicsItem 是场景中 item 的基类。
图形视图提供了一些典型形状的标准 item,当然,我们也可以自定义 item。除此之外,QGraphicsItem 还支持以下特性:
- 鼠标按下、移动、释放和双击事件,以及鼠标悬浮事件、滚轮事件和上下文菜单事件
- 键盘输入焦点和键盘事件
- 拖放
- 分组:通过父子关系,或 QGraphicsItemGroup
- 碰撞检测
下面,一起来看看 QGraphicsScene 对 QGraphicsItem 的管理,主要包括:单击、选择、移动、缩放、删除等。
为了实现以上功能,我们主要实现了 QGraphicsScene 和 QGraphicsItem 对应的事件,通过鼠标和键盘来操作。
操作细节主要包括:
- 选择:点击左键、按 Shift 键可以单选,按下 Ctrl 可进行多选。
- 添加:点击左键
- 删除:点击右键,删除鼠标下的 item;当按下 Ctrl 选择多个 items 时,按下 Backspace 键,将选中的全部删除。
- 移动:点击左键,选择 item,然后移动鼠标;当按下 Ctrl 选择多个 items 时,可以移动选中的 items。
- 缩放:按 Alt 键,然后鼠标拖拽 item 的边界。
二、示例

main.cpp
#include "widget.h"#include <QApplication>#include <QGraphicsView>int main(int argc, char* argv[]){QApplication a(argc, argv);// 创建 itemCustomItem* pItem = new CustomItem();pItem->setRect(20, 20, 60, 60);// 将 item 添加至场景中CustomScene scene;scene.setSceneRect(0, 0, 400, 300);scene.addItem(pItem);// 为视图设置场景QGraphicsView view;view.setScene(&scene);view.show();return a.exec();}
widget.h
#ifndef CUSTOM_ITEM_H#define CUSTOM_ITEM_H#include <QGraphicsRectItem>#include <QGraphicsScene>//QGraphicsScene管理QGraphicsItem(单击/选择/移动/缩放/删除)// 自定义 Itemclass CustomItem : public QGraphicsRectItem {public:explicit CustomItem(QGraphicsItem* parent = 0);protected:// Shift+左键:进行选择 Alt:准备缩放void mousePressEvent(QGraphicsSceneMouseEvent* event);// Alt+拖拽:进行缩放 移动void mouseMoveEvent(QGraphicsSceneMouseEvent* event);void mouseReleaseEvent(QGraphicsSceneMouseEvent* event);// 使item可使用qgraphicsitem_castint type() const;private:QPointF m_centerPointF;bool m_bResizing;};// 自定义 Sceneclass CustomScene : public QGraphicsScene {protected:// 左键:添加item 右键:移除itemvoid mousePressEvent(QGraphicsSceneMouseEvent* event);void mouseMoveEvent(QGraphicsSceneMouseEvent* event);// Backspace键移除itemvoid keyPressEvent(QKeyEvent* event);};#endif // CUSTOM_ITEM_H
widget.cpp
#include "widget.h"#include <QDebug>#include <QGraphicsSceneMouseEvent>#include <QKeyEvent>#include <QtMath>// 自定义 ItemCustomItem::CustomItem(QGraphicsItem* parent): QGraphicsRectItem(parent){// 画笔 - 边框色QPen p = pen();p.setWidth(2);p.setColor(QColor(0, 160, 230));setPen(p);// 画刷 - 背景色setBrush(QColor(247, 160, 57));// 可选择、可移动setFlags(QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsMovable);}void CustomItem::mousePressEvent(QGraphicsSceneMouseEvent* event){if (event->button() == Qt::LeftButton) {if (event->modifiers() == Qt::ShiftModifier) {qDebug() << "Custom item left clicked with shift key.";// 选中 itemsetSelected(true);} else if (event->modifiers() == Qt::AltModifier) {qDebug() << "Custom item left clicked with alt key.";// 重置 item 大小double radius = boundingRect().width() / 2.0;QPointF topLeft = boundingRect().topLeft();m_centerPointF = QPointF(topLeft.x() + pos().x() + radius, topLeft.y() + pos().y() + radius);QPointF pos = event->scenePos();qDebug() << boundingRect() << radius << this->pos() << pos << event->pos();double dist = sqrt(pow(m_centerPointF.x() - pos.x(), 2) + pow(m_centerPointF.y() - pos.y(), 2));if (dist / radius > 0.8) {qDebug() << dist << radius << dist / radius;m_bResizing = true;} else {m_bResizing = false;}} else {qDebug() << "Custom item left clicked.";QGraphicsItem::mousePressEvent(event);event->accept();}} else if (event->button() == Qt::RightButton) {qDebug() << "Custom item right clicked.";event->ignore();}}void CustomItem::mouseMoveEvent(QGraphicsSceneMouseEvent* event){if ((event->modifiers() == Qt::AltModifier) && m_bResizing) {QPointF pos = event->scenePos();double dist = sqrt(pow(m_centerPointF.x() - pos.x(), 2) + pow(m_centerPointF.y() - pos.y(), 2));setRect(m_centerPointF.x() - this->pos().x() - dist, m_centerPointF.y() - this->pos().y() - dist, dist * 2, dist * 2);} else if (event->modifiers() != Qt::AltModifier) {qDebug() << "Custom item moved.";QGraphicsItem::mouseMoveEvent(event);qDebug() << "moved" << pos();}}void CustomItem::mouseReleaseEvent(QGraphicsSceneMouseEvent* event){if ((event->modifiers() == Qt::AltModifier) && m_bResizing) {m_bResizing = false;} else {QGraphicsItem::mouseReleaseEvent(event);}}int CustomItem::type() const{return UserType + 1;}// 自定义 Scenevoid CustomScene::mousePressEvent(QGraphicsSceneMouseEvent* event){qDebug() << "Custom scene clicked.";QGraphicsScene::mousePressEvent(event);if (!event->isAccepted()) {if (event->button() == Qt::LeftButton) {// 在 Scene 上添加一个自定义 itemQPointF point = event->scenePos();CustomItem* item = new CustomItem();item->setRect(point.x() - 25, point.y() - 25, 60, 60);addItem(item);} else if (event->button() == Qt::RightButton) {// 检测光标下是否有 itemQGraphicsItem* itemToRemove = NULL;foreach (QGraphicsItem* item, items(event->scenePos())) {if (item->type() == QGraphicsItem::UserType + 1) {itemToRemove = item;break;}}// 从 Scene 上移除 itemif (itemToRemove != NULL)removeItem(itemToRemove);}}}void CustomScene::mouseMoveEvent(QGraphicsSceneMouseEvent* event){qDebug() << "Custom scene moved.";QGraphicsScene::mouseMoveEvent(event);}void CustomScene::keyPressEvent(QKeyEvent* event){if (event->key() == Qt::Key_Backspace) {// 移除所有选中的 itemsqDebug() << "selected items " << selectedItems().size();while (!selectedItems().isEmpty()) {removeItem(selectedItems().front());}} else {QGraphicsScene::keyPressEvent(event);}}
