qt QGraphicsView绘图进阶重写QGraphicsPolygonItem或者QGraphicsPathItem类实现多线段功能,并能够显示、修改多线段节点

一、具体功能描述

重写QGraphicsPolygonItem/类或者QGraphicsPathItem类实现绘制多线段功能,并能够对节点进行移动,删除,同时重写 QGraphicsScene与QGraphicsView已实现对场景的指定位置缩放大小,上下左右键移动显示内容等;
搭配风速的自记纸实例,能完整的描绘出迹线轨迹,同时移动轨迹节点。
完整项目代码文件请查看下载资源,具体操作示例如下图示:
在这里插入图片描述

二、具体代码实现

01 重写QGraphicsRectItem类生成多线段节点SizeHandleRect类

多线段的节点,有画线的节点和外接矩形的节点,在添加节点是根据不同类型生成不同的节点,同时声明各种枚举作为矩形常量与状态,由于需要移动节点类型需要对QGraphicsRectItem类的按住移动事件进行重写;
同时作为多线段的子节点,需要添加一个显示状态和唯一uuid标识方便进行节点的删除与移动;

enum { SELECTION_HANDLE_SIZE = 6, SELECTION_MARGIN = 10 };//节点宽度大小
enum SelectionHandleState { SelectionHandleOff, SelectionHandleInactive, SelectionHandleActive };//显示状态
enum { Handle_None = 0 , LeftTop , Top, RightTop, Right, RightBottom, Bottom, LeftBottom, Left };//边框节点所在位置

class SizeHandleRect :public QGraphicsRectItem
{
public:
    enum { Type = UserType + 2 }; //设置类型,用于匹配到的内容判断类型
    int type() const override
    {
        return Type;
    }

    SizeHandleRect(QGraphicsItem* parent , QString d , bool control = false );
    SizeHandleRect(QGraphicsItem* parent ,QPointF QPo, QString d , bool control = false );
    QString dir() const  { return m_dir; }
    void setState(SelectionHandleState st);//设置当前状态
    void move(qreal x, qreal y );
    SelectionHandleState m_state;//显示状态
    QColor borderColor;//绘色
    QPointF QPoLocats;
protected:
    void mousePressEvent(QGraphicsSceneMouseEvent *event) override;
    void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override;
    void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override;
    void hoverEnterEvent(QGraphicsSceneHoverEvent *e ) override;//获取鼠标
    void hoverLeaveEvent(QGraphicsSceneHoverEvent *e ) override;//鼠标移开
    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); 
private:
    const QString m_dir;//唯一标识
    bool   m_controlPoint;//是否是矩形框的节点还是多线段的节点
    QPoint C_down;//按下坐标

};

节点相关功能具体实现:
1.实例化内容时,隐藏并添加相关信息

SizeHandleRect::SizeHandleRect(QGraphicsItem* parent ,QPointF QPo , QString d, bool control)
    :QGraphicsRectItem(-SELECTION_HANDLE_SIZE/2,
                      -SELECTION_HANDLE_SIZE/2,
                       SELECTION_HANDLE_SIZE,
                       SELECTION_HANDLE_SIZE,parent)
    ,m_dir(d)
    ,m_controlPoint(control)
    ,m_state(SelectionHandleOff)
    ,borderColor("black")
    ,QPoLocats(QPo)
{

    this->setAcceptHoverEvents(true);
    setFlag(QGraphicsItem::ItemIgnoresTransformations,true);
    setFlag(QGraphicsItem::ItemIsMovable, true);
    setFlag(QGraphicsItem::ItemIsSelectable, true);
    setFlag(QGraphicsItem::ItemSendsGeometryChanges, true);
    hide();
}

2.重绘SizeHandleRect类型内容显示,设置当前节点状态

//print
void SizeHandleRect::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    painter->save();
    painter->setPen(Qt::SolidLine);
    painter->setBrush(QBrush(borderColor));

    painter->setRenderHint(QPainter::Antialiasing,false);

    if ( m_controlPoint  )
    {
        painter->setPen(QPen(Qt::red,Qt::SolidLine));
        painter->setBrush(Qt::green);
        painter->drawEllipse(rect().center(),3,3);
    }else
        painter->drawRect(rect());
    painter->restore();
}

//设置节点内容
void SizeHandleRect::setState(SelectionHandleState st)
{
    if (st == m_state)
        return;
    switch (st) {
    case SelectionHandleOff:
        hide();
        break;
    case SelectionHandleInactive:
    case SelectionHandleActive:
        show();
        break;
    }
    borderColor = Qt::white;
    m_state = st;
}

3.节点移动以及获取焦点事件
在重写事件时,为了不移除继承的类型的事件方法,可以通过 QGraphicsRectItem::mousePressEvent(event);
这种方式继续执行继承的类型事件方法;

void SizeHandleRect::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
      setCursor(Qt::ClosedHandCursor);
      QGraphicsRectItem::mousePressEvent(event);
}

void SizeHandleRect::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{

    QGraphicsRectItem::mouseMoveEvent(event);
    //移动时修改节点所在位置
    if( parentItem()->type() == Polylines::Type)
    {
        Polylines * line=qgraphicsitem_cast<Polylines*>(parentItem());
       line->UpdatePoint(dir(),QPointF(this->pos().x(),this->pos().y()));
       QPoLocats=QPointF(this->pos().x(),this->pos().y());
    }
}
void SizeHandleRect::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
    setCursor(Qt::OpenHandCursor);
    QGraphicsRectItem::mouseReleaseEvent(event);
}
void SizeHandleRect::hoverEnterEvent(QGraphicsSceneHoverEvent *e)
{
    borderColor = Qt::blue;
    update();
    QGraphicsRectItem::hoverEnterEvent(e);
}

void SizeHandleRect::hoverLeaveEvent(QGraphicsSceneHoverEvent *e)
{
    borderColor = Qt::white;
    setCursor(Qt::ArrowCursor);
    update();
    QGraphicsRectItem::hoverLeaveEvent(e);

}
02 重写QGraphicsPolygonItem类或者QGraphicsPathItem类生成多线段Polylines类

生成的Polylines类需要保存子节点SizeHandleRect的集合以及坐标点与唯一标识的内容,

public:
     enum { Type = UserType + 1 };
     int type() const override
     {
      return Type;
     }
public:
    typedef QList<SizeHandleRect*> Handles;
    Handles m_handles;
//    QMap 插入后再遍历是按照key的顺序来排序的(汉字除外,汉字的排序顺序很奇怪,不是正常的字母顺序);
//    QHash插入后再遍历是没有顺序的;
//    QMap<QString,QPointF> MapList;
//    QHash<QString,QPointF> MapList;
    QList<QPair<QString,QPointF>> pairs;
    QBrush m_brush;

也需要对节点的新增,删除,修改,刷新方法

//添加节点
 void Polylines::AddPointNotSort(QPointF Po)
 {
     QUuid id = QUuid::createUuid();
     QString strId = id.toString();

     SizeHandleRect *shr = new SizeHandleRect(this, Po,strId, true);
     shr->setState(SelectionHandleActive);
      QPair<QString, QPointF>  pairone(strId,Po);
     pairs.append(pairone);
     m_handles.append(shr);
     update();
 }
//坐标点批量添加
void Polylines::AddPoint(QList<QPoint> Polist)
{
    int count=0;
    foreach(QPoint it ,Polist)
    {
        count++;
//        QString str = QString("%1").arg(count,10,10,QLatin1Char('0'));
        QUuid id = QUuid::createUuid();
        QString strId = id.toString();
       /* QDateTime local(QDateTime::currentDateTime());
        QString localTime = local.toString("yyyy-MM-dd:hh:mm:ss.zzz");*/
        //QMap按照key自动排序输出
        SizeHandleRect *shr = new SizeHandleRect(this, it,strId, true);
        shr->setState(SelectionHandleOff);
        QPair<QString, QPointF>  pairone(strId,it);
        pairs.append(pairone);
//        MapList.insert(strId,it);
        m_handles.append(shr);
    }
}
 //移除节点
 void removeByHRect(SizeHandleRect *r)
    {
        for(int i=0;i<pairs.size();i++)
        {
            if(pairs[i].first==r->dir())
            {
             pairs.removeAt(i);
             break;
            }
        }
        m_handles.removeAll(r);
    }

以及最重要的对坐标点的绘制重写

QPainterPath Polylines::shape() const
{
    QPainterPath path;
    QPolygonF poly;
    for(int i=0;i<pairs.size();i++)
    {
        poly.append(pairs[i].second);
    }
    path.addPolygon(poly);
    path.closeSubpath();
    path.setFillRule(Qt::WindingFill);
    QPainterPathStroker stroker;
    stroker.setWidth(pen().widthF());
    return stroker.createStroke(path);

}
QRectF Polylines::boundingRect() const
{
    return shape().controlPointRect();
}

void Polylines::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    updatehandles();
    Q_UNUSED(option);
    Q_UNUSED(widget);

    painter->setPen(pen());
    painter->drawPolyline(GetPoints());
}
03 重写QGraphicsScene类修改事件执行

通过重写QGraphicsScene类来实现对多线段以及多线段节点的移除

//当不存在节点时删除
void  QGraphicsSceneOverride::DelectPolyline()
{
    foreach (QGraphicsItem *item, this->items())
    {
        if(item->type() == Polylines::Type)
        {
            Polylines * poly=qgraphicsitem_cast<Polylines*>(item);
            if(poly->m_handles.size()==0 && poly->pairs.size()==0)
                removeItem(poly);
        }
    }
}
//选中框中内容时删除
void QGraphicsSceneOverride::removeItemByRect(QRectF Rect)
{

    foreach (QGraphicsItem *item, this->items())
    {
        if(item->type() == SizeHandleRect::Type)
        {
            SizeHandleRect * rect=qgraphicsitem_cast<SizeHandleRect*>(item);
            if(Rect.contains(rect->QPoLocats))
            {
                if(rect->parentItem()!=NULL && rect->parentItem()->type() == Polylines::Type)
                {
                    Polylines* line=qgraphicsitem_cast<Polylines*>(rect->parentItem());
                    if(line->doubleClick)
                    {
                        line->removeByHRect(rect);
                        removeItem(rect);
                        DelectPolyline();
                    }
                }

            }

        }
    }

}
04 重写QGraphicsView类
A. QGraphicsView实现指定位置放大缩小

根据鼠标所在的位置,放大缩小界面指定倍数
其中Dx,Dy为x,y坐标的放大缩小倍数,Pos为鼠标所在位置,缩小倍数值为0-1之间,放大倍数为1-2之间:

void QGraphicsViewOverride::Setwheelscale(double Dx,double Dy,QPoint Pos)
{
    // 获取当前的鼠标所在的view坐标;
    QPoint prev_viewPos = Pos;
    // 获取当前鼠标相对于scene的位置;
    QPointF prev_scenePos = this->mapToScene(prev_viewPos);
    scale(Dx,Dy);

   this->scene()->setSceneRect(this->mapToScene(this->rect()).boundingRect());  //调整scene,使得scene和view一直,主要是为了排除掉scroll
      //获取缩放后的scene坐标
      QPointF scenePos = this->mapToScene(prev_viewPos);
      //获取缩放前后的坐标差值,即为需要进行move的位移
      QPointF disPointF = scenePos - prev_scenePos;
      //    qDebug()<<prev_scenePos<<" ::: "<<scenePos<<disPointF;
//      qDebug()<<this->scene()->sceneRect();
      //调整位置
      this->scene()->setSceneRect(this->scene()->sceneRect().x()-disPointF.x(),
                                  this->scene()->sceneRect().y()-disPointF.y(),
                                  this->scene()->sceneRect().width(),
                                  this->scene()->sceneRect().height());
      this->scene()->update();


}
B.QGraphicsView 上下左右方向键移动窗体内容显示

实现QGraphicsView显示内容的指定长度的滑动
Dx,Dy,x,y方向移动的距离

void QGraphicsViewOverride::SetMoveShow(double Dx,double Dy)
 {
//      this->scene()->setSceneRect(this->mapToScene(this->rect()).boundingRect());  //调整scene,使得scene和view一直,主要是为了排除掉scroll

//     cout<<this->scene()->sceneRect().x()<<" "<<Dx<<endl;
//     cout<<this->scene()->sceneRect().y()<<" "<<Dy<<endl;
//     cout<<" ----"<<endl;
     //调整位置
     this->scene()->setSceneRect(this->scene()->sceneRect().x()+Dx,
                                 this->scene()->sceneRect().y()+Dy,
                                 this->scene()->sceneRect().width(),
                                 this->scene()->sceneRect().height());
     this->scene()->update();
 }
C.QGraphicsView 以及自适应显示QGraphicsScene内容

当QGraphicsScene中包含图片时,此时能将图片自适应边框,居中显示到QGraphicsView控件中,

void QGraphicsViewOverride::self_adaption()
 {
    cout<<"----------"<<endl;
    QRectF view=this->rect();
//    cout <<" X: "<<view.x() <<" Y: "<<view.y() <<" width: "<<view.width() <<" height: "<<view.height() <<endl;
    QRectF sceneF=this->scene()->itemsBoundingRect();
//    cout <<" X: "<<sceneF.x() <<" Y: "<<sceneF.y() <<" width: "<<sceneF.width() <<" height: "<<sceneF.height() <<endl;
    QPoint PoView0(0,0);
    QPointF Poscene0=this->mapToScene(PoView0);
//    cout<<" X: "<<Poscene0.x()<<" Y: "<< Poscene0.y()<<endl;
    QPoint PoView1(view.width(),0);
    QPointF Poscene1=this->mapToScene(PoView1);
//    cout<<" X: "<<Poscene1.x()<<" Y: "<< Poscene1.y()<<endl;
    QPoint PoView2(0,view.height());
    QPointF Poscene2=this->mapToScene(PoView2);
//    cout<<" X: "<<Poscene2.x()<<" Y: "<< Poscene2.y()<<endl;

    double dx=sceneF.width()/(Poscene1.x()-Poscene0.x());
    double dy=sceneF.height()/(Poscene2.y()-Poscene0.y());
    double min=dx>dy?dy:dx;
    double max=dx>dy?dx:dy;
//    cout<<max<<endl;
    if(max!=0 && max!=1)
    {
        scale(1/max,1/max);
        this->scene()->setSceneRect(this->mapToScene(this->rect()).boundingRect());
        sceneF=this->scene()->itemsBoundingRect();
        //中心对中心
        QPointF viewTosceneCenter = this->mapToScene(QPoint(view.width()/2,view.height()/2));
        QPointF sceneCenter(sceneF.width()/2,sceneF.height()/2);
        SetMoveShow(sceneCenter.x()-viewTosceneCenter.x(),sceneCenter.y()-viewTosceneCenter.y());
    }
 }
D. 带入重写的QGraphicsView事件中,实现功能

//滑轮滚动缩小屏幕内容

void QGraphicsViewOverride::wheelEvent(QWheelEvent *event)
{
     if(event->delta()>0)
     {
        Setwheelscale(1.3,1.3,event->pos());
      }
      else{
          Setwheelscale(1 / 1.3, 1 / 1.3,event->pos());
      }

      QGraphicsView::wheelEvent(event);          // 执行QLineEdit类的默认事件处理
}

通过快捷键详细指定位置放大,上下左右滑动功能;

void QGraphicsViewOverride::keyReleaseEvent(QKeyEvent *event)
{
    if(event->modifiers()==(Qt::ControlModifier) && event->key()==Qt::Key_F )
    {
       Setwheelscale(1.9,1.9,PoMove);
    //         QMessageBox::information(this,"Click", "Ctrl+F");
    }
    else if(event->modifiers()==(Qt::ControlModifier) && event->key()==Qt::Key_G)
    {
        Setwheelscale(1 / 1.9,1 / 1.9,PoMove);
    }
    else if( event->key()==Qt::Key_Left)
    {
//        QMessageBox::information(this,"Click", "Key_Left");
         SetMoveShow(5,0);
    }
    else if( event->key()==Qt::Key_Right)
    {
          SetMoveShow(-5,0);
    }
    else if( event->key()==Qt::Key_Up)
    {
         SetMoveShow(0,5);
    }
    else if( event->key()==Qt::Key_Down)
    {
         SetMoveShow(0,-5);
    }
}

  • 5
    点赞
  • 55
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 13
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

得鹿梦鱼、

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值