qt-C++语法笔记之Qt Graphics View 框架中的类型辨析完全指南

qt-C++语法笔记之Qt Graphics View 框架中的类型辨析完全指南

code review!

文章目录

  • [qt-C++语法笔记之Qt Graphics View 框架中的类型辨析完全指南](#qt-C++语法笔记之Qt Graphics View 框架中的类型辨析完全指南)
    • 一、核心概念概览
      • [1.1 总览大表](#1.1 总览大表)
    • 二、先说结论
      • [2.1 `QLine`、`QRect`、`QPainterPath` 是"数据",不是"图元"](#2.1 QLineQRectQPainterPath 是"数据",不是"图元")
      • [2.2 `QGraphicsLineItem`、`QGraphicsRectItem`、`QGraphicsPathItem` 都能直接构造实例](#2.2 QGraphicsLineItemQGraphicsRectItemQGraphicsPathItem 都能直接构造实例)
      • [2.3 `QGraphicsItem` 不是"现成能画东西的具体图元"](#2.3 QGraphicsItem 不是"现成能画东西的具体图元")
        • [2.3.1 `QGraphicsItem` 的特点](#2.3.1 QGraphicsItem 的特点)
      • [2.4 `QAbstractGraphicsShapeItem` 不能直接实例化](#2.4 QAbstractGraphicsShapeItem 不能直接实例化)
    • 三、继承关系辨析
      • [3.1 继承关系总览表](#3.1 继承关系总览表)
      • [3.2 完整层级关系表](#3.2 完整层级关系表)
      • [3.3 继承脉络表](#3.3 继承脉络表)
    • 四、关键设计问题解析
      • [4.1 为什么 `QGraphicsItem` 不能像 `QGraphicsRectItem` 一样直接用?](#4.1 为什么 QGraphicsItem 不能像 QGraphicsRectItem 一样直接用?)
      • [4.2 为什么 `QAbstractGraphicsShapeItem` 也不能直接用?](#4.2 为什么 QAbstractGraphicsShapeItem 也不能直接用?)
    • 五、实战选择指南
      • [5.1 什么时候"直接用现成图元",什么时候"自定义"?](#5.1 什么时候"直接用现成图元",什么时候"自定义"?)
        • [5.1.1 适合直接用 `QGraphicsLineItem / RectItem / PathItem`](#5.1.1 适合直接用 QGraphicsLineItem / RectItem / PathItem)
        • [5.1.2 适合继承 `QGraphicsLineItem / RectItem / PathItem`](#5.1.2 适合继承 QGraphicsLineItem / RectItem / PathItem)
        • [5.1.3 适合继承 `QGraphicsItem`](#5.1.3 适合继承 QGraphicsItem)
        • [5.1.4 适合继承 `QAbstractGraphicsShapeItem`](#5.1.4 适合继承 QAbstractGraphicsShapeItem)
      • [5.2 选择决策表](#5.2 选择决策表)
    • 六、最常见的混淆点
      • [6.1 混淆 1:`QRect` 和 `QGraphicsRectItem` 不是一个层次](#6.1 混淆 1:QRectQGraphicsRectItem 不是一个层次)
      • [6.2 混淆 2:`QPainterPath` 和 `QGraphicsPathItem` 不是一个层次](#6.2 混淆 2:QPainterPathQGraphicsPathItem 不是一个层次)
      • [6.3 混淆 3:`QGraphicsItem` 是"接口框架",不是"现成图形"](#6.3 混淆 3:QGraphicsItem 是"接口框架",不是"现成图形")
    • 七、简化记忆法
      • [7.1 数据 vs 图元](#7.1 数据 vs 图元)
      • [7.2 现成图元 vs 抽象基类](#7.2 现成图元 vs 抽象基类)
      • [7.3 用一句话概括每个类](#7.3 用一句话概括每个类)
    • 八、常见判断与结论
      • [8.1 各类能否直接构造实例?](#8.1 各类能否直接构造实例?)
      • [8.2 关键结论汇总](#8.2 关键结论汇总)
    • 九、自定义图元实现指南
      • [9.1 三种自定义方式横向对比](#9.1 三种自定义方式横向对比)
      • [9.2 最小可用自定义代码对比](#9.2 最小可用自定义代码对比)
        • [9.2.1 继承 `QGraphicsRectItem`](#9.2.1 继承 QGraphicsRectItem)
        • [9.2.2 继承 `QAbstractGraphicsShapeItem`](#9.2.2 继承 QAbstractGraphicsShapeItem)
        • [9.2.3 继承 `QGraphicsItem`](#9.2.3 继承 QGraphicsItem)
      • [9.3 自定义 `QGraphicsItem` 时必须知道的核心函数](#9.3 自定义 QGraphicsItem 时必须知道的核心函数)
      • [9.4 `boundingRect()`、`shape()`、实际绘制范围 三者区别](#9.4 boundingRect()shape()、实际绘制范围 三者区别)
        • [9.4.1 例子](#9.4.1 例子)
    • [十、`QPainterPath` 的特殊作用](#十、QPainterPath 的特殊作用)
      • [10.1 `QPainterPath` 在自定义图元里特别重要](#10.1 QPainterPath 在自定义图元里特别重要)
        • [10.1.1 示例代码](#10.1.1 示例代码)
    • 十一、常见图元类型与对应类
    • 总结与快速查询

一、核心概念概览

下面用一个大表格把这些概念放在一起进行辨析。Qt Graphics View 框架中分为两条主要线:

  1. 几何数据类QLine / QRect / QPainterPath
  2. 图元对象类QGraphicsLineItem / QGraphicsRectItem / QGraphicsPathItem / QGraphicsItem / QAbstractGraphicsShapeItem

1.1 总览大表

类名 类别 表示的本质 能否直接实例化 是否可直接加入 QGraphicsScene 是否自带绘制实现 是否适合作为自定义基类 典型用途 备注
QLine / QLineF 几何数据类 一条线段的数据 可以 不可以 保存线段坐标 只是"数据",不是图元
QRect / QRectF 几何数据类 一个矩形区域的数据 可以 不可以 保存矩形范围 也是纯数据
QPainterPath 几何数据类 一条路径的数据集合 可以 不可以 复杂轮廓、曲线、命中区域 常用于自定义绘制和 shape()
QGraphicsLineItem 图元类 基于线段的现成图元 可以 可以 可以 直接画线,或继承扩展 内部通常持有 QLineF
QGraphicsRectItem 图元类 基于矩形的现成图元 可以 可以 可以 直接画矩形,或继承扩展 内部通常持有 QRectF
QGraphicsPathItem 图元类 基于路径的现成图元 可以 可以 可以 直接画复杂路径,或继承扩展 内部通常持有 QPainterPath
自定义 QGraphicsLineItem 子类 图元类 在线图元基础上扩展行为 可以 可以 继承父类已有实现 可以 给线增加交互、业务逻辑 若只扩展行为,这是很方便的选择
自定义 QGraphicsRectItem 子类 图元类 在矩形图元基础上扩展行为 可以 可以 继承父类已有实现 可以 可拖动矩形、选择框等 常用于节点框、包围框
自定义 QGraphicsPathItem 子类 图元类 在路径图元基础上扩展行为 可以 可以 继承父类已有实现 可以 连线、箭头、复杂形状 自定义连线常见
QGraphicsItem 图元抽象基类 所有图元的最基础接口 不能直接正常使用为具体图元 理论上子类可以,自己本身不适合直接用 没有完整具体图形实现 非常适合 完全自定义图元 需重写至少 boundingRect()paint()
QAbstractGraphicsShapeItem 图元抽象中间基类 pen/brush 属性的形状图元基类 不能直接实例化 不可以 不提供完整具体形状 适合 自定义"有轮廓/填充"的图元 它是抽象类
自定义 QGraphicsItem 子类 图元类 完全自主定义外观和行为 可以 可以 由你自己实现 是最终常见方案 不规则图元、组合逻辑、复杂交互 灵活度最高

二、先说结论

2.1 QLineQRectQPainterPath 是"数据",不是"图元"

它们都能直接构造实例,例如:

cpp 复制代码
QLineF line(0, 0, 100, 100);
QRectF rect(0, 0, 80, 60);
QPainterPath path;
path.moveTo(0, 0);
path.lineTo(100, 50);

但它们不能直接放进 QGraphicsScene ,因为它们不是 QGraphicsItem

2.2 QGraphicsLineItemQGraphicsRectItemQGraphicsPathItem 都能直接构造实例

例如:

cpp 复制代码
scene->addItem(new QGraphicsLineItem(QLineF(0, 0, 100, 100)));
scene->addItem(new QGraphicsRectItem(QRectF(0, 0, 80, 60)));
scene->addItem(new QGraphicsPathItem(path));

它们是 Qt 已经帮你写好的具体图元类,能直接显示。

同时你也可以继承它们来自定义:

cpp 复制代码
class MyLineItem : public QGraphicsLineItem
{
public:
    using QGraphicsLineItem::QGraphicsLineItem;
};

这类继承适合:

  • 已经是线/矩形/路径
  • 只想增加事件处理、状态、业务属性
  • 不想从零重写绘制逻辑

2.3 QGraphicsItem 不是"现成能画东西的具体图元"

2.3.1 QGraphicsItem 的特点

更准确地说:

  • 它是基类
  • 通常不会直接拿来实例化使用
  • 因为你必须自己定义图元边界和绘制方式

在实际开发语义里,可以理解为:

不能直接作为一个可用的具体图元来构造使用,必须继承它并实现关键虚函数。

典型自定义写法:

cpp 复制代码
class MyItem : public QGraphicsItem
{
public:
    QRectF boundingRect() const override
    {
        return QRectF(0, 0, 100, 50);
    }

    void paint(QPainter *painter,
               const QStyleOptionGraphicsItem *option,
               QWidget *widget = nullptr) override
    {
        painter->setBrush(Qt::yellow);
        painter->drawEllipse(boundingRect());
    }
};

然后:

cpp 复制代码
scene->addItem(new MyItem);

2.4 QAbstractGraphicsShapeItem 不能直接实例化

这个可以比较明确地说:

  • 它是抽象类
  • 主要作用是给子类提供公共的 pen / brush 管理
  • 它本身不代表某个具体形状
  • 所以不能直接构造实例

它的典型子类包括:

  • QGraphicsRectItem
  • QGraphicsEllipseItem
  • QGraphicsPathItem
  • QGraphicsPolygonItem
  • QGraphicsSimpleTextItem 不是它的子类
  • QGraphicsLineItem不是它的子类

这一点很容易混:

是否继承自 QAbstractGraphicsShapeItem
QGraphicsRectItem
QGraphicsPathItem
QGraphicsEllipseItem
QGraphicsPolygonItem
QGraphicsLineItem

因为线通常只有 pen,没有封闭填充区域,所以它没有走 "shape item" 这套抽象。

三、继承关系辨析

3.1 继承关系总览表

直接/间接基类关系 是否抽象 说明
QGraphicsItem 基础根类 是抽象基类语义 所有图元的核心接口
QAbstractGraphicsShapeItem 继承 QGraphicsItem 为"有画笔/画刷"的形状图元提供公共能力
QGraphicsLineItem 继承 QGraphicsItem 具体线图元
QGraphicsRectItem 继承 QAbstractGraphicsShapeItem 具体矩形图元
QGraphicsPathItem 继承 QAbstractGraphicsShapeItem 具体路径图元

3.2 完整层级关系表

层级 类名 是什么 是否抽象 能否直接 new 后作为图元使用 你通常要不要继承它 典型职责
几何数据层 QLineF 线段数据 保存线段几何信息
几何数据层 QRectF 矩形数据 保存矩形范围
几何数据层 QPainterPath 路径数据 保存复杂路径、曲线、轮廓
图元抽象层 QGraphicsItem 所有图元共同基类 经常 定义图元的坐标、绘制、事件、碰撞等接口
图元抽象层 QAbstractGraphicsShapeItem 形状图元抽象基类 有时 QGraphicsItem 基础上补充 pen/brush
图元具体实现层 QGraphicsLineItem 线图元 可以 可以 直接显示线段
图元具体实现层 QGraphicsRectItem 矩形图元 可以 可以 直接显示矩形
图元具体实现层 QGraphicsPathItem 路径图元 可以 可以 直接显示路径
图元具体实现层 自定义 QGraphicsItem 完全自定义图元 可以 这是结果类 实现任意外观与交互
图元具体实现层 自定义 QAbstractGraphicsShapeItem 自定义形状图元 可以 这是结果类 实现带轮廓/填充的自定义图形
图元具体实现层 自定义 QGraphicsRectItem 在现成图元上扩展 可以 这是结果类 在已有绘制基础上加逻辑

3.3 继承脉络表

下面是你最关心的继承脉络。

父类 是否属于"具体图元" 自带 paint() 具体实现 自带 boundingRect() 具体实现 备注
QGraphicsItem 无(核心基类) 最底层抽象接口
QAbstractGraphicsShapeItem QGraphicsItem 只增加 pen/brush 这类公共能力
QGraphicsLineItem QGraphicsItem 线不是"填充形状",所以不走 QAbstractGraphicsShapeItem
QGraphicsRectItem QAbstractGraphicsShapeItem 典型 shape item
QGraphicsPathItem QAbstractGraphicsShapeItem 典型 shape item

可以把它理解成:

text 复制代码
QGraphicsItem
├─ QAbstractGraphicsShapeItem
│  ├─ QGraphicsRectItem
│  ├─ QGraphicsEllipseItem
│  ├─ QGraphicsPathItem
│  └─ QGraphicsPolygonItem
└─ QGraphicsLineItem

四、关键设计问题解析

4.1 为什么 QGraphicsItem 不能像 QGraphicsRectItem 一样直接用?

因为 QGraphicsItem 只定义了"图元应该具备什么能力",但没有定义你到底长什么样

一个图元至少要回答两个问题:

  1. 你占多大区域?

    对应 boundingRect()

  2. 你怎么画出来?

    对应 paint()

QGraphicsRectItem 已经替你回答了:

  • 区域就是它的矩形
  • 绘制就是用 pen/brush 画矩形

QGraphicsItem 不知道你想画:

  • 箭头
  • 节点
  • 多边形
  • 流程图块
  • 自定义控件样式图元

所以它必须让你自己实现。

4.2 为什么 QAbstractGraphicsShapeItem 也不能直接用?

因为它只是一个**"半成品抽象层"**。

它帮你准备了这类通用属性:

  • setPen(...)
  • pen()
  • setBrush(...)
  • brush()

但它仍然不知道你到底是:

  • 矩形
  • 椭圆
  • 路径
  • 多边形
  • 圆角气泡框

所以它也不能单独拿来作为完整图元使用。

你可以理解为:

提供了什么 没提供什么
QGraphicsItem 图元框架 图形外观
QAbstractGraphicsShapeItem 图元框架 + pen/brush 具体形状

五、实战选择指南

5.1 什么时候"直接用现成图元",什么时候"自定义"?

5.1.1 适合直接用 QGraphicsLineItem / RectItem / PathItem

如果你的需求只是:

  • 画一条线
  • 画一个矩形
  • 画一条路径
  • 设置颜色、线宽、选中、移动

那么直接用现成类就够了:

cpp 复制代码
auto *item = new QGraphicsRectItem(0, 0, 100, 60);
item->setBrush(Qt::green);
item->setPen(QPen(Qt::black, 2));
scene->addItem(item);
5.1.2 适合继承 QGraphicsLineItem / RectItem / PathItem

如果你只是想在现成图元上加一点行为,比如:

  • 鼠标点击响应
  • hover 高亮
  • 保存业务 id
  • 连线端点逻辑
  • 选中后显示控制点

那么继承现成图元最省事:

cpp 复制代码
class MyRectItem : public QGraphicsRectItem
{
public:
    MyRectItem(const QRectF &rect) : QGraphicsRectItem(rect) {}

protected:
    void mousePressEvent(QGraphicsSceneMouseEvent *event) override
    {
        setBrush(Qt::red);
        QGraphicsRectItem::mousePressEvent(event);
    }
};
5.1.3 适合继承 QGraphicsItem

如果你需要:

  • 非规则图形
  • 一个图元里画很多内容
  • 自定义命中区域
  • 特殊坐标逻辑
  • 完全控制绘制过程

那么直接继承 QGraphicsItem 最灵活。

例如一个节点图元,同时画:

  • 圆角矩形背景
  • 标题文字
  • 图标
  • 输入输出端口

这时继承 QGraphicsRectItem 反而不自然,通常会直接继承 QGraphicsItem

5.1.4 适合继承 QAbstractGraphicsShapeItem

如果你的自定义图元本质上是一个"形状":

  • 有轮廓 pen
  • 有填充 brush
  • 你希望复用 shape item 的通用属性管理

那么可以继承它。

比如:

cpp 复制代码
class MyShapeItem : public QAbstractGraphicsShapeItem
{
public:
    QRectF boundingRect() const override
    {
        return QRectF(0, 0, 100, 100);
    }

    void paint(QPainter *painter,
               const QStyleOptionGraphicsItem *,
               QWidget *) override
    {
        painter->setPen(pen());
        painter->setBrush(brush());
        painter->drawRoundedRect(boundingRect(), 10, 10);
    }
};

这里 pen()brush() 就是从 QAbstractGraphicsShapeItem 继承来的便利接口。

5.2 选择决策表

这是开发中最实用的一张表。

需求场景 推荐继承类 原因 举例
就想画一条普通线 直接用 QGraphicsLineItem 现成可用 辅助线、边框线
就想画一个普通矩形 直接用 QGraphicsRectItem 现成可用 选区框、节点框
就想画复杂路径 直接用 QGraphicsPathItem 现成可用 曲线、轮廓、连接线
图形已经是线/矩形/路径,只想加点击、拖动、hover、业务数据 继承对应现成类 省事,复用现成绘制和边界计算 自定义连线、自定义节点框
图形本质是"带填充和边框的自定义形状" 继承 QAbstractGraphicsShapeItem 复用 pen/brush 体系 圆角框、气泡框、自定义卡片
图形完全不规则,或者一个 item 里画很多元素 继承 QGraphicsItem 灵活度最高 流程图节点、拓扑节点、带端口组件
命中区域、边界区域、绘制都很特殊 继承 QGraphicsItem 完全自己控制 箭头、可编辑贝塞尔边、复杂交互组件

六、最常见的混淆点

6.1 混淆 1:QRectQGraphicsRectItem 不是一个层次

名称 含义
QRectF 只是矩形数据
QGraphicsRectItem 可以显示在场景里的矩形图元

可以理解成:

  • QRectF = "矩形参数"
  • QGraphicsRectItem = "拿这个参数画出来的图元对象"

6.2 混淆 2:QPainterPathQGraphicsPathItem 不是一个层次

名称 含义
QPainterPath 路径数据
QGraphicsPathItem 显示该路径的图元

6.3 混淆 3:QGraphicsItem 是"接口框架",不是"现成图形"

它更像"所有图元的父类规范"。

它要求你告诉 Qt:

  • 我占多大范围:boundingRect()
  • 我怎么画:paint()

不提供现成几何形状。

七、简化记忆法

7.1 数据 vs 图元

数据类 图元类
QLineF QGraphicsLineItem
QRectF QGraphicsRectItem
QPainterPath QGraphicsPathItem

即:

前者是"描述图形的数据"

后者是"场景中真正显示的对象"

7.2 现成图元 vs 抽象基类

现成图元 抽象基类
QGraphicsLineItem QGraphicsItem
QGraphicsRectItem QAbstractGraphicsShapeItem
QGraphicsPathItem QAbstractGraphicsShapeItem

即:

名字里带具体图形名的,往往是可以直接用的

名字偏"抽象概念"的,往往是给你继承的

7.3 用一句话概括每个类

一句话概括
QLineF 一条线段的数据
QRectF 一个矩形的数据
QPainterPath 一组路径/曲线的数据
QGraphicsLineItem Qt 内置的"线图元"
QGraphicsRectItem Qt 内置的"矩形图元"
QGraphicsPathItem Qt 内置的"路径图元"
QGraphicsItem 所有图元的抽象父类,适合完全自定义
QAbstractGraphicsShapeItem pen/brush 支持的形状图元抽象基类

八、常见判断与结论

8.1 各类能否直接构造实例?

问题 答案 说明
QLineQRectQPainterPath 能直接构造实例吗? 但它们只是数据类,不是场景图元
QGraphicsLineItemQGraphicsRectItemQGraphicsPathItem 能直接构造实例吗? 它们是具体图元类,可以直接 new 后加入 QGraphicsScene
自定义 QGraphicsLineItemQGraphicsRectItemQGraphicsPathItem 可以吗? 可以 这很常见,尤其是给现成图元增加交互和业务逻辑时
QGraphicsItemQAbstractGraphicsShapeItem 都不能直接构造实例吗? 是的 QAbstractGraphicsShapeItem 是抽象类;QGraphicsItem 本质上也不是拿来直接当具体图元用的
自定义 QGraphicsItem 可以吗? 当然可以 这是最常见的完全自定义方案

8.2 关键结论汇总

说法 对错 说明
QLineFQRectFQPainterPath 可以直接构造实例 但它们只是数据类
QGraphicsLineItemQGraphicsRectItemQGraphicsPathItem 可以直接构造实例 它们是具体图元
可以自定义继承 QGraphicsLineItemQGraphicsRectItemQGraphicsPathItem 常用于加交互或业务逻辑
QAbstractGraphicsShapeItem 不能直接构造实例 它是抽象类
QGraphicsItem 不能直接当作现成图元来用 必须实现关键虚函数后作为子类使用
自定义 QGraphicsItem 是最灵活方案 但工作量最大

九、自定义图元实现指南

9.1 三种自定义方式横向对比

自定义方式 基类 开发成本 灵活度 可复用 Qt 现成绘制 推荐场景
继承 QGraphicsLineItem/RectItem/PathItem 具体图元类 改行为,不大改外观
继承 QAbstractGraphicsShapeItem 抽象形状基类 中高 自定义形状,但仍然想用 pen/brush
继承 QGraphicsItem 最基础抽象类 最高 完全自定义复杂图元

9.2 最小可用自定义代码对比

9.2.1 继承 QGraphicsRectItem

适合:本质上就是矩形,只加一点行为。

cpp 复制代码
class MyRectItem : public QGraphicsRectItem
{
public:
    explicit MyRectItem(const QRectF &rect, QGraphicsItem *parent = nullptr)
        : QGraphicsRectItem(rect, parent)
    {
        setBrush(Qt::yellow);
        setPen(QPen(Qt::black, 2));
        setFlag(QGraphicsItem::ItemIsSelectable);
        setFlag(QGraphicsItem::ItemIsMovable);
    }

protected:
    void mousePressEvent(QGraphicsSceneMouseEvent *event) override
    {
        setBrush(Qt::red);
        QGraphicsRectItem::mousePressEvent(event);
    }
};

特点

  • 不用自己写 boundingRect()
  • 不用自己写 paint()
  • 改行为最方便
9.2.2 继承 QAbstractGraphicsShapeItem

适合:是"形状",但不是 Qt 现成那几种形状。

cpp 复制代码
class MyShapeItem : public QAbstractGraphicsShapeItem
{
public:
    explicit MyShapeItem(QGraphicsItem *parent = nullptr)
        : QAbstractGraphicsShapeItem(parent)
    {
        setPen(QPen(Qt::blue, 2));
        setBrush(QColor(200, 220, 255));
    }

    QRectF boundingRect() const override
    {
        return QRectF(0, 0, 120, 80);
    }

    void paint(QPainter *painter,
               const QStyleOptionGraphicsItem *,
               QWidget *) override
    {
        painter->setPen(pen());
        painter->setBrush(brush());
        painter->drawRoundedRect(boundingRect(), 12, 12);
    }
};

特点

  • 你要自己画
  • pen/brush 不用自己维护字段
  • QGraphicsItem 省一点
9.2.3 继承 QGraphicsItem

适合:完全自定义。

cpp 复制代码
class MyItem : public QGraphicsItem
{
public:
    explicit MyItem(QGraphicsItem *parent = nullptr)
        : QGraphicsItem(parent)
    {
        setFlag(ItemIsMovable);
        setFlag(ItemIsSelectable);
    }

    QRectF boundingRect() const override
    {
        return QRectF(0, 0, 140, 90);
    }

    void paint(QPainter *painter,
               const QStyleOptionGraphicsItem *option,
               QWidget *widget = nullptr) override
    {
        Q_UNUSED(option)
        Q_UNUSED(widget)

        painter->setPen(QPen(Qt::black, 2));
        painter->setBrush(QColor(240, 240, 180));
        painter->drawRoundedRect(0, 0, 140, 90, 10, 10);

        painter->setPen(Qt::black);
        painter->drawText(QRectF(0, 0, 140, 90), Qt::AlignCenter, "My Item");
    }
};

特点

  • 最自由
  • 也最需要你自己负责

9.3 自定义 QGraphicsItem 时必须知道的核心函数

函数 是否常常必须重写 作用 不重写会怎样
boundingRect() 必须 告诉场景此图元占据的外接矩形 无法正确重绘、碰撞、更新
paint() 必须 真正绘制图元内容 图元不会被正确画出
shape() 常用但非必须 定义更精确的命中/碰撞区域 默认通常只按外接矩形或粗略区域处理
contains() 有时 判断某点是否在图元内 通常依赖 shape()
itemChange() 常用 监听位置、选择状态等变化 无法优雅响应属性变化
mousePressEvent() 常用 鼠标按下处理 使用默认行为
mouseMoveEvent() 常用 鼠标拖动处理 使用默认行为
hoverEnterEvent() / hoverLeaveEvent() 常用 悬停高亮等 无 hover 反馈

9.4 boundingRect()shape()、实际绘制范围 三者区别

这是自定义里另一个高频混淆点。

名称 作用 精度 主要用途
boundingRect() 图元外接矩形 重绘优化、场景索引、更新区域
shape() 图元真实形状路径 命中测试、碰撞检测、选择区域
paint() 画出来的内容 实际显示结果 取决于你 最终视觉表现
9.4.1 例子

你画一个圆:

  • boundingRect() 可能返回包住圆的矩形
  • shape() 返回真正的圆形路径
  • paint() 里调用 drawEllipse()

如果你只写 boundingRect() 不写 shape()

那点击检测有时会显得"像矩形在响应",不够精确。

十、QPainterPath 的特殊作用

10.1 QPainterPath 在自定义图元里特别重要

虽然 QPainterPath 不是图元,但在自定义 QGraphicsItem 时非常有用。

用途 说明
作为实际绘制路径 painter->drawPath(path)
作为 shape() 返回值 精确命中检测
计算轮廓边界 path.boundingRect()
表示复杂曲线/箭头/贝塞尔图形 非规则图元的基础
10.1.1 示例代码

例如:

cpp 复制代码
class MyPathLikeItem : public QGraphicsItem
{
public:
    QRectF boundingRect() const override
    {
        return m_path.boundingRect().adjusted(-2, -2, 2, 2);
    }

    QPainterPath shape() const override
    {
        return m_path;
    }

    void paint(QPainter *painter,
               const QStyleOptionGraphicsItem *,
               QWidget *) override
    {
        painter->setPen(QPen(Qt::darkGreen, 2));
        painter->drawPath(m_path);
    }

private:
    QPainterPath m_path;
};

十一、常见图元类型与对应类

图元类型 对应类 直接父类 备注
单实线/虚实线/双实线 QGraphicsLineItem QGraphicsItem 线条样式通过 QPen 属性自定义;双实线需自定义(如多线或路径)。
矩形 QGraphicsRectItem QAbstractGraphicsShapeItem 支持描边和填充;QAbstractGraphicsShapeItem 提供笔刷支持。
椭圆 QGraphicsEllipseItem QAbstractGraphicsShapeItem 类似矩形,支持完整圆或椭圆。
多边形 QGraphicsPolygonItem QAbstractGraphicsShapeItem 支持任意多边形顶点定义。
自定义复杂线/形状 QGraphicsPathItem QAbstractGraphicsShapeItem 用于路径绘制,可扩展实现虚实线变体或双线。

总结与快速查询

最终决策流程图

  1. 我想画一个简单图形(线/矩形/路径)?

    • 是 → 直接用现成类(QGraphicsLineItem / QGraphicsRectItem / QGraphicsPathItem
    • 否 → 下一步
  2. 我只是想在现成图形上加点交互或数据?

    • 是 → 继承对应的现成类
    • 否 → 下一步
  3. 我的图形本质是"有 pen/brush 的形状"?

    • 是 → 继承 QAbstractGraphicsShapeItem
    • 否 → 下一步
  4. 我的图形完全自定义或很复杂?

    • 是 → 继承 QGraphicsItem
相关推荐
山居秋暝LS1 小时前
安装C++版opencv和opencv_contrib
开发语言·c++·opencv
U盘失踪了2 小时前
调用大模型API上下文关联
笔记
sakiko_2 小时前
UIKit学习笔记3-布局、滚动视图、隐藏或显示视图
前端·笔记·学习·objective-c·swift·uikit
谭欣辰2 小时前
LCS(最长公共子序列)详解
开发语言·c++·算法
koo3642 小时前
周报5.3
笔记
Cando学算法2 小时前
鸽笼原理(抽屉原理)
c++·算法·学习方法
郝学胜-神的一滴3 小时前
跨平台动态库与头文件:从原理到命名的深度解析
linux·c++·程序人生·unix·cmake
代码中介商3 小时前
C++ 仿函数(Functor)深度解析:从基础到应用
开发语言·c++
王老师青少年编程3 小时前
csp信奥赛C++高频考点专项训练之字符串 --【字符串基础】:[NOIP 2018 普及组] 标题统计
c++·字符串·csp·高频考点·信奥赛·专项训练·标题统计