一、多类型Item绘制实现原理
1.继承体系设计
1)基于 QGraphicsItem 类建立继承体系,作为所有图形项的基类
2)通过派生类实现不同类型 Item 的特有绘制逻辑
3)使用枚举类型或类标识符区分不同 Item 类别
2. 多态绘制机制
1)重写 QGraphicsItem 的 paint() 虚函数实现具体绘制逻辑
2)在 paint() 方法中根据 Item 类型执行不同的绘制操作
3)通过 boundingRect() 定义每个 Item 的边界区域
3.工厂模式应用
1)采用工厂方法模式创建不同类型的 Item 实例
2)通过统一接口创建 Item,隐藏具体实现细节
3)支持运行时动态添加新的 Item 类型
二、Item 变换功能实现原理
1.变换体系结构
1)利用 QGraphicsItem 内置的变换功能
2)通过 QTransform 类实现底层矩阵变换
2.交互控制机制
1)设置 ItemIsMovable 标志启用拖动功能
2)通过信号-槽机制响应变换操作
3.变换控制点管理
1)设置变换原点(transform origin)控制旋转和缩放基准点
2)实现变换约束(如保持长宽比)
4.视图协同工作
1)通过 QGraphicsView 的视口变换实现整体缩放
2)处理场景坐标与视图坐标的转换
三、主要代码示例
1.mainwindow
.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QGraphicsScene>
#include <QTreeWidget>
#include <QTimer>
#include "graphicsview.h"
#include "graphicsscene.h"
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
public slots:
void updateUI();
void treeItemAddShape(QTreeWidgetItem *item, int column);
private:
Ui::MainWindow *ui;
QTimer m_timer;
GraphicsView *view;
GraphicsScene * scene;
QTreeWidgetItem *selectItem;
QTreeWidgetItem *rotationItem;
QTreeWidgetItem *ellipseItem;
QTreeWidgetItem *rectItem;
QTreeWidgetItem *lineItem;
QTreeWidgetItem *roundRectItem;
QTreeWidgetItem *polylineItem;
QTreeWidgetItem *polygonItem;
};
#endif // MAINWINDOW_H
.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDateTime>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
scene = new GraphicsScene(this);
scene->setSceneRect(QRectF(-2400 / 2 , -2400 / 2 , 2400, 2400));
scene->setBackgroundBrush(Qt::white);
view = new GraphicsView(this);
setCentralWidget(view);
view->setScene(scene);
connect(&m_timer,SIGNAL(timeout()),this,SLOT(updateUI()));
m_timer.start(100);
ui->dockWidget->setMinimumWidth(280);
ui->treeWidget->setColumnCount(1);
ui->treeWidget->setSelectionMode(QAbstractItemView::SingleSelection);
connect(ui->treeWidget,&QTreeWidget::itemClicked,this,&MainWindow::treeItemAddShape);
selectItem = new QTreeWidgetItem(ui->treeWidget,QStringList("选择"));
selectItem->setData(0,Qt::UserRole+1,selection);
rotationItem = new QTreeWidgetItem(ui->treeWidget,QStringList("旋转"));
rotationItem->setData(0,Qt::UserRole+1,rotation);
ellipseItem = new QTreeWidgetItem(ui->treeWidget,QStringList("椭圆"));
ellipseItem->setData(0,Qt::UserRole+1,ellipse);
rectItem = new QTreeWidgetItem(ui->treeWidget,QStringList("矩形"));
rectItem->setData(0,Qt::UserRole+1,rectangle);
lineItem = new QTreeWidgetItem(ui->treeWidget,QStringList("直线"));
lineItem->setData(0,Qt::UserRole+1,line);
roundRectItem = new QTreeWidgetItem(ui->treeWidget,QStringList("圆角矩形"));
roundRectItem->setData(0,Qt::UserRole+1,roundrect);
polylineItem = new QTreeWidgetItem(ui->treeWidget,QStringList("多边线"));
polylineItem->setData(0,Qt::UserRole+1,polyline);
polygonItem = new QTreeWidgetItem(ui->treeWidget,QStringList("多边形"));
polygonItem->setData(0,Qt::UserRole+1,polygon);
}
void MainWindow::treeItemAddShape(QTreeWidgetItem *item, int column)
{
Q_UNUSED(column);
if(item == selectItem)
DrawTool::c_drawShape = selection;
if(item == rotationItem)
DrawTool::c_drawShape = rotation;
if(item == ellipseItem)
DrawTool::c_drawShape = ellipse;
if(item == rectItem)
DrawTool::c_drawShape = rectangle;
if(item == lineItem)
DrawTool::c_drawShape = line;
if(item == roundRectItem)
DrawTool::c_drawShape = roundrect;
if(item == polylineItem)
DrawTool::c_drawShape = polyline;
if(item == polygonItem)
DrawTool::c_drawShape = polygon;
}
void MainWindow::updateUI()
{
// for(int i = 0;i < ui->treeWidget->topLevelItemCount();i++)
// {
// if(ui->treeWidget->topLevelItem(i)->data(0,Qt::UserRole+1).toInt() == DrawTool::c_drawShape)
// ui->treeWidget->setCurrentItem(ui->treeWidget->topLevelItem(i));
// }
}
MainWindow::~MainWindow()
{
delete ui;
}
.ui
2.graphicsview
.h
#ifndef GRAPHICSVIEW_H
#define GRAPHICSVIEW_H
#include <QGraphicsView>
#include <QWheelEvent>
#include <QScrollBar>
class GraphicsView : public QGraphicsView
{
Q_OBJECT
public:
explicit GraphicsView(QWidget *parent = nullptr);
void setVerScrollValue(int y);
void setHorScrollValue(int x);
private:
void zoomIn();
void zoomOut();
protected:
//缩放
void wheelEvent(QWheelEvent *event) override;
void mousePressEvent(QMouseEvent *event) override;
void mouseMoveEvent(QMouseEvent *event) override;
void mouseReleaseEvent(QMouseEvent *event) override;
signals:
public slots:
private:
bool isDrag = false;
QPoint lastPos;
};
#endif // GRAPHICSVIEW_H
.cpp
#include "graphicsview.h"
GraphicsView::GraphicsView(QWidget *parent) : QGraphicsView(parent)
{
setRenderHint(QPainter::Antialiasing);
setMouseTracking(true);//不加不会在鼠标未点击的情况下进入鼠标移动事件
setFrameShape(QFrame::NoFrame);
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
}
void GraphicsView::setVerScrollValue(int y)
{
//设置的view是左上角的点
QScrollBar *vScrolBar = verticalScrollBar();
vScrolBar->setValue(y);
}
void GraphicsView::setHorScrollValue(int x)
{
//设置的view是左上角的点
QScrollBar *hScrolBar = horizontalScrollBar();
hScrolBar->setValue(x);
}
void GraphicsView::zoomIn()
{
scale(1.1,1.1);
}
void GraphicsView::zoomOut()
{
scale(1 / 1.1, 1 / 1.1);
}
void GraphicsView::wheelEvent(QWheelEvent *event)
{
if(event->modifiers() == Qt::ControlModifier)
{
// 获取当前鼠标相对于view的位置;
QPointF cursorPoint = event->pos();
// 获取当前鼠标相对于scene的位置;
QPointF scenePos = this->mapToScene(QPoint(cursorPoint.x(), cursorPoint.y()));
// 获取view的宽高;
qreal viewWidth = this->viewport()->width();
qreal viewHeight = this->viewport()->height();
// 获取当前鼠标位置相当于view大小的横纵比例;
qreal hScale = cursorPoint.x() / viewWidth;
qreal vScale = cursorPoint.y() / viewHeight;
//滚轮的滚动量
QPoint scrollAmount = event->angleDelta();
//正值表示放大,负值表示缩小
scrollAmount.y() > 0 ? zoomIn() : zoomOut();
// 将scene坐标转换为放大缩小后的坐标; //map(QPoint):创建并返回一个QPoint对象,该对象是给定点的副本,映射到这个矩阵定义的坐标系统中。注意,转换后的坐标会四舍五入到最接近的整数。
QPointF viewPoint = this->matrix().map(scenePos);//matrix():返回视图的当前转换矩阵。如果没有设置当前转换,则返回单位矩阵。
// 通过滚动条控制view放大缩小后的展示scene的位置;
setHorScrollValue(int(viewPoint.x() - viewWidth * hScale));
setVerScrollValue(int(viewPoint.y() - viewHeight * vScale));
}
}
void GraphicsView::mousePressEvent(QMouseEvent *event)
{
if(event->button() == Qt::RightButton)
{
isDrag = true;
lastPos = event->pos();
}
QGraphicsView::mousePressEvent(event);
}
void GraphicsView::mouseMoveEvent(QMouseEvent *event)
{
if(isDrag)
{
QPointF increment = this->matrix().map(mapToScene(event->pos()) - mapToScene(lastPos));
setHorScrollValue(int(horizontalScrollBar()->value() - increment.x()));
setVerScrollValue(int(verticalScrollBar()->value() - increment.y()));
lastPos = event->pos();
//qDebug()<<"x:"<<hScrolBar->value()<<" y:"<<vScrolBar->value();
}
QGraphicsView::mouseMoveEvent(event);
}
void GraphicsView::mouseReleaseEvent(QMouseEvent *event)
{
if(isDrag)
isDrag = false;
QGraphicsView::mouseReleaseEvent(event);
}
3.graphicsscene
.h
#ifndef GRAPHICSSCENE_H
#define GRAPHICSSCENE_H
#include <QGraphicsScene>
#include <QMimeData>
#include "drawtool.h"
#include "item.h"
QT_BEGIN_NAMESPACE
class QGraphicsSceneMouseEvent;
class QMenu;
class QPointF;
class QGraphicsLineItem;
class QFont;
class QGraphicsTextItem;
class QColor;
QT_END_NAMESPACE
class GraphicsScene : public QGraphicsScene
{
Q_OBJECT
public:
explicit GraphicsScene(QObject *parent = nullptr);
void mouseEvent(QGraphicsSceneMouseEvent *mouseEvent );
protected:
void mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent) Q_DECL_OVERRIDE;
void mouseMoveEvent(QGraphicsSceneMouseEvent *mouseEvent) Q_DECL_OVERRIDE;
void mouseReleaseEvent(QGraphicsSceneMouseEvent *mouseEvent) Q_DECL_OVERRIDE;
void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) override;
void drawBackground(QPainter *painter, const QRectF &rect) override;
private:
//小点的间距
int m_Xspacing;
int m_Yspacing;
};
#endif // GRAPHICSSCENE_H
.cpp
#include "graphicsscene.h"
GraphicsScene::GraphicsScene(QObject *parent) : QGraphicsScene(parent)
{
m_Xspacing = 10;
m_Yspacing = 10;
}
void GraphicsScene::mouseEvent(QGraphicsSceneMouseEvent *mouseEvent)
{
switch( mouseEvent->type() ){
case QEvent::GraphicsSceneMousePress:
QGraphicsScene::mousePressEvent(mouseEvent);
break;
case QEvent::GraphicsSceneMouseMove:
QGraphicsScene::mouseMoveEvent(mouseEvent);
break;
case QEvent::GraphicsSceneMouseRelease:
QGraphicsScene::mouseReleaseEvent(mouseEvent);
break;
case QEvent::GraphicsSceneMouseDoubleClick:
QGraphicsScene::mouseDoubleClickEvent(mouseEvent);
break;
default:
break;
}
}
void GraphicsScene::mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent)
{
DrawTool * tool = DrawTool::findTool( DrawTool::c_drawShape );
if ( tool )
tool->mousePressEvent(mouseEvent,this);
}
void GraphicsScene::mouseMoveEvent(QGraphicsSceneMouseEvent *mouseEvent)
{
DrawTool * tool = DrawTool::findTool( DrawTool::c_drawShape );
if ( tool )
tool->mouseMoveEvent(mouseEvent,this);
}
void GraphicsScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *mouseEvent)
{
DrawTool * tool = DrawTool::findTool( DrawTool::c_drawShape );
if ( tool )
tool->mouseReleaseEvent(mouseEvent,this);
}
void GraphicsScene::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
{
DrawTool *tool = DrawTool::findTool( DrawTool::c_drawShape );
if(tool)
tool->mouseDoubleClickEvent(event,this);
}
void GraphicsScene::drawBackground(QPainter *painter, const QRectF &rect)
{
QGraphicsScene::drawBackground(painter,rect);
painter->fillRect(sceneRect(),QBrush(Qt::white));
for(int x = -sceneRect().width()/2; x < sceneRect().width()/2;x = x+m_Xspacing)
{
for(int y = -sceneRect().height()/2; y < sceneRect().height()/2;y = y+m_Yspacing)
{
painter->setPen(QPen(Qt::black));
painter->drawPoint(x,y);
}
}
}
4.drawtool
.h
#ifndef DRAWTOOL
#define DRAWTOOL
#include <QGraphicsScene>
#include <QList>
#include <QCursor>
#include <QRubberBand>
#include "graphicsscene.h"
#include "item.h"
QT_BEGIN_NAMESPACE
class QGraphicsScene;
class QGraphicsSceneMouseEvent;
class QMenu;
class QPointF;
class QGraphicsLineItem;
class QFont;
class QGraphicsTextItem;
class QColor;
class QAbstractGraphicsShapeItem;
QT_END_NAMESPACE
class GraphicsScene;
class GraphicsItem;
enum DrawShape
{
selection ,
rotation ,
rectangle ,
roundrect ,
ellipse ,
polygon,
polyline,
line
};
class DrawTool
{
public:
DrawTool(DrawShape shape);
virtual void mousePressEvent(QGraphicsSceneMouseEvent *event, GraphicsScene *scene);
virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event, GraphicsScene *scene);
virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *event, GraphicsScene *scene);
virtual void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event, GraphicsScene *scene);
DrawShape m_drawShape;
QRubberBand *m_rubberBand;
static DrawTool* findTool(DrawShape drawShape);
static QList<DrawTool*> c_tools;
static QPointF c_down;
static quint32 c_nDownFlags;
static QPointF c_last;
static DrawShape c_drawShape;
};
class SelectTool : public DrawTool
{
public:
SelectTool();
virtual void mousePressEvent(QGraphicsSceneMouseEvent *event, GraphicsScene *scene);
virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event, GraphicsScene *scene);
virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *event, GraphicsScene *scene);
// 选中的虚线框
QGraphicsPathItem *m_dashRect;
//
bool m_hoverSizer;
//
QPointF m_oppositePoint;
};
class RotationTool : public DrawTool
{
public:
RotationTool();
virtual void mousePressEvent(QGraphicsSceneMouseEvent *event, GraphicsScene *scene);
virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event, GraphicsScene *scene);
virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *event, GraphicsScene *scene);
qreal m_lastAngle;
QGraphicsPathItem * m_dashRect;
};
class RectTool : public DrawTool
{
public:
RectTool(DrawShape drawShape);
virtual void mousePressEvent(QGraphicsSceneMouseEvent *event, GraphicsScene *scene) override;
virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event, GraphicsScene *scene) override;
virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *event, GraphicsScene *scene) override;
virtual void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event, GraphicsScene *scene) override;
GraphicsRectItem * m_item;
};
class PolygonTool : public DrawTool
{
public:
PolygonTool(DrawShape shape);
virtual void mousePressEvent(QGraphicsSceneMouseEvent *event, GraphicsScene *scene);
virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event, GraphicsScene *scene);
virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *event, GraphicsScene *scene);
virtual void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event, GraphicsScene *scene);
GraphicsPolygonItem *m_item;
};
#endif // DRAWTOOL
.cpp
#include "drawtool.h"
#include "item.h"
#include <QDebug>
#include <QGraphicsSceneMouseEvent>
#include <QGraphicsView>
#include <QGraphicsLineItem>
#include <QtMath>
#define PI 3.14
QList<DrawTool*> DrawTool::c_tools;
QPointF DrawTool::c_down;
QPointF DrawTool::c_last;
quint32 DrawTool::c_nDownFlags;
DrawShape DrawTool::c_drawShape = selection;
static SelectTool selectTool;
static RectTool rectTool(rectangle);
static RectTool roundRectTool(roundrect);
static RectTool ellipseTool(ellipse);
static PolygonTool lineTool(line);
static PolygonTool polygonTool(polygon);
static PolygonTool polylineTool(polyline);
static RotationTool rotationTool;
enum SelectMode
{
none,
netSelect,
move,
size,
rotate,
editor,//修改折线或者多边形内部的控制点
editing//表示图元在编辑状态
};
SelectMode selectMode = none;
Direction nDragHandle = None;
static void setCursor(QGraphicsScene * scene , const QCursor & cursor )
{
QList<QGraphicsView*> views = scene->views();
if ( views.count() > 0 ){
QGraphicsView * view = views.first();
view->viewport()->setCursor(cursor);
}
}
DrawTool::DrawTool(DrawShape shape)
{
m_drawShape = shape ;
m_rubberBand = nullptr;
c_tools.push_back(this);
}
void DrawTool::mousePressEvent(QGraphicsSceneMouseEvent *event, GraphicsScene *scene)
{
Q_UNUSED(scene);
c_down = event->scenePos();
c_last = event->scenePos();
}
void DrawTool::mouseMoveEvent(QGraphicsSceneMouseEvent *event, GraphicsScene *scene)
{
Q_UNUSED(scene);
c_last = event->scenePos();
}
void DrawTool::mouseReleaseEvent(QGraphicsSceneMouseEvent *event, GraphicsScene *scene)
{
if (event->scenePos() == c_down)
c_drawShape = selection;
setCursor(scene,Qt::ArrowCursor);
}
void DrawTool::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event, GraphicsScene *scene)
{
scene->mouseEvent(event);
}
DrawTool *DrawTool::findTool(DrawShape drawShape)
{
QList<DrawTool*>::const_iterator iter = c_tools.constBegin();
for(;iter != c_tools.constEnd();++iter)
{
if((*iter)->m_drawShape == drawShape)
return (*iter);
}
return 0;
}
SelectTool::SelectTool()
:DrawTool(selection)
{
m_dashRect = 0;
m_hoverSizer = false;
}
void SelectTool::mousePressEvent(QGraphicsSceneMouseEvent *event, GraphicsScene *scene)
{
DrawTool::mousePressEvent(event,scene);
//悬停筛选器,表示是否悬停在图元的sizehandle上
if (!m_hoverSizer)
scene->mouseEvent(event);
if ( event->button() != Qt::LeftButton )
{
qDebug()<<"点击的不为左键"<<event->button();
return;
}
nDragHandle = None;
selectMode = none;
QList<QGraphicsItem *> items = scene->selectedItems();
GraphicsItem *item = 0;
if ( items.count() > 0 )
{
if(items.count() == 1)
item = qgraphicsitem_cast<GraphicsItem*>(items.first());
else
selectMode = SelectMode::move;
}
if ( item != 0 )
{
nDragHandle = (Direction)item->collidesTheSizeHandle(event->scenePos().toPoint());
if ( nDragHandle <= Left)
selectMode = size;
else if( nDragHandle == None)
selectMode = SelectMode::move;
else if(nDragHandle > None)
selectMode = editor;
if( nDragHandle <= Left)
//m_oppositePoint 是图元坐标
m_oppositePoint = item->opposite(nDragHandle);
if( m_oppositePoint.x() == 0)
m_oppositePoint.setX(1);
if( m_oppositePoint.y() == 0)
m_oppositePoint.setY(1);
}
//点击之后,点击的坐标处有图元,但是没有选中的图元,说明点到了编辑中的图元
QGraphicsItem *itemByPoint = scene->itemAt(c_down,scene->views().first()->transform());
if(itemByPoint && items.isEmpty())
{
selectMode = editing;
}
if( selectMode == none )
{
selectMode = netSelect;
QList<QGraphicsView*> views = scene->views();
if ( views.count() > 0 ){
QGraphicsView * view = views.first();
//view->setDragMode(QGraphicsView::RubberBandDrag);
//QRubberBand设置橡皮筋类
if(!m_rubberBand)
{
m_rubberBand = new QRubberBand(QRubberBand::Line,view);
}
m_rubberBand->setGeometry(QRect(view->mapFromScene(DrawTool::c_down),QSize()));
m_rubberBand->show();
}
}
//一个图元移动时才有虚框生成
if ( selectMode == SelectMode::move )
{
if (m_dashRect )
{
scene->removeItem(m_dashRect);
delete m_dashRect;
m_dashRect = 0;
}
QPainterPath path;
foreach(QGraphicsItem *item_1,items)
{
QTransform trans;
trans.rotate(item_1->rotation());
QPainterPath itemPath = trans.map(item_1->shape());
itemPath.translate(item_1->scenePos());
path.addPath(itemPath);
}
m_dashRect = new QGraphicsPathItem(path);
m_dashRect->setPen(Qt::DashLine);
scene->addItem(m_dashRect);
}
}
void SelectTool::mouseMoveEvent(QGraphicsSceneMouseEvent *event, GraphicsScene *scene)
{
DrawTool::mouseMoveEvent(event,scene);
//用于框选图元,当自上而下时包围图元为选中,自下而上时触碰图元为选中
if(m_rubberBand && c_drawShape == selection)
{
scene->clearSelection();
QGraphicsView *view = scene->views().first();
QList<QGraphicsItem *> list;
m_rubberBand->setGeometry(QRect(view->mapFromScene(DrawTool::c_down),view->mapFromScene(event->scenePos().toPoint())).normalized());
if(DrawTool::c_down.y() < event->scenePos().y())
{
list = view->items(m_rubberBand->geometry(),Qt::ContainsItemBoundingRect);
}
else
{
list = view->items(m_rubberBand->geometry(),Qt::IntersectsItemShape);
}
foreach(QGraphicsItem *item,list)
{
item->setSelected(true);
}
}
bool isGroup = false;
QList<QGraphicsItem *> items = scene->selectedItems();
GraphicsItem * item = nullptr;
if ( items.count() == 1 )
{
item = qgraphicsitem_cast<GraphicsItem*>(items.first());
if ( item != 0 )
{
if ( nDragHandle != None && selectMode == size )
{
if(m_oppositePoint.isNull())
{
m_oppositePoint = item->opposite(nDragHandle);
if(m_oppositePoint.x() == 0)
m_oppositePoint.setX(1);
if(m_oppositePoint.y() == 0)
m_oppositePoint.setY(1);
}
QPointF distance = item->mapFromScene(c_down) - m_oppositePoint;
if(distance.x() == 0)
distance.setX(1);
if(distance.y() == 0)
distance.setY(1);
QPoint newDistance = (item->mapFromScene(c_last) - m_oppositePoint).toPoint();
if(newDistance.x() == 0)
newDistance.setX(1);
if(newDistance.y() == 0)
newDistance.setY(1);
qreal sx = (qreal)newDistance.x() / (qreal)distance.x();
qreal sy = (qreal)newDistance.y() / (qreal)distance.y();
item->stretch(nDragHandle,sx,sy,m_oppositePoint.toPoint());
}
else if( nDragHandle > None && selectMode == editor)
{
item->resizeTo(nDragHandle,c_last.toPoint());
}
else if(nDragHandle == None && c_drawShape == selection )
{
int handle = item->collidesWithHandle(event->scenePos().toPoint(),true);
if ( handle != None)
{
setCursor( scene,item->getCursor(handle) );
//点中sizehandle
m_hoverSizer = true;
}
else
{
setCursor(scene,Qt::ArrowCursor);
m_hoverSizer = false;
}
}
}
QGraphicsItemGroup *item1 = qgraphicsitem_cast<QGraphicsItemGroup*>(items.first());
if ( item1 != NULL )
{
isGroup = true;
}
}
if ( items.count() >= 1 && selectMode == SelectMode::move )
{
//坐标超过view视图时产生拖拽事件
if ( m_dashRect )
{
int x = c_last.x() - c_down.x();
int y = c_last.y() - c_down.y();
m_dashRect->setPos(QPoint(x,y));
}
}
else if ( (selectMode != size && items.count() > 1) || isGroup || selectMode == editing )
{
scene->mouseEvent(event);
}
}
void SelectTool::mouseReleaseEvent(QGraphicsSceneMouseEvent *event, GraphicsScene *scene)
{
if(m_rubberBand)
{
delete m_rubberBand;
m_rubberBand = nullptr;
}
DrawTool::mouseReleaseEvent(event,scene);
QList<QGraphicsItem *> items = scene->selectedItems();
if ( items.count() >= 1 )
{
GraphicsItem * item = qgraphicsitem_cast<GraphicsItem*>(items.first());
if ( item != 0 && selectMode == SelectMode::move && c_last != c_down )
{
int x = c_last.x() - c_down.x();
int y = c_last.y() - c_down.y();
foreach(QGraphicsItem *item_1,items)
{
QPointF point = item_1->pos()+QPointF(x,y);
item_1->setPos(point.toPoint());
}
}
else if(item != 0 && selectMode == size && c_last != c_down)
{
item->updateCoordinate();
}
else if(item != 0 && selectMode == editor && c_last != c_down)
{
item->resizeTo(nDragHandle,c_last.toPoint());
}
}
if (selectMode == netSelect ){
QList<QGraphicsView*> views = scene->views();
if ( views.count() > 0 ){
QGraphicsView * view = views.first();
view->setDragMode(QGraphicsView::NoDrag);
}
}
if (m_dashRect )
{
scene->removeItem(m_dashRect);
delete m_dashRect;
m_dashRect = 0;
}
selectMode = none;
nDragHandle = None;
m_hoverSizer = false;
m_oppositePoint = QPointF();
scene->mouseEvent(event);
}
RotationTool::RotationTool()
:DrawTool(rotation)
{
m_lastAngle = 0;
m_dashRect = 0;
}
void RotationTool::mousePressEvent(QGraphicsSceneMouseEvent *event, GraphicsScene *scene)
{
DrawTool::mousePressEvent(event,scene);
QList<QGraphicsItem *> items = scene->selectedItems();
if ( items.count() == 1 ){
GraphicsItem * item = qgraphicsitem_cast<GraphicsItem*>(items.first());
if ( item != 0 ){
nDragHandle = (Direction)item->collidesTheSizeHandle(event->scenePos().toPoint());
if ( nDragHandle != None)
{
QPointF origin = item->mapToScene(item->boundingRect().center());
qreal len_y = c_last.y() - origin.y();
qreal len_x = c_last.x() - origin.x();
qreal angle = atan2(len_y,len_x)*180/PI;
m_lastAngle = angle;
selectMode = SelectMode::rotate;
if (m_dashRect ){
scene->removeItem(m_dashRect);
delete m_dashRect;
m_dashRect = 0;
}
m_dashRect = new QGraphicsPathItem(item->shape());
m_dashRect->setPen(Qt::DashLine);
m_dashRect->setPos(item->pos());
m_dashRect->setTransformOriginPoint(item->transformOriginPoint());
m_dashRect->setTransform(item->transform());
m_dashRect->setRotation(item->rotation());
scene->addItem(m_dashRect);
setCursor(scene,QCursor(QPixmap(":/image/rotation.png")));
}
else{
scene->clearSelection();
c_drawShape = selection;
}
}
}
}
void RotationTool::mouseMoveEvent(QGraphicsSceneMouseEvent *event, GraphicsScene *scene)
{
DrawTool::mouseMoveEvent(event,scene);
QList<QGraphicsItem *> items = scene->selectedItems();
if ( items.count() == 1 ){
GraphicsItem * item = qgraphicsitem_cast<GraphicsItem*>(items.first());
if ( item != 0 && nDragHandle != None && selectMode == SelectMode::rotate ){
QPointF origin = item->mapToScene(item->boundingRect().center());
qreal len_y = c_last.y() - origin.y();
qreal len_x = c_last.x() - origin.x();
qreal angle = atan2(len_y,len_x)*180/PI;
angle = item->rotation() + int(angle - m_lastAngle);
if ( angle > 360 )
angle -= 360;
if ( m_dashRect ){
m_dashRect->setRotation( angle );
scene->update();
}
setCursor(scene,QCursor(QPixmap(":/image/rotation.png")));
}
else if ( item )
{
int handle = item->collidesTheSizeHandle(event->scenePos().toPoint());
if ( handle != None)
setCursor(scene,QCursor(QPixmap(":/image/rotation.png")));
else
setCursor(scene,Qt::ArrowCursor);
}
}
}
void RotationTool::mouseReleaseEvent(QGraphicsSceneMouseEvent *event, GraphicsScene *scene)
{
DrawTool::mouseReleaseEvent(event,scene);
QList<QGraphicsItem *> items = scene->selectedItems();
if ( items.count() == 1 ){
GraphicsItem * item = qgraphicsitem_cast<GraphicsItem*>(items.first());
if (item != 0 && nDragHandle != None && selectMode == SelectMode::rotate)
{
QPointF origin = item->mapToScene(item->boundingRect().center());
//QPointF delta = c_last - origin ;
qreal len_y = c_last.y() - origin.y();
qreal len_x = c_last.x() - origin.x();
qreal angle = atan2(len_y,len_x)*180/PI;
angle = item->rotation() + int(angle - m_lastAngle);
if ( angle > 360 )
angle -= 360;
item->setRotation(angle);
}
}
setCursor(scene,Qt::ArrowCursor);
selectMode = none;
nDragHandle = None;
m_lastAngle = 0;
if (m_dashRect )
{
scene->removeItem(m_dashRect);
delete m_dashRect;
m_dashRect = 0;
}
}
RectTool::RectTool(DrawShape drawShape)
:DrawTool(drawShape)
{
}
void RectTool::mousePressEvent(QGraphicsSceneMouseEvent *event, GraphicsScene *scene)
{
if(event->button() == Qt::RightButton)
{
DrawTool::c_drawShape = selection;
return;
}
DrawTool::mousePressEvent(event,scene);
scene->clearSelection();
switch ( c_drawShape )
{
case rectangle:
{
m_item = new GraphicsRectItem(QRect(-1,-1,2,2),NULL);
m_item->setPen(Qt::SolidLine);
}
break;
case ellipse:
{
m_item = new GraphicsEllipseItem(QRect(-1,-1,2,2),NULL);
m_item->setPen(Qt::SolidLine);
}
break;
case roundrect:
{
m_item = new GraphicsRoundRectItem(QRect(-1,-1,2,2),NULL);
m_item->setPen(Qt::SolidLine);
}
break;
}
if ( m_item == 0) return;
//生成图元与网格对齐
scene->addItem(m_item);
QPoint point = event->scenePos().toPoint();
c_down += QPointF(1,1);
c_last += QPointF(1,1);
m_item->setPos(point);
m_item->setSelected(true);
selectMode = size;
nDragHandle = RightBottom;
}
void RectTool::mouseMoveEvent(QGraphicsSceneMouseEvent *event, GraphicsScene *scene)
{
setCursor(scene,Qt::CrossCursor);
selectTool.mouseMoveEvent(event,scene);
}
void RectTool::mouseReleaseEvent(QGraphicsSceneMouseEvent *event, GraphicsScene *scene)
{
if ( event->scenePos() == c_down - QPoint(1,1) )
{
if ( m_item != 0)
{
scene->removeItem(m_item);
}
selectTool.mousePressEvent(event,scene);
}
selectTool.mouseReleaseEvent(event,scene);
}
void RectTool::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event, GraphicsScene *scene)
{
scene->mouseEvent(event);
}
PolygonTool::PolygonTool(DrawShape shape) :DrawTool(shape)
{
m_item = nullptr;
}
void PolygonTool::mousePressEvent(QGraphicsSceneMouseEvent *event, GraphicsScene *scene)
{
DrawTool::mousePressEvent(event,scene);
if (event->button() != Qt::LeftButton)
return;
if(m_item == nullptr)
{
scene->clearSelection();
if( c_drawShape == polygon)
m_item = new GraphicsPolygonItem(nullptr);
else if( c_drawShape == polyline)
m_item = new GraphicsPolylineItem(nullptr);
else if ( c_drawShape == line)
m_item = new GraphicsLineItem(nullptr);
m_item->setPos(event->scenePos());
scene->addItem(m_item);
m_item->addPoint(c_down.toPoint());
m_item->setSelected(true);
}
m_item->addPoint(c_down.toPoint());
selectMode = size;
nDragHandle = m_item->getCurrentCount();
}
void PolygonTool::mouseMoveEvent(QGraphicsSceneMouseEvent *event, GraphicsScene *scene)
{
DrawTool::mouseMoveEvent(event,scene);
setCursor(scene,Qt::CrossCursor);
if(m_item != nullptr)
if(nDragHandle != None && selectMode == size)
{
m_item->resizeTo(nDragHandle,c_last.toPoint());
}
}
void PolygonTool::mouseReleaseEvent(QGraphicsSceneMouseEvent *event, GraphicsScene *scene)
{
DrawTool::mousePressEvent(event,scene);//重置c_down、c_last点
if(m_item == nullptr) return;
if( c_drawShape == line)
{
m_item->endPoint(event->scenePos().toPoint());
m_item->updateCoordinate();
m_item = nullptr;
selectMode = none;
c_drawShape = selection;
setCursor(scene,Qt::ArrowCursor);
}
}
void PolygonTool::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event, GraphicsScene *scene)
{
DrawTool::mouseDoubleClickEvent(event,scene);
//结束绘画
m_item->endPoint(event->scenePos().toPoint());
m_item->updateCoordinate();
m_item = nullptr;
selectMode = none;
c_drawShape = selection;
}
5.item
.h
#ifndef ITEM_H
#define ITEM_H
#include <QGraphicsObject>
#include <QGraphicsScene>
#include <QGraphicsSceneMouseEvent>
#include <QPainter>
#include <QPainterPath>
#include <QDebug>
#include "sizehandle.h"
class GraphicsItem :public QGraphicsObject//QAbstractGraphicsShapeItem,public QObject
{
public:
GraphicsItem(QGraphicsItem *parent = nullptr);
enum {Type = UserType+1};
int type() const override {return Type;}
int collidesTheSizeHandle(const QPoint & point) const;
virtual void resizeTo(int dir, const QPoint &point);
virtual QPoint origin() const {return QPoint(0,0);}//起点
virtual Qt::CursorShape getCursor(int dir);
virtual QRectF rect() const {return m_localRect;}
virtual QPoint opposite(int dir);
virtual void stretch(int handle , double sx , double sy , const QPoint & origin) = 0;
// handle位置
virtual QPoint pointHandle(Direction dir_1);
typedef QVector<QGraphicsRectItem*> Handles;
Handles m_handles;
int collidesWithHandle(const QPoint &point,bool isHandle) const;
//宽度或高度修改之后,及时更新外边框矩形等坐标信息
virtual void updateCoordinate();
//设置sizeHandle状态
void setState(SelectionHandleState st);
QPoint collidesHandlePos(int dir);
QBrush brush() const{return m_brush;}
QPen pen() const{return m_pen;}
void setBrush(const QBrush &brush){m_brush = brush;update();}
void setPen(const QPen &pen){m_pen = pen;update();}
protected:
//更新handles
virtual void updatehandles();
virtual void mousePressEvent(QGraphicsSceneMouseEvent *event) override;
void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override;
void mouseReleaseEvent(QGraphicsSceneMouseEvent *even) override;
virtual QVariant itemChange(GraphicsItemChange change, const QVariant &value) override;
int m_width;
int m_height;
//
QRectF m_localRect;
QBrush m_brush;
QPen m_pen;
};
//矩形
class GraphicsRectItem : public GraphicsItem
{
Q_OBJECT
public:
GraphicsRectItem(const QRect &rect,QGraphicsItem *parent = nullptr);
QRectF boundingRect() const;
QPainterPath shape() const;
//拖拽sizehandle改变图元大小
virtual void resizeTo(int dir, const QPoint &point);
virtual QRectF rect() const {return m_localRect;}
//
virtual void stretch(int handle,double sx,double sy,const QPoint &origin);
virtual void updateCoordinate();
protected:
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
QRectF m_initialRect;
};
//椭圆
class GraphicsEllipseItem :public GraphicsRectItem
{
public:
GraphicsEllipseItem(const QRect &rect,QGraphicsItem *parent = nullptr);
QPainterPath shape() const;
protected:
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
};
//圆角矩形
class GraphicsRoundRectItem :public GraphicsRectItem
{
public:
GraphicsRoundRectItem(const QRect &rect,QGraphicsItem *parent = nullptr);
QPainterPath shape() const;//返回虚线图形
protected:
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
private:
int m_xRadius;
int m_yRadius;
};
//多边形
class GraphicsPolygonItem :public GraphicsItem
{
public:
GraphicsPolygonItem(QGraphicsItem *parent = nullptr);
QRectF boundingRect() const;
QPainterPath shape() const;
virtual void addPoint(const QPoint & point);
virtual void endPoint(const QPoint & point);
virtual void resizeTo(int dir, const QPoint &point);
virtual QRectF rect() const {return m_localRect;}
void stretch(int handle,double sx,double sy,const QPoint &origin);
virtual Direction getCurrentCount(){return static_cast<Direction>(m_points.size()+ None);}
virtual void updateCoordinate();
void setFlip(QTransform transForm,Qt::Axis axis);
protected:
void paint(QPainter *painter,const QStyleOptionGraphicsItem *option,QWidget *widget);
void updatehandles();
QPolygon m_points;//点集
QPolygon m_initialPoints;
QRectF m_localRect;
};
//直线
class GraphicsLineItem :public GraphicsPolygonItem
{
public:
GraphicsLineItem(QGraphicsItem *parent = nullptr);
GraphicsLineItem(QPoint startPoint,QPoint endPoint,QGraphicsItem *parent = nullptr);
QPainterPath shape() const override;
virtual void addPoint(const QPoint &point) override;
virtual void endPoint(const QPoint &point) override;
virtual void updateCoordinate() override;
protected:
void updatehandles() override;
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
};
//折线
class GraphicsPolylineItem :public GraphicsPolygonItem
{
public:
GraphicsPolylineItem(QGraphicsItem *parent = nullptr);
QPainterPath shape() const override;
protected:
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
};
#endif // ITEM_H
.cpp
#include "item.h"
static QPainterPath qt_graphicsItem_shapeFromPath(const QPainterPath &path, const QPen &pen)
{
// We unfortunately need this hack as QPainterPathStroker will set a width of 1.0
// if we pass a value of 0.0 to QPainterPathStroker::setWidth()
const qreal penWidthZero = qreal(0.00000001);
if (path == QPainterPath() || pen == Qt::NoPen)
return path;
QPainterPathStroker ps;
ps.setCapStyle(pen.capStyle());
if (pen.widthF() <= 0.0)
ps.setWidth(penWidthZero);
else
ps.setWidth(pen.widthF());
ps.setJoinStyle(pen.joinStyle());
ps.setMiterLimit(pen.miterLimit());
QPainterPath p = ps.createStroke(path);
p.addPath(path);
return p;
}
GraphicsItem::GraphicsItem(QGraphicsItem *parent)
:QGraphicsObject(parent)
{
//添加sizihandle,设置可移动、可选择、改变大小信号功能
m_handles.reserve(None);
for (int i= LeftTop;i <= Left;++i)
{
SizeHandleRect *shr = new SizeHandleRect(this, static_cast<Direction>(i), false);
m_handles.push_back(shr);
}
setFlag(QGraphicsItem::ItemIsMovable, true);
setFlag(QGraphicsItem::ItemIsSelectable, true);
setFlag(QGraphicsItem::ItemSendsGeometryChanges, true);
//this->setAcceptHoverEvents(true);
}
void GraphicsItem::updatehandles()
{
const QRectF &geom = this->boundingRect();
const Handles::iterator hend = m_handles.end();
for (Handles::iterator it = m_handles.begin(); it != hend; ++it)
{
SizeHandleRect *hndl = static_cast<SizeHandleRect*>(*it);
switch (hndl->dir()) {
case LeftTop:
hndl->move(geom.x(),geom.y());
break;
case Top:
hndl->move(geom.x()+geom.width()/2,geom.y());
break;
case RightTop:
hndl->move(geom.x()+geom.width(),geom.y());
break;
case Right:
hndl->move(geom.x()+geom.width(),geom.y()+geom.height()/2);
break;
case RightBottom:
hndl->move(geom.x()+geom.width(),geom.y()+geom.height());
break;
case Bottom:
hndl->move(geom.x()+geom.width()/2,geom.y()+geom.height());
break;
case LeftBottom:
hndl->move(geom.x(),geom.y()+geom.height());
break;
case Left:
hndl->move(geom.x(),geom.y()+geom.height()/2);
break;
default:
break;
}
}
}
void GraphicsItem::updateCoordinate()
{
}
void GraphicsItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
QGraphicsItem::mousePressEvent(event);
if(event->button() == Qt::RightButton)
{
if(!isSelected())
{
scene()->clearSelection();
setSelected(true);
}
}
}
void GraphicsItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
QGraphicsItem::mouseMoveEvent(event);
}
void GraphicsItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *even)
{
QGraphicsItem::mouseReleaseEvent(even);
}
QVariant GraphicsItem::itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant &value)
{
if ( change == QGraphicsItem::ItemSelectedHasChanged )
{
//qDebug()<<" Item Selected : " << value.toString();
setState(value.toBool() ? SelectionHandleActive : SelectionHandleOff);
}
else if ( change == QGraphicsItem::ItemRotationHasChanged )
{
//qDebug()<<"Item Rotation Changed:" << value.toString();
}
else if ( change == QGraphicsItem::ItemTransformOriginPointHasChanged )
{
//qDebug()<<"ItemTransformOriginPointHasChanged:" << value.toPointF();
}
return value;
}
void GraphicsItem::setState(SelectionHandleState st)
{
const Handles::iterator hend = m_handles.end();
for (Handles::iterator it = m_handles.begin();it != hend;++it)
dynamic_cast<SizeHandleRect*>(*it)->setState(static_cast<SelectionHandleState>(st));
}
int GraphicsItem::collidesTheSizeHandle(const QPoint &point) const
{
const Handles::const_reverse_iterator hend = m_handles.rend();
for (Handles::const_reverse_iterator it = m_handles.rbegin(); it != hend; ++it)
{
if (dynamic_cast<SizeHandleRect*>(*it)->hitTest(point))
{
return dynamic_cast<SizeHandleRect*>(*it)->dir();
}
}
return None;
}
Qt::CursorShape GraphicsItem::getCursor(int dir)
{
switch (dir) {
case Right:
return Qt::SizeHorCursor;
case RightTop:
return Qt::SizeBDiagCursor;
case RightBottom:
return Qt::SizeFDiagCursor;
case LeftBottom:
return Qt::SizeBDiagCursor;
case Bottom:
return Qt::SizeVerCursor;
case LeftTop:
return Qt::SizeFDiagCursor;
case Left:
return Qt::SizeHorCursor;
case Top:
return Qt::SizeVerCursor;
default:
return Qt::SizeAllCursor;
}
return Qt::ArrowCursor;
}
QPoint GraphicsItem::opposite(int dir)
{
int diraction;
switch (dir) {
case 0:
diraction = 4;
break;
case 1:
diraction = 5;
break;
case 2:
diraction = 6;
break;
case 3:
diraction = 7;
break;
case 4:
diraction = 0;
break;
case 5:
diraction = 1;
break;
case 6:
diraction = 2;
break;
case 7:
diraction = 3;
break;
}
return pointHandle((Direction)diraction);
}
QPoint GraphicsItem::pointHandle(Direction dir_1)
{
Direction dir = static_cast<Direction>(dir_1);
return m_handles.at(dir)->pos().toPoint();
}
int GraphicsItem::collidesWithHandle(const QPoint &point, bool isHandle) const
{
const Handles::const_reverse_iterator hend = m_handles.rend();
for (Handles::const_reverse_iterator it = m_handles.rbegin();it != hend;++it)
{
QPoint pt = (*it)->mapFromScene(point).toPoint();
if(!isHandle)
{
//扩大handle范围更容易被选中
qreal width = (m_width/3)*2;
qreal height = (m_height/3)*2;
QPoint point_1 = (*it)->boundingRect().center().toPoint();
QRectF rect(point_1.x()-m_width/3,point_1.y()-m_height/3,width,height);
if (rect.contains(pt)&& contains(pt)) //
{
int a = dynamic_cast<SizeHandleRect*>(*it)->dir();
//只允许上下左右点连接,其余点不可连接
if(a%2)
{
return a;
}
}
}
else
{
if ((*it)->contains(pt)) // && contains(pt)
{
return dynamic_cast<SizeHandleRect*>(*it)->dir();
}
}
}
return None;
}
QPoint GraphicsItem::collidesHandlePos(int dir)
{
return mapToScene(m_handles.at(dir)->pos()).toPoint();
}
void GraphicsItem::resizeTo(int dir, const QPoint &point)
{
Q_UNUSED(dir);
Q_UNUSED(point);
}
//矩形
GraphicsRectItem::GraphicsRectItem(const QRect & rect , QGraphicsItem *parent)
:GraphicsItem(parent)
{
//初始化属性
m_width = rect.width();
m_height = rect.height();
m_initialRect = rect;
m_localRect = m_initialRect;
updatehandles();
}
void GraphicsRectItem::stretch(int handle , double sx, double sy, const QPoint &origin)
{
QTransform trans;
switch (handle) {
case Right:
case Left:
sy = 1;
break;
case Top:
case Bottom:
sx = 1;
break;
default:
break;
}
trans.translate(origin.x(),origin.y());
trans.scale(sx,sy);
trans.translate(-origin.x(),-origin.y());
prepareGeometryChange();
m_localRect = trans.mapRect(m_initialRect);
m_width = m_localRect.width();
m_height = m_localRect.height();
updatehandles();
}
void GraphicsRectItem::updateCoordinate()
{
GraphicsItem::updateCoordinate();
QPoint pt1,pt2,delta;
//在项坐标中返回转换的源点(用于旋转和放缩)
pt1 = mapToScene(transformOriginPoint()).toPoint();
pt2 = mapToScene(m_localRect.center()).toPoint();
delta = pt1 - pt2;
m_width < 2 ? m_width = 2 : m_width = m_width;
m_height < 2 ? m_height = 2 : m_height = m_height;
if (!parentItem())
{
prepareGeometryChange();//为了通过m_width和m_height来改变boundingRect
m_localRect = QRectF(-m_width/2,-m_height/2,m_width,m_height);
m_width = m_localRect.width();
m_height = m_localRect.height();
setTransform(transform().translate(delta.x(),delta.y()));
setTransformOriginPoint(m_localRect.center());
moveBy(-delta.x(),-delta.y());
setTransform(transform().translate(-delta.x(),-delta.y()));
updatehandles();
}
m_initialRect = m_localRect;
}
//定义一个矩形(包含左上点坐标以及宽度、高度)
QRectF GraphicsRectItem::boundingRect() const
{
return m_localRect;
}
QPainterPath GraphicsRectItem::shape() const
{
QPainterPath path;
path.addRect(rect());
return qt_graphicsItem_shapeFromPath(path,pen());
}
void GraphicsRectItem::resizeTo(int dir, const QPoint &point)
{
QPoint local = mapFromScene(point).toPoint();
QRect delta = this->rect().toRect();
switch (dir) {
case Right:
delta.setRight(local.x());
break;
case RightTop:
delta.setTopRight(local);
break;
case RightBottom:
delta.setBottomRight(local);
break;
case LeftBottom:
delta.setBottomLeft(local);
break;
case Bottom:
delta.setBottom(local.y());
break;
case LeftTop:
delta.setTopLeft(local);
break;
case Left:
delta.setLeft(local.x());
break;
case Top:
delta.setTop(local.y());
break;
default:
break;
}
prepareGeometryChange();
m_width = delta.width()>0?delta.width():-delta.width();
m_height = delta.height()>0?delta.height():-delta.height();
updatehandles();
}
void GraphicsRectItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
Q_UNUSED(option);
Q_UNUSED(widget);
painter->setPen(pen());
painter->setBrush(brush());
painter->drawRect(m_localRect);
updatehandles();
}
//椭圆
GraphicsEllipseItem::GraphicsEllipseItem(const QRect &rect, QGraphicsItem *parent)
:GraphicsRectItem(rect,parent)
{
}
QPainterPath GraphicsEllipseItem::shape() const
{
QPainterPath path;
path.addEllipse(boundingRect());
return qt_graphicsItem_shapeFromPath(path,pen());
}
void GraphicsEllipseItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
Q_UNUSED(option);
Q_UNUSED(widget);
QRectF rc = rect();
painter->setPen(pen());
painter->setBrush(brush());
painter->drawEllipse(rc);
updatehandles();
}
//圆角矩形
GraphicsRoundRectItem::GraphicsRoundRectItem(const QRect &rect, QGraphicsItem *parent)
:GraphicsRectItem(rect,parent)
{
m_xRadius = 15;
m_yRadius = 15;
}
QPainterPath GraphicsRoundRectItem::shape() const
{
QPainterPath path;
path.addRoundedRect(boundingRect(),m_xRadius,m_yRadius);
return qt_graphicsItem_shapeFromPath(path,pen());
}
void GraphicsRoundRectItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
Q_UNUSED(option);
Q_UNUSED(widget);
QRectF rc = boundingRect();
painter->setPen(pen());
painter->setBrush(brush());
painter->drawRoundedRect(rc,m_xRadius,m_yRadius);
updatehandles();
}
//多边形
GraphicsPolygonItem::GraphicsPolygonItem(QGraphicsItem *parent)
: GraphicsItem(parent)
{
m_points.clear();
setPen(QPen(Qt::black));
setBrush(QBrush(Qt::red));
}
QRectF GraphicsPolygonItem::boundingRect() const
{
return shape().controlPointRect();
}
QPainterPath GraphicsPolygonItem::shape() const
{
QPainterPath path;
path.addPolygon(m_points);
path.closeSubpath();
return qt_graphicsItem_shapeFromPath(path,pen());
}
void GraphicsPolygonItem::addPoint(const QPoint &point)
{
QPoint pt = QPoint(mapFromScene(point).toPoint().x() ,mapFromScene(point).toPoint().y());
m_points.append(pt);
Direction dir = static_cast<Direction>(m_points.size()+None) ;
SizeHandleRect *shr = new SizeHandleRect(this,dir,true);
shr->setState(SelectionHandleState::SelectionHandleActive);
m_handles.push_back(shr);
updatehandles();
}
void GraphicsPolygonItem::endPoint(const QPoint &point)
{
Q_UNUSED(point);
int nPoints = m_points.count();
if(nPoints>3)
{
QVector<QGraphicsRectItem *>::iterator it = m_handles.end() - 1;
delete *it;
m_handles.erase(it);
m_points.remove(nPoints - 1);
m_handles.resize(m_handles.size());
}
m_initialPoints = m_points;
}
void GraphicsPolygonItem::resizeTo(int dir, const QPoint &point)
{
QPoint pt = mapFromScene(point).toPoint();
if(dir <= None) return;
m_points[dir - None -1] = pt;
prepareGeometryChange();
m_localRect = m_points.boundingRect();
m_initialPoints = m_points;
updatehandles();
}
void GraphicsPolygonItem::updateCoordinate()
{
GraphicsItem::updateCoordinate();
QPoint pt1,pt2,delta;
QPolygonF pts = mapToScene(m_points);
if (parentItem()==NULL)
{
pt1 = mapToScene(transformOriginPoint()).toPoint();
pt2 = mapToScene(boundingRect().center()).toPoint();
delta = pt1 - pt2;
for (int i = 0; i < pts.count() ; ++i )
pts[i]+=delta;
prepareGeometryChange();
m_points = mapFromScene(pts).toPolygon();
m_localRect = m_points.boundingRect();
setTransform(transform().translate(delta.x(),delta.y()));
moveBy(-delta.x(),-delta.y());
setTransform(transform().translate(-delta.x(),-delta.y()));
updatehandles();
}
m_initialPoints = m_points;
}
void GraphicsPolygonItem::stretch(int handle, double sx, double sy, const QPoint &origin)
{
QTransform trans;
switch (handle) {
case Top:
case Bottom:
sx = 1;
break;
case Left:
case Right:
sy = 1;
break;
default:
break;
}
//沿着x轴移动坐标系dx,沿着y轴移动坐标系dy,并返回一个矩阵的引用。
trans.translate(origin.x(),origin.y());
trans.scale(sx,sy);
trans.translate(-origin.x(),-origin.y());
prepareGeometryChange();
m_points = trans.map(m_initialPoints);
m_localRect = m_points.boundingRect();
updatehandles();
}
void GraphicsPolygonItem::setFlip(QTransform transForm, Qt::Axis axis)
{
Q_UNUSED(axis);
m_points = transForm.map(m_points);
updatehandles();
updateCoordinate();
}
void GraphicsPolygonItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
Q_UNUSED(option);
Q_UNUSED(widget);
painter->setBrush(brush());
painter->setPen(pen());
painter->drawPolygon(m_points);
}
void GraphicsPolygonItem::updatehandles()
{
GraphicsItem::updatehandles();
for(int i = 0;i<m_points.size();i++)
{
dynamic_cast<SizeHandleRect*>( m_handles[Center + i] )->move(m_points[i].x(),m_points[i].y());
}
}
//直线
GraphicsLineItem::GraphicsLineItem(QGraphicsItem *parent)
:GraphicsPolygonItem(parent)
{
m_handles.clear();
}
GraphicsLineItem::GraphicsLineItem(QPoint startPoint, QPoint endPoint, QGraphicsItem *parent)
:GraphicsPolygonItem(parent)
{
m_handles.clear();
addPoint(mapToScene(startPoint).toPoint());
addPoint(mapToScene(endPoint).toPoint());
}
QPainterPath GraphicsLineItem::shape() const
{
QPainterPath path;
path.addPolygon(m_points);
path.closeSubpath();
return qt_graphicsItem_shapeFromPath(path,pen());
}
void GraphicsLineItem::addPoint(const QPoint &point)
{
m_points.push_back(mapFromScene(point).toPoint());
Direction dir = static_cast<Direction>(m_points.size()+None);
SizeHandleRect *rect = new SizeHandleRect(this,dir,true);
rect->setState(SelectionHandleActive);
m_handles.push_back(rect);
}
void GraphicsLineItem::endPoint(const QPoint &point)
{
Q_UNUSED(point);
m_initialPoints = m_points;
updatehandles();
}
void GraphicsLineItem::updateCoordinate()
{
GraphicsItem::updateCoordinate();
QPoint pt1,pt2,delta;
QPolygon pts = mapToScene(m_points).toPolygon();
if (parentItem() == nullptr)
{
pt1 = mapToScene(transformOriginPoint()).toPoint();
pt2 = mapToScene(boundingRect().center()).toPoint();
delta = pt1 - pt2;
for(int i=0;i<pts.count();++i)
pts[i] += delta;
prepareGeometryChange();
m_points = mapFromScene(pts).toPolygon();
m_localRect = m_points.boundingRect();
setTransform(transform().translate(delta.x(),delta.y()));
moveBy(-delta.x(),-delta.y());
setTransform(transform().translate(-delta.x(),-delta.y()));
updatehandles();
}
m_initialPoints = m_points;
}
void GraphicsLineItem::updatehandles()
{
for(int i = 0;i<m_handles.size();i++)
{
m_handles.at(i)->setPos(m_points[i]);
}
}
void GraphicsLineItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
Q_UNUSED(option);
Q_UNUSED(widget);
painter->setPen(pen());
painter->drawLine(m_points[0],m_points[1]);
}
//折线
GraphicsPolylineItem::GraphicsPolylineItem(QGraphicsItem *parent)
: GraphicsPolygonItem(parent)
{
}
QPainterPath GraphicsPolylineItem::shape() const
{
QPainterPath path;
path.addPolygon(m_points);
return qt_graphicsItem_shapeFromPath(path,pen());
}
void GraphicsPolylineItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
Q_UNUSED(option);
Q_UNUSED(widget);
painter->setPen(pen());
painter->drawPolyline(m_points);
}
6.sizehandle
.h
#ifndef SIZEHANDLE
#define SIZEHANDLE
#include <QGraphicsRectItem>
#include <QList>
QT_BEGIN_NAMESPACE
class QFocusEvent;
class QGraphicsItem;
class QGraphicsScene;
class QGraphicsSceneMouseEvent;
QT_END_NAMESPACE
enum { SELECTION_HANDLE_SIZE = 6, SELECTION_MARGIN = 10 };
enum SelectionHandleState
{
SelectionHandleOff,
SelectionHandleInactive,
SelectionHandleActive
};
enum Direction
{
LeftTop = 0,
Top,
RightTop,
Right,
RightBottom,
Bottom,
LeftBottom,
Left,
Center,
contral,
None,
Start,
End,
};
class SizeHandleRect :public QGraphicsRectItem
{
public:
SizeHandleRect(QGraphicsItem *parent , Direction d, bool contralPoint);
Direction dir() const { return m_dir; }
void updateCursor();
void setState(SelectionHandleState st);
bool hitTest( const QPointF & point );
void move(qreal x, qreal y );
protected:
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
private:
const Direction m_dir;
bool m_controlPoint;
SelectionHandleState m_state;
//根据Direction初始化sizehandle
void initSizeHandleRect();
};
#endif // SIZEHANDLE
.cpp
#include "sizehandle.h"
#include <QGraphicsScene>
#include <QGraphicsSceneContextMenuEvent>
#include <QMenu>
#include <QPainter>
#include <qdebug.h>
#include <qrect.h>
SizeHandleRect::SizeHandleRect(QGraphicsItem* parent , Direction d, bool controlPoint)
:QGraphicsRectItem(),m_dir(d),m_controlPoint(controlPoint)
{
m_state = SelectionHandleOff;
initSizeHandleRect();
setParentItem(parent);
setTransformOriginPoint(boundingRect().center());
hide();
}
void SizeHandleRect::updateCursor()
{
switch (m_dir) {
case Right:
setCursor(Qt::SizeHorCursor);
return;
case RightTop:
setCursor(Qt::SizeBDiagCursor);
return;
case RightBottom:
setCursor(Qt::SizeFDiagCursor);
return;
case LeftBottom:
setCursor(Qt::SizeBDiagCursor);
return;
case Bottom:
setCursor(Qt::SizeVerCursor);
return;
case LeftTop:
setCursor(Qt::SizeFDiagCursor);
return;
case Left:
setCursor(Qt::SizeHorCursor);
return;
case Top:
setCursor(Qt::SizeVerCursor);
return;
default:
break;
}
setCursor(Qt::ArrowCursor);
}
void SizeHandleRect::initSizeHandleRect()
{
switch (m_dir) {
case Right:
setRect(0,-SELECTION_HANDLE_SIZE/2,SELECTION_HANDLE_SIZE,SELECTION_HANDLE_SIZE);
break;
case RightTop:
setRect(0,-SELECTION_HANDLE_SIZE,SELECTION_HANDLE_SIZE,SELECTION_HANDLE_SIZE);
break;
case RightBottom:
setRect(0,0,SELECTION_HANDLE_SIZE,SELECTION_HANDLE_SIZE);
break;
case LeftBottom:
setRect(-SELECTION_HANDLE_SIZE,0,SELECTION_HANDLE_SIZE,SELECTION_HANDLE_SIZE);
break;
case Bottom:
setRect(-SELECTION_HANDLE_SIZE/2,0,SELECTION_HANDLE_SIZE,SELECTION_HANDLE_SIZE);
break;
case LeftTop:
setRect(-SELECTION_HANDLE_SIZE,-SELECTION_HANDLE_SIZE,SELECTION_HANDLE_SIZE,SELECTION_HANDLE_SIZE);
break;
case Left:
setRect(-SELECTION_HANDLE_SIZE,-SELECTION_HANDLE_SIZE/2,SELECTION_HANDLE_SIZE,SELECTION_HANDLE_SIZE);
break;
case Top:
setRect(-SELECTION_HANDLE_SIZE/2,-SELECTION_HANDLE_SIZE,SELECTION_HANDLE_SIZE,SELECTION_HANDLE_SIZE);
break;
default:
setRect(-SELECTION_HANDLE_SIZE/2,-SELECTION_HANDLE_SIZE/2,SELECTION_HANDLE_SIZE,SELECTION_HANDLE_SIZE);
break;
}
}
void SizeHandleRect::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
painter->setPen(Qt::SolidLine);
painter->setBrush(QBrush(QColor("white")));
painter->setRenderHints(QPainter::Antialiasing,false);
if ( m_dir == Center )//中心点
{
painter->setBrush(QBrush(Qt::blue));
painter->drawEllipse(rect());
}
else if(m_controlPoint)
{
painter->setPen(QPen(Qt::red,Qt::SolidLine));
painter->setBrush(Qt::green);
painter->drawEllipse(rect().center(),3,3);
}
else
painter->drawRect(rect());
}
void SizeHandleRect::setState(SelectionHandleState st)
{
if (st == m_state)
return;
switch (st) {
case SelectionHandleOff:
hide();
break;
case SelectionHandleInactive:
case SelectionHandleActive:
show();
break;
}
m_state = st;
}
bool SizeHandleRect::hitTest(const QPointF &point)
{
QPointF pt = mapFromScene(point);
return rect().contains(pt);
}
void SizeHandleRect::move(qreal x, qreal y)
{
setPos(x,y);
}
四、运行结果
资源见https://download.csdn.net/download/m0_67254672/90960244