QGraphicsItem 有3种方式进行变换:1. 最简单方便的是使用 setRotation() 、setScale();2. 使用 setTransform() 进行复杂变换;3. 还可以使用 setTransformations()进行多项组合变换及自定义变换。同时应用这三种方式将产生叠加效果,并以 QTransform 形式返回。由于QTransform 是以矩阵为基础进行运算,所以叠加时运算的顺序将影响最后的运算结果。 QGraphicsItem 叠加变换时按照如下顺序进行:首先,应用 setTransform() 设置的 transform;其次,叠加运算 setTransformations() 设置的 transformations(按照 list 中的顺序执行);然后,叠加运算 setRotation() 设置的 rotation;最后,叠加运算 setScale()设置的 scale。这些规则体现在 qgraphicsitem_p.h 中的 QGraphicsItemPrivate 结构体中,源码如下:

struct QGraphicsItemPrivate::TransformData
{
QTransform transform;// 对应setTransform()设置的值
qreal scale; // 对应setScale()设置的值
qreal rotation; // 对应setRotation()设置的值
qreal xOrigin;
qreal yOrigin;
QList<QGraphicsTransform *> graphicsTransforms; // 对应setTransformations()设置的设置的值
bool onlyTransform; TransformData() :
scale(1.0), rotation(0.0),
xOrigin(0.0), yOrigin(0.0),
onlyTransform(true)
{ } QTransform computedFullTransform(QTransform *postmultiplyTransform = nullptr) const
{
// 此处进行叠加运算
if (onlyTransform) {
if (!postmultiplyTransform || postmultiplyTransform->isIdentity())
return transform;
if (transform.isIdentity())
return *postmultiplyTransform;
return transform * *postmultiplyTransform;
} QTransform x(transform);
if (!graphicsTransforms.isEmpty()) {
QMatrix4x4 m;
for (int i = 0; i < graphicsTransforms.size(); ++i)
graphicsTransforms.at(i)->applyTo(&m);
x *= m.toTransform();
}
x.translate(xOrigin, yOrigin);
x.rotate(rotation);
x.scale(scale, scale);
x.translate(-xOrigin, -yOrigin);
if (postmultiplyTransform)
x *= *postmultiplyTransform;
return x;
}
};

从源码中可以看出,transform、scale、rotation、graphicsTransforms单独存储。通过setRotation()设置的值存储在rotation,通过setScale()设置的值存储在scale。而rotation()和scale()返回的值就是QGraphicsItemPrivate中的变量rotation和scale。从qgraphicsitem.cpp源码中可以看到rotation和scale的存储方法。

qreal QGraphicsItem::rotation() const
{
if (!d_ptr->transformData)
return 0;
return d_ptr->transformData->rotation;
} void QGraphicsItem::setRotation(qreal angle)
{
prepareGeometryChange();
qreal newRotation = angle; if (d_ptr->flags & ItemSendsGeometryChanges) {
// Notify the item that the rotation is changing.
const QVariant newRotationVariant(itemChange(ItemRotationChange, angle));
newRotation = newRotationVariant.toReal();
} if (!d_ptr->transformData)
d_ptr->transformData = new QGraphicsItemPrivate::TransformData; if (d_ptr->transformData->rotation == newRotation)
return; d_ptr->transformData->rotation = newRotation;
d_ptr->transformData->onlyTransform = false;
d_ptr->dirtySceneTransform = 1; // Send post-notification.
if (d_ptr->flags & ItemSendsGeometryChanges)
itemChange(ItemRotationHasChanged, newRotation); if (d_ptr->isObject)
emit static_cast<QGraphicsObject *>(this)->rotationChanged(); d_ptr->transformChanged();
}

如果需要获取当前图元旋转的角度,从 rotation() 方法中的得到的角度只是通过 setRotation() 方法设置的角度,该角度不包含 transform 和 graphicsTransforms 中隐含的角度。当然,如果图元只是进行了角度变换,可以从 sceneTransform() 方法获取 Transform,利用文章《Qt 从 QTransform 逆向解出 Translate/Scale/Rotate(平移/缩放/旋转)分析》介绍的方法可以逆向解析出图元旋转的角度。

如果需要获取图元每次变化的角度、缩放大小及平移信息,可以只使用 setTransformations() 方法来控制图元进行变化,因为 QGraphicsTransform 的子类可以完全由开发者自己控制,所执行的变换都可以很容易的计算出来。例如:QGraphicsTransform 的子类 QGraphicsRotation 提供了 angle() 和 setAngle(qreal) 方法;QGraphicsScale 提供了 setXScale(qreal) 、xScale() const 、setYScale(qreal) 、yScale() const 等方法。

控制图元变换时 setTransformations() 方法更灵活一些,特别是对图元进行变换动画时,编写 QGraphicsTransform 的子类,很容易控制动画。具体代码可以参考GitHub项目 Compelling Data Designer 中 BIDesigner/animation

/tranfromanimation 的实现方法。通过扩展 QGraphicsTransform 实现子类 GraphicsRotationZ 、 GraphicsRotationY 、 GraphicsRotationX 、 QGraphicsTranslation 实现了旋转、水平翻转、垂直翻转、按路径移动、缩放等动画效果。

从源码解析 QGraphicsItem 旋转、缩放、平移、transform等变换操作,利用QGraphicsTransform实现变形动画的更多相关文章

  1. Netty 源码解析(八): 回到 Channel 的 register 操作

    原创申明:本文由公众号[猿灯塔]原创,转载请说明出处标注 今天是猿灯塔“365篇原创计划”第八篇. 接下来的时间灯塔君持续更新Netty系列一共九篇 Netty 源码解析(一): 开始 Netty 源 ...

  2. Laravel源码解析--看看Lumen到底比Laravel轻在哪里

    在前面一篇<Laravel源码解析--Laravel生命周期详解>中我们利用xdebug详细了解了下Laravel一次请求中到底做了哪些处理.今天我们跟 Lumen 对比下,看看 Lume ...

  3. Netty 源码解析(三): Netty 的 Future 和 Promise

    今天是猿灯塔“365篇原创计划”第三篇. 接下来的时间灯塔君持续更新Netty系列一共九篇 Netty 源码解析(一): 开始 Netty 源码解析(二): Netty 的 Channel 当前:Ne ...

  4. Netty 源码解析(九): connect 过程和 bind 过程分析

    原创申明:本文由公众号[猿灯塔]原创,转载请说明出处标注 今天是猿灯塔“365篇原创计划”第九篇. 接下来的时间灯塔君持续更新Netty系列一共九篇 Netty 源码解析(一): 开始 Netty 源 ...

  5. Netty 源码解析(七): NioEventLoop 工作流程

    原创申明:本文由公众号[猿灯塔]原创,转载请说明出处标注 今天是猿灯塔“365篇原创计划”第七篇. 接下来的时间灯塔君持续更新Netty系列一共九篇 Netty 源码解析(一): 开始 Netty 源 ...

  6. Netty 源码解析(六): Channel 的 register 操作

    原创申明:本文由公众号[猿灯塔]原创,转载请说明出处标注 今天是猿灯塔“365篇原创计划”第六篇. 接下来的时间灯塔君持续更新Netty系列一共九篇   Netty 源码解析(一 ):开始 Netty ...

  7. Netty 源码解析(五): Netty 的线程池分析

    今天是猿灯塔“365篇原创计划”第五篇. 接下来的时间灯塔君持续更新Netty系列一共九篇 Netty 源码解析(一): 开始 Netty 源码解析(二): Netty 的 Channel Netty ...

  8. Netty 源码解析(四): Netty 的 ChannelPipeline

    今天是猿灯塔“365篇原创计划”第四篇. 接下来的时间灯塔君持续更新Netty系列一共九篇 Netty 源码解析(一): 开始 Netty 源码解析(二): Netty 的 Channel Netty ...

  9. Netty 源码解析(二):Netty 的 Channel

    本文首发于微信公众号[猿灯塔],转载引用请说明出处 接下来的时间灯塔君持续更新Netty系列一共九篇 Netty源码解析(一):开始 当前:Netty 源码解析(二): Netty 的 Channel ...

  10. Netty系列之源码解析(一)

    本文首发于微信公众号[猿灯塔],转载引用请说明出处 接下来的时间灯塔君持续更新Netty系列一共九篇 当前:Netty 源码解析(一)开始 Netty 源码解析(二): Netty 的 Channel ...

随机推荐

  1. Spring Security 基于JWT的单点登陆(SSO)开发及原理解析

    JDK1.8Spring boot 2.xSpring Security 5.x 单点登录(Single Sign On),简称为SSO,是目前比较流行的企业业务整合的解决方案之一. SSO的定义是在 ...

  2. Java后台管理框架的开源项目

    1.ThinkGem / JeeSite(开发人员/项目名称) JeeSite是您快速完成项目的最佳基础平台解决方案,JeeSite是您想学习Java平台的最佳学习案例,JeeSite还是接私活的最佳 ...

  3. 联想服务器安装Centos8.3

    准备 1.服务器型号:ThinkSystem SR158 2.安装系统:Centos8.3 3.刻镜像工具:rufus 启动盘制作 我这里选择的是rufus,没有用UltraISO,因为制作的镜像经常 ...

  4. Linux下mysql的二进制安装与部分练习

    鲸英训练营 0531作业 一.单选题(每题5分,总分25分) 1. 在MySQL中,一般使用( A )语句来指定一个已有数据库作为当前工作数据库. A.USED B.USE C.BEGIN D.GO ...

  5. 插入dp学习笔记

    定义 插入 \(\text{dp}\) 适用于计数.求最优解且具有选择.排列元素过程等题目. 插入 \(\text{dp}\) 大致分为两类: 乱搞型:状态定义天马行空,但始终围绕着将新元素插入到旧元 ...

  6. LangChain基础篇 (06)

    LangChain 核心模块 Agent(构建复杂应用的代理系统) ReAct: Reasoning + Acting ReAct Prompt 由 few-shot task-solving tra ...

  7. redis的连接池和管道

    NoSQL泛指非关系型的数据库   非关系型数据库和关系型数据库的差别: 性能NOSQL是基于键值对的,可以想象成表中的主键和值的对应关系,而且不需要经过SQL层的解析,所以性能非常高 可扩展性同样也 ...

  8. 理解 SystemVerilog 中的循环与并发线程

    1. 首先理解 scope 的概念 除了常见的module.interface.class.task以及function等等,另外,begin-end block 和 fork-join block ...

  9. C# 性能优化 --- Lazy<T> 用法学习

    参考原文:https://kb.cnblogs.com/page/99182/ 延迟实例化,对于需要创建大量对象,而又不需要立即使用的场景非常有用.一下实例说明了Lazy<T>的用法. u ...

  10. 常用的linux命令以及详解

    Linux系统中包含了大量的命令,这些命令是用户与系统交互的主要方式.以下是一些常用的Linux命令及其详细解释: 1. 文件和目录操作 ls:列出目录内容. ls:列出当前目录的文件和子目录. ls ...