Series Article Directory
Simple use of QGraphicsItem primitives (1)
QGraphicsItem primitive drag drawing (2)
QGraphicsItem primitive rotation scaling and custom primitives (3)
QGraphicsItem mouse drags the primitive to zoom and stretch (4)
foreword
Continuing from the previous article, this article mainly explains how to rotate by dragging primitives with the mouse
1. Effect demonstration
Second, the process analysis
1. Mouse hover
When I select an element, when the mouse hovers near the rotation circle of the element, the corresponding mouse ico style needs to be displayed, but the mouse hover event trigger is composed of the following parts:
1) Primitive settings receive mouse hover events
// Receive mouse hover events this->setAcceptHoverEvents(true);
2) The mouse hover entry and exit events of the primitive are determined by the boundingRect area, so it needs to be modified. We actually display the drawn m_rect, but only the area containing the rotating connection dotted line and the rotating circle, which is convenient for triggering the mouse hover event (there are Big guys with better solutions can leave a message in the comment area)
QRectF RectItem::boundingRect() const { // Because the mouse hover entry event of the primitive is triggered in this area, but when the mouse is selected, the rotation button extended by the dotted line cannot trigger the mouse hover entry event, so special processing is required. // However, the actual drawing display is still based on the size of the primitive. // Set the drawing boundary of the primitive to be dAdjust pixels away from the primitive return QRectF(m_rect.x(), m_rect.y() - (m_dLineLen + 2.0 * m_dCircleRadius), m_rect.width(), m_rect.height() + (m_dLineLen + 2.0 * m_dCircleRadius)). adjusted(-m_dAdjust, -m_dAdjust, m_dAdjust, m_dAdjust); }
3) Draw rectangular primitives and selected areas
void RectItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { // Redraw function, draw rectangle Q_UNUSED(widget); // Empty rectangles are not drawn if(m_rect.isEmpty()) { return; } // Set up brushes and brushes painter->setPen(QPen(Qt::black, 1)); painter->setBrush(Qt::green); // draw rectangle painter->drawRect(m_rect); // Draw a virtual box when selected if (option->state & QStyle::State_Selected) { // Get the primitive drawing area QRectF rect = getRect();// this->boundingRect(); // draw dashed box painter->setPen(QPen(option->palette.windowText(), 0, Qt::DashLine)); painter->setBrush(Qt::NoBrush); // Sets the spacing of the dashed box from the drawing area, since brushes have width const qreal pad = painter->pen().widthF() / 2 + m_dAdjust; painter->drawRect(rect.adjusted(-pad, -pad, pad, pad)); // Add a dotted line to connect a circle for rotation // draw lines painter->drawLine(rect.center().x(), rect.top() - m_dLineLen, rect.center().x(), rect.top()); // draw circle painter->drawEllipse(rect.center().x() - m_dCircleRadius, rect.top() - m_dLineLen - 2.0 * m_dCircleRadius, 2.0 * m_dCircleRadius, 2.0 * m_dCircleRadius); } }
2. Rotation processing
The code is as follows (example):
void RectItem::RotateRect(const QPointF &mousePos) { // Set the center point as the origin QPointF originPos = this->getRect().center(); // Extend two lines from the origin, the line connecting the point when the mouse is pressed and the point where the current mouse position is located QLineF p1 = QLineF(originPos, m_pressPos); QLineF p2 = QLineF(originPos, mousePos); // Rotation angle qreal dRotateAngle = p2.angleTo(p1); // Set the center of rotation this->setTransformOriginPoint(originPos); // Calculate the current rotation angle qreal dCurAngle = this->rotation() + dRotateAngle; while (dCurAngle > 360.0) { dCurAngle -= 360.0; } // Set the rotation angle this->setRotation(dCurAngle); // refresh display this->update(); }
3. After rotating the primitive, the mouse pulls up the floating icon
After the graphic element is rotated and then pulled up, when the mouse hovers into the pull-up area, the icon of the mouse will definitely change. I am lazy here and wrote a garbage algorithm. Friends who want to have a better experience can do it. Set the mouse icon as a picture, and achieve better effect by rotating the picture
void RectItem::RotateCursor(qreal dAngle, Qt::CursorShape eCursor) { // The actual displayed mouse icon Qt::CursorShape eRealCursor = eCursor; // The rotation angle is between [0, 360°), only the change between [0, 180) needs to be considered while (dAngle > 180.0) { dAngle -= 180.0; } // [0, 30°) do not need to change if((0.0 <= dAngle && dAngle < 30.0) || (150.0 <= dAngle && dAngle < 180.0)) { // 1. If it is [0,30) or [150,180), do not process eRealCursor = eCursor; } else if((30.0 <= dAngle && dAngle < 60.0)) { // 2. If it is [30,60), then [lower left and upper right] -> [left and right], [upper left and right] -> [up and down]... and so on if(eCursor == Qt::SizeBDiagCursor) { eRealCursor = Qt::SizeHorCursor; } else if(eCursor == Qt::SizeBDiagCursor) { eRealCursor = Qt::SizeHorCursor; } else if(eCursor == Qt::SizeVerCursor) { eRealCursor = Qt::SizeBDiagCursor; } else if(eCursor == Qt::SizeHorCursor) { eRealCursor = Qt::SizeFDiagCursor; } } else if((60.0 <= dAngle && dAngle < 120.0)) { // 3. If it is [60,120), then [lower left and upper right] -> [upper left and right lower], [left and right] -> [up and down]... and so on if(eCursor == Qt::SizeBDiagCursor) { eRealCursor = Qt::SizeFDiagCursor; } else if(eCursor == Qt::SizeFDiagCursor) { eRealCursor = Qt::SizeBDiagCursor; } else if(eCursor == Qt::SizeVerCursor) { eRealCursor = Qt::SizeHorCursor; } else if(eCursor == Qt::SizeHorCursor) { eRealCursor = Qt::SizeVerCursor; } } else if((120.0 <= dAngle && dAngle < 150.0)) { // 3. If it is [120,150), then [lower left and upper right] -> [up and down], [upper left and right lower] -> [left and right]... and so on if(eCursor == Qt::SizeBDiagCursor) { eRealCursor = Qt::SizeVerCursor; } else if(eCursor == Qt::SizeFDiagCursor) { eRealCursor = Qt::SizeHorCursor; } else if(eCursor == Qt::SizeVerCursor) { eRealCursor = Qt::SizeFDiagCursor; } else if(eCursor == Qt::SizeHorCursor) { eRealCursor = Qt::SizeBDiagCursor; } } this->setCursor(eRealCursor); }
4. Primitive displacement problem
For primitives with a rotation angle, after stretching, rotating the primitives again will cause displacement. For the specific principle, please refer to this article by another big guy: After QGraphicsItem is rotated, the coordinate change mechanism is analyzed , the writing is very detailed. If you want to see the phenomenon, just do not deal with it. We need to add the following code to deal with it after the mouse is pulled up.
void RectItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { // Prevent the right mouse button to pop up the menu, left click to move if (event->button() == Qt::LeftButton && // left button pressed m_eMouseHandle != Mouse_None) // Only handle drag and lift { // To solve a rectangle with a rotation angle, after stretching and rotating again, the rotation center should still be the previous coordinate, and manually setting it as the center will cause a drift problem auto rr = this->getRect(); auto angle = qDegreesToRadians(this->rotation()); auto p1 = rr.center(); auto origin = this->transformOriginPoint(); QPointF p2 = QPointF(0, 0); p2.setX(origin.x() + qCos(angle)*(p1.x() - origin.x()) - qSin(angle)*(p1.y() - origin.y())); p2.setY(origin.y() + qSin(angle)*(p1.x() - origin.x()) + qCos(angle)*(p1.y() - origin.y())); auto diff = p1 - p2; this->setRect(rr.adjusted(-diff.x(), -diff.y(), -diff.x(), -diff.y())); this->setTransformOriginPoint(this->getRect().center()); this->update(); // Restore selection after dragging this->setSelected(true); } return QGraphicsItem::mouseReleaseEvent(event); }
Summarize
I feel that there are many things to summarize. The next chapter will explain the coordinates of the primitives and the coordinates of the relative scene in detail!