#include "graphicsItem/graphicsItemGroup.h" #include #include #include #include #include GraphicsItemGroup::GraphicsItemGroup(QGraphicsItem *parent) : AbstractShapeType(parent) { m_type = T_group; m_boundingRect = QRectF(); m_lastBoudingRect = QGraphicsItemGroup::boundingRect(); //初始化缩放操作用的handle m_vecHanle.reserve(H_left); for(int i = H_leftTop; i <= H_left; i++) { ItemControlHandle* pHandle = new ItemControlHandle(this); pHandle->setType(T_resize); pHandle->setTag(i); m_vecHanle.push_back(pHandle); } for(int i = H_rotate_leftTop; i <= H_rotate_leftBottom; i++) { ItemControlHandle* pHandle = new ItemControlHandle(this); pHandle->setType(T_rotate); pHandle->setTag(i); m_vecHanle.push_back(pHandle); } setFlag(QGraphicsItem::ItemIsMovable, true); setFlag(QGraphicsItem::ItemIsSelectable, true); setFlag(QGraphicsItem::ItemSendsGeometryChanges, true); setAcceptHoverEvents(true); } GraphicsItemGroup::~GraphicsItemGroup() { } QPainterPath GraphicsItemGroup::shape() { QPainterPath path; path.addRect(m_boundingRect); return path; } void GraphicsItemGroup::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) { if (option->state & QStyle::State_Selected) //是选中状态,绘制选中框 { int nPenWidth = 1; painter->setPen(QPen(QColor(70,70,70), nPenWidth, Qt::DashLine)); //蓝色的外框 painter->setBrush(Qt::NoBrush); painter->drawRect(m_boundingRect_selected); //绘制变换原点 /*QPointF originPoint = transformOriginPoint(); //qDebug() << "originPoint:" << originPoint << " boundingRect:" << m_boundingRect; painter->setBrush(Qt::red); painter->drawEllipse(QPointF(0,0), 4, 4); painter->setBrush(Qt::blue); painter->drawEllipse(originPoint, 4, 4);*/ } } void GraphicsItemGroup::contextMenuEvent(QGraphicsSceneContextMenuEvent* event) { Q_UNUSED(event); } QVariant GraphicsItemGroup::itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant& value) { if (change == QGraphicsItem::ItemSelectedHasChanged) { QGraphicsItemGroup *group = dynamic_cast(parentItem()); if(!group) setHandleVisible(value.toBool()); else //在某一组群中,由组群展示是否选中,自身不做展示 { setSelected(false); return QVariant::fromValue(false); } } return QGraphicsItemGroup::itemChange(change, value); } void GraphicsItemGroup::syncRotationDataFromParent(const double& data) { /*m_dSyncRotationByParent += data; //让角度保持在正负180的区间,也就是上下两个半圈,这样易于象限判断 if (m_dSyncRotationByParent > 180) m_dSyncRotationByParent -= 360; if (m_dSyncRotationByParent < -180) m_dSyncRotationByParent += 360; //同步给子项 foreach (QGraphicsItem* item, childItems()) { if(qgraphicsitem_cast(item)) continue; AbstractShape* shape = qgraphicsitem_cast(item); if(shape && shape->getType()==T_group) { GraphicsItemGroup *group = qgraphicsitem_cast(item); if(group) group->syncRotationDataFromParent(data); } else { GraphicsBaseItem *baseItem = qgraphicsitem_cast(item); if (baseItem) baseItem->syncRotationDataFromParent(data); } }*/ } void GraphicsItemGroup::updateCoordinate() //当执行了resie和editShape函数后,boundingRect发生了变换,需要将item的原点(以中心点为原点)校准至boundingRect.center() { if (!parentItem()) { if (m_boundingRect.isNull()) { m_boundingRect = QGraphicsItemGroup::boundingRect(); m_dWidth = m_boundingRect.width(); m_dHeight = m_boundingRect.height(); } //因为group包含子item,boundingRect会根据子item自动计算生成,所以不再通过重设boundingRect(中心和原点重合)重合的方式,因为此种方式需要子item做各种同步计算 //最简单的方式是,每次boundingRect发生变化(缩放)后重新设置变换中心,但是会出现漂移(因为item只存储旋转角度数据,每帧都根据角度进行重绘),具体见开发学习笔记,消除这个漂移即可 prepareGeometryChange(); //具体计算 double dAngle = qDegreesToRadians(rotation()); QPointF centerPt = m_boundingRect.center(); QPointF originPt = transformOriginPoint(); QPointF targetPt = QPointF(0, 0); targetPt.setX(originPt.x() + qCos(dAngle)*(centerPt.x() - originPt.x()) - qSin(dAngle)*(centerPt.y() - originPt.y())); targetPt.setY(originPt.y() + qSin(dAngle)*(centerPt.x() - originPt.x()) + qCos(dAngle)*(centerPt.y() - originPt.y())); QPointF delta = targetPt - centerPt; //m_boundingRect.adjust(delta.x(), delta.y(), delta.x(), delta.y()); setTransformOriginPoint(m_boundingRect.center()); if(dAngle != 0) moveBy(delta.x(), delta.y()); //QTimer::singleShot(2000,[&](){moveBy(-delta.x(), -delta.y());}); updateHandles(); } //调用该组内所有item(过滤handle)相关函数 foreach (QGraphicsItem *item, childItems()) { GraphicsBaseItem *baseItem = qgraphicsitem_cast(item); if (baseItem && !qgraphicsitem_cast(baseItem)) { baseItem->updateCoordinate(); } } m_lastBoudingRect = m_boundingRect; } void GraphicsItemGroup::resize(int nHandle,double dSX, double dSY, const QPointF& basePoint) { switch (nHandle) { case H_left: case H_right: dSY = 1; //拖拽的是左点右点,为水平缩放,忽略垂直变化 break; case H_top: case H_bottom: dSX = 1; //拖拽的是顶点底点,为垂直缩放,忽略水平变化 break; default: break; } QTransform trans; //缩放是以图元变换原点为基准,所以每帧都先移动移动到想要的基准点,缩放之后再移回 trans.translate(basePoint.x(), basePoint.y()); trans.scale(dSX, dSY); trans.translate(-basePoint.x(), -basePoint.y()); prepareGeometryChange(); //如果直接将变换应用到item(setTransform(trans)),相当于把坐标进行拉伸,会有明显的拉伸感,所以应用到图形点上效果更好 //setTransform(trans); m_boundingRect = trans.mapRect(m_lastBoudingRect); m_dWidth = m_boundingRect.width(); m_dHeight = m_boundingRect.height(); updateHandles(); //调用该组内所有item(过滤handle)相关函数 foreach (QGraphicsItem *item, childItems()) { GraphicsBaseItem *baseItem = qgraphicsitem_cast(item); if (baseItem && !qgraphicsitem_cast(baseItem)) { //qDebug() << "groupBounding: " << m_boundingRect << " itemBounding:" << baseItem->boundingRect(); baseItem->resize(nHandle, dSX, dSY, baseItem->mapFromParent(basePoint)); } } } void GraphicsItemGroup::createOperationCopy() { m_pOperationCopy = new QGraphicsPathItem(this->shape()); m_pOperationCopy->setPen(Qt::DashLine); m_pOperationCopy->setPos(this->pos()); m_pOperationCopy->setTransformOriginPoint(this->transformOriginPoint()); m_pOperationCopy->setTransform(this->transform()); m_pOperationCopy->setRotation(this->rotation()); m_pOperationCopy->setScale(this->scale()); m_pOperationCopy->setZValue(this->zValue()); QGraphicsScene* scene = this->scene(); if(scene && m_pOperationCopy) { scene->addItem(m_pOperationCopy); m_movingIniPos = this->pos(); } } void GraphicsItemGroup::removeOperationCopy() { QGraphicsScene* scene = this->scene(); if(scene && m_pOperationCopy) { if(this->pos() != m_pOperationCopy->pos()) this->setPos(m_pOperationCopy->pos()); //本体移动到副本的位置 if(this->rotation() != m_pOperationCopy->rotation()) { double dAngle = m_pOperationCopy->rotation(); this->setRotation(dAngle); //本体旋转至副本的角度 //子item的旋转数据并不会和所在组同步,需要手动同步 foreach (QGraphicsItem* item, childItems()) { if(qgraphicsitem_cast(item)) continue; // AbstractShape* shape = qgraphicsitem_cast(item); // if(shape && shape->getType()==T_group) // { // GraphicsItemGroup *group = qgraphicsitem_cast(item); // if(group) // group->syncRotationDataFromParent(dAngle); // } // else { GraphicsBaseItem *baseItem = qgraphicsitem_cast(item); if (baseItem) baseItem->syncRotationDataFromParent(dAngle); } } } scene->removeItem(m_pOperationCopy); delete m_pOperationCopy; m_pOperationCopy = nullptr; } } void GraphicsItemGroup::moveOperationCopy(const QPointF& distance) { if(m_pOperationCopy) m_pOperationCopy->setPos(m_movingIniPos + distance); } void GraphicsItemGroup::rotateOperationCopy(const double& dAngle) { if(m_pOperationCopy) { m_pOperationCopy->setRotation(dAngle); } } void GraphicsItemGroup::addItems(const QList& items) { foreach (QGraphicsItem *item, items) { item->setSelected(false); addToGroup(item); //m_listItem.push_back(item); } updateCoordinate(); } #include "util/serializationUtils.h" #include "graphicsItem/graphicsBaseItem.h" #include "graphicsItem/itemControlHandle.h" QJsonObject GraphicsItemGroup::serialize() const { QJsonObject obj = serializeCommon(this, "group"); obj["pen"] = serializePen(m_pen); obj["brush"] = serializeBrush(m_brush); QJsonArray children; const auto kids = childItems(); for (QGraphicsItem *child : kids) { if (qgraphicsitem_cast(child)) continue; QJsonObject childObj; if (auto *group = dynamic_cast(child)) childObj = group->serialize(); else if (auto *shape = qgraphicsitem_cast(child)) childObj = shape->serialize(); if (!childObj.isEmpty()) children.append(childObj); } obj["children"] = children; return obj; } #include "util/serializationRegistry.h" QGraphicsItem *GraphicsItemGroup::createFromJson(const QJsonObject &obj) { GraphicsItemGroup *group = new GraphicsItemGroup(); applyCommon(group, obj); QJsonArray children = obj["children"].toArray(); QList childItems; for (const QJsonValue &val : children) { QGraphicsItem *child = createItemFromJson(val.toObject()); if (child) childItems.append(child); } // Child positions in JSON are group-relative, but addToGroup() // treats child pos() as scene-absolute. Convert before grouping. for (QGraphicsItem *child : childItems) child->setPos(group->mapToScene(child->pos())); group->addItems(childItems); group->setPen(deserializePen(obj["pen"].toObject())); group->setBrushColor(QColor(obj["brush"].toObject()["color"].toString())); group->setHandleVisible(false); return group; } static struct GroupItemRegistrar { GroupItemRegistrar() { registerItemFactory("group", &GraphicsItemGroup::createFromJson); } } _groupItemRegistrar;