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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值