一、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 0x8000
void 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:
//更新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();
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);
else
ps.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来改变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::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比较两个浮点型的大小,如果相等,返回true
return;
painter->setPen(pen());
painter->drawLine(line);
painter->setPen(QPen(pen().color(), pen().width()));//, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin
qreal 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