Qt 图形场景框架完整解析
Qt 的图形视图框架是一个强大的二维图形系统,用于可视化和交互大量自定义项目。它基于经典的 Model-View-Controller 模式实现:
📊 核心架构概览
数据层 表现层 控制层
┌─────────┐ ┌─────────┐ ┌─────────┐
│ Graphics│←→ │Graphics │←→ │Graphics │
│ Item │ │ Scene │ │ View │
│ (模型) │ │ (舞台) │ │ (窗口) │
└─────────┘ └─────────┘ └─────────┘
🎭 1. QGraphicsScene(图形场景)
核心职责
场景是所有图元的容器,相当于一个无限大的虚拟画布。
主要特性
cpp
class CustomScene : public QGraphicsScene
{
Q_OBJECT
public:
CustomScene(QObject *parent = nullptr)
: QGraphicsScene(parent)
{
//场景虽然是无限大的,但是可以是使用下面的限制显示区域
// 设置场景边界(可选)
setSceneRect(0, 0, 2000, 2000); //建议显示区域
// 运行时可以随时更改
setSceneRect(-500, -500, 1000, 1000); // 扩大区域
/*
(-500,-500) (500,-500)
┌─────────────────────────────────┐
│ │
│ 场景区域 │
│ 中心在(0,0) │
│ │
(-500,500) (500,500)
*/
setSceneRect(-50, -50, 100, 100); // 缩小区域
// 也可以让场景自动计算
setSceneRect(scene->itemsBoundingRect()); // 设置为刚好包含所有项目的矩形
// 设置背景
setBackgroundBrush(QBrush(Qt::lightGray));
// 设置网格背景
setBackgroundBrush(QBrush(Qt::white));
QPen gridPen(QColor(220, 220, 220));
gridPen.setWidth(1);
// 通过绘制函数添加网格
}
};
场景功能详解
坐标系统
cpp
// Qt中的三个坐标系统
class CoordinateSystems : QObject {
// 场景坐标:全局统一坐标,浮点精度
QPointF scenePos = QPointF(100.5, 200.3);
// 项目坐标:相对于项目自身的坐标
// 项目1的局部坐标(0,0) = 场景坐标(100,100)
// 视图坐标:窗口显示的像素坐标
QPoint viewPos = QPoint(150, 75);
};
项目管理
cpp
QGraphicsScene scene;
// 添加/删除项目
auto *item = scene.addRect(0, 0, 100, 100);
scene.removeItem(item);
// 项目查询
QList<QGraphicsItem*> items = scene.items();
QGraphicsItem *itemAtPos = scene.itemAt(QPointF(150, 150), QTransform());
// 项目选择
item->setFlag(QGraphicsItem::ItemIsSelectable, true);
item->setSelected(true);
QList<QGraphicsItem*> selected = scene.selectedItems();
事件处理
cpp
void CustomScene::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
if (event->button() == Qt::LeftButton) {
// 创建新项目
auto *rect = new QGraphicsRectItem(0, 0, 50, 50);
rect->setPos(mapToScene(event->pos()));
addItem(rect);
}
QGraphicsScene::mousePressEvent(event);
}
场景层管理
cpp
// 创建图层(按Z值排序)
QGraphicsItem *backgroundLayer; // Z值:-1000
QGraphicsItem *mainLayer; // Z值:0
QGraphicsItem *overlayLayer; // Z值:1000
// 设置Z值
item->setZValue(1.5);
🎨 2. QGraphicsItem(图形图元)
图元类型体系
QGraphicsItem(基类)
├── QAbstractGraphicsShapeItem(抽象形状)
│ ├── QGraphicsEllipseItem(椭圆)
│ ├── QGraphicsPolygonItem(多边形)
│ ├── QGraphicsRectItem(矩形)
│ └── QGraphicsSimpleTextItem(简单文本)
├── QGraphicsLineItem(线条)
├── QGraphicsPixmapItem(图片)
├── QGraphicsTextItem(富文本)
├── QGraphicsPathItem(路径)
└── 用户自定义Item
自定义图元实现
cpp
class CustomGraphicsItem : public QGraphicsItem
{
public:
explicit CustomGraphicsItem(QGraphicsItem *parent = nullptr)
: QGraphicsItem(parent), color(Qt::blue)
{
// 启用图元特性
setFlags(QGraphicsItem::ItemIsMovable |
QGraphicsItem::ItemIsSelectable |
QGraphicsItem::ItemSendsGeometryChanges);
// 接受悬停事件
setAcceptHoverEvents(true);
}
QRectF boundingRect() const override
{
// 返回图元的边界矩形(用于碰撞检测、重绘区域等)
return QRectF(-50, -50, 100, 100);
}
void paint(QPainter *painter,
const QStyleOptionGraphicsItem *option,
QWidget *widget = nullptr) override
{
// 绘制图元
painter->setPen(QPen(color, 2));
painter->setBrush(QBrush(color.lighter()));
painter->drawRoundedRect(boundingRect(), 10, 10);
// 选中状态高亮
if (isSelected()) {
painter->setPen(QPen(Qt::red, 1, Qt::DashLine));
painter->drawRect(boundingRect().adjusted(-2, -2, 2, 2));
}
}
// 碰撞检测
QPainterPath shape() const override
{
QPainterPath path;
path.addRoundedRect(boundingRect(), 10, 10);
return path;
}
protected:
void mousePressEvent(QGraphicsSceneMouseEvent *event) override
{
qDebug() << "Item clicked at:" << event->pos();
QGraphicsItem::mousePressEvent(event);
}
void hoverEnterEvent(QGraphicsSceneHoverEvent *event) override
{
color = Qt::green;
update(); // 触发重绘
QGraphicsItem::hoverEnterEvent(event);
}
QVariant itemChange(GraphicsItemChange change,
const QVariant &value) override
{
if (change == ItemPositionChange && scene()) {
// 限制移动范围
QPointF newPos = value.toPointF();
QRectF sceneRect = scene()->sceneRect();
if (!sceneRect.contains(newPos)) {
newPos.setX(qMin(qMax(newPos.x(), sceneRect.left()),
sceneRect.right() - boundingRect().width()));
newPos.setY(qMin(qMax(newPos.y(), sceneRect.top()),
sceneRect.bottom() - boundingRect().height()));
return newPos;
}
}
return QGraphicsItem::itemChange(change, value);
}
private:
QColor color;
};
图元交互标志
cpp
// 启用的标志
item->setFlags(QGraphicsItem::ItemIsMovable); // 可移动
item->setFlags(QGraphicsItem::ItemIsSelectable); // 可选择
item->setFlags(QGraphicsItem::ItemIsFocusable); // 可获得焦点
item->setFlags(QGraphicsItem::ItemClipsToShape); // 裁剪到形状
item->setFlags(QGraphicsItem::ItemIgnoresTransformations); // 忽略变换
// 接受的事件类型
item->setAcceptDrops(true); // 接受拖放
item->setAcceptHoverEvents(true); // 接受悬停事件
item->setAcceptTouchEvents(true); // 接受触摸事件
🖼️ 3. QGraphicsView(图形视图)
核心功能
视图是场景的显示窗口,处理用户输入和显示。
cpp
class CustomGraphicsView : public QGraphicsView
{
public:
CustomGraphicsView(QGraphicsScene *scene, QWidget *parent = nullptr)
: QGraphicsView(scene, parent)
{
setupView();
}
private:
void setupView()
{
// 渲染设置
setRenderHint(QPainter::Antialiasing); // 抗锯齿
setRenderHint(QPainter::SmoothPixmapTransform); // 平滑图像变换
setRenderHint(QPainter::TextAntialiasing); // 文本抗锯齿
// 视图设置
setDragMode(QGraphicsView::RubberBandDrag); // 橡皮筋选择模式
setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
setResizeAnchor(QGraphicsView::AnchorViewCenter);
// 优化选项
setOptimizationFlag(QGraphicsView::DontAdjustForAntialiasing, true);
setCacheMode(QGraphicsView::CacheBackground); // 缓存背景
setViewportUpdateMode(QGraphicsView::BoundingRectViewportUpdate);
// 交互设置
setInteractive(true);
setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
}
void wheelEvent(QWheelEvent *event) override
{
// 缩放控制
if (event->modifiers() & Qt::ControlModifier) {
double scaleFactor = 1.15;
if (event->angleDelta().y() > 0) {
scale(scaleFactor, scaleFactor);
} else {
scale(1.0 / scaleFactor, 1.0 / scaleFactor);
}
event->accept();
} else {
QGraphicsView::wheelEvent(event);
}
}
void mousePressEvent(QMouseEvent *event) override
{
if (event->button() == Qt::MidButton) {
// 中键拖动
setDragMode(QGraphicsView::ScrollHandDrag);
QMouseEvent fakeEvent(event->type(), event->localPos(),
Qt::LeftButton, Qt::LeftButton,
event->modifiers());
QGraphicsView::mousePressEvent(&fakeEvent);
} else if (event->button() == Qt::RightButton) {
// 右键缩放
QGraphicsView::mousePressEvent(event);
} else {
QGraphicsView::mousePressEvent(event);
}
}
};
坐标转换系统
cpp
// 重要:坐标系统转换
QGraphicsView view;
// 视图坐标 ↔ 场景坐标
QPoint viewPoint(100, 50);
QPointF scenePoint = view.mapToScene(viewPoint);
QPointF backToView = view.mapFromScene(scenePoint);
// 项目坐标 ↔ 场景坐标
QPointF itemPoint = item->mapToScene(QPointF(0, 0));
QPointF scenePointToItem = item->mapFromScene(QPointF(100, 100));
// 视图坐标 ↔ 项目坐标
QPoint viewPos = view.mapFromScene(item->mapToScene(QPointF(0, 0)));
🔄 4. 三者交互关系
创建和使用完整示例
cpp
#include <QApplication>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QGraphicsRectItem>
#include <QGraphicsEllipseItem>
#include <QVBoxLayout>
class GraphicsExample : public QWidget
{
public:
GraphicsExample(QWidget *parent = nullptr) : QWidget(parent)
{
// 1. 创建场景
scene = new QGraphicsScene(this);
scene->setSceneRect(0, 0, 800, 600);
// 2. 创建基础图元
QGraphicsRectItem *rect = scene->addRect(50, 50, 100, 50);
rect->setBrush(QBrush(Qt::blue));
rect->setFlag(QGraphicsItem::ItemIsMovable);
rect->setToolTip("可移动的矩形");
QGraphicsEllipseItem *ellipse = scene->addEllipse(200, 100, 80, 80);
ellipse->setBrush(QBrush(Qt::red));
ellipse->setFlag(QGraphicsItem::ItemIsSelectable);
// 3. 添加文本
QGraphicsTextItem *text = scene->addText("Hello Qt Graphics");
text->setPos(300, 200);
text->setDefaultTextColor(Qt::darkGreen);
// 4. 创建自定义图元
CustomGraphicsItem *customItem = new CustomGraphicsItem();
scene->addItem(customItem);
customItem->setPos(400, 300);
// 5. 创建视图
view = new CustomGraphicsView(scene);
view->setMinimumSize(600, 400);
// 6. 布局
QVBoxLayout *layout = new QVBoxLayout(this);
layout->addWidget(view);
// 7. 设置窗口属性
setWindowTitle("Qt Graphics View Framework 示例");
// 8. 连接信号槽
connect(scene, &QGraphicsScene::selectionChanged, [this]() {
qDebug() << "选中项目数量:" << scene->selectedItems().size();
});
}
private:
QGraphicsScene *scene;
CustomGraphicsView *view;
};
性能优化技巧
cpp
// 1. 批量操作
scene->setItemIndexMethod(QGraphicsScene::NoIndex); // 小场景
// 或
scene->setItemIndexMethod(QGraphicsScene::BspTreeIndex); // 大场景
// 2. 图元优化
item->setCacheMode(QGraphicsItem::DeviceCoordinateCache); // 设备坐标缓存
item->setOpacity(0.5); // 硬件加速的透明度
// 3. 视图优化
view->setViewportUpdateMode(QGraphicsView::FullViewportUpdate); // 简单场景
view->setViewportUpdateMode(QGraphicsView::MinimalViewportUpdate); // 复杂场景
view->setOptimizationFlag(QGraphicsView::DontSavePainterState, true);
// 4. 避免频繁重绘
QRectF updateRect = item->boundingRect().adjusted(-5, -5, 5, 5);
scene->update(updateRect); // 只更新需要重绘的区域
🎯 5. 高级功能
动画和变换
cpp
// 使用 QGraphicsItemAnimation(旧的)或 QPropertyAnimation
QPropertyAnimation *animation = new QPropertyAnimation(item, "pos");
animation->setDuration(1000);
animation->setStartValue(QPointF(0, 0));
animation->setEndValue(QPointF(200, 200));
animation->setEasingCurve(QEasingCurve::InOutQuad);
animation->start();
// 使用变换
QTransform transform;
transform.translate(100, 50);
transform.rotate(45);
transform.scale(1.5, 1.0);
item->
setTransform(transform);
拖放支持
cpp
class DragItem : public QGraphicsRectItem
{
public:
DragItem() : QGraphicsRectItem(0, 0, 100, 100)
{
setFlag(QGraphicsItem::ItemIsMovable);
setFlag(QGraphicsItem::ItemSendsScenePositionChanges);
setAcceptDrops(true);
}
protected:
void dragEnterEvent(QGraphicsSceneDragDropEvent *event) override
{
if (event->mimeData()->hasFormat("application/x-custom")) {
event->setAccepted(true);
setBrush(Qt::yellow);
}
}
void dropEvent(QGraphicsSceneDragDropEvent *event) override
{
// 处理拖放数据
qDebug() << "Drop data:" << event->mimeData()->
data("application/x-custom");
setBrush(Qt::green);
}
};
打印和导出
cpp
// 打印场景
QPrinter printer(QPrinter::HighResolution);
QPrintDialog dialog(&printer);
if (dialog.exec() == QDialog::Accepted) {
QPainter painter(&printer);
scene->render(&painter);
}
// 导出为图片
QPixmap pixmap(scene->sceneRect().size().toSize());
QPainter painter(&pixmap);
scene->render(&painter);
pixmap.save("scene.png");
🚀 6. 实际应用场景
流程图编辑器
cpp
class FlowChartEditor : public QGraphicsView
{
// 节点、连接线、端口等图元的实现
// 支持拖拽创建、连接、编辑属性
};
class DiagramNode : public QGraphicsItem
{
// 流程节点:标题、输入输出端口、属性面板
};
class ConnectionLine : public QGraphicsPathItem
{
// 连接线:贝塞尔曲线、箭头、连接点吸附
};
CAD绘图工具
cpp
class CADView : public QGraphicsView
{
// 支持缩放、平移、测量、捕捉
// 图层管理、尺寸标注、矢量图形
};
游戏编辑器
cpp
class TileMapEditor : public QGraphicsScene
{
// 瓦片地图:网格布局、碰撞检测
// 精灵管理、动画预览、碰撞体编辑
};
📝 使用建议
-
小规模项目:使用标准图元,继承 QGraphicsItem 或 QGraphicsObject
-
中等规模:考虑自定义图元,实现 paint() 和 boundingRect()
-
大规模项目:
- 使用 BSP 树索引
- 实现图元缓存
- 分批更新
- 考虑 OpenGL 渲染后端
-
需要信号槽:继承 QGraphicsObject
-
高性能需求:减少重绘区域,使用硬件加速
-
复杂交互:合理使用事件过滤器,实现多级事件处理
Qt 图形视图框架非常灵活,适用于从简单的图形显示到复杂的交互式应用程序开发。合理利用其特性可以构建出强大、高效的图形界面应用。