首页 > 其他分享 >QT 自定义QGraphicsItem 缩放后旋转 图形出现漂移问题

QT 自定义QGraphicsItem 缩放后旋转 图形出现漂移问题

时间:2024-03-14 20:34:10浏览次数:42  
标签:QT 自定义 缩放 moveOffset 旋转 offset QPointF QGraphicsItem

实现自定义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,自定义,缩放,moveOffset,旋转,offset,QPointF,QGraphicsItem
From: https://www.cnblogs.com/ITnoteforlsy/p/18073700

相关文章

  • 【SpringBoot】自定义工具类实现Excel数据新建表存入MySQL数据库
    ......
  • 有手就会Python自定义模块使用
    1.自定义模块自定义模块一般是在项目中根据自己的需求进行的封装项目中自定义了额一个模块,module.pyname="张三"age=23weight=160height=187deftest():print("测试的方法")defdemo():print("天使的眼泪")deffn():print("老鼠爱大米")2.......
  • Qt TCP (小型聊天窗口)
    实现的具体功能为:服务器端需要主动监听,可以主动断开连接,可以发送信息给客户端客户端需要输入主机,端口号,需要主动连接,可以主动断开连接,可以发送信息给服务器端服务器端和客户端都能看到聊天记录服务器端的搭建:创建一个TCP_Server项目1.首先在.Pro文件中添加:QT+=network......
  • Qt TcpSocket的掉线解决方法
    1、tcpip协议没什么好说的,号称保证传输准确可靠,使命必达。协议很大一坨,实际应用上用得上的很少。2、问题所在在使用tcp协议跟服务器连接后(分别测试过windows系统,ubuntu系统单片机带tcpip协议的通讯模块)都发现不能及时反馈或反馈网络已断;(在同一个局域网内和公共网络上......
  • Qt TCP通信客户端断开连接有哪些方法
    在QT中,可以使用以下方法来处理TCP通信客户端断开连接的情况:使用QAbstractSocket::disconnected信号:当客户端与服务器断开连接时,该信号会被触发。你可以连接这个信号到一个槽函数,在槽函数中处理断开连接的逻辑。QObject::connect(socket,&QTcpSocket::disconnected,this,&You......
  • PyQT可视化开发-保姆级教程
    背景因为在公司中负责小工具的开发(数据处理文件生成等),不可避免的会使用到Python的窗体界面,最初都是手敲出来的,费时费力,后来在网上百度了一下,发现一款比较好用的工具-QTdesigner,使用鼠标拖拽就可以完成窗体的设计,方便好用。话不多说。现在就体验一下这个工具的强大之处吧。1.......
  • sed 替换时自定义分隔符
    如果嫌转义字符转来转去麻烦,也可以自定义替换里的分隔符,而不是用默认的”\”。在替换命令s里自定义替换分隔符,这里用的@,也可以用|、!、^等特殊符号。前提是文本内不会出现该分隔符。注意,这里指定的分隔符不支持多个字符,比如@|作为一个分隔符是不支持的。将文件内的/替换为\cat......
  • CRM端自定义数据抽取增强为输入值抽取
    因某些原因,导致一些数据没有从ERP传到CRM。R3AR2中定义客户,物料,或者价格等的请求。然后修改R3AR4程序:REPORTzcrm_bdoc_batch_startMESSAGE-IDsmof.INCLUDEsmofdirekt.*---------------------------------------------------------------------*Tabledefinitions*......
  • MATLAB神经网络——如何自定义属于自己的训练流程
    网络上大部分matlab神经网络训练流程都应用matlab内置的相关训练函数进行训练,如何让matlab神经网络训练过程拥有像pytorch一样的训练过程呢?本文将通过一个案例介绍如何利用matlab自定义自己的训练流程,希望对你有所启迪,让我们开始吧!clear,clc加载并处理原始数据  我们使用......
  • Qt "QWidget: Must construct a Qapplication before a Qwidget"错误
    问题该错误信息表示在创建QWidget(窗口部件)之前必须先创建QApplication(应用程序)。这是因为QApplication在创建QWidget之前会进行一些初始化操作,确保正确运行应用程序。要解决这个错误,您需要在创建QWidget之前先创建QApplication。以下是处理该错误的常见方法:1.在......