经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 大数据/云/AI » 人工智能基础 » 查看文章
QT 自定义QGraphicsItem 缩放后旋转 图形出现漂移问题
来源:cnblogs  作者:永不停转  时间:2024/3/15 9:02:00  对本文有异议

实现自定义QGraphicsItem缩放和旋转时,遇到了这样一个问题:将item旋转一个角度,然后拖拽放大,再次进行旋转时图像会发生漂移。原本以为是放大后中心点位置没有改变,导致旋转时以原中心的旋转出现了偏移,但是重新设置旋转中心 setTransformOriginPoint(rect.center()); 并没有起作用,图像仍然出现漂移。通过查阅相关问题发现是item旋转后item坐标系保持不变、原点保持不变,item重绘时先恢复未应用transform的坐标系然后再应用transform,为保证item坐标系相对item没有变化,坐标系会按照新的旋转中心进行旋转,从而导致图形发生漂移。

帮助文件中提到:

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

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

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

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

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

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

部分实现代码:

  1. /**
  2. * 本示例采用的是给rect添加了调整控件,此代码是调整控件中的代码
  3. * from 为鼠标在scene上移动的起始位置
  4. * to 为鼠标移动的结束位置
  5. */
  6. void RectSelector::sizeAdjusterMove(const QPointF &from, const QPointF &to)
  7. {
  8. // 将坐标映射到item坐标系
  9. QPointF itemFrom = parentItem()->mapFromScene(from);
  10. QPointF itemTo = parentItem()->mapFromScene(to);
  11. QPointF moveOffset = itemTo - itemFrom;
  12. // 累计偏移量,图形缩放偏移小于1时不重绘
  13. sizeOffsetTotal += moveOffset;
  14. if(abs(sizeOffsetTotal.x()) < 1 && abs(sizeOffsetTotal.y()) < 1){
  15. return;
  16. }
  17. moveOffset = sizeOffsetTotal;
  18. AdjustPoint *point = (AdjustPoint *)sender();
  19. QRectF offset(0,0,0,0);
  20. // 判断是哪个控制点控制图形缩放,计算该控制点多图形的改变
  21. QPointF centerOffset(0,0);
  22. if (point->getId() == "topLeft") {
  23. offset.setTopLeft(moveOffset);
  24. centerOffset = moveOffset/2;
  25. } else if(point->getId() == "topMid"){
  26. offset.setTop(moveOffset.y());
  27. centerOffset.setY(moveOffset.y()/2);
  28. } else if(point->getId() == "topRight"){
  29. offset.setTopRight(moveOffset);
  30. centerOffset = moveOffset/2;
  31. } else if(point->getId() == "left"){
  32. offset.setLeft(moveOffset.x());
  33. centerOffset.setX(moveOffset.x()/2);
  34. } else if(point->getId() == "right"){
  35. offset.setRight(moveOffset.x());
  36. centerOffset.setX(moveOffset.x()/2);
  37. } else if(point->getId() == "bottomLeft"){
  38. offset.setBottomLeft(moveOffset);
  39. centerOffset = moveOffset/2;
  40. } else if(point->getId() == "bottomMid"){
  41. offset.setBottom(moveOffset.y());
  42. centerOffset.setY(moveOffset.y()/2);
  43. } else if(point->getId() == "bottomRight"){
  44. offset.setBottomRight(moveOffset);
  45. centerOffset = moveOffset/2;
  46. }
  47. // 更新选中框大小
  48. QRectF newRect = rect.adjusted(offset.left(), offset.top(), offset.right(), offset.bottom());
  49. if (newRect.width() <= 0 || newRect.height() <= 0){
  50. return;
  51. }
  52. refreshSelectRect(newRect);
  53. // 计算原点在scene上移动的距离
  54. QPointF src = parentItem()->mapToScene(0,0);
  55. QPointF dst = parentItem()->mapToScene(centerOffset);
  56. QPointF posOffset = dst - src;
  57. QPointF oldPos = parentItem()->pos();
  58. QPointF newPos = QPointF(oldPos.x() + posOffset.x(), oldPos.y() + posOffset.y());
  59. // 调整被控图形的pos坐标,可以保证在有旋转角度时图形位置不会跳动
  60. parentItem()->setPos(newPos);
  61. // 发出大小改变信号
  62. emit rectSizeChanged(offset);
  63. // 清空累计信息
  64. sizeOffsetTotal.setX(0);
  65. sizeOffsetTotal.setY(0);
  66. }
  67. void RectSelector::refreshSelectRect(const QRectF &newRect)
  68. {
  69. prepareGeometryChange();
  70. rect = newRect;
  71. update();
  72. // 重新定位调整点
  73. setSizeAdjusterPos(rect);
  74. setCornerAdjusterPos();
  75. setRotateAdjusterPos(rect);
  76. }
  77. /**
  78. * 角度旋转,from,to与sizeAdjusterMove相同
  79. */
  80. void RectSelector::rotateAdjusterMove(const QPointF &from, const QPointF &to)
  81. {
  82. // 找到原点
  83. QPointF origin = parentItem()->pos();
  84. emit rectRotateChanged(QLineF(origin, to).angleTo(QLineF(origin, from)));
  85. }

参考文章:
Qt中QTransform的translate和rotate实现过程
QGraphicsRectItem美观实现缩放,旋转,平移
QGraphicsItem鼠标拖动旋转(五)
QGraphicsItem旋转后,坐标变化机制解析
Qt:QGraphicsItem对象setPos(),setScale(),setRotation()操作后Item坐标和Scene坐标的变化

原文链接:https://www.cnblogs.com/ITnoteforlsy/p/18073700

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号