QGraphicsItem图元的简单使用(一)
QGraphicsItem图元拖动绘制(二)
QGraphicsItem图元旋转缩放和自定义图元(三)
QGraphicsItem鼠标拖动图元进行缩放拉伸(四)
接上篇,该篇主要讲解一下如何通过鼠标拖动图元进行旋转
当我选中图元时,鼠标悬浮进入到图元的-旋转圆附近时,需要显示对应的鼠标ico样式,但是鼠标的悬浮事件触发是由以下几个部分组成的:
1)图元设置接收鼠标悬浮事件
// 接收鼠标悬浮事件
this->setAcceptHoverEvents(true);
2)图元的鼠标悬浮进入和离开事件,是由boundingRect区域决定的,所以需要修改,我们实际显示绘制的还是m_rect,只是包含旋转的连接虚线和旋转圆的区域,方便触发鼠标悬浮事件(有更好方案的大佬可以评论区留言)
QRectF RectItem::boundingRect() const
{
// 因为图元的鼠标悬浮进入事件触发是在该区域内,但是鼠标选中时,通过虚线延长的旋转按钮是无法触发鼠标悬浮进入事件的,所以需要特殊处理
// 但是实际绘制显示,还是以图元大小为准
// 设置图元绘制边界距离图元dAdjust个像素
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)绘制矩形图元,以及选中区域
void RectItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
// 重绘函数,绘制矩形
Q_UNUSED(widget);
// 空矩形就不绘制
if(m_rect.isEmpty())
{
return;
}
// 设置画笔和画刷
painter->setPen(QPen(Qt::black, 1));
painter->setBrush(Qt::green);
// 绘制矩形
painter->drawRect(m_rect);
// 绘制选中时的虚框
if (option->state & QStyle::State_Selected)
{
// 获取图元绘制区域
QRectF rect = getRect();// this->boundingRect();
// 绘制虚线框
painter->setPen(QPen(option->palette.windowText(), 0, Qt::DashLine));
painter->setBrush(Qt::NoBrush);
// 设置虚线框距离绘图区域的间距,因为画笔有宽度
const qreal pad = painter->pen().widthF() / 2 + m_dAdjust;
painter->drawRect(rect.adjusted(-pad, -pad, pad, pad));
// 加一条虚线,连接一个圆,用来做旋转处理
// 绘制连线
painter->drawLine(rect.center().x(), rect.top() - m_dLineLen, rect.center().x(), rect.top());
// 绘制圆
painter->drawEllipse(rect.center().x() - m_dCircleRadius, rect.top() - m_dLineLen - 2.0 * m_dCircleRadius,
2.0 * m_dCircleRadius, 2.0 * m_dCircleRadius);
}
}
代码如下(示例):
void RectItem::RotateRect(const QPointF &mousePos)
{
// 设置中心点为原点
QPointF originPos = this->getRect().center();
// 从原点延伸出去两条线,鼠标按下时的点和当前鼠标位置所在点的连线
QLineF p1 = QLineF(originPos, m_pressPos);
QLineF p2 = QLineF(originPos, mousePos);
// 旋转角度
qreal dRotateAngle = p2.angleTo(p1);
// 设置旋转中心
this->setTransformOriginPoint(originPos);
// 计算当前旋转的角度
qreal dCurAngle = this->rotation() + dRotateAngle;
while (dCurAngle > 360.0) {
dCurAngle -= 360.0;
}
// 设置旋转角度
this->setRotation(dCurAngle);
// 刷新显示
this->update();
}
图元在旋转之后,再进行拉升,那么在鼠标悬浮进入拉升区域时,鼠标的图标肯定会有所变化,我这边偷懒写了一个垃圾算法,想做的体验更好的朋友,可以设置鼠标图标为图片,通过旋转图片来达到更好的效果
void RectItem::RotateCursor(qreal dAngle, Qt::CursorShape eCursor)
{
// 实际显示的鼠标图标
Qt::CursorShape eRealCursor = eCursor;
// 旋转角度是[0,360°)之间,只需要考虑[0,180)之间的变化就行
while (dAngle > 180.0) {
dAngle -= 180.0;
}
// 【0,30°)不需要变化
if((0.0 <= dAngle && dAngle < 30.0) || (150.0 <= dAngle && dAngle < 180.0))
{ // 1、如果是[0,30)或[150,180),则不做处理
eRealCursor = eCursor;
}
else if((30.0 <= dAngle && dAngle < 60.0))
{ // 2、如果是[30,60),则【左下右上】->【左右】,【左上右下】->【上下】...以此类推
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、如果是[60,120),则【左下右上】->【左上右下】,【左右】->【上下】...以此类推
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、如果是[120,150),则【左下右上】->【上下】,【左上右下】->【左右】...以此类推
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);
}
有旋转角度的图元,在拉伸之后,再次旋转图元,会发生位移,具体原理可以参考另外一个大佬的这篇文章:QGraphicsItem旋转后,坐标变化机制解析,写的很详细,想看现象的直接不做处理就行,我们需要在鼠标拉升图元后,添加如下代码处理
void RectItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
// 防止鼠标右键弹出菜单后,左击移动
if (event->button() == Qt::LeftButton && // 左键按下
m_eMouseHandle != Mouse_None) // 只处理拖动拉升
{
// 解决有旋转角度的矩形,拉伸之后,再次旋转,旋转中心该仍然为之前坐标,手动设置为中心,会产生漂移的问题
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();
// 拖动结束后恢复选中
this->setSelected(true);
}
return QGraphicsItem::mouseReleaseEvent(event);
}
感觉要总结的东西有很多,下章将详细讲解一下图元的坐标以及相对场景的坐标!