实现图元的打组、解组,以及打组后的缩放、移动和旋转等操作

This commit is contained in:
duanshengchao 2024-08-30 17:23:33 +08:00
parent 7f4c48d66f
commit a4b8556a47
17 changed files with 408 additions and 22 deletions

View File

@ -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

View File

@ -3,6 +3,8 @@
#include <QGraphicsScene>
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*);

View File

@ -260,6 +260,8 @@ protected:
QVector<ItemControlHandle*> m_vecHanle;
};
typedef AbstractShapeType<QGraphicsItem> AbstractShape;
class GraphicsBaseItem : public QObject, public AbstractShapeType<QGraphicsItem>
{
Q_OBJECT

View File

@ -0,0 +1,36 @@
#ifndef GRAPHICSITEMGROUP_H
#define GRAPHICSITEMGROUP_H
#include "graphicsBaseItem.h"
class GraphicsItemGroup : public QObject, public AbstractShapeType<QGraphicsItemGroup>
{
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<QGraphicsItem*>&);
QList<QGraphicsItem*> 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<QGraphicsItem*> m_listItem;
};
#endif

View File

@ -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();

View File

@ -12,6 +12,8 @@
#include <QUndoCommand>
#include <QGraphicsScene>
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<QGraphicsItem*> 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<QGraphicsItem*> m_listItem;
};
#endif

View File

@ -1,5 +1,6 @@
#include "designerScene.h"
#include "util/selectorManager.h"
#include "graphicsItem/graphicsItemGroup.h"
#include <QPainter>
#include <QGraphicsSceneMouseEvent>
@ -125,3 +126,20 @@ void DesignerScene::callParentEvent(QGraphicsSceneMouseEvent* mouseEvent)
break;
}
}
GraphicsItemGroup* DesignerScene::createGroup()
{
QList<QGraphicsItem*> listItem = selectedItems();
if(listItem.isEmpty())
return nullptr;
GraphicsItemGroup* group = new GraphicsItemGroup();
group->addItems(listItem);
addItem(group);
return group;
}
void DesignerScene::destroyGroup()
{
}

View File

@ -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())

View File

@ -0,0 +1,217 @@
#include "graphicsItem/graphicsItemGroup.h"
#include <QGraphicsScene>
#include <QPainter>
#include <QStyleOption>
GraphicsItemGroup::GraphicsItemGroup(QGraphicsItem *parent)
: AbstractShapeType<QGraphicsItemGroup>(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<QGraphicsItemGroup *>(parentItem());
if(!group)
setHandleVisible(value.toBool());
else //在某一组群中,由组群展示是否选中,自身不做展示
{
setSelected(false);
return QVariant::fromValue<bool>(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<GraphicsBaseItem *>(item);
if (baseItem && !qgraphicsitem_cast<ItemControlHandle *>(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<GraphicsBaseItem *>(item);
if (baseItem && !qgraphicsitem_cast<ItemControlHandle *>(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<QGraphicsItem*>& items)
{
foreach (QGraphicsItem *item, items)
{
item->setSelected(false);
addToGroup(item);
m_listItem.push_back(item);
}
updateCoordinate();
}

View File

@ -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坐标系否则会有跳转

View File

@ -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);

View File

@ -1,5 +1,6 @@
#include "mainwindow.h"
#include "graphicsItem/graphicsItemGroup.h"
#include "ui_mainwindow.h"
#include <QWidgetAction>
@ -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<QGraphicsItem*> listItem = scene->selectedItems();
if(listItem.count() != 1)
return; //只能选择一个解组
QGraphicsItem* item = listItem.first();
if(!item)
return;
GraphicsItemGroup *group = dynamic_cast<GraphicsItemGroup*>(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()

View File

@ -37,7 +37,7 @@ void BaseSelector::mousePressEvent(QGraphicsSceneMouseEvent* event, DesignerScen
QList<QGraphicsItem *> items = scene->selectedItems();
if (items.count() == 1) //只有一个选中
{
GraphicsBaseItem* item = qgraphicsitem_cast<GraphicsBaseItem*>(items.first());
AbstractShape* item = qgraphicsitem_cast<AbstractShape*>(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<GraphicsBaseItem*>(items.at(n));
AbstractShape* item = qgraphicsitem_cast<AbstractShape*>(items.at(n));
item->createOperationCopy();
}
}
@ -108,7 +108,7 @@ void BaseSelector::mouseMoveEvent(QGraphicsSceneMouseEvent* event, DesignerScene
if (items.count() == 1)
{
GraphicsBaseItem* item = qgraphicsitem_cast<GraphicsBaseItem*>(items.first());
AbstractShape* item = qgraphicsitem_cast<AbstractShape*>(items.first());
if(item)
{
if(ms_nDragHandle == H_none)

View File

@ -25,7 +25,7 @@ void MovingSelector::mouseMoveEvent(QGraphicsSceneMouseEvent* event, DesignerSce
QList<QGraphicsItem *> items = scene->selectedItems();
for(int n = 0; n < items.size(); n++)
{
GraphicsBaseItem* item = qgraphicsitem_cast<GraphicsBaseItem*>(items.at(n));
AbstractShape* item = qgraphicsitem_cast<AbstractShape*>(items.at(n));
if(item)
item->moveOperationCopy(ms_ptMouseLast - ms_ptMouseDown);
}
@ -36,7 +36,7 @@ void MovingSelector::mouseReleaseEvent(QGraphicsSceneMouseEvent* event, Designer
QList<QGraphicsItem *> items = scene->selectedItems();
for(int n = 0; n < items.size(); n++)
{
GraphicsBaseItem* item = qgraphicsitem_cast<GraphicsBaseItem*>(items.at(n));
AbstractShape* item = qgraphicsitem_cast<AbstractShape*>(items.at(n));
if(item)
item->removeOperationCopy();
}

View File

@ -24,7 +24,7 @@ void RotationSelector::mouseMoveEvent(QGraphicsSceneMouseEvent* event, DesignerS
QList<QGraphicsItem *> items = scene->selectedItems();
if (items.count() == 1)
{
GraphicsBaseItem* item = qgraphicsitem_cast<GraphicsBaseItem*>(items.first());
AbstractShape* item = qgraphicsitem_cast<AbstractShape*>(items.first());
if(item)
{
//计算夹角
@ -53,7 +53,7 @@ void RotationSelector::mouseReleaseEvent(QGraphicsSceneMouseEvent* event, Design
QList<QGraphicsItem *> items = scene->selectedItems();
for(int n = 0; n < items.size(); n++)
{
GraphicsBaseItem* item = qgraphicsitem_cast<GraphicsBaseItem*>(items.at(n));
AbstractShape* item = qgraphicsitem_cast<AbstractShape*>(items.at(n));
if(item)
{
item->removeOperationCopy();

View File

@ -25,7 +25,7 @@ void ScalingSelector::mouseMoveEvent(QGraphicsSceneMouseEvent* event, DesignerSc
QList<QGraphicsItem *> items = scene->selectedItems();
if (items.count() == 1)
{
GraphicsBaseItem* item = qgraphicsitem_cast<GraphicsBaseItem*>(items.first());
AbstractShape* item = qgraphicsitem_cast<AbstractShape*>(items.first());
if(item)
{
if(ms_nDragHandle != H_none)
@ -56,7 +56,7 @@ void ScalingSelector::mouseReleaseEvent(QGraphicsSceneMouseEvent* event, Designe
QList<QGraphicsItem *> items = scene->selectedItems();
if (items.count() == 1)
{
GraphicsBaseItem* item = qgraphicsitem_cast<GraphicsBaseItem*>(items.first());
AbstractShape* item = qgraphicsitem_cast<AbstractShape*>(items.first());
if(item && ms_ptMouseLast != ms_ptMouseDown)
{
item->updateCoordinate();

View File

@ -60,6 +60,8 @@
<addaction name="actionZoomFit"/>
<addaction name="actionGrid"/>
<addaction name="separator"/>
<addaction name="actionGroup"/>
<addaction name="actionUngroup"/>
</widget>
<action name="actionNew">
<property name="icon">
@ -237,6 +239,28 @@
<enum>QAction::MenuRole::NoRole</enum>
</property>
</action>
<action name="actionGroup">
<property name="text">
<string>群组</string>
</property>
<property name="toolTip">
<string>群组</string>
</property>
<property name="menuRole">
<enum>QAction::MenuRole::NoRole</enum>
</property>
</action>
<action name="actionUngroup">
<property name="text">
<string>解组</string>
</property>
<property name="toolTip">
<string>解组</string>
</property>
<property name="menuRole">
<enum>QAction::MenuRole::NoRole</enum>
</property>
</action>
</widget>
<resources/>
<connections/>