QGraphicsView、QGraphicsScene和QGraphicsItem图形视图框架(八)QGraphicsProxyWidget的使用
一、QGraphicsProxyWidget介绍
1.核心定义
Qt图形视图框架中的代理类,用于将QWidget及其子类嵌入QGraphicsScene中,使其成为场景中的图形项(QGraphicsItem)。
继承自QGraphicsWidget,在控件(Widget)与图形项(Item)间建立桥梁。
2.核心作用
坐标转换:自动处理QWidget的整数坐标与QGraphicsItem浮点坐标的映射关系。
事件转发:转发鼠标、键盘、拖放等事件,确保嵌入控件的交互功能正常。
弹窗支持:自动为子控件(如QComboBox的下拉菜单)创建嵌套代理,确保弹出内容正确显示。
3.使用
1)创建控件并添加到场景
QGraphicsScene的addWidget方法直接在场景中添加控件,会返回一个QGraphicsProxyWidget
示例:
// 创建控件并添加到场景
QPushButton *button = new QPushButton("按钮");
QGraphicsScene *scene = new QGraphicsScene;
QGraphicsProxyWidget *proxy = scene->addWidget(button); // 自动创建代理:ml-citation{ref="2,12" data="citationList"}
proxy->setPos(100, 50); // 设置场景位置
2)先创建代理,后绑定控件
示例:
// 先创建代理,后绑定控件
QGraphicsProxyWidget *proxy = new QGraphicsProxyWidget;
QLineEdit *line = new QLineEdit();
proxy->setWidget(line);
scene->addItem(proxy);
二、主要代码示例
item
.h
#ifndef ITEM_H
#define ITEM_H#include <QGraphicsObject>
#include <QGraphicsScene>
#include <QGraphicsSceneMouseEvent>
#include <QPainter>
#include <QPainterPath>
#include <QtMath>
#include <QDebug>
#include <QGraphicsProxyWidget>
#include <QLineEdit>
#include "sizehandle.h"#define PROPERTY_BACKGROUNDCOLOR 0x01
#define PROPERTY_POSITIONX 0x02
#define PROPERTY_POSITIONY 0x4
#define PROPERTY_WIDTH 0x08
#define PROPERTY_HEIGHT 0x10
#define PROPERTY_LINECOLOR 0x20
#define PROPERTY_LINEWIDTH 0x40
#define PROPERTY_LINETYPE 0x80
#define PROPERTY_RADIUSX 0x100
#define PROPERTY_RADIUSY 0x200
#define PROPERTY_ROTATION 0x400
#define PROPERTY_LINEEND 0x800
#define PROPERTY_TEXT 0x1000
#define PROPERTY_TEXTCOLOR 0x2000
#define PROPERTY_TEXTFONT 0x4000
#define PROPERTY_TEXTALIGN 0x8000void getPropertyName(QStringList &list,long long propertyValue);//线段类型
enum LINE_END
{TYPR_NO_END,//无TYPE_HOLLOW_END,//TYPE_SOLID_LEFT_END,TYPE_SOLID_RIGHT_END,TYPE_LINE_END
};//
struct ItemProperty
{int x;int y;int width;int height;QBrush backgroundColor;QBrush lineBrush;qreal lineWidth;int lineType;//线型QString lineEnd;//线端(直线)int rotation;//旋转角int xRadius;//x半径(圆角矩形)int yRadius;//y半径(圆角矩形)QString text;//文本QColor textColor;//文本颜色QFont textFont;//文本字体QString textAlign;//文本对齐方式};class GraphicsItem :public QGraphicsObject//QAbstractGraphicsShapeItem,public QObject
{Q_OBJECT
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();}virtual void setFlip(QTransform transForm,Qt::Axis axis);qreal width() const {return m_width;}qreal height() const {return m_height;}//属性long long m_property;virtual void setProperty(ItemProperty property);virtual void getProperty(ItemProperty &property);signals:void sigToChangeProperty();protected://更新handlesvirtual 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();virtual void setProperty(ItemProperty property);virtual void getProperty(ItemProperty &property);
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;//返回虚线图形void setProperty(ItemProperty property);void getProperty(ItemProperty &property);
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);void updatehandles();protected:void paint(QPainter *painter,const QStyleOptionGraphicsItem *option,QWidget *widget);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;virtual void addPoint(const QPoint &point);virtual void endPoint(const QPoint &point);virtual void updateCoordinate();void updatehandles();void setProperty(ItemProperty property);void getProperty(ItemProperty &property);void painterLineEnd(QPainter *painter,QPoint LeftPoint,QPoint RightPoint);protected:void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);private:int m_nleftIndex;int m_nrightIndex;
};//折线
class GraphicsPolylineItem :public GraphicsPolygonItem
{
public:GraphicsPolylineItem(QGraphicsItem *parent = nullptr);QPainterPath shape() const override;protected:void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
};//贝塞尔曲线
class GraphicsBezierItem :public GraphicsPolygonItem
{
public:GraphicsBezierItem(QGraphicsItem *parent = nullptr);QPainterPath shape() const override;protected:void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
};//文本
class GraphicsTextEditItem :public GraphicsRectItem
{
public:GraphicsTextEditItem(const QRect &rect,QGraphicsItem *parent = nullptr);void updateCoordinate() ;virtual void stretch(int handle , double sx , double sy , const QPoint & origin) ;void setProperty(ItemProperty property) ;void getProperty(ItemProperty &property) ;protected:bool eventFilter(QObject *object, QEvent *event) ;void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) ;private:QGraphicsProxyWidget *proxyTextEdit;QLineEdit *textEdit;QPalette palette;QFont textFont;Qt::Alignment textAlign;
};#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);elseps.setWidth(pen.widthF());ps.setJoinStyle(pen.joinStyle());ps.setMiterLimit(pen.miterLimit());QPainterPath p = ps.createStroke(path);p.addPath(path);return p;
}void getPropertyName(QStringList &list, long long propertyValue)
{if(propertyValue & PROPERTY_BACKGROUNDCOLOR){list << "背景颜色";}if(propertyValue & PROPERTY_POSITIONX){list << "位置X";}if(propertyValue & PROPERTY_POSITIONY){list << "位置Y";}if(propertyValue & PROPERTY_WIDTH){list << "长度";}if(propertyValue & PROPERTY_HEIGHT){list << "宽度";}if(propertyValue & PROPERTY_LINECOLOR){list << "线条颜色";}if(propertyValue & PROPERTY_LINEWIDTH){list << "线宽";}if(propertyValue & PROPERTY_LINETYPE){list << "线型";}if(propertyValue & PROPERTY_RADIUSX){list << "角半径X";}if(propertyValue & PROPERTY_RADIUSY){list << "角半径Y";}if(propertyValue & PROPERTY_ROTATION){list << "旋转角度";}if(propertyValue & PROPERTY_LINEEND){list << "线端";}if(propertyValue & PROPERTY_TEXT){list << "文本";}if(propertyValue & PROPERTY_TEXTCOLOR){list << "文本颜色";}if(propertyValue & PROPERTY_TEXTFONT){list << "文本字体";}if(propertyValue & PROPERTY_TEXTALIGN){list << "文本对齐方式";}
}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);setPen(QPen(QBrush(QColor("black")),2));setBrush(QBrush(QColor(Qt::red)));//this->setAcceptHoverEvents(true);m_property = PROPERTY_BACKGROUNDCOLOR | PROPERTY_POSITIONX | PROPERTY_POSITIONY| PROPERTY_LINECOLOR | PROPERTY_LINEWIDTH | PROPERTY_LINETYPE | PROPERTY_ROTATION;
}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);
}void GraphicsItem::setFlip(QTransform transForm, Qt::Axis axis)
{Q_UNUSED(axis);setTransform(transForm);
}void GraphicsItem::setProperty(ItemProperty property)
{QColor linecolor = property.lineBrush.color();//linecolor.setAlpha(property.Linealpha);QPen pen(QBrush(linecolor),property.lineWidth);switch(property.lineType){case 0:pen.setStyle(Qt::SolidLine);break;case 1:pen.setStyle(Qt::DashLine);break;case 2:pen.setStyle(Qt::DotLine);break;case 3:pen.setStyle(Qt::DashDotLine);break;case 4:pen.setStyle(Qt::DashDotDotLine);break;}setPen(pen);QColor color = property.backgroundColor.color();setBrush(QBrush(color));setPos(property.x,property.y);setRotation(property.rotation);
}void GraphicsItem::getProperty(ItemProperty &property)
{property.lineWidth = pen().width();property.lineBrush = pen().brush();//property.Linealpha = pen().brush().color().alpha();property.x = pos().x();property.y = pos().y();property.backgroundColor = brush();//property.alpha = brush().color().alpha();property.lineType = pen().style()-1;property.rotation = rotation();
}//矩形
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();m_property = m_property | PROPERTY_WIDTH | PROPERTY_HEIGHT;
}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来改变boundingRectm_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::setProperty(ItemProperty property)
{GraphicsItem::setProperty(property);m_width = property.width;m_height = property.height;//setRotation(m_angle);updateCoordinate();
}void GraphicsRectItem::getProperty(ItemProperty &property)
{GraphicsItem::getProperty(property);property.width = m_width;property.height = m_height;
}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;m_property = m_property | PROPERTY_RADIUSX | PROPERTY_RADIUSY;
}QPainterPath GraphicsRoundRectItem::shape() const
{QPainterPath path;path.addRoundedRect(boundingRect(),m_xRadius,m_yRadius);return qt_graphicsItem_shapeFromPath(path,pen());
}void GraphicsRoundRectItem::setProperty(ItemProperty property)
{GraphicsRectItem::setProperty(property);m_xRadius = property.xRadius;m_yRadius = property.yRadius;
}void GraphicsRoundRectItem::getProperty(ItemProperty &property)
{GraphicsRectItem::getProperty(property);property.xRadius = m_xRadius;property.yRadius = m_yRadius;
}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::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());}
}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);
}//直线
GraphicsLineItem::GraphicsLineItem(QGraphicsItem *parent):GraphicsPolygonItem(parent)
{m_handles.clear();m_property = m_property &~ PROPERTY_BACKGROUNDCOLOR;m_property = m_property | PROPERTY_LINEEND;
}GraphicsLineItem::GraphicsLineItem(QPoint startPoint, QPoint endPoint, QGraphicsItem *parent):GraphicsPolygonItem(parent)
{m_handles.clear();addPoint(mapToScene(startPoint).toPoint());addPoint(mapToScene(endPoint).toPoint());m_property = m_property &~ PROPERTY_BACKGROUNDCOLOR;m_property = m_property | PROPERTY_LINEEND;
}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::setProperty(ItemProperty property)
{GraphicsItem::setProperty(property);m_nleftIndex = property.lineEnd.section(',',0,0).toUInt();m_nrightIndex = property.lineEnd.section(',',1,1).toUInt();
}void GraphicsLineItem::getProperty(ItemProperty &property)
{GraphicsItem::getProperty(property);property.lineEnd = QString("%1,%2").arg(m_nleftIndex).arg(m_nrightIndex);
}void GraphicsLineItem::painterLineEnd(QPainter *painter, QPoint LeftPoint, QPoint RightPoint)
{static const double Pi = 3.14159265358979323846264338327950288419717;static double TwoPi = 2.0 * Pi;QLineF line(LeftPoint,RightPoint);if (qFuzzyCompare(line.length(), qreal(0.)))//qFuzzyCompare比较两个浮点型的大小,如果相等,返回truereturn;painter->setPen(pen());painter->drawLine(line);painter->setPen(QPen(pen().color(), pen().width()));//, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoinqreal arrowSize = 10;double angle = ::acos(line.dx() / line.length());if (line.dy() >= 0)angle = TwoPi - angle;QPoint leftPoint1 = LeftPoint - QPoint(sin(angle - Pi / 3) * arrowSize,cos(angle - Pi / 3) * arrowSize);QPoint leftPoint2 = LeftPoint - QPoint(sin(angle - Pi + Pi / 3) * arrowSize,cos(angle - Pi + Pi / 3) * arrowSize);QPoint rightPoint1 = RightPoint + QPoint(sin(angle - Pi / 3) * arrowSize,cos(angle - Pi / 3) * arrowSize);QPoint rightPoint2 = RightPoint + QPoint(sin(angle - Pi + Pi / 3) * arrowSize,cos(angle - Pi + Pi / 3) * arrowSize);if(m_nleftIndex == TYPE_HOLLOW_END){painter->drawLine(QLineF(leftPoint1,LeftPoint));painter->drawLine(QLineF(leftPoint2,LeftPoint));}else if(m_nleftIndex == TYPE_SOLID_LEFT_END){QPolygonF points;points.append(leftPoint1);points.append(leftPoint2);points.append(LeftPoint);painter->setBrush(Qt::black);painter->drawPolygon(points);}else if(m_nleftIndex == TYPE_SOLID_RIGHT_END){QPoint centralPoint = (leftPoint1 + leftPoint2)/2;QPoint destArrowP1 = centralPoint + QPoint(sin(angle - Pi / 3) * arrowSize,cos(angle - Pi / 3) * arrowSize);QPoint destArrowP2 = centralPoint + QPoint(sin(angle - Pi + Pi / 3) * arrowSize,cos(angle - Pi + Pi / 3) * arrowSize);QPolygonF points;points.append(destArrowP1);points.append(destArrowP2);points.append(centralPoint);painter->setBrush(Qt::black);painter->drawPolygon(points);}else if(m_nleftIndex == TYPE_LINE_END){QPoint centralPoint = (leftPoint1 + leftPoint2)/2;QPoint destArrowP1 = centralPoint + QPoint(sin(angle - Pi / 3) * arrowSize,cos(angle - Pi / 3) * arrowSize);QPoint destArrowP2 = centralPoint + QPoint(sin(angle - Pi + Pi / 3) * arrowSize,cos(angle - Pi + Pi / 3) * arrowSize);painter->drawLine(destArrowP1,destArrowP2);}if(m_nrightIndex == TYPE_HOLLOW_END){painter->drawLine(QLineF(rightPoint1,RightPoint));painter->drawLine(QLineF(rightPoint2,RightPoint));}else if(m_nrightIndex == TYPE_SOLID_LEFT_END){QPolygonF points;points.append(rightPoint1);points.append(rightPoint2);points.append(RightPoint);painter->setBrush(Qt::black);painter->drawPolygon(points);}else if(m_nrightIndex == TYPE_SOLID_RIGHT_END){QPoint centralPoint = (rightPoint1 + rightPoint2)/2;QPoint destArrowP5 = centralPoint - QPoint(sin(angle - Pi / 3) * arrowSize,cos(angle - Pi / 3) * arrowSize);QPoint destArrowP6 = centralPoint - QPoint(sin(angle - Pi + Pi / 3) * arrowSize,cos(angle - Pi + Pi / 3) * arrowSize);QPolygonF points;points.append(destArrowP5);points.append(destArrowP6);points.append(centralPoint);painter->setBrush(Qt::black);painter->drawPolygon(points);}else if(m_nrightIndex == TYPE_LINE_END){QPoint centralPoint = (rightPoint1 + rightPoint2)/2;QPoint destArrowP5 = centralPoint - QPoint(sin(angle - Pi / 3) * arrowSize,cos(angle - Pi / 3) * arrowSize);QPoint destArrowP6 = centralPoint - QPoint(sin(angle - Pi + Pi / 3) * arrowSize,cos(angle - Pi + Pi / 3) * arrowSize);painter->drawLine(destArrowP5,destArrowP6);}
}void GraphicsLineItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{Q_UNUSED(option);Q_UNUSED(widget);if(m_points.size() > 1)painterLineEnd(painter,m_points[0],m_points[1]);
}//折线
GraphicsPolylineItem::GraphicsPolylineItem(QGraphicsItem *parent): GraphicsPolygonItem(parent)
{m_property = m_property &~ PROPERTY_BACKGROUNDCOLOR;
}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);
}//贝塞尔曲线
GraphicsBezierItem::GraphicsBezierItem(QGraphicsItem *parent): GraphicsPolygonItem(parent)
{m_property = m_property &~ PROPERTY_BACKGROUNDCOLOR;
}QPainterPath GraphicsBezierItem::shape() const
{QPainterPath path;path.addPolygon(m_points);return qt_graphicsItem_shapeFromPath(path,pen());
}void GraphicsBezierItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{Q_UNUSED(option);Q_UNUSED(widget);QPainterPath path;path.moveTo(m_points.at(0));int i = 1;while(i+2 < m_points.size()){path.cubicTo(m_points[i],m_points[i+1],m_points[i+2]);i += 3;}while(i<m_points.size()){path.lineTo(m_points[i]);i += 1;}painter->drawPath(path);
}GraphicsTextEditItem::GraphicsTextEditItem(const QRect &rect, QGraphicsItem *parent):GraphicsRectItem(rect,parent)
{textFont.setFamily("Arial");textFont.setPointSize(10);proxyTextEdit = new QGraphicsProxyWidget(this);proxyTextEdit->setParent(this);textEdit = new QLineEdit();textEdit->resize(m_width,m_height);textEdit->setFont(textFont);textEdit->setMinimumSize(2,2);textEdit->setReadOnly(true);textEdit->setFrame(false);textEdit->setContextMenuPolicy(Qt::NoContextMenu);textEdit->installEventFilter(proxyTextEdit);proxyTextEdit->setWidget(textEdit);proxyTextEdit->setPos(m_localRect.x(),m_localRect.y());proxyTextEdit->installEventFilter(this);connect(textEdit,&QLineEdit::editingFinished,this,&GraphicsTextEditItem::sigToChangeProperty);m_property = m_property | PROPERTY_TEXT | PROPERTY_TEXTCOLOR | PROPERTY_TEXTFONT | PROPERTY_TEXTALIGN;textAlign = Qt::AlignLeft;
}void GraphicsTextEditItem::updateCoordinate()
{GraphicsItem::updateCoordinate();QPoint pt1,pt2,delta;//在项坐标中返回转换的源点(用于旋转和放缩)pt1 = mapToScene(transformOriginPoint()).toPoint();pt2 = mapToScene(m_localRect.center()).toPoint();delta = pt1 - pt2;if (!parentItem()){prepareGeometryChange();m_localRect = QRectF(-m_width/2,-m_height/2,m_width,m_height);textEdit->resize(m_localRect.width(),m_localRect.height());proxyTextEdit->setPos(m_localRect.x(),m_localRect.y());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;
}void GraphicsTextEditItem::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();textEdit->resize(m_width,m_height);proxyTextEdit->setPos(m_localRect.x(),m_localRect.y());updatehandles();
}void GraphicsTextEditItem::setProperty(ItemProperty property)
{GraphicsRectItem::setProperty(property);QColor color = property.backgroundColor.color();//color.setAlpha(property.alpha);palette.setBrush(QPalette::Base,color);proxyTextEdit->setPalette(QPalette(QColor(),color));palette.setColor(QPalette::Text,property.textColor);textEdit->setPalette(palette);textEdit->setFont(property.textFont);textEdit->setText(property.text);if(property.textAlign == "左对齐")textAlign = Qt::AlignLeft;else if (property.textAlign == "右对齐")textAlign = Qt::AlignRight;else if (property.textAlign == "居中")textAlign = Qt::AlignHCenter;else if (property.textAlign == "自适应")textAlign = Qt::AlignJustify;textEdit->setAlignment(textAlign);textEdit->update();
}void GraphicsTextEditItem::getProperty(ItemProperty &property)
{GraphicsRectItem::getProperty(property);property.backgroundColor = palette.brush(QPalette::Base);property.text = textEdit->text();//property.alpha = palette.brush(QPalette::Base).color().alpha();property.textFont = textEdit->font();property.textColor = palette.color(QPalette::Text);if(textAlign == Qt::AlignLeft)property.textAlign = "左对齐";else if (textAlign == Qt::AlignRight)property.textAlign = "右对齐";else if (textAlign == Qt::AlignHCenter)property.textAlign = "居中";else if (textAlign == Qt::AlignJustify)property.textAlign = "自适应";
}bool GraphicsTextEditItem::eventFilter(QObject *object, QEvent *event)
{if(object == proxyTextEdit){if(event->type() == QEvent::GraphicsSceneMousePress){if(!isSelected() && textEdit->isReadOnly()){//qDebug() << "选中文本图元";QGraphicsScene *scene = this->scene();QMouseEvent *sceneEvent = static_cast<QMouseEvent*>(event);if(sceneEvent->modifiers() != Qt::ControlModifier)scene->clearSelection();setSelected(true);return true;}}else if(event->type() == QEvent::KeyPress){QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);if(keyEvent->key() == Qt::Key_Return || keyEvent->key() == Qt::Key_Enter){//qDebug() << "回车键确认编辑";setSelected(true);textEdit->setReadOnly(true);this->setFocus();//return true; // 拦截事件继续传递 拦截之后回车编辑器不会触发结束编辑的信号}}else if(event->type() == QEvent::GraphicsSceneMouseDoubleClick){//qDebug() << "文本 事件过滤 鼠标双击事件";setSelected(false);if(textEdit->isReadOnly()){textEdit->setReadOnly(false);//QGraphicsProxyWidget 默认未启用 QGraphicsItem::ItemAcceptsInputMethod 标志,导致输入法事件无法正确传递给 QLineEdit。//首次编辑可能因焦点切换临时激活,但二次编辑时标志状态失效。 所以每次都需要设置proxyTextEdit->setFlags(QGraphicsItem::ItemIsFocusable | QGraphicsItem::ItemUsesExtendedStyleOption| QGraphicsItem::ItemSendsGeometryChanges | QGraphicsItem::ItemAcceptsInputMethod);textEdit->setFocus();//触发结束Q_EMIT sigToChangeProperty();}}else if( event->type() == QEvent::FocusOut){//qDebug() << "文本 事件过滤 失去键盘事件";setSelected(true);textEdit->setReadOnly(true);}}return GraphicsRectItem::eventFilter(object,event);
}void GraphicsTextEditItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{Q_UNUSED(option);Q_UNUSED(widget);painter->setPen(pen());painter->setBrush(brush());painter->drawRect(m_localRect);
}
三、运行结果
资源见https://download.csdn.net/download/m0_67254672/90960244