Results 1 to 8 of 8

Thread: How to implement a shape resizer with QGraphicsPolygonItem?

  1. #1
    Join Date
    Apr 2020
    Posts
    8
    Thanks
    1
    Thanked 1 Time in 1 Post

    Default How to implement a shape resizer with QGraphicsPolygonItem?

    I have used this code to implement resizing in my QGraphicsRectItem and QGraphicsEllipseItem, since both Rect and Ellipse have implemented function SetRect(qreal x, qreal y, qreal width, qreal height) it was not a problem, but with QGraphicsPolygonItem the nearest function is setPolygon() but it only takes a QPolygonf& Polygon as parameter which doesn't work with my code of sizeGripItem

    mainwindow.cpp
    Qt Code:
    1. class RectResizer : public SizeGripItem::Resizer // resize class for rectagle
    2. {
    3. public:
    4. virtual void operator()(QGraphicsItem* item, const QRectF& rect)
    5. {
    6. QGraphicsRectItem* rectItem =
    7. dynamic_cast<QGraphicsRectItem*>(item);
    8.  
    9. if (rectItem)
    10. {
    11. rectItem->setRect(rect);
    12. }
    13. }
    14. };
    15.  
    16. class EllipseResizer : public SizeGripItem::Resizer //resize class for ellipse
    17. {
    18. public:
    19. virtual void operator()(QGraphicsItem* item, const QRectF& rect)
    20. {
    21. QGraphicsEllipseItem* ellipseItem =
    22. dynamic_cast<QGraphicsEllipseItem*>(item);
    23.  
    24. if (ellipseItem)
    25. {
    26. ellipseItem->setRect(rect);
    27. }
    28. }
    29. };
    30.  
    31. class PolygonResizer : public SizeGripItem::Resizer // resize class for polygon
    32. {
    33. public:
    34. virtual void operator()(QGraphicsItem* item, const QRectF& rect)
    35. {
    36. QGraphicsPolygonItem* polygonItem =
    37. dynamic_cast<QGraphicsPolygonItem*>(item);
    38.  
    39. if (polygonItem)
    40. {
    41. polygonItem->setPolygon(rect);
    42. } //^ doesn't work when i attempt to resize shape, it changes to rectangle
    43. }
    44. };
    To copy to clipboard, switch view to plain text mode 

    sizegripitem.cpp
    Qt Code:
    1. #include "sizegripitem.h"
    2. #include <QBrush>
    3.  
    4. SizeGripItem::HandleItem::HandleItem(int positionFlags, SizeGripItem* parent)
    5. : QGraphicsRectItem(-4, -4, 8, 8, parent),
    6. positionFlags_(positionFlags),
    7. parent_(parent)
    8. {
    9. setBrush(QBrush(Qt::lightGray));
    10. setFlag(ItemIsMovable);
    11. setFlag(ItemSendsGeometryChanges);
    12. }
    13.  
    14. int SizeGripItem::HandleItem::positionFlags() const
    15. {
    16. return positionFlags_;
    17. }
    18.  
    19. QVariant SizeGripItem::HandleItem::itemChange(GraphicsItemChange change,
    20. const QVariant &value)
    21. {
    22. QVariant retVal = value;
    23.  
    24. if (change == ItemPositionChange)
    25. {
    26. retVal = restrictPosition(value.toPointF());
    27. }
    28. else if (change == ItemPositionHasChanged)
    29. {
    30. QPointF pos = value.toPointF();
    31.  
    32. switch (positionFlags_)
    33. {
    34. case TopLeft:
    35. parent_->setTopLeft(pos);
    36. break;
    37. case Top:
    38. parent_->setTop(pos.y());
    39. break;
    40. case TopRight:
    41. parent_->setTopRight(pos);
    42. break;
    43. case Right:
    44. parent_->setRight(pos.x());
    45. break;
    46. case BottomRight:
    47. parent_->setBottomRight(pos);
    48. break;
    49. case Bottom:
    50. parent_->setBottom(pos.y());
    51. break;
    52. case BottomLeft:
    53. parent_->setBottomLeft(pos);
    54. break;
    55. case Left:
    56. parent_->setLeft(pos.x());
    57. break;
    58. }
    59. }
    60.  
    61. return retVal;
    62. }
    63.  
    64. QPointF SizeGripItem::HandleItem::restrictPosition(const QPointF& newPos)
    65. {
    66. QPointF retVal = pos();
    67.  
    68. if (positionFlags_ & Top || positionFlags_ & Bottom)
    69. retVal.setY(newPos.y());
    70.  
    71. if (positionFlags_ & Left || positionFlags_ & Right)
    72. retVal.setX(newPos.x());
    73.  
    74. if (positionFlags_ & Top && retVal.y() > parent_->rect_.bottom())
    75. retVal.setY(parent_->rect_.bottom());
    76. else if (positionFlags_ & Bottom && retVal.y() < parent_->rect_.top())
    77. retVal.setY(parent_->rect_.top());
    78.  
    79. if (positionFlags_ & Left && retVal.x() > parent_->rect_.right())
    80. retVal.setX(parent_->rect_.right());
    81. else if (positionFlags_ & Right && retVal.x() < parent_->rect_.left())
    82. retVal.setX(parent_->rect_.left());
    83.  
    84. return retVal;
    85. }
    86.  
    87. SizeGripItem::SizeGripItem(Resizer* resizer, QGraphicsItem* parent)
    88. : QGraphicsItem(parent),
    89. resizer_(resizer)
    90. {
    91. if (parentItem())
    92. rect_ = parentItem()->boundingRect();
    93.  
    94. handleItems_.append(new HandleItem(TopLeft, this));
    95. handleItems_.append(new HandleItem(Top, this));
    96. handleItems_.append(new HandleItem(TopRight, this));
    97. handleItems_.append(new HandleItem(Right, this));
    98. handleItems_.append(new HandleItem(BottomRight, this));
    99. handleItems_.append(new HandleItem(Bottom, this));
    100. handleItems_.append(new HandleItem(BottomLeft, this));
    101. handleItems_.append(new HandleItem(Left, this));
    102. updateHandleItemPositions();
    103. }
    104.  
    105. SizeGripItem::~SizeGripItem()
    106. {
    107. if (resizer_)
    108. delete resizer_;
    109. }
    110.  
    111. QRectF SizeGripItem::boundingRect() const
    112. {
    113. return rect_;
    114. }
    115.  
    116. void SizeGripItem::paint(QPainter* painter,
    117. const QStyleOptionGraphicsItem* option,
    118. QWidget* widget)
    119. {
    120. }
    121.  
    122. #define IMPL_SET_FN(TYPE, POS) \
    123. void SizeGripItem::set ## POS (TYPE v) \
    124. { \
    125. rect_.set ## POS (v); \
    126. doResize(); \
    127. }
    128.  
    129. IMPL_SET_FN(qreal, Top)
    130. IMPL_SET_FN(qreal, Right)
    131. IMPL_SET_FN(qreal, Bottom)
    132. IMPL_SET_FN(qreal, Left)
    133. IMPL_SET_FN(const QPointF&, TopLeft)
    134. IMPL_SET_FN(const QPointF&, TopRight)
    135. IMPL_SET_FN(const QPointF&, BottomRight)
    136. IMPL_SET_FN(const QPointF&, BottomLeft)
    137.  
    138. void SizeGripItem::doResize()
    139. {
    140. if (resizer_)
    141. {
    142. (*resizer_)(parentItem(), rect_);
    143. updateHandleItemPositions();
    144. }
    145. }
    146.  
    147. void SizeGripItem::updateHandleItemPositions()
    148. {
    149. foreach (HandleItem* item, handleItems_)
    150. {
    151. item->setFlag(ItemSendsGeometryChanges, false);
    152.  
    153. switch (item->positionFlags())
    154. {
    155. case TopLeft:
    156. item->setPos(rect_.topLeft());
    157. break;
    158. case Top:
    159. item->setPos(rect_.left() + rect_.width() / 2 - 1,
    160. rect_.top());
    161. break;
    162. case TopRight:
    163. item->setPos(rect_.topRight());
    164. break;
    165. case Right:
    166. item->setPos(rect_.right(),
    167. rect_.top() + rect_.height() / 2 - 1);
    168. break;
    169. case BottomRight:
    170. item->setPos(rect_.bottomRight());
    171. break;
    172. case Bottom:
    173. item->setPos(rect_.left() + rect_.width() / 2 - 1,
    174. rect_.bottom());
    175. break;
    176. case BottomLeft:
    177. item->setPos(rect_.bottomLeft());
    178. break;
    179. case Left:
    180. item->setPos(rect_.left(),
    181. rect_.top() + rect_.height() / 2 - 1);
    182. break;
    183. }
    184.  
    185. item->setFlag(ItemSendsGeometryChanges, true);
    186. }
    187. }
    To copy to clipboard, switch view to plain text mode 

    sizegripitem.h
    Qt Code:
    1. #ifndef SIZEGRIPITEM_H
    2. #define SIZEGRIPITEM_H
    3.  
    4.  
    5. #include <QGraphicsItem>
    6. #include <QGraphicsRectItem>
    7.  
    8. class SizeGripItem : public QGraphicsItem
    9. {
    10. private:
    11. enum
    12. {
    13. Top = 0x1,
    14. Bottom = 0x2,
    15. Left = 0x4,
    16. TopLeft = Top | Left,
    17. BottomLeft = Bottom | Left,
    18. Right = 0x8,
    19. TopRight = Top | Right,
    20. BottomRight = Bottom | Right
    21. };
    22.  
    23. class HandleItem : public QGraphicsRectItem
    24. {
    25. public:
    26. HandleItem(int positionFlags, SizeGripItem* parent);
    27. int positionFlags() const;
    28.  
    29. protected:
    30. virtual QVariant itemChange(GraphicsItemChange change,
    31. const QVariant &value);
    32.  
    33. private:
    34. QPointF restrictPosition(const QPointF& newPos);
    35.  
    36. int positionFlags_;
    37. SizeGripItem* parent_;
    38. };
    39.  
    40. public:
    41. class Resizer
    42. {
    43. public:
    44. virtual void operator()(QGraphicsItem* item,
    45. const QRectF& rect) = 0;
    46.  
    47. };
    48.  
    49. SizeGripItem(Resizer* resizer = 0, QGraphicsItem* parent = 0);
    50. virtual ~SizeGripItem();
    51. virtual QRectF boundingRect() const;
    52. virtual void paint(QPainter* painter,
    53. const QStyleOptionGraphicsItem* option,
    54. QWidget* widget = 0);
    55. void setTopLeft(const QPointF& pos);
    56. void setTop(qreal y);
    57. void setTopRight(const QPointF& pos);
    58. void setRight(qreal x);
    59. void setBottomRight(const QPointF& pos);
    60. void setBottom(qreal y);
    61. void setBottomLeft(const QPointF& pos);
    62. void setLeft(qreal x);
    63.  
    64. private:
    65. void doResize();
    66. void updateHandleItemPositions();
    67.  
    68. QList<HandleItem*> handleItems_;
    69. QRectF rect_;
    70. Resizer* resizer_;
    71. };
    72.  
    73. #endif // SIZEGRIPITEM_H
    To copy to clipboard, switch view to plain text mode 

    I can't really think of a way to resize a polygon since the library misses on a lot of functions Rect and Ellipse do have implemented. Thanks for any help or suggestions on how to proceed with resizing a polygon item

  2. #2
    Join Date
    Jan 2008
    Location
    Alameda, CA, USA
    Posts
    4,720
    Thanks
    259
    Thanked 760 Times in 750 Posts
    Qt products
    Qt5
    Platforms
    Windows Android

    Default Re: How to implement a shape resizer with QGraphicsPolygonItem?

    Your call to setPolygon() using a QRect doesn't work because there is an overload for the QPolygon constructor that creates a rectangle polygon from a QRect.

    For any QGraphicsItem, you can retrieve the boundingRect(). If you have the new rect, you can compute the scaling factor(s) from the sizes of the bounding rect and the new rect, and then use those to create a QTransform that will scale the polygon. You can then set the scaled polygon as the new one. Depending on where the origin of your polygon is, you may have to translate it to (0,0) before scaling it, otherwise it might result in a translation as well. See the section on "Related Non-Members" in QTransform.
    <=== The Great Pumpkin says ===>
    Please use CODE tags when posting source code so it is more readable. Click "Go Advanced" and then the "#" icon to insert the tags. Paste your code between them.

  3. #3
    Join Date
    Apr 2020
    Posts
    8
    Thanks
    1
    Thanked 1 Time in 1 Post

    Default Re: How to implement a shape resizer with QGraphicsPolygonItem?

    Hi,
    Yes that sounds like a solution that would solve my issue, is there any example available online that I could bounce from? I looked through QtTransform library but I can't see a way I could implement it with a mouse event when the user would try to drag for example a triangle shape like this:
    Qt Code:
    1. QPolygonF Triangle;
    2. Triangle.append(QPointF(-200.,0));
    3. Triangle.append(QPointF(0.,-200));
    4. Triangle.append(QPointF(200.,0));
    5.  
    6.  
    7. QModelIndex index = ui->listView->currentIndex(); //annotating the shape with a selected name from a text file
    8. QString itemText = index.data(Qt::DisplayRole).toString();
    9.  
    10. QGraphicsPolygonItem* triangleItem = new QGraphicsPolygonItem(Triangle);
    11. QColor brush_color(Qt::green); //fill color
    12. brush_color.setAlpha(50); // alpha index makes brush color more opaque
    13. QPen blackpen(Qt::black); //border color
    14. blackpen.setWidth(2); // border width
    15. triangleItem->setBrush(brush_color);
    16. triangleItem->setPen(blackpen);
    17. triangleItem->setFlag(QGraphicsItem::ItemIsMovable); // making the object draggable across graphics view
    18. scene->addItem(triangleItem);
    19.  
    20. QGraphicsTextItem *RectText = new QGraphicsTextItem(itemText, triangleItem);
    To copy to clipboard, switch view to plain text mode 

    All my polygon items are done in this way, just different shapes.
    Thanks for your help.

  4. #4
    Join Date
    Jan 2008
    Location
    Alameda, CA, USA
    Posts
    4,720
    Thanks
    259
    Thanked 760 Times in 750 Posts
    Qt products
    Qt5
    Platforms
    Windows Android

    Default Re: How to implement a shape resizer with QGraphicsPolygonItem?

    I don't see what this code has to do with your PolygonResizer code you posted earlier. As I said, in the operator() of that class, you have the new rect and you can retrieve the current boundingRect() of the polygon. The ratio of the two heights gives you the y scale, the ratio of the two widths gives the x scale. You retrieve the QPolygon from the QGraphicsPolygonItem, construct a QTransform from the two scales, and apply that to the polygon to get a new scaled polygon. Set that polygon on the QGraphicsPolygonItem instance.

    Wikipedia will tell you how to construct the transform matrix using homogeneous coordinates.

    The only trick is that the transform scales about the origin (0, 0) point. If this origin is not at the center of your polygon, scaling may also result in translation in one direction and/or the other. So to fix this, you translate to (0,0), scale, then translate back to the original point.
    <=== The Great Pumpkin says ===>
    Please use CODE tags when posting source code so it is more readable. Click "Go Advanced" and then the "#" icon to insert the tags. Paste your code between them.

  5. #5
    Join Date
    Apr 2020
    Posts
    8
    Thanks
    1
    Thanked 1 Time in 1 Post

    Default Re: How to implement a shape resizer with QGraphicsPolygonItem?

    Well my polygon resizer would work with this triangle I just posted, and similar shapes with 5 and more points. But I can't figure the transformation out from what you are saying, if you could maybe please post a example or code snippet that might help me get on the right track. I watched a few videos from the official Qt youtube channel for transformation and translation of graphics items but can't really see the correlation with my issue, besides using bounding rect.

  6. #6
    Join Date
    Jan 2008
    Location
    Alameda, CA, USA
    Posts
    4,720
    Thanks
    259
    Thanked 760 Times in 750 Posts
    Qt products
    Qt5
    Platforms
    Windows Android

    Default Re: How to implement a shape resizer with QGraphicsPolygonItem?

    Maybe I don't understand your issue. I thought you were trying to implement resizing of graphics items using resize handles on the bounding rectangles of the items.

    What you implemented in your initial post works fine for rectangles and ellipses because they are defined by their bounding rectangles. Polygons and other items are defined by an arbitrary closed set of points connected by lines, but they do have a bounding rectangle, the smallest rectangle that contains all of the points. QPolygon (on which QGraphicsPolygonItem is based) has a constructor that takes a QRect argument, but that creates a rectangle polygon. That is the problem you ran into when you tried to implement the same code for resizing polygons that you used for the rectangle and ellipse.

    The Wikipedia article I pointed you to shows how to construct a scale transformation using homogenous coordinates, which is what QTransform uses. Your resize code needs to look something like this (and of course you will add checks to avoid divide by zero):

    Qt Code:
    1. float sx = float( rect.width() ) / float( boundingRect.width() );
    2. float sy = float( rect.height() ) / float( boundingRect.height() );
    3.  
    4. QTransform transform;
    5. transform.scale( sx, sy );
    6.  
    7. QPolygonF oldPolygon = item->polygon();
    8. QPolygonF newPolygon = oldPolygon * transform;
    9.  
    10. item->setPolygon( newPolygon );
    To copy to clipboard, switch view to plain text mode 

    This code will change the aspect ratio. If you want to keep the same aspect ratio, then you will need to change one of the scale factors so that the new width and height preserve the same ratio as the old width and height.

    But like I said earlier, the scale transformation scales the object around its (0,0) point. If the polygon's origin (0,0) is not at the center of the polygon, then scaling will probably also move it. If that happens to you, you will have to scale in three steps: translate your polygon to (0,0), scale, translate back. But by default, QGraphicsItem coordinates have the origin at their center (except for QGraphicsTextItem, QGraphicsPixmapItem, and maybe a few others that have their origin at topLeft).
    <=== The Great Pumpkin says ===>
    Please use CODE tags when posting source code so it is more readable. Click "Go Advanced" and then the "#" icon to insert the tags. Paste your code between them.

  7. The following user says thank you to d_stranz for this useful post:

    xd123 (16th April 2020)

  8. #7
    Join Date
    Apr 2020
    Posts
    8
    Thanks
    1
    Thanked 1 Time in 1 Post

    Default Re: How to implement a shape resizer with QGraphicsPolygonItem?

    Thank you so much. I now completely see what you meant, I'm sorry for not understanding it the first time :D working with Qt for just about 4 months now. I implemented your idea and it works just as I wanted it to. Really appreciate your help and willingness to explain your solution.

    If anybody stumbles on this issue too here is the working polygon resizer class:
    Qt Code:
    1. class PolygonResizer : public SizeGripItem::Resizer // resize class for polygon
    2. {
    3. public:
    4. virtual void operator()(QGraphicsItem* item, const QRectF& rect)
    5. {
    6. QGraphicsPolygonItem* polygonItem =
    7. dynamic_cast<QGraphicsPolygonItem*>(item);
    8.  
    9. QRectF itemBoundingRect = item->boundingRect();
    10. float sx = float( rect.width() ) / float( itemBoundingRect.width() );
    11. float sy = float( rect.height() ) / float( itemBoundingRect.height() );
    12.  
    13. QTransform transform;
    14. transform.scale( sx, sy );
    15.  
    16. QPolygonF oldPolygon = polygonItem->polygon();
    17. QPolygonF newPolygon = oldPolygon * transform;
    18.  
    19. if (polygonItem)
    20. {
    21. polygonItem->setPolygon( newPolygon );
    22. }
    23. }
    24. };
    To copy to clipboard, switch view to plain text mode 

  9. The following user says thank you to xd123 for this useful post:

    d_stranz (17th April 2020)

  10. #8
    Join Date
    Jan 2008
    Location
    Alameda, CA, USA
    Posts
    4,720
    Thanks
    259
    Thanked 760 Times in 750 Posts
    Qt products
    Qt5
    Platforms
    Windows Android

    Default Re: How to implement a shape resizer with QGraphicsPolygonItem?

    Happy to help. A couple of suggestions:

    - Since itemBoundingRect and rect are already QRectF (which is floating point based), there is no need to cast width() and height() to float. They already are. I was assuming they were QRect, which is integer based and thus needed casting to avoid rounding errors on division. (if either height or width of the rect is less than the height or width of the bounding rect, integer division would always yield zero as the scale factor).

    - You should move the check on polygonItem validity to right after the dynamic_cast. If it is null, then retrieving the oldPolygon a few lines later will blow up.

    - You should also use qgraphicsitem_cast<> instead of dynamic_cast<> in all of your resizing methods.
    <=== The Great Pumpkin says ===>
    Please use CODE tags when posting source code so it is more readable. Click "Go Advanced" and then the "#" icon to insert the tags. Paste your code between them.

Similar Threads

  1. resizer view item does not work properly
    By paule22 in forum Qt Programming
    Replies: 0
    Last Post: 22nd July 2015, 19:44
  2. QGraphicsPolygonItem Points
    By dacrawler in forum Newbie
    Replies: 1
    Last Post: 23rd April 2012, 06:59
  3. QGraphicsPolygonItem with holes
    By natnan in forum Qt Programming
    Replies: 0
    Last Post: 2nd August 2010, 10:44
  4. I need help in QGraphicsPolygonItem
    By c_srikanth1984 in forum Qt Programming
    Replies: 0
    Last Post: 15th April 2009, 11:53
  5. QPaintEngine and QGraphicsPolygonItem
    By magland in forum Qt Programming
    Replies: 1
    Last Post: 19th August 2008, 17:03

Tags for this Thread

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  
Digia, Qt and their respective logos are trademarks of Digia Plc in Finland and/or other countries worldwide.