Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. [Solved]Resizable & rotatable GraphicsItem
QtWS25 Last Chance

[Solved]Resizable & rotatable GraphicsItem

Scheduled Pinned Locked Moved Unsolved General and Desktop
6 Posts 3 Posters 4.6k Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • M Offline
    M Offline
    Mammamia
    wrote on 30 May 2016, 13:36 last edited by Mammamia 6 Jun 2016, 04:13
    #1

    Re: [SOLVED] Resizing a rotated QGraphicsRectItem

    I am trying to do an application in which I want to achieve the functionality of rotating and resizing the Graphics Item which is basically a Rectangle drawn using paint.

    I have used following code to achieve this functionality.
    This code is not working correctly when I rotate it and then resize it and then again try to rotate then the centre point will be jumped to another point.

    void CRoiUI::mouseMoveEvent(QGraphicsSceneMouseEvent * event)
    {
      if (m_IsResizing)
      {
        QPointF ptMouseMoveInItemsCoord = mapFromScene(event->scenePos());
        switch (m_ResizeCorner)
        {
        case TOP_LEFT:
          if (this->scene()->sceneRect().contains(event->scenePos()))
          {
            m_RoiBoundingRect.setTopLeft(ptMouseMoveInItemsCoord);
            m_RoiBoundingRect = m_RoiBoundingRect.normalized();
          }
          break;
        case TOP:
          if (this->scene()->sceneRect().contains(event->scenePos()))
          {
            m_RoiBoundingRect.setTop(ptMouseMoveInItemsCoord.y());
            m_RoiBoundingRect = m_RoiBoundingRect.normalized();
          }
          break;
        case TOP_RIGHT:
          if (this->scene()->sceneRect().contains(event->scenePos()))
          {
            m_RoiBoundingRect.setTopRight(ptMouseMoveInItemsCoord);
            m_RoiBoundingRect = m_RoiBoundingRect.normalized();
          }
          break;
        case RIGHT:
          if (this->scene()->sceneRect().contains(event->scenePos()))
          {
            m_RoiBoundingRect.setRight(ptMouseMoveInItemsCoord.x());
            m_RoiBoundingRect = m_RoiBoundingRect.normalized();
          }
          break;
        case BOTTOM_RIGHT:
          if (this->scene()->sceneRect().contains(event->scenePos()))
          {
            m_RoiBoundingRect.setBottomRight(ptMouseMoveInItemsCoord);
            m_RoiBoundingRect = m_RoiBoundingRect.normalized();
          }
          break;
        case BOTTOM:
          if (this->scene()->sceneRect().contains(event->scenePos()))
          {
            m_RoiBoundingRect.setBottom(ptMouseMoveInItemsCoord.y());
            m_RoiBoundingRect = m_RoiBoundingRect.normalized();
          }
          break;
        case BOTTOM_LEFT:
          if (this->scene()->sceneRect().contains(event->scenePos()))
          {
            m_RoiBoundingRect.setBottomLeft(ptMouseMoveInItemsCoord);
            m_RoiBoundingRect = m_RoiBoundingRect.normalized();
          }
          break;
        case LEFT:
          if (this->scene()->sceneRect().contains(event->scenePos()))
          {
            m_RoiBoundingRect.setLeft(ptMouseMoveInItemsCoord.x());
            m_RoiBoundingRect = m_RoiBoundingRect.normalized();
          }
          break;
        case ROTATE:
          if (this->scene()->sceneRect().contains(event->scenePos()))
          {
            QLineF line(m_RoiBoundingRect.center(), ptMouseMoveInItemsCoord);
            double rotations = line.angle(QLineF(0, 0, 1, 0));
            if (line.dy() <= 0)
            {
              rotations = 180.0 - rotations;
            }
            else
            {
              rotations = rotations - 180.0;
            }
            m_Angle = rotations;
            m_RoiBoundingRect = m_RoiBoundingRect.normalized();
            setTransformOriginPoint(m_RoiBoundingRect.center());
            setRotation(rotation() + m_Angle);
          }
          break;
        }
        prepareGeometryChange();
        update();
      }
      else
      {
        prepareGeometryChange();
        update();
        QGraphicsItem::mouseMoveEvent(event);
      }
    }
    
    //bounding rect
    QRectF CRoiUI::boundingRect() const
    {
      return m_RoiBoundingRect;
    }
    

    Please let me know how this rotation issue can be fixed.

    1 Reply Last reply
    0
    • A Offline
      A Offline
      Asperamanca
      wrote on 30 May 2016, 15:39 last edited by
      #2

      I can't quite put my finger on it, but changing the position of the boundingRect plus using rotation is bound to produce some interesting results. I would instead try to define a boundingRect which has the origin exactly where you want the item rotated, and move the item around using plain old "setPos".

      1 Reply Last reply
      0
      • M Offline
        M Offline
        Mammamia
        wrote on 31 May 2016, 06:33 last edited by
        #3

        Find my test code here.
        //rectangleitem.h

        #ifndef RECTANGLEITEM_H
        #define RECTANGLEITEM_H
        
        #include <QGraphicsItem>
        
        enum ResizeCorners
        {
          TOP_LEFT,
          TOP,
          TOP_RIGHT,
          RIGHT,
          BOTTOM_RIGHT,
          BOTTOM,
          BOTTOM_LEFT,
          LEFT,
          ROTATE
        };
        
        class RectangleItem : public QGraphicsItem
        {
          QRectF m_BoundingRect;
        
          QRectF m_ActualRect;
        
          //! Resizable handles around the shape
          QVector<QRectF> m_ResizeHandles;
        
          //! Arrow line used as rotation handle
          QLineF m_RotateLine;
        
          //! Arrow head
          QPolygonF m_AngleHandle;
        
          double m_Angle;
        
          bool m_IsResizing;
        
          bool m_MousePressed;
        
          ResizeCorners m_ResizeCorner;
        
        public:
          RectangleItem();
        
          QRectF boundingRect() const;
        
          void paint(QPainter * painter, const QStyleOptionGraphicsItem * option, QWidget * widget = 0);
        
          void mousePressEvent(QGraphicsSceneMouseEvent * event);
          void mouseMoveEvent(QGraphicsSceneMouseEvent * event);
          void mouseReleaseEvent(QGraphicsSceneMouseEvent * event);
          void hoverEnterEvent(QGraphicsSceneHoverEvent * event);
          void hoverLeaveEvent(QGraphicsSceneHoverEvent * event);
          void hoverMoveEvent(QGraphicsSceneHoverEvent * event);
          void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event);
          bool mousePosOnHandles(QPointF pos);
        };
        
        #endif // RECTANGLEITEM_H
        

        //rectangleitem.cpp

        #include "rectangleitem.h"
        #include <QPainter>
        #include <QGraphicsScene>
        #include <QGraphicsView>
        #include <QGraphicsSceneMouseEvent>
        #include <QDebug>
        
        #define PIE 3.1415926535897932384626433832795
        
        RectangleItem::RectangleItem()
        {
          m_BoundingRect = QRectF(0, 0, 200, 100);
          m_ResizeHandles.fill(QRect(0, 0, 0, 0), 8); //initially empty handles
          m_MousePressed = false;
          m_IsResizing = false;
          setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemSendsGeometryChanges);
          setPos(100, 150);
        }
        
        QRectF RectangleItem::boundingRect() const
        {
          return m_BoundingRect;
        }
        
        void RectangleItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
        {
          Q_UNUSED(option)
          Q_UNUSED(widget)
          painter->fillRect(boundingRect(), QBrush(QColor(128, 128, 255, 128)));
        
          //resize handles
          double scale = this->scene()->views().at(0)->transform().m11(); //get current sace factor
          float rectSize = 4 / scale; //this is to maintain same size for resize handle rects
          QRectF handles(0, 0, rectSize, rectSize);
          QRectF m_CornerRect = m_BoundingRect.adjusted(2, 2, -2, -2);
        
          handles.moveCenter(m_CornerRect.topLeft());  //TopLeft
          m_ResizeHandles.replace(0, handles);
        
          handles.moveCenter(m_CornerRect.topRight()); //TopRight
          m_ResizeHandles.replace(2, handles);
        
          handles.moveCenter(m_CornerRect.bottomRight());  //BottomRight
          m_ResizeHandles.replace(4, handles);
        
          handles.moveCenter(m_CornerRect.bottomLeft()); //BottomLeft
          m_ResizeHandles.replace(6, handles);
        
          QPointF center(m_CornerRect.center().x(), m_CornerRect.top()); //Top
          handles.moveCenter(center);
          m_ResizeHandles.replace(1, handles);
        
          center = QPointF(m_CornerRect.right(), m_CornerRect.center().y()); //Right
          handles.moveCenter(center);
          m_ResizeHandles.replace(3, handles);
        
          center = QPointF(m_CornerRect.center().x(), m_CornerRect.bottom());  //Bottom
          handles.moveCenter(center);
          m_ResizeHandles.replace(5, handles);
        
          center = QPointF(m_CornerRect.left(), m_CornerRect.center().y());  //Left
          handles.moveCenter(center);
          m_ResizeHandles.replace(7, handles);
        
          //arrow line
          float size = m_CornerRect.width();
          m_RotateLine.setP1(m_ResizeHandles.at(7).center());
          m_RotateLine.setP2(QPointF(m_ResizeHandles.at(7).center().x() + (size / 4), m_ResizeHandles.at(7).center().y()));
        
          //angle handle
          qreal arrowSize = 6.0 / scale;
          QPointF point = m_RotateLine.p2();
          double angle = ::acos(m_RotateLine.dx() / m_RotateLine.length());
          if (m_RotateLine.dy() >= 0)
            angle = (2.0 * PIE) - angle;
          QPointF destArrowP1 = point + QPointF(sin(angle - PIE / 3.0) * arrowSize, cos(angle - PIE / 3.0) * arrowSize);
          QPointF destArrowP2 = point + QPointF(sin(angle - PIE + PIE / 3.0) * arrowSize , cos(angle - PIE + PIE / 3.0) * arrowSize);
        
          float points[] = { point.x(), point.y(), destArrowP1.x(), destArrowP1.y(), destArrowP2.x(), destArrowP2.y() };
          m_AngleHandle = QPolygonF() << m_RotateLine.p2() << destArrowP1 << destArrowP2;
        
          QPen pens;
          pens.setCosmetic(true); //to maintain same width of pen across zoom levels
          //draw rect
          painter->setPen(pens);
          painter->drawRect(m_CornerRect);
        
          //draw arrow handle
          QPen arrowPen;
          arrowPen.setCosmetic(true);
          arrowPen.setColor(Qt::yellow);
          painter->setBrush(Qt::black);
          painter->setPen(arrowPen);
          painter->drawLine(m_RotateLine);
          painter->drawPolygon(m_AngleHandle);
        
          //draw resize handles
          pens.setColor(QColor(255, 255, 255));
          painter->setBrush(Qt::black);
          painter->setPen(pens);
          painter->drawRects(m_ResizeHandles);
        }
        
        void RectangleItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
        {
          m_MousePressed = true;
          m_IsResizing = mousePosOnHandles(event->scenePos()); //to check event on corners or not
          if (m_IsResizing)
          {
            m_ActualRect = m_BoundingRect;
          }
          QGraphicsItem::mousePressEvent(event);
        }
        
        void RectangleItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
        {
          if (m_IsResizing)
          {
            QPointF ptMouseMoveInItemsCoord = mapFromScene(event->scenePos());
            switch (m_ResizeCorner)
            {
            case TOP_LEFT:
              if (this->scene()->sceneRect().contains(event->scenePos()))
              {
                m_BoundingRect.setTopLeft(ptMouseMoveInItemsCoord);
                m_BoundingRect = m_BoundingRect.normalized();
                qDebug() << "TOP_LEFT::transformOriginPoint()" << m_BoundingRect.center();
              }
              break;
            case TOP:
              if (this->scene()->sceneRect().contains(event->scenePos()))
              {
                m_BoundingRect.setTop(ptMouseMoveInItemsCoord.y());
                m_BoundingRect = m_BoundingRect.normalized();
                qDebug() << "TOP::transformOriginPoint()" << m_BoundingRect.center();
              }
              break;
            case TOP_RIGHT:
              if (this->scene()->sceneRect().contains(event->scenePos()))
              {
                m_BoundingRect.setTopRight(ptMouseMoveInItemsCoord);
                m_BoundingRect = m_BoundingRect.normalized();
                qDebug() << "TOP_RIGHT::transformOriginPoint()" << m_BoundingRect.center();
              }
              break;
            case RIGHT:
              if (this->scene()->sceneRect().contains(event->scenePos()))
              {
                m_BoundingRect.setRight(ptMouseMoveInItemsCoord.x());
                m_BoundingRect = m_BoundingRect.normalized();
                qDebug() << "RIGHT::transformOriginPoint()" << m_BoundingRect.center();
              }
              break;
            case BOTTOM_RIGHT:
              if (this->scene()->sceneRect().contains(event->scenePos()))
              {
                m_BoundingRect.setBottomRight(ptMouseMoveInItemsCoord);
                m_BoundingRect = m_BoundingRect.normalized();
                qDebug() << "BOTTOM_RIGHT::transformOriginPoint()" << m_BoundingRect.center();
              }
              break;
            case BOTTOM:
              if (this->scene()->sceneRect().contains(event->scenePos()))
              {
                //m_BoundingRect.setBottom(event->pos().y());
                m_BoundingRect.setBottom(ptMouseMoveInItemsCoord.y());
                m_BoundingRect = m_BoundingRect.normalized();
                qDebug() << "BOTTOM::transformOriginPoint()" << m_BoundingRect.center();
              }
              break;
            case BOTTOM_LEFT:
              if (this->scene()->sceneRect().contains(event->scenePos()))
              {
                m_BoundingRect.setBottomLeft(ptMouseMoveInItemsCoord);
                m_BoundingRect = m_BoundingRect.normalized();
                qDebug() << "BOTTOM_LEFT::transformOriginPoint()" << m_BoundingRect.center();
              }
              break;
            case LEFT:
              if (this->scene()->sceneRect().contains(event->scenePos()))
              {
                m_BoundingRect.setLeft(ptMouseMoveInItemsCoord.x());
                m_BoundingRect = m_BoundingRect.normalized();
                qDebug() << "LEFT::transformOriginPoint()" << m_BoundingRect.center();
              }
              break;
            case ROTATE:
              if (this->scene()->sceneRect().contains(event->scenePos()))
              {
                QLineF line(m_BoundingRect.center(), ptMouseMoveInItemsCoord);
                double rotations = line.angle(QLineF(0, 0, 1, 0));
                if (line.dy() <= 0)
                {
                  rotations = 180.0 - rotations;
                }
                else
                {
                  rotations = rotations - 180.0;
                }
                m_Angle = rotations;
                m_BoundingRect = m_BoundingRect.normalized();
                setTransformOriginPoint(m_BoundingRect.center());
                setRotation(rotation() + m_Angle);
                qDebug() << "transformOriginPoint()" << transformOriginPoint();
                qDebug() << "sceneBoundingRect()" << sceneBoundingRect();
              }
              break;
            }
            qDebug() << "boundingRect()" << m_BoundingRect;
            prepareGeometryChange();
            update();
          }
          else
          {
            prepareGeometryChange();
            update();
            QGraphicsItem::mouseMoveEvent(event);
          }
        }
        
        void RectangleItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
        {
          m_MousePressed = false;
          m_IsResizing = false;
          prepareGeometryChange();
          update();
          QGraphicsItem::mouseReleaseEvent(event);
        }
        
        void RectangleItem::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
        {
        
        }
        
        void RectangleItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
        {
        
        }
        
        void RectangleItem::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
        {
        
        }
        
        void RectangleItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
        {
        
        }
        
        bool RectangleItem::mousePosOnHandles(QPointF pos)
        {
          bool resizable = false;
          int rem4Index = 8;// +(qRound(this->rotation()) / 45);
          if (mapToScene(m_ResizeHandles[(0 + rem4Index) % 8]).containsPoint(pos, Qt::WindingFill))
          {
            m_ResizeCorner = TOP_LEFT;
            resizable = true;
          }
          else if (mapToScene(m_ResizeHandles[(1 + rem4Index) % 8]).containsPoint(pos, Qt::WindingFill))
          {
            m_ResizeCorner = TOP;
            resizable = true;
          }
          else if (mapToScene(m_ResizeHandles[(2 + rem4Index) % 8]).containsPoint(pos, Qt::WindingFill))
          {
            m_ResizeCorner = TOP_RIGHT;
            resizable = true;
          }
          else if (mapToScene(m_ResizeHandles[(3 + rem4Index) % 8]).containsPoint(pos, Qt::WindingFill))
          {
            m_ResizeCorner = RIGHT;
            resizable = true;
          }
          else if (mapToScene(m_ResizeHandles[(4 + rem4Index) % 8]).containsPoint(pos, Qt::WindingFill))
          {
            m_ResizeCorner = BOTTOM_RIGHT;
            resizable = true;
          }
          else if (mapToScene(m_ResizeHandles[(5 + rem4Index) % 8]).containsPoint(pos, Qt::WindingFill))
          {
            m_ResizeCorner = BOTTOM;
            resizable = true;
          }
          else if (mapToScene(m_ResizeHandles[(6 + rem4Index) % 8]).containsPoint(pos, Qt::WindingFill))
          {
            m_ResizeCorner = BOTTOM_LEFT;
            resizable = true;
          }
          else if (mapToScene(m_ResizeHandles[(7 + rem4Index) % 8]).containsPoint(pos, Qt::WindingFill))
          {
            m_ResizeCorner = LEFT;
            resizable = true;
          }
          else if (mapToScene(m_AngleHandle).containsPoint(pos, Qt::WindingFill))
          {
            m_ResizeCorner = ROTATE;
            resizable = true;
          }
          return resizable;
        }
        

        //main.cpp

        #include "rectangleitem.h"
        #include <QApplication>
        #include <QGraphicsScene>
        #include <QGraphicsView>
        
        int main(int argc, char *argv[])
        {
          QApplication a(argc, argv);
        
          QGraphicsScene scene;
          scene.addItem(new RectangleItem());
          scene.setSceneRect(0, 0, 400, 400);
        
          QGraphicsView view(&scene);
          view.resize(420, 420);
          view.show();
        
          return a.exec();
        }
        
        1 Reply Last reply
        0
        • M Offline
          M Offline
          Mammamia
          wrote on 1 Jun 2016, 07:15 last edited by
          #4

          Can anyone tell me what is going wrong in my above code and what I should change to make my custom graphics item resizeable & rotatable.

          1 Reply Last reply
          0
          • M Offline
            M Offline
            Mammamia
            wrote on 6 Jun 2016, 04:11 last edited by
            #5

            Finally I got help from Qt support and solved this issue.

            We should not use setTransformOriginPoint() while performing the rotation instead we should calculate the new center point soon after we finished resizing and set it.

            In mouseReleaseEvent() method after line m_IsResizing = false;
            add these lines:

            m_IsResizing = false;
            if (m_ActualRect != m_BoundingRect) 
            { // Rotating won't trigger this, only resizing.
              auto oldScenePos = scenePos();
              setTransformOriginPoint(m_BoundingRect.center());
              auto newScenePos = scenePos();
              auto oldPos = pos();
              setPos(oldPos.x() + (oldScenePos.x() - newScenePos.x()), oldPos.y() + (oldScenePos.y() - newScenePos.y()));
            }
            

            I hope this will be helpful for someone who wants to implement a custom graphicsitem which can be rotatable as well as resizeable.

            1 Reply Last reply
            0
            • Z Offline
              Z Offline
              ZacDu
              wrote on 2 Jun 2024, 10:31 last edited by
              #6

              Thanks. Your post is very helpful to me.

              1 Reply Last reply
              0

              • Login

              • Login or register to search.
              • First post
                Last post
              0
              • Categories
              • Recent
              • Tags
              • Popular
              • Users
              • Groups
              • Search
              • Get Qt Extensions
              • Unsolved