diff --git a/CMakeLists.txt b/CMakeLists.txt index ee73233..06cd261 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -53,6 +53,7 @@ set(H_HEADER_FILES include/graphicsItem/graphicsBaseItem.h include/graphicsItem/graphicsRectItem.h include/graphicsItem/graphicsPolygonItem.h + include/graphicsItem/graphicsItemGroup.h ) set(CPP_SOURCE_FILES source/main.cpp @@ -75,6 +76,7 @@ set(CPP_SOURCE_FILES source/graphicsItem/graphicsBaseItem.cpp source/graphicsItem/graphicsRectItem.cpp source/graphicsItem/graphicsPolygonItem.cpp + source/graphicsItem/graphicsItemGroup.cpp ) set(UI_FILES ui/mainwindow.ui diff --git a/include/designerScene.h b/include/designerScene.h index 7cef347..5aad91b 100644 --- a/include/designerScene.h +++ b/include/designerScene.h @@ -3,6 +3,8 @@ #include +class GraphicsItemGroup; + class DesignerScene : public QGraphicsScene { Q_OBJECT @@ -13,9 +15,12 @@ public: void setGridVisible(bool); void setView(QGraphicsView* view) { m_pView = view; } - QGraphicsView *getView() { return m_pView; } + QGraphicsView* getView() { return m_pView; } void callParentEvent(QGraphicsSceneMouseEvent*); + GraphicsItemGroup* createGroup(); + void destroyGroup(); + signals: void signalAddItem(QGraphicsItem*); diff --git a/include/graphicsItem/graphicsBaseItem.h b/include/graphicsItem/graphicsBaseItem.h index 9604b73..598ab2f 100644 --- a/include/graphicsItem/graphicsBaseItem.h +++ b/include/graphicsItem/graphicsBaseItem.h @@ -260,6 +260,8 @@ protected: QVector m_vecHanle; }; +typedef AbstractShapeType AbstractShape; + class GraphicsBaseItem : public QObject, public AbstractShapeType { Q_OBJECT diff --git a/include/graphicsItem/graphicsItemGroup.h b/include/graphicsItem/graphicsItemGroup.h new file mode 100644 index 0000000..f29a12b --- /dev/null +++ b/include/graphicsItem/graphicsItemGroup.h @@ -0,0 +1,36 @@ +#ifndef GRAPHICSITEMGROUP_H +#define GRAPHICSITEMGROUP_H + +#include "graphicsBaseItem.h" + +class GraphicsItemGroup : public QObject, public AbstractShapeType +{ + Q_OBJECT + +public: + GraphicsItemGroup(QGraphicsItem *parent = 0); + virtual ~GraphicsItemGroup(); + + void resize(int,double, double, const QPointF&); + void updateCoordinate(); + + void createOperationCopy(); + void removeOperationCopy(); + void moveOperationCopy(const QPointF&); + void rotateOperationCopy(const double&); + + void addItems(const QList&); + QList getItems() {return m_listItem;} + +protected: + virtual QPainterPath shape(); + virtual void paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget*); + virtual void contextMenuEvent(QGraphicsSceneContextMenuEvent*); + virtual QVariant itemChange(QGraphicsItem::GraphicsItemChange, const QVariant&); + +private: + QRectF m_lastBoudingRect; //记录上一时刻的boundingRect + QList m_listItem; +}; + +#endif diff --git a/include/mainwindow.h b/include/mainwindow.h index 48475fc..c4637a3 100644 --- a/include/mainwindow.h +++ b/include/mainwindow.h @@ -39,6 +39,8 @@ private slots: void onAction_zoomIn(); void onAction_zoomOut(); void onAction_zoomFit(); + void onAction_createGroup(); + void onAction_destroyGroup(); void onSignal_addItem(QGraphicsItem*); void onSignal_deleteItem(); diff --git a/include/operationCommand.h b/include/operationCommand.h index 627be23..1c87131 100644 --- a/include/operationCommand.h +++ b/include/operationCommand.h @@ -12,6 +12,8 @@ #include #include +class GraphicsItemGroup; + class AddItemCommand : public QUndoCommand { public: @@ -43,4 +45,36 @@ private: QGraphicsScene* m_pGraphicsScene; }; +class CreateItemGoupCommand : public QUndoCommand +{ +public: + explicit CreateItemGoupCommand(GraphicsItemGroup* group, QGraphicsScene* graphicsScene, QUndoCommand* parent = 0); + ~CreateItemGoupCommand(); + +public: + void undo() override; + void redo() override; + +private: + QGraphicsItemGroup* m_pGroup; + QGraphicsScene* m_pGraphicsScene; + QList m_listItem; +}; + +class DestroyItemGoupCommand : public QUndoCommand +{ +public: + explicit DestroyItemGoupCommand(GraphicsItemGroup* group, QGraphicsScene* graphicsScene, QUndoCommand* parent = 0); + ~DestroyItemGoupCommand(); + +public: + void undo() override; + void redo() override; + +private: + QGraphicsItemGroup* m_pGroup; + QGraphicsScene* m_pGraphicsScene; + QList m_listItem; +}; + #endif diff --git a/source/designerScene.cpp b/source/designerScene.cpp index c2873c8..6bd12c7 100644 --- a/source/designerScene.cpp +++ b/source/designerScene.cpp @@ -1,5 +1,6 @@ #include "designerScene.h" #include "util/selectorManager.h" +#include "graphicsItem/graphicsItemGroup.h" #include #include @@ -125,3 +126,20 @@ void DesignerScene::callParentEvent(QGraphicsSceneMouseEvent* mouseEvent) break; } } + +GraphicsItemGroup* DesignerScene::createGroup() +{ + QList listItem = selectedItems(); + if(listItem.isEmpty()) + return nullptr; + + GraphicsItemGroup* group = new GraphicsItemGroup(); + group->addItems(listItem); + addItem(group); + return group; +} + +void DesignerScene::destroyGroup() +{ + +} diff --git a/source/drawingPanel.cpp b/source/drawingPanel.cpp index 02c20da..e51e3d8 100644 --- a/source/drawingPanel.cpp +++ b/source/drawingPanel.cpp @@ -51,6 +51,16 @@ void DrawingPanel::grahpicsViewZoomFit() m_pGraphicsView->zoomFit(); } +GraphicsItemGroup* DrawingPanel::createItemGroup() +{ + return m_pGraphicsScene->createGroup(); +} + +void DrawingPanel::destroyItemGroup() +{ + m_pGraphicsScene->destroyGroup(); +} + void DrawingPanel::onSignal_addGraphicsItem(GraphicsItemType& itemType) { if(SelectorManager::getInstance()) diff --git a/source/graphicsItem/graphicsItemGroup.cpp b/source/graphicsItem/graphicsItemGroup.cpp new file mode 100644 index 0000000..92985a9 --- /dev/null +++ b/source/graphicsItem/graphicsItemGroup.cpp @@ -0,0 +1,217 @@ +#include "graphicsItem/graphicsItemGroup.h" +#include +#include +#include + +GraphicsItemGroup::GraphicsItemGroup(QGraphicsItem *parent) + : AbstractShapeType(parent) +{ + m_boundingRect = QRectF(); + + //初始化缩放操作用的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(); + // painter->setBrush(Qt::red); + // 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::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(); + } + + QPointF pt1, pt2, delta; + pt1 = mapToScene(transformOriginPoint()); + pt2 = mapToScene(m_boundingRect.center()); + delta = pt1 - pt2; + + prepareGeometryChange(); + //boundingRect发生变化后原点偏离中心,将原点设为中心点,因为group内含有其它item,所以采用transform变换的方式 + //m_boundingRect = QRectF(-m_dWidth / 2, -m_dHeight / 2, m_dWidth, m_dHeight); + setTransform(transform().translate(delta.x(), delta.y())); + setTransformOriginPoint(m_boundingRect.center()); + //更改原点后自身绘制会在新坐标系下进行,会有跳转,所以需要将item整体做对应的移动 + moveBy(-delta.x(), -delta.y()); + updateHandles(); + } + + m_lastBoudingRect = m_boundingRect; + + //调用该组内所有item(过滤handle)相关函数 + foreach (QGraphicsItem *item, childItems()) + { + GraphicsBaseItem *baseItem = qgraphicsitem_cast(item); + if (baseItem && !qgraphicsitem_cast(baseItem)) + { + baseItem->updateCoordinate(); + } + } +} + +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(); + 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)) + { + 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()) + this->setRotation(m_pOperationCopy->rotation()); //本体旋转至副本的角度 + + 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(); +} diff --git a/source/graphicsItem/graphicsPolygonItem.cpp b/source/graphicsItem/graphicsPolygonItem.cpp index b5f7409..6d79f40 100644 --- a/source/graphicsItem/graphicsPolygonItem.cpp +++ b/source/graphicsItem/graphicsPolygonItem.cpp @@ -41,13 +41,13 @@ void GraphicPolygonItem::updateHandles() void GraphicPolygonItem::updateCoordinate() //当执行了resie和editShape函数后,boundingRect发生了变换,需要将item的原点(以中心点为原点)校准至boundingRect.center() { - QPointF pt1, pt2, delta; - pt1 = mapToScene(QPointF(0, 0)); - pt2 = mapToScene(m_boundingRect.center()); - delta = pt1 - pt2; - if (!parentItem()) { + QPointF pt1, pt2, delta; + pt1 = mapToScene(QPointF(0, 0)); + pt2 = mapToScene(m_boundingRect.center()); + delta = pt1 - pt2; + prepareGeometryChange(); //更改图形绘制节点坐标,让图形中心和原点对齐 QPolygonF ptsOnScene = mapToScene(m_points); //所有操作都在scene坐标系下进行,然后在还原至item坐标系,否则会有跳转 diff --git a/source/graphicsItem/graphicsRectItem.cpp b/source/graphicsItem/graphicsRectItem.cpp index 569a1cc..7636580 100644 --- a/source/graphicsItem/graphicsRectItem.cpp +++ b/source/graphicsItem/graphicsRectItem.cpp @@ -66,13 +66,13 @@ void GraphicsRectItem::updateHandles() void GraphicsRectItem::updateCoordinate() //当执行了resie和editShape函数后,boundingRect发生了变换,需要将item的原点(以中心点为原点)校准至boundingRect.center() { - QPointF pt1, pt2, delta; - pt1 = mapToScene(QPointF(0, 0)); - pt2 = mapToScene(m_boundingRect.center()); - delta = pt1 - pt2; - if (!parentItem()) { + QPointF pt1, pt2, delta; + pt1 = mapToScene(QPointF(0, 0)); + pt2 = mapToScene(m_boundingRect.center()); + delta = pt1 - pt2; + prepareGeometryChange(); //boundingRect发生变化后原点偏离中心,将原点设为中心点 m_boundingRect = QRectF(-m_dWidth / 2, -m_dHeight / 2, m_dWidth, m_dHeight); diff --git a/source/mainwindow.cpp b/source/mainwindow.cpp index 1c3491a..0bb3fbc 100644 --- a/source/mainwindow.cpp +++ b/source/mainwindow.cpp @@ -1,5 +1,6 @@ #include "mainwindow.h" +#include "graphicsItem/graphicsItemGroup.h" #include "ui_mainwindow.h" #include @@ -115,6 +116,8 @@ void CMainWindow::initializeAction() connect(ui->actionZoomIn, SIGNAL(triggered()), this, SLOT(onAction_zoomIn())); connect(ui->actionZoomOut, SIGNAL(triggered()), this, SLOT(onAction_zoomOut())); connect(ui->actionZoomFit, SIGNAL(triggered()), this, SLOT(onAction_zoomFit())); + connect(ui->actionGroup, SIGNAL(triggered()), this, SLOT(onAction_createGroup())); + connect(ui->actionUngroup, SIGNAL(triggered()), this, SLOT(onAction_destroyGroup())); } void CMainWindow::onAction_zoomIn() @@ -132,10 +135,43 @@ void CMainWindow::onAction_zoomFit() m_pDrawingPanel->grahpicsViewZoomFit(); } +void CMainWindow::onAction_createGroup() +{ + GraphicsItemGroup* group = m_pDrawingPanel->createItemGroup(); + if(group) + { + QGraphicsScene* scene = m_pDrawingPanel->getQGraphicsScene(); + QUndoCommand* createItemGropu = new CreateItemGoupCommand(group, scene); + m_pUndoStack->push(createItemGropu); + } +} + +void CMainWindow::onAction_destroyGroup() +{ + QGraphicsScene* scene = m_pDrawingPanel->getQGraphicsScene(); + QList listItem = scene->selectedItems(); + if(listItem.count() != 1) + return; //只能选择一个解组 + + QGraphicsItem* item = listItem.first(); + if(!item) + return; + + GraphicsItemGroup *group = dynamic_cast(item); + if(group) + { + QUndoCommand* destroyItemGropu = new DestroyItemGoupCommand(group, scene); + m_pUndoStack->push(destroyItemGropu); + } +} + void CMainWindow::onSignal_addItem(QGraphicsItem* item) { - QUndoCommand* addItemCommand = new AddItemCommand(item, item->scene()); - m_pUndoStack->push(addItemCommand); + if(item) + { + QUndoCommand* addItemCommand = new AddItemCommand(item, item->scene()); + m_pUndoStack->push(addItemCommand); + } } void CMainWindow::onSignal_deleteItem() diff --git a/source/util/baseSelector.cpp b/source/util/baseSelector.cpp index 5850d79..bdba19c 100644 --- a/source/util/baseSelector.cpp +++ b/source/util/baseSelector.cpp @@ -37,7 +37,7 @@ void BaseSelector::mousePressEvent(QGraphicsSceneMouseEvent* event, DesignerScen QList items = scene->selectedItems(); if (items.count() == 1) //只有一个选中 { - GraphicsBaseItem* item = qgraphicsitem_cast(items.first()); + AbstractShape* item = qgraphicsitem_cast(items.first()); if(item) { //需要增加当前是否点击在控制点的判断函数 @@ -87,7 +87,7 @@ void BaseSelector::mousePressEvent(QGraphicsSceneMouseEvent* event, DesignerScen for(int n = 0; n < items.size(); n++) { //创建副本 - GraphicsBaseItem* item = qgraphicsitem_cast(items.at(n)); + AbstractShape* item = qgraphicsitem_cast(items.at(n)); item->createOperationCopy(); } } @@ -108,7 +108,7 @@ void BaseSelector::mouseMoveEvent(QGraphicsSceneMouseEvent* event, DesignerScene if (items.count() == 1) { - GraphicsBaseItem* item = qgraphicsitem_cast(items.first()); + AbstractShape* item = qgraphicsitem_cast(items.first()); if(item) { if(ms_nDragHandle == H_none) diff --git a/source/util/movingSelector.cpp b/source/util/movingSelector.cpp index e5dd413..3a21621 100644 --- a/source/util/movingSelector.cpp +++ b/source/util/movingSelector.cpp @@ -25,7 +25,7 @@ void MovingSelector::mouseMoveEvent(QGraphicsSceneMouseEvent* event, DesignerSce QList items = scene->selectedItems(); for(int n = 0; n < items.size(); n++) { - GraphicsBaseItem* item = qgraphicsitem_cast(items.at(n)); + AbstractShape* item = qgraphicsitem_cast(items.at(n)); if(item) item->moveOperationCopy(ms_ptMouseLast - ms_ptMouseDown); } @@ -36,7 +36,7 @@ void MovingSelector::mouseReleaseEvent(QGraphicsSceneMouseEvent* event, Designer QList items = scene->selectedItems(); for(int n = 0; n < items.size(); n++) { - GraphicsBaseItem* item = qgraphicsitem_cast(items.at(n)); + AbstractShape* item = qgraphicsitem_cast(items.at(n)); if(item) item->removeOperationCopy(); } diff --git a/source/util/rotationSelector.cpp b/source/util/rotationSelector.cpp index 8e792d0..fc0b2ca 100644 --- a/source/util/rotationSelector.cpp +++ b/source/util/rotationSelector.cpp @@ -24,7 +24,7 @@ void RotationSelector::mouseMoveEvent(QGraphicsSceneMouseEvent* event, DesignerS QList items = scene->selectedItems(); if (items.count() == 1) { - GraphicsBaseItem* item = qgraphicsitem_cast(items.first()); + AbstractShape* item = qgraphicsitem_cast(items.first()); if(item) { //计算夹角 @@ -53,7 +53,7 @@ void RotationSelector::mouseReleaseEvent(QGraphicsSceneMouseEvent* event, Design QList items = scene->selectedItems(); for(int n = 0; n < items.size(); n++) { - GraphicsBaseItem* item = qgraphicsitem_cast(items.at(n)); + AbstractShape* item = qgraphicsitem_cast(items.at(n)); if(item) { item->removeOperationCopy(); diff --git a/source/util/scalingSelector.cpp b/source/util/scalingSelector.cpp index a9110b7..ceae42f 100644 --- a/source/util/scalingSelector.cpp +++ b/source/util/scalingSelector.cpp @@ -25,7 +25,7 @@ void ScalingSelector::mouseMoveEvent(QGraphicsSceneMouseEvent* event, DesignerSc QList items = scene->selectedItems(); if (items.count() == 1) { - GraphicsBaseItem* item = qgraphicsitem_cast(items.first()); + AbstractShape* item = qgraphicsitem_cast(items.first()); if(item) { if(ms_nDragHandle != H_none) @@ -56,7 +56,7 @@ void ScalingSelector::mouseReleaseEvent(QGraphicsSceneMouseEvent* event, Designe QList items = scene->selectedItems(); if (items.count() == 1) { - GraphicsBaseItem* item = qgraphicsitem_cast(items.first()); + AbstractShape* item = qgraphicsitem_cast(items.first()); if(item && ms_ptMouseLast != ms_ptMouseDown) { item->updateCoordinate(); diff --git a/ui/mainwindow.ui b/ui/mainwindow.ui index 2b0c15e..bab0139 100644 --- a/ui/mainwindow.ui +++ b/ui/mainwindow.ui @@ -60,6 +60,8 @@ + + @@ -237,6 +239,28 @@ QAction::MenuRole::NoRole + + + 群组 + + + 群组 + + + QAction::MenuRole::NoRole + + + + + 解组 + + + 解组 + + + QAction::MenuRole::NoRole + +