Qt进阶开发:Graphics View图形视图框架

发布于:2025-03-30 ⋅ 阅读:(39) ⋅ 点赞:(0)

一、图形视图框架简介

图形视图框架提供了一个基于图形项的模型视图编程方法,主要由场景、视图和图形项三部分组成,这三部分分别由QGraphicsScene(场景)、QGraphicsView(视图)、以及QGraphicsItem(图元项)这三个类来表示。多个视图可以查看一个场景,场景中包含各种各样几何形状的图形项。图形视图框架在Qt4.2中被引入,用来代替以前的QCanvas类组。图形视图框架可以管理数量庞大的自定义2D图形项,并且可以与它们进行交互。使用视图部件可以使这些图形项可视化,视图还支持缩放和旋转。框架中包含了一个事件传播构架,提供了和场景中的图形项进行准确的双精度交互的能力,图形项可以出来键盘事件,鼠标的按下、移动、释放和双击事件。还可以跟踪鼠标的移动。图形视图框架使用一个BSP(Binary Space Partitioning)树来快速发现图形项,也正是因为如此,它可以实时显示一个巨大的场景,甚至包含上百万个图形项。

二、GraphicsView坐标系统

场景坐标系统

图形视图框架基于笛卡尔坐标系统,一个图形项在场景中的位置和几何形状由x坐标和y坐标来表示,当使用一个没有变换的视图来观察场景时,场景中的一个单元代表屏幕上的一个像素。图形视图框架中有3个有效的坐标系统:图形项坐标、场景坐标和视图坐标。为了方应用,图形视图框架中提供了一些便捷函数来完成3个坐标系统之间的映射。进行绘图时,场景坐标对应QPainter的逻辑坐标,视图坐标对应设备坐标。
场景坐标是所有图形项的基础坐标系统。场景坐标系统描述了每一个顶层图形项的位置,也用于处理所有从视图传到场景中的事件。场景坐标的原点在场景的中心,x和y坐标分别向右和向下增大。每一个场景中的图形项除了拥有一个图形项的本地坐标和边界矩形外,还都拥有一个场景坐标(GraphicsItem::senePos())和一个场景中的边界矩形(GraphicsItem::sceneBoundingRect())。场景坐标用来描述图形项在场景坐标系统中的位置,而图形项的场景边界矩形用于GraphicsScene判断场景中的哪些区域进行了更改。
在这里插入图片描述
在 Qt Graphics View 框架中,QGraphicsScene 负责管理 所有的图形项 (QGraphicsItem),并提供一个 全局 2D 坐标系 来确定它们的位置。QGraphicsScene 的坐标系是基于二维笛卡尔坐标系,单位是像素,默认情况下:

  • X 轴: 向右递增(正方向)
  • Y 轴: 向下递增(正方向)

默认坐标系统示意图:

Y ()
  |
  |  (0,0)  ← 默认场景坐标原点
  |--------X ()
  |Y ()

3. QGraphicsScene 场景坐标的使用
(1) 创建 QGraphicsScene 并设置坐标范围
默认情况下,QGraphicsScene 没有边界,你可以在任意坐标点放置 QGraphicsItem。但可以手动设置场景的坐标范围:

QGraphicsScene scene;
scene.setSceneRect(-200, -200, 400, 400); // 设置场景坐标范围

坐标范围:

Y ()
   |
(-200, -200) -------------- (200, -200)X ()
   |                                 |
   |       (0,0) 场景中心点         |
   |                                 |
(-200, 200) -------------- (200, 200)
   |Y ()

(2) 在场景中添加 QGraphicsItem
每个 QGraphicsItem 都位于场景坐标系统 中。可以通过 setPos(x, y) 设置它在场景中的位置:

QGraphicsRectItem *rectItem = new QGraphicsRectItem(-50, -50, 100, 100); // 以 (-50, -50) 为左上角,大小 100x100
scene.addItem(rectItem);
rectItem->setPos(100, 100); // 在场景坐标 (100,100) 处放置该图元

此时 rectItem 实际位置:局部坐标: boundingRect() = (-50, -50, 100, 100)
场景坐标:

(50, 50) ----------------- (150, 50)
   |                             |
   |        矩形区域            |
   |                             |
(50, 150) ---------------- (150, 150)

(3) 获取 QGraphicsItem 的场景坐标

QPointF scenePos = rectItem->scenePos(); // 获取图元在场景中的位置
QRectF itemRect = rectItem->boundingRect(); // 获取图元的局部坐标范围
qDebug() << "Item scene position:" << scenePos;
qDebug() << "Item bounding rect:" << itemRect;

(4) 视图坐标 (QGraphicsView) ↔ 场景坐标 (QGraphicsScene)
QGraphicsView 负责显示 QGraphicsScene,它有自己的视图坐标系统,可以与场景坐标互相转换。
视图坐标 → 场景坐标

QPoint viewPos(50, 50);
QPointF scenePoint = view.mapToScene(viewPos); // 视图坐标 (50,50) 转换为 场景坐标

场景坐标 → 视图坐标

QPointF scenePoint(100, 100);
QPoint viewPoint = view.mapFromScene(scenePoint); // 场景坐标 (100,100) 转换为 视图坐标

4. 场景坐标变换
QGraphicsScene 本身的坐标系不会改变,但图形项 (QGraphicsItem) 可以通过变换修改自身在场景中的坐标表现。
(1) 移动 (translate)

rectItem->moveBy(50, 50); // 向右 & 向下移动 50 像素

(2) 旋转 (rotate)

rectItem->setRotation(45); // 顺时针旋转 45°

(3) 缩放 (scale)

rectItem->setScale(2.0); // 放大 2 倍

5. 场景坐标转换示例
示例:鼠标点击获取场景坐标

void mousePressEvent(QMouseEvent *event) {
    QPoint viewPos = event->pos(); // 获取鼠标点击的视图坐标
    QPointF scenePos = view.mapToScene(viewPos); // 转换为场景坐标

    QGraphicsItem *item = scene.itemAt(scenePos, QTransform()); // 获取该位置的Item
    if (item) {
        QPointF itemPos = item->mapFromScene(scenePos); // 转换为 Item 内部坐标
        qDebug() << "View:" << viewPos << "Scene:" << scenePos << "Item:" << itemPos;
    }
}

图形项坐标系统

图形项使用自己的本地坐标系统,坐标通常是以它们的中心为原点(0, 0),而这也是所有变换的中心。当要创建一个自定义图形项时,只需要考虑图形项的坐标系统,QGraphicsScene和QGraphicsView会完成其他所有的转换。而且,一个图形项的边界矩形和图形形状都是在图形项坐标系统中的。图形项的位置是指图形项的原点在其父图形项或者场景中的位置。如果一个图形项在另一个图形项之中,那么它被称为子图形项,而包含它的图形项称为它的父图形项。所有没有父图形项的图形项都会在场景的坐标系统中,它们被称为顶层图形项。可以使用setPos()函数来指定图形项的位置,如果没有指定,则默认出现在父图形项或者场景的原点处。
请添加图片描述
在 QGraphicsScene(图形场景)中,每个 QGraphicsItem(图形项)都有自己的坐标系统。当一个 QGraphicsItem 被添加到 QGraphicsScene 时,它的场景坐标(Scene Coordinates)决定了它在整个场景中的位置。
在 QGraphicsScene 中,一个 QGraphicsItem 可能涉及以下三种坐标系:
(1) 本地坐标(Item Coordinates)

  • 也称为项坐标,它是 QGraphicsItem 自身的坐标系。
  • boundingRect() 定义了该项的边界框,在本地坐标系中表示。
  • 例如,一个矩形 QGraphicsRectItem(0, 0, 50, 50) 的本地坐标范围是 (0,0) 到 (50,50),无论它在场景中的位置如何,boundingRect() 始终是这个范围。

(2) 场景坐标(Scene Coordinates)

  • QGraphicsScene 的全局坐标系,所有 QGraphicsItem 在该坐标系中都有唯一的位置。
  • QGraphicsItem::scenePos() 返回该图形项的场景坐标。
  • mapToScene() 可用于将本地坐标转换为场景坐标。

示例:

QGraphicsRectItem *item = new QGraphicsRectItem(0, 0, 50, 50);
scene.addItem(item);
item->setPos(100, 200); // 设置场景坐标

qDebug() << "场景坐标:" << item->scenePos(); // (100, 200)

虽然 boundingRect() 仍然是 (0, 0, 50, 50),但 scenePos() 显示该项在场景中的位置 (100, 200)。

(3) 视图坐标(View Coordinates)
QGraphicsView 负责渲染 QGraphicsScene,并将场景坐标映射到视图坐标(屏幕像素)。mapFromScene() 用于将场景坐标转换为视图坐标。

2. 坐标转换
Qt 提供了多个方法来进行坐标转换:
在这里插入图片描述
示例:

QPointF scenePoint = item->mapToScene(0, 0); // 获取本地 (0,0) 对应的场景坐标
QPointF localPoint = item->mapFromScene(scenePoint); // 将场景坐标转换回本地坐标

3. 实践示例
(1) 在 QGraphicsScene 中添加一个矩形

#include <QApplication>
#include <QGraphicsView>
#include <QGraphicsScene>
#include <QGraphicsRectItem>
#include <QDebug>

int main(int argc, char *argv[]) {
    QApplication app(argc, argv);

    // 创建场景
    QGraphicsScene scene;

    // 创建矩形项
    QGraphicsRectItem *item = new QGraphicsRectItem(0, 0, 100, 50);
    scene.addItem(item);

    // 设置场景坐标 (200, 150)
    item->setPos(200, 150);

    // 获取不同坐标系的位置
    qDebug() << "本地坐标 boundingRect:" << item->boundingRect(); // QRectF(0, 0, 100, 50)
    qDebug() << "场景坐标:" << item->scenePos(); // QPointF(200, 150)
    
    // 创建视图并显示
    QGraphicsView view(&scene);
    view.show();

    return app.exec();
}

解析:

  • boundingRect() 仍然是 (0,0,100,50),它始终是相对于自身的。
  • scenePos() 返回 (200, 150),表示该项在 QGraphicsScene 里的位置。

4. 视图交互:鼠标点击获取场景坐标
在 QGraphicsView 里,可以监听鼠标事件,获取点击位置的场景坐标:

void mousePressEvent(QMouseEvent *event) override {
    QPoint viewPos = event->pos(); // 视图坐标
    QPointF scenePos = mapToScene(viewPos); // 转换为场景坐标
    qDebug() << "视图坐标:" << viewPos << "转换后场景坐标:" << scenePos;
}

(2) 计算父子项的坐标关系
如果 QGraphicsItem 有父项,它的坐标是相对于父项的:

// 创建父项
QGraphicsRectItem *parentItem = new QGraphicsRectItem(0, 0, 100, 50);
scene.addItem(parentItem);
parentItem->setPos(100, 100); // 场景坐标 (100,100)

// 创建子项
QGraphicsRectItem *childItem = new QGraphicsRectItem(0, 0, 50, 25, parentItem);
childItem->setPos(20, 10); // 相对于父项的偏移量

// 坐标信息
qDebug() << "父项的场景坐标:" << parentItem->scenePos();  // (100, 100)
qDebug() << "子项的相对坐标:" << childItem->pos();         // (20, 10)
qDebug() << "子项的场景坐标:" << childItem->mapToScene(0, 0); // (120, 110)

视图坐标系统

在 QGraphicsView 中,视图坐标、场景坐标 和 图形项(Item)坐标 之间可以相互转换。
视图的坐标系统就是部件的坐标。视图坐标的每一个单位对应一个像素,原点(0, 0)总在QGraohicsView视口的左上角,而右下角(宽,高)。所有的鼠标事件和拖放事件最初都是使用视图坐标接收的,

1. 视图坐标(View Coordinates)
定义:

  • 视图坐标是 QGraphicsView 窗口控件的像素坐标,以 QGraphicsView 左上角 (0,0) 为原点。
  • 视图中的所有像素单位都使用 整数坐标(因为它们与 QWidget 相关)。
  • 主要用于鼠标事件、交互、屏幕绘制等。
    请添加图片描述
    示例:
    假设 QGraphicsView 具有 800×600 的大小:
  • 视图左上角坐标是 (0,0)
  • 右下角坐标是 (799,599)
  • 这个坐标系不会随 QGraphicsScene 改变,而是固定在窗口控件的像素坐标系中
void MyView::mousePressEvent(QMouseEvent *event) {
    QPoint viewPos = event->pos(); // 视图坐标 (像素)
    qDebug() << "鼠标点击的视图坐标:" << viewPos;
}

坐标映射

当处理场景中的图形项时,将坐标或者一个任意的形状从场景映射到图形项、或者从一个图形项映射到另一个图形项、又或者从视图映射到场景中,这些坐标变换都是很常用的。例如,在QGraphicsView的视口上单击了鼠标,则可调用QGraphicsView::mapToScene()以及QGraphicsScene::itemAt()来获取光标下的图形项;如果要获取一个图形项在视口中的位置,那么可以先在图形项上调用QGraphicsItem::mapToScene(),然后在视图上调用QGraphicsView::mapFromScene();如果要获取在视图的一个椭圆形中包含的图形项,则可以先传递一个QPainterPath对象作为参数给mapToScene()函数,然后传递映射后的路径给QGraphicsScene::items()函数。
不仅可以在视图、场景和图形项之间使用坐标映射,还可以在子图形项、父图形项或者图形项、图形项之间进行坐标映射。所有的映射函数都可以映射点、矩形、多边形和路径。图形视图框架提供的所有映射函数如下表所示:
在这里插入图片描述

三、QGraphicsScene(场景)

QGraphicsScene 是 Qt Graphics View 框架(QGraphicsView)中的场景管理类,是不可见的,它用于管理所有的图形元素(QGraphicsItem)。
场景拥有以下功能:

  • 存储和管理图形对象(如矩形、圆、文本、图片)。
  • 支持碰撞检测、事件处理、图层管理。
  • 与 QGraphicsView 配合,在不同的视图中显示相同的场景。
  • 提供无变换的渲染功能,主要用于打印。

解析:

  • parentItem 位置 (100,100) 是相对于场景的。
  • childItem 位置 (20,10) 是相对于父项的。
  • mapToScene(0,0) 计算出子项的场景坐标 (120,110)。

示例代码:

#include <QApplication>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QGraphicsRectItem>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    // 创建场景
    QGraphicsScene scene;
    scene.setSceneRect(0, 0, 500, 500);

    QGraphicsRectItem *rect = scene.addRect(50, 50, 100, 50, QPen(Qt::black), QBrush(Qt::blue));
    QGraphicsEllipseItem *ellipse = scene.addEllipse(200, 100, 50, 50, QPen(Qt::red), QBrush(Qt::green));
    scene.addText("Hello, Qt!")->setPos(100, 200);
    scene.addPixmap(QPixmap("image.png"))->setPos(300, 300);

    // 创建视图
    QGraphicsView view(&scene);
    view.show();
    
    return a.exec();
}

四、QGraphicsView (视图)

QGraphicsView 是 Qt Graphics View Framework(图形视图框架)的核心组件之一,它用于在 窗口 中显示 QGraphicsScene(场景)中的图形项(QGraphicsItem),并提供滚动、缩放、旋转、变换 等功能。

QGraphicsView(视图)的主要作用:

  • 负责 显示 QGraphicsScene。
  • 提供 坐标转换(视图坐标 ⇄ 场景坐标),支持 缩放、平移、旋转。
  • 处理 用户输入事件(鼠标、键盘等)。

架构示意图:

+--------------------+
|  QGraphicsView     |  ←  负责显示场景,并提供交互功能
|  +--------------+  |
|  | QGraphicsScene |  |  ←  管理所有 QGraphicsItem
|  | +----------+ |  |
|  | | QGraphicsItem |  |  ←  具体图形元素
|  | +----------+ |  |
|  +--------------+  |
+--------------------+

QGraphicsView是视图窗口部件,使场景内容可视化,可以连接多个视图到一个场景,也可以为相同数据源的数据集提供不同的视图。QGraphicsView是可滚动的窗口部件,可以提供滚动条来浏览大的场景。如果需要使用OpenGL,可以使用QGraphicsView::setViewport()将视图设置为QGLWidget组件。
视图接收键盘和鼠标的输入事件,并把事件翻译为场景事件(将坐标转换为场景的坐标),再发送到场景。使用变换矩阵函数QGraphicsView::martix()可以变换场景的坐标系统,通过变换场景的坐标系统可以实现场景的缩放和旋转。为了方便,QGraphicsView提供了视图和场景的坐标转换函数:QGraphicsView::mapToScene()和QGraphicsView::mapFromScene()。

示例代码:

#include <QApplication>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QGraphicsRectItem>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    //新建场景
    QGraphicsScene* secne = new QGraphicsScene;
    //创建图形项
    QGraphicsRectItem* rectItem = new QGraphicsRectItem(0,0,100,100);
    //将图形项添加到场景中
    secne->addItem(rectItem);

    //创建视图
    QGraphicsView view(secne);
    //view.setScene(secne);    //也可以这样设置场景
    //设置场景的前景色
    view.setForegroundBrush(QColor(255,255,0,100));
    //设置场景的背景色
    view.setBackgroundBrush(QPixmap("./BrushStroke_Coloured_Variant_A.png"));
    //设置场景与视图对齐
    view.setAlignment(Qt::AlignTop | Qt::AlignLeft);
    view.resize(640,480);
    view.show();

    return a.exec();
}

在这里插入图片描述
这里新建了视图部件,并指定了要可视化的场景。然后为该视图设置了场景前景色和背景图片。一个场景分为3层:图形项层(ItemLayer)、前景层(ForegroundLayer)和背景层(BackgroundLayer)。场景的绘制总是从背景层开始,然后是图形项层,最后是前景层。前景层和背景层都可以使用QBrush进行填充,比如使用渐变和贴图等。
代码中使用了QGraphicsView类中的函数设置了场景中的背景和前景,其实也可以使用QGraphicsScene 中的同名函数来实现,不过它们的效果并不完全一样。如果使用QGraphicsScene对象设置了场景背景或者前景,那么对所有关联了该场景的视图都有效,而QGraphicsView对象设置的场景的背景或者前景只对它本身对应的视图有效。

五、QGraphicsItem( 图元)

QGraphicsItem 是 Qt Graphics View Framework(图形视图框架)中的图形项基类,用于表示 场景(QGraphicsScene) 中的各类图形元素,如矩形、椭圆、文本、图片等。

标准图元:

  • QGraphicsEllipseItem :提供了一个椭圆图元
  • QGraphicsLineItem:提供了一个线段图元
  • QGraphicsPathItem :提供任意路径图元
  • QGraphicsPixmapItem :提供了一个像素图图元
  • QGraphicsPolygonItem:提供了一个多边形图元
  • QGraphicsRectItem: 提供了一个矩形图元
  • QGraphicsSimpleTextItem:提供了一个简单的文本标签图元
  • QGraphicsTextItem:提供了一个高级的文本浏览图元

在这里插入图片描述
用户可以继承QGraphicsItem实现自定义的图元。QGraphicsItem图元主要特性如下:

  • 支持鼠标按下、移动、释放、双击、悬停、滚动和右键菜单事件。
  • 支持键盘输入焦点和按键事件
  • 支持拖拽事件
  • 支持分组,使用父子关系和QGraphicsItemGroup
  • 支持碰撞检测

示例代码:绘制矩形

#include <QApplication>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QGraphicsRectItem>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    //新建场景
    QGraphicsScene* secne = new QGraphicsScene;

    //创建图元
    QGraphicsRectItem* RectItem = new QGraphicsRectItem(10,10,150,150);
    //将图元添加到场景中
    secne->addItem(RectItem);
    //设置图元可移动的
    RectItem->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsFocusable);

    //创建视图
    QGraphicsView view(secne);//设置场景
    //设置场景与视图对齐
    view.setAlignment(Qt::AlignTop | Qt::AlignLeft);
    //设置拖动模式
    view.setDragMode(QGraphicsView::RubberBandDrag);

    view.resize(640,480);
    view.show();

    return a.exec();
}

在这里插入图片描述
示例代码:绘制图片

#include <QApplication>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QGraphicsRectItem>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    //新建场景
    QGraphicsScene* secne = new QGraphicsScene;

    QGraphicsPixmapItem *item = new QGraphicsPixmapItem();//创建qt标准图元
    item->setPixmap(QPixmap::fromImage(QImage("./BrushStroke_Coloured_Variant_B.png")));//设置图片
    secne->addItem(item);//往场景里添加图元
    QGraphicsView *view = new QGraphicsView(secne);
    view->setScene(secne);//QGraphicsView设置场景
    view->show();

    return a.exec();
}

在这里插入图片描述

六、窗口部件、布局和内嵌部件

从Qt4.4开始通过QGraphicsWidget类引入了支持几何和布局的图形项。图形部件QGraphicsWidget与QWidget很相似,但是与QWidget不同,它不是继承自QPaintDevice,而是QGraphicsItem。通过它可以实现一个拥有事件、信号和槽、大小和策略的完整的部件,还可以使用QGraphicsLinearLayout和QGraphicsGridLayout来实现部件的布局。
QGraphicsWidget继承自QGraphicsObject和QGraphicsLayoutItem,而QGraphicsObject
继承自QObject和QGraphicsItem,所以QGraphicsWidget既拥有一千窗口部件的一些特性也拥有图形项的一些特性,图形视图框架提供了对任意的窗口部件嵌入场景的无缝支持,这是通过QGraphicsWidget的子类QGraphicsProxyWidget实现的。可以使用QGraphicsScene类的addWidget()函数将任何一个窗口部件嵌入到场景中,这也可以通过创建QGraphicsProxyWidget类的实例来实现。

示例代码:

#include <QApplication>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QGraphicsWidget>
#include <QTextEdit>
#include <QPushButton>
#include <QGraphicsProxyWidget>
#include <QGraphicsLinearLayout>
#include <QObject>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    QGraphicsScene scene;
    // 创建部件,并关联它们的信号和槽
    QTextEdit *edit = new QTextEdit;
    QPushButton *button = new QPushButton("clear");
    QObject::connect(button, &QPushButton::clicked, edit, &QTextEdit::clear);
    // 将部件添加到场景中
    QGraphicsWidget *textEdit = scene.addWidget(edit);
    QGraphicsWidget * pushButton = scene.addWidget(button);
    // 将部件添加到部件管理器中
    QGraphicsLinearLayout *layout = new QGraphicsLinearLayout;
    layout->addItem(textEdit);
    layout->addItem(pushButton);
    // 创建图形部件,设置其为一个顶层窗口,然后在其上应用布局
    QGraphicsWidget * form = new QGraphicsWidget;
    form->setWindowFlags(Qt::Window);
    form->setWindowTitle("Widget Item");
    form->setLayout(layout);

    // 将图形部件进行扭曲,然后添加到场景中
    form->setTransform(QTransform().shear(2, -0.5), true);
    scene.addItem(form);

    QGraphicsView view(&scene);
    view.show();

    return a.exec();
}

效果显示:
在这里插入图片描述

七、完整实例代码

自定义图元代码实现:
.h文件

#ifndef PIXITEM_H
#define PIXITEM_H

#include <QGraphicsItem> // 图元类
#include <QPixmap>
#include <QPainter>

class PixItem : public QGraphicsItem
{
public:
    PixItem(QPixmap pixmap);

protected:
    // 此函数只刷新固定区域,返回调整成实际物体所在位置的包含区域
    QRectF boundingRect() const override;
    // 绘图操作
    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override;

private:
    QPixmap m_pix;

};
#endif // PIXITEM_H

.cpp文件

#include "pixitem.h"

PixItem::PixItem(QPixmap pixmap) : m_pix(pixmap)
{

}

// 此函数只刷新固定区域,返回调整成实际物体所在位置的包含区域
QRectF PixItem::boundingRect() const
{
    /*
       QRectF函数:使用浮点精度来定义平面当真的矩形
       对象参数列表,前两个参数是矩形的左上角x和y坐标
       后两个参数宽和高(长度包含七点,所以实际长度减1)
    */
    return  QRectF(-2 - m_pix.width() / 2, -2 - m_pix.height() /2, m_pix.width() + 4, m_pix.height() + 4);
}
// 绘图操作
void PixItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget )
{
    /*
       drawPixmap()使用CPU处理,减轻CPU负担
       QLabel很耗费CPU,播放画面卡顿
    */
    painter->drawPixmap(-m_pix.width() / 2, -m_pix.height() / 2, m_pix);
}

图元绑定到场景中的实现代码:
.h文件

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QWidget>
#include <QGraphicsView> // 视图类
#include <QGraphicsScene> // 场景类
#include <QFrame>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QFrame>
#include <QGroupBox>
#include <QSlider>
#include "pixitem.h"

class MainWindow : public QWidget
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

public:
    void CreateControlFrameFunc(); // 创建控件框架

private slots:
    void rotateFunc(int); // 旋转
    void scaleFunc(int); // 缩放
    void leanFunc(int); // 倾斜

private:
    int m_iAngle; // 角度
    qreal m_scalevalues; // 缩放
    qreal m_leanvalues; // 倾斜

    QGraphicsView *m_view; // 视图对象
    QFrame * m_controlFrame; // 控制边框样式
    PixItem *m_pixItem;
};
#endif // MAINWINDOW_H

.cpp文件

#include "mainwindow.h"
#include <cmath>

MainWindow::MainWindow(QWidget *parent)
    : QWidget(parent)
    , m_iAngle(0)
    , m_scalevalues(2)
    , m_leanvalues(2)
{
    setWindowTitle(("图形视图框架--应用程序测试"));

    // 场景
    QGraphicsScene *sence = new QGraphicsScene;
    sence->setSceneRect(-200, 200, 400, 400);

    // 图元
    QPixmap pixmap = QPixmap("./BrushStroke_Coloured_Variant_A.png");
    m_pixItem = new PixItem(pixmap);

    sence->addItem(m_pixItem); // 将图元添加到场景
    m_pixItem->setPos(0, 0);

    // 视图
    m_view = new QGraphicsView;
    m_view->setScene(sence);
    m_view->setMinimumSize(800, 600);

    m_controlFrame = new QFrame;
    CreateControlFrameFunc();

    QHBoxLayout *hLayout = new QHBoxLayout(this);
    hLayout->addWidget(m_view);
    hLayout->addWidget(m_controlFrame);

    this->setLayout(hLayout); // 向布局中添加子布局
}

MainWindow::~MainWindow()
{
}

void MainWindow::CreateControlFrameFunc()
{
    // 旋转
    QSlider *rotateSilder = new QSlider(this);
    rotateSilder->setOrientation(Qt::Horizontal);
    rotateSilder->setRange(0, 360);

    QHBoxLayout *rotateLayout = new QHBoxLayout(this);
    rotateLayout->addWidget(rotateSilder);

    QGroupBox *rotateGroup = new QGroupBox("图形旋转");
    rotateGroup->setLayout(rotateLayout);

    // 缩放
    QSlider *scaleSilder = new QSlider(this);
    scaleSilder->setOrientation(Qt::Horizontal);
    scaleSilder->setRange(0, 2 * m_scalevalues);
    scaleSilder->setValue(m_scalevalues);

    QHBoxLayout *scaleLayout = new QHBoxLayout(this);
    scaleLayout->addWidget(scaleSilder);

    QGroupBox *scaleGroup = new QGroupBox("图形缩放");
    scaleGroup->setLayout(scaleLayout);

    // 倾斜
    QSlider *leanvaSilder = new QSlider(this);
    leanvaSilder->setOrientation(Qt::Horizontal);
    leanvaSilder->setRange(0, 2 * m_scalevalues);
    leanvaSilder->setValue(m_scalevalues);

    QHBoxLayout *leanvaLayout = new QHBoxLayout(this);
    leanvaLayout->addWidget(leanvaSilder);

    QGroupBox *leanvaGroup = new QGroupBox("图形倾斜");
    leanvaGroup->setLayout(leanvaLayout);


    QVBoxLayout *vLayoutFrame = new QVBoxLayout(this);
    vLayoutFrame->addWidget(rotateGroup);
    vLayoutFrame->addWidget(scaleGroup);
    vLayoutFrame->addWidget(leanvaGroup);

    m_controlFrame->setLayout(vLayoutFrame);

    // 信号与槽函数连接
    connect(rotateSilder, &QSlider::valueChanged, this, &MainWindow::rotateFunc);
    connect(scaleSilder, &QSlider::valueChanged, this, &MainWindow::scaleFunc);
    connect(leanvaSilder, &QSlider::valueChanged, this, &MainWindow::leanFunc);
}

void MainWindow::rotateFunc(int val)
{
    m_view->rotate(val - m_iAngle);
    m_iAngle = val;
}

void MainWindow::scaleFunc(int val)
{
    qreal qs;
    if (val > m_scalevalues) {
        qs = pow(1.1, (val - m_scalevalues));
    } else {
        qs = pow(1/1.1, (m_scalevalues - val));
    }
    m_view->scale(qs, qs);
    m_scalevalues = val;
}

void MainWindow::leanFunc(int val)
{
    m_view->shear((val - m_leanvalues) * 2.0, 0);
    m_leanvalues = val;
}

输出结果:
在这里插入图片描述