实现自定义QGraphicsItem缩放和旋转时,遇到了这样一个问题:将item旋转一个角度,然后拖拽放大,再次进行旋转时图像会发生漂移。原本以为是放大后中心点位置没有改变,导致旋转时以原中心的旋转出现了偏移,但是重新设置旋转中心 setTransformOriginPoint(rect.center()); 并没有起作用,图像仍然出现漂移。通过查阅相关问题发现是item旋转后item坐标系与scene坐标系不再重叠出现的坐标换算问题。

帮助文件中提到:

  1. 对QGraphicsItem进行变换不影响原点坐标 pos();
  2. 对QGraphicsItem进行变换不影响本地坐标系;
  3. 对QGraphicsItem进行变换顺序不同会导致最终结果不同;

可以看出QGraphicsItem旋转后,item的坐标系也跟着旋转。QGraphicsItem旋转后再缩放,坐标变换如下图所示:

当item放大后,pos() 在scene中的位置没有变,item的坐标系位置也没有变化。当再次旋转时,item进行重绘,重绘坐标原点仍然是原来的pos()坐标。重绘时先绘制图形,然后应用该图形的transform。而rect.center()的坐标已经不再是缩放时的center,所以会发生图形漂移。如下示意图,灰色方块为旋转前的图形,黑色方框为旋转后的图形,绿色方框为缩放后的图形,绿色方块为重绘时,放大后图形应绘制的位置,重绘后旋转时会以该方块的中心进行旋转,而旋转后与原来的位置出现偏差。

参考文章QGraphicsItem旋转后,坐标变化机制解析,计算在原坐标系中TransformOriginPoint的实际位置可以解决旋转漂移的问题。具体计算方法比较复杂,但是我感觉有更简单的处理方法可以解决这个问题。

解决思路:缩放后对pos()坐标进行更改,将新rect的中心点设置为新的pos(),这样当再次旋转触发重绘时,新rect的位置不会发生变化。此方法更简单,不用自己去计算新的旋转中心。

建议:自定义QGraphicsItem时一定要将pos()设置为rect的中心点,即rect 的中心坐标为(0,0), 左topleft坐标为(-width/2, -height/2)

部分实现代码:

/**
* 本示例采用的是给rect添加了调整控件,此代码是调整控件中的代码
* from 为鼠标在scene上移动的起始位置
* to 为鼠标移动的结束位置
*/
void RectSelector::sizeAdjusterMove(const QPointF &from, const QPointF &to)
{
// 将坐标映射到item坐标系
QPointF itemFrom = parentItem()->mapFromScene(from);
QPointF itemTo = parentItem()->mapFromScene(to);
QPointF moveOffset = itemTo - itemFrom;
// 累计偏移量,图形缩放偏移小于1时不重绘
sizeOffsetTotal += moveOffset;
if(abs(sizeOffsetTotal.x()) < 1 && abs(sizeOffsetTotal.y()) < 1){
return;
}
moveOffset = sizeOffsetTotal; AdjustPoint *point = (AdjustPoint *)sender();
QRectF offset(0,0,0,0);
// 判断是哪个控制点控制图形缩放,计算该控制点多图形的改变
QPointF centerOffset(0,0);
if (point->getId() == "topLeft") {
offset.setTopLeft(moveOffset);
centerOffset = moveOffset/2;
} else if(point->getId() == "topMid"){
offset.setTop(moveOffset.y());
centerOffset.setY(moveOffset.y()/2);
} else if(point->getId() == "topRight"){
offset.setTopRight(moveOffset);
centerOffset = moveOffset/2;
} else if(point->getId() == "left"){
offset.setLeft(moveOffset.x());
centerOffset.setX(moveOffset.x()/2);
} else if(point->getId() == "right"){
offset.setRight(moveOffset.x());
centerOffset.setX(moveOffset.x()/2);
} else if(point->getId() == "bottomLeft"){
offset.setBottomLeft(moveOffset);
centerOffset = moveOffset/2;
} else if(point->getId() == "bottomMid"){
offset.setBottom(moveOffset.y());
centerOffset.setY(moveOffset.y()/2);
} else if(point->getId() == "bottomRight"){
offset.setBottomRight(moveOffset);
centerOffset = moveOffset/2;
}
// 更新选中框大小
QRectF newRect = rect.adjusted(offset.left(), offset.top(), offset.right(), offset.bottom());
if (newRect.width() <= 0 || newRect.height() <= 0){
return;
}
refreshSelectRect(newRect);
// 计算原点在scene上移动的距离
QPointF src = parentItem()->mapToScene(0,0);
QPointF dst = parentItem()->mapToScene(centerOffset);
QPointF posOffset = dst - src;
QPointF oldPos = parentItem()->pos();
QPointF newPos = QPointF(oldPos.x() + posOffset.x(), oldPos.y() + posOffset.y());
// 调整被控图形的pos坐标,可以保证在有旋转角度时图形位置不会跳动
parentItem()->setPos(newPos);
// 发出大小改变信号
emit rectSizeChanged(offset);
// 清空累计信息
sizeOffsetTotal.setX(0);
sizeOffsetTotal.setY(0);
} void RectSelector::refreshSelectRect(const QRectF &newRect)
{
prepareGeometryChange();
rect = newRect;
update();
// 重新定位调整点
setSizeAdjusterPos(rect);
setCornerAdjusterPos();
setRotateAdjusterPos(rect);
} /**
* 角度旋转,from,to与sizeAdjusterMove相同
*/
void RectSelector::rotateAdjusterMove(const QPointF &from, const QPointF &to)
{
// 找到原点
QPointF origin = parentItem()->pos(); emit rectRotateChanged(QLineF(origin, to).angleTo(QLineF(origin, from)));
}

参考文章:

Qt中QTransform的translate和rotate实现过程

QGraphicsRectItem美观实现缩放,旋转,平移

QGraphicsItem鼠标拖动旋转(五)

QGraphicsItem旋转后,坐标变化机制解析

Qt:QGraphicsItem对象setPos(),setScale(),setRotation()操作后Item坐标和Scene坐标的变化

QT 自定义QGraphicsItem 缩放后旋转 图形出现漂移问题的更多相关文章

  1. iOS--------手势识别的详细使用:拖动、缩放、旋转、点击、手势依赖、自定义手势

    1.UIGestureRecognizer介绍 手势识别在iOS上非常重要,手势操作移动设备的重要特征,极大的增加了移动设备使用便捷性. iOS系统在3.2以后,为方便开发这使用一些常用的手势,提供了 ...

  2. Three.js 保存camera(视角)设置到数据库,包括场景的缩放、旋转、移动等

    最近在做的项目中遇到需要保存当前的3d管道视角设置的问题,用户希望在对3d场景内的管道进行了缩放.旋转.移动之后可以将场景当前的视角状态保存在数据库中,并在下次加载时读取. 经过不断的尝试和研究,在同 ...

  3. View的平移、缩放、旋转以及位置、坐标系

    原创 2015年05月12日 13:15:29 标签: Android / Scroll / Scale / Translation / Rotation 24733 Android开发中,经常会接触 ...

  4. OpenCV2:图像的几何变换,平移、镜像、缩放、旋转(2)

    在OpenCV2:图像的几何变换,平移.镜像.缩放.旋转(1)主要介绍了图像变换中的向前映射.向后映射.处理变换过程中浮点坐标像素值的插值算法,并且基于OpenCV2实现了两个简单的几何变换:平移和镜 ...

  5. OpenCV2:图像的几何变换,平移、镜像、缩放、旋转(1)

    图像的几何变换是在不改变图像内容的前提下对图像像素的进行空间几何变换,主要包括了图像的平移变换.镜像变换.缩放和旋转等.本文首先介绍了图像几何变换的一些基本概念,然后再OpenCV2下实现了图像的平移 ...

  6. Android动画及图片的缩放和旋转

    Android动画有2种,一种是Tween Animation,另一种是Frame Animation,先说说Tween动画吧. Tween动画是对视图对象中的内容进行一系列简单的转换,比如位置的移动 ...

  7. OpenGL立方体在世界坐标系中_缩放_旋转_平移_顶点片源着色器_光照作用_棋盘纹理贴图

    读取bmp等图片格式中的像素还有难度,就先用这个棋盘图象素来弄了 代码打错一个就一直First-chance exception ,貌似还有一个要用q或者Q才能成功退出,不知道缺少哪句,我用窗口红叉退 ...

  8. java处理图片--图片的缩放,旋转和马赛克化

    这是我自己结合网上的一些资料封装的java图片处理类,支持图片的缩放,旋转,马赛克化.(转载请注明出处:http://blog.csdn.net/u012116457) 不多说,上代码: packag ...

  9. 自定义QGraphicsItem

    简述: QGraphicsItem 是场景中 item 的基类.图形视图提供了一些典型形状的标准 item,例如:矩形 ( QGraphicsRectItem ).椭圆 ( QGraphicsElli ...

  10. 【python图像处理】图像的缩放、旋转与翻转

    [python图像处理]图像的缩放.旋转与翻转 图像的几何变换,如缩放.旋转和翻转等,在图像处理中扮演着重要的角色,python中的Image类分别提供了这些操作的接口函数,下面进行逐一介绍. 1.图 ...

随机推荐

  1. 7、后端学习规划:PHP学习 - 学习规划系列文章

    PHP在其阵营中被称为世界上最好的编程语言.笔者在学习了其一些框架后也有一定的经验,当时主要根据其框架进行的开发.对于PHP来说,在编程排行榜上也是有名的存在,现在还有一些网站应用也是用的PHP进行的 ...

  2. 为Win12做准备?微软Win11 23H2将集成AI助手:GPT4免费用

    微软日前确认今年4季度推出Win11 23H2,这是Win11第二个年度更新. Win11 23H2具体有哪些功能升级,现在还不好说,但它会集成微软的Copilot,它很容易让人想到多年前的" ...

  3. NC16610 [NOIP2009]Hankson的趣味题

    题目链接 题目 题目描述 Hanks博士是BT(Bio-Tech,生物技术)领域的知名专家,他的儿子名叫Hankson.现在,刚刚放学回家的Hankson正在思考一个有趣的问题. 今天在课堂上,老师讲 ...

  4. Python def() 后的-> 符号的作用

    python – 定义函数 def 后面的 ->,:表示的含义-> 常常出现在python函数定义的函数名后面,为函数添加元数据,描述函数返回的类型. : 表示参数的类型建议符示例: de ...

  5. 【OpenGL ES】立方体贴图(6张图)

    1 前言 ​ 本文通过一个立方体贴图的例子,讲解三维纹理贴图的应用,案例中使用 6 张不同的图片给立方体贴图,图片如下: ​ 本文涉及到的知识点主要包含:三维绘图.MVP 矩阵变换.纹理贴图,读者如果 ...

  6. springboot中前端ajax如何给controller提交数组参数?

    说明 我有个需求,前端批量添加一堆商品明细.也就是说会有一个商品ID,然后一堆商品明细,多行. 如此一来,针对后端接口肯定是要以数组或列表方式接收这个商品明细数组了. 前端代码 关键地方在于以form ...

  7. linux 快速安装LAMP教程

    最近学习linux,安装lamp遇到一些问题,记录下来分享一下: ------------------------------------------------------------------- ...

  8. 问题处理:java.sql.SQLException: Value '0000-00-00 00:00:00' can not be represented as java.sql.Timestamp

    问题说明 今天跑spring boot项目,在查看列表数据时后台抛异常了,一看是这玩意: 问题原因 "0000-00-00 00:00:00"在mysql中是作为一个特殊值存在的但 ...

  9. 【Android逆向】IDA动态调试JNI_OnLoad 和 .init_array

    由于 JNI_OnLoad 和 .init_array 执行时间很早,so一加载到内存中就执行了,所以动态调试步骤会稍微要麻烦一些 1. 进入手机, 执行./android_server (如果是64 ...

  10. OpenCV开发笔记(六十三):红胖子8分钟带你深入了解SIFT特征点(图文并茂+浅显易懂+程序源码)

    若该文为原创文章,未经允许不得转载原博主博客地址:https://blog.csdn.net/qq21497936原博主博客导航:https://blog.csdn.net/qq21497936/ar ...