【Qt图形】详细介绍Qt中的图形场景、图元、视图

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
{
    // 瓦片地图:网格布局、碰撞检测
    // 精灵管理、动画预览、碰撞体编辑
};

📝 使用建议

  1. 小规模项目:使用标准图元,继承 QGraphicsItem 或 QGraphicsObject

  2. 中等规模:考虑自定义图元,实现 paint() 和 boundingRect()

  3. 大规模项目

    • 使用 BSP 树索引
    • 实现图元缓存
    • 分批更新
    • 考虑 OpenGL 渲染后端
  4. 需要信号槽:继承 QGraphicsObject

  5. 高性能需求:减少重绘区域,使用硬件加速

  6. 复杂交互:合理使用事件过滤器,实现多级事件处理

Qt 图形视图框架非常灵活,适用于从简单的图形显示到复杂的交互式应用程序开发。合理利用其特性可以构建出强大、高效的图形界面应用。

相关推荐
用户805533698031 天前
不止三件套:QObject 属性系统全关键字与运行时反射!
c++·qt
xcyxiner1 天前
DicomViewer (vcpkg Windows和ubuntu编译)7
qt
Quz6 天前
QML Hello World 入门示例
qt
xcyxiner9 天前
DicomViewer (dcmtk读取dcm文件)5
qt
xcyxiner10 天前
DicomViewer (后台线程处理文件)4
qt
xcyxiner10 天前
DicomViewer (添加模型类)3
qt
xcyxiner11 天前
DicomViewer (目录调整) 2
qt
xcyxiner11 天前
dcmtk vtk vtk-dicom(gdcm) 编译(debug) v2
qt
桥田智能13 天前
桥田智能 QT-650S:面向白车身焊装的 800kg 重载快换解决方案
开发语言·qt·系统架构
森G13 天前
75、服务器源码解析---------云视频服务项目
linux·服务器·网络·c++·qt