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

相关推荐
千疑千寻~2 小时前
【Qt图形】Qt中的图形场景、图元、视图 的坐标的转换
qt
一叶之秋141212 小时前
Qt常用控件(三)
开发语言·qt
_OP_CHEN12 小时前
【从零开始的Qt开发指南】(二十三)Qt 界面优化之绘图实战:解锁自定义界面的无限可能
开发语言·qt
郝学胜-神的一滴12 小时前
使用QVideoWidget实现高效视频播放:从基础到高级应用
开发语言·c++·qt·程序人生·音视频
叶之道12 小时前
MacOS 配置 Qt 开发环境
qt·macos
爱吃泡芙的小白白12 小时前
Qt 3D老树新花:为何在工业与车载领域仍是首选?
开发语言·qt·3d
余衫马12 小时前
Qt for Python:PySide6 入门指南
开发语言·c++·python·qt
Wiktok13 小时前
PySide6中的QSS(Qt Style Sheet,类似CSS)支持的属性
qt·pyside6·qss
枫叶丹413 小时前
【Qt开发】Qt系统(七)-> Qt网络安全
c语言·开发语言·c++·qt·网络安全