当前位置:   article > 正文

使用QGraphicsItem自定义同心圆、旋转矩形和箭头等Item框体组件_qstyleoptiongraphicsitem

qstyleoptiongraphicsitem

1、背景

在视觉项目开发过程中碰到了图像显示和ROI矩形框或其他框体的显示的需求,最早我在开发过程中直接将Halcon的显示窗口直接贴在Qt的控件上,这样就省去了图像转换后再绘图的操作(Halcon具有独特的图像格式HObject),但是Halcon没有图层的概念,只有create_drawing_object_circle这些算子可以使用,但这些在图像实时刷新的时候比较耗时且也没有图层可以操作(Win环境实时效果还行,Linux下较难实现实时效果),采用Qpixmap显示在UI端,并使用QGraphicsItem来实现自定义的图形显示需求,效果比使用Halcon窗口显示要好很多,本篇就如何实现自定义的QGraphicsItem开发实现各种图形的显示进行展开。

2、效果展示

目前根据需求,给出了如下图所示的图形的自定义效果,可以根据需要创建不同形状的图形框:

 

3、自定义创建同心圆

3.1 同心圆的创建

首先在创建的同心圆构造类里,有中心点,两个圆半径,以及两个圆上的Edge点(用于拖动改变圆大小),其类的定义如下

  1. // 同心圆
  2. class BConcentricCircle : public BCircle
  3. {
  4. public:
  5.     BConcentricCircle(qreal x, qreal y, qreal radius1, qreal radius2, ItemType type);
  6.     enum { Type = 22};
  7.     int type() const
  8.     {
  9.         return Type;
  10.     }
  11.     void updateOtherRadius();
  12.     void setAnotherEdge(QPointF p);
  13. protected:
  14.     virtual QRectF boundingRect() const override;
  15.     virtual void paint(QPainter *painter,
  16.                        const QStyleOptionGraphicsItem *option,
  17.                        QWidget *widget) override;
  18.     virtual void contextMenuEvent(QGraphicsSceneContextMenuEvent *event) override;
  19. public:
  20.     QPointF m_another_edge;
  21.     qreal m_another_radius;
  22. };

同心圆的策略是在圆的基础上再画一个圆,所以在其构造函数中要先去定义同心圆的几个点-圆心、圆上边缘点。

  1. BConcentricCircle::BConcentricCircle(qreal x, qreal y, qreal radius1, qreal radius2, ItemType type)
  2.     : BCircle(x, y, radius1, type), m_another_edge(x+radius2*sqrt(2)/2, y+radius2*sqrt(2)/2)
  3. {
  4.     BPointItem *point = new BPointItem(this, m_another_edge, BPointItem::Special);
  5.     point->setParentItem(this);
  6.     m_pointList.append(point);
  7.     m_pointList.setRandColor();
  8.     updateOtherRadius();
  9. }

由构造函数可知,同心圆是由一个圆和另一个圆组成,其包含BCircle(x, y, radius1, type),再以圆心和m_another_edge(x+radius2*sqrt(2)/2, y+radius2*sqrt(2)/2)去画另一个圆。其他部分实现如下:

  1. void BConcentricCircle::updateOtherRadius()
  2. {
  3.     m_another_radius = sqrt(pow(m_center.x() - m_another_edge.x(), 2) +
  4.                             pow(m_center.y() - m_another_edge.y(), 2));
  5. }
  6. void BConcentricCircle::setAnotherEdge(QPointF p)
  7. {
  8.     m_another_edge = p;
  9. }
  10. QRectF BConcentricCircle::boundingRect() const
  11. {
  12.     qreal temp = m_radius > m_another_radius ? m_radius : m_another_radius;
  13.     return QRectF(m_center.x() - temp, m_center.y() - temp, temp * 2, temp * 2);
  14. }
  15. void BConcentricCircle::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
  16. {
  17.     painter->setPen(this->pen());
  18.     painter->setBrush(this->brush());
  19.     QRectF ret(m_center.x() - m_another_radius, m_center.y() - m_another_radius, m_another_radius * 2, m_another_radius * 2);
  20.     painter->drawEllipse(ret);
  21.     BCircle::paint(painter, option, widget);
  22. }

3.2 同心圆改变大小

创建同心圆时为我们创建了3个点,一个圆心点,一个内圆边缘点,一个外圆边缘点,拖动圆心点时,可实现Item整体的移动,而拖动内圆或外圆上的点时可以改变圆的大小。这里是创建一个点的类来实现拖动改变的效果。

  1. class BPointItem : public QObject, public QAbstractGraphicsShapeItem
  2. {
  3.     Q_OBJECT
  4. public:
  5.     enum PointType {
  6.         Center = 0, // 中心点
  7.         Edge,       // 边缘点(可拖动改变图形的形状、大小)
  8.         Special     // 特殊功能点
  9.     };
  10.     BPointItem(QAbstractGraphicsShapeItem* parent, QPointF p, PointType type);
  11.     QPointF getPoint() { return m_point; }
  12.     void setPoint(QPointF p) { m_point = p; }
  13. protected:
  14.     virtual QRectF boundingRect() const override;
  15.     virtual void paint(QPainter *painter,
  16.                        const QStyleOptionGraphicsItem *option,
  17.                        QWidget *widget) override;
  18.     virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override;
  19. private:
  20.     QPointF m_point;
  21.     PointType m_type;
  22. };

其中Paint函数用于画点,其实现函数如下:

  1. void BPointItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
  2. {
  3.     Q_UNUSED(option);
  4.     Q_UNUSED(widget);
  5.     painter->setPen(this->pen());
  6.     painter->setBrush(this->brush());
  7.     this->setPos(m_point);
  8.     switch (m_type) {
  9.     case Center:
  10.         painter->drawEllipse(-4, -4, 8, 8);
  11.         break;
  12.     case Edge:
  13.         painter->drawRect(QRectF(-4, -4, 8, 8));
  14.         break;
  15.     case Special:
  16.         painter->drawRect(QRectF(-4, -4, 8, 8));
  17.         break;
  18.     default: break;
  19.     }
  20. }

然后通过mouseMoveEvent事件函数修改边缘点位置后驱动同心圆类里的Paint函数进行重绘

  1. void BPointItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
  2. {
  3.     if ( event->buttons() == Qt::LeftButton ) {
  4.         qreal dx = event->scenePos().x() - event->lastScenePos().x();
  5.         qreal dy = event->scenePos().y() - event->lastScenePos().y();
  6.         BGraphicsItem* item = static_cast<BGraphicsItem *>(this->parentItem());
  7.         BGraphicsItem::ItemType itemType = item->getType();
  8.         switch (m_type) {
  9.         case Center: {
  10.                 item->moveBy(dx, dy);
  11.                 this->scene()->update();            
  12.         } break;
  13.         case Edge: {
  14.                 switch (itemType) {
  15.                 case BGraphicsItem::ItemType::Ellipse: {
  16.                         BEllipse *ellipse = dynamic_cast<BEllipse *>(item);
  17.                         ellipse->setEdge(m_point);
  18.                } break;
  19.                case BGraphicsItem::ItemType::Circle: {
  20.                         BCircle *circle = dynamic_cast<BCircle *>(item);
  21.                         circle->setEdge(m_point);
  22.                         circle->updateRadius();
  23.                } break;
  24.                case BGraphicsItem::ItemType::Concentric_Circle: {
  25.                        BCircle *circle = dynamic_cast<BCircle *>(item);
  26.                        circle->setEdge(m_point);
  27.                        circle->updateRadius();
  28.                } break;
  29.                case BGraphicsItem::ItemType::Pie: {
  30.                        BPie *pie = dynamic_cast<BPie *>(item);
  31.                        pie->setEdge(m_point);
  32.                        pie->updateRadius();
  33.                        pie->updateAngle();
  34.                } break;
  35.                case BGraphicsItem::ItemType::Chord: {
  36.                        BChord *chord = dynamic_cast<BChord *>(item);
  37.                        chord->setEdge(m_point);
  38.                        chord->updateRadius();
  39.                        chord->updateEndAngle();
  40.                } break;
  41.                case BGraphicsItem::ItemType::Rectangle: {
  42.                        BRectangle *rectangle = dynamic_cast<BRectangle *>(item);
  43.                        rectangle->setEdge(m_point);
  44.                } break;
  45.             default: break;
  46.             }
  47.         } break;
  48.         default: break;
  49.         }

4、自定义创建箭头

这里基本操作跟步骤3差不多,这里我在使用的时候不需要修改箭头直线的长短,所以我只进行了箭头的创建类:

  1. //箭头
  2. class BArrow : public QGraphicsItem
  3. {
  4.   public:
  5.     BArrow();
  6.     BArrow(QPointF startPoint,QPointF endPoint);
  7.     void setLineItem(QPointF startP, QPointF endP);
  8.     void setColor(QColor color);
  9.         enum { Type = 33};
  10.     int type() const
  11.     {
  12.         return Type;
  13.     }
  14. protected:
  15.     virtual QRectF boundingRect() const override;
  16.     virtual void paint(QPainter *painter,
  17.                        const QStyleOptionGraphicsItem *option,
  18.                        QWidget *widget) override;
  19. private:
  20.     void CreatePointNodes(void);
  21. private:
  22.     QPointF m_EndP;
  23.     QPointF m_points[3];           //保存箭头的顶点
  24.     QColor  m_Color;               //设置箭头颜色
  25. };

这里我们通过起点和终点来创建一个箭头直线,使用setLineItem函数来修改箭头直线的尺寸,使用setColor函数来设置箭头的颜色。实现代码如下:

  1. BArrow::BArrow()
  2. {
  3. }
  4. BArrow::BArrow(QPointF startPoint, QPointF endPoint)
  5. {
  6.     setFlag(ItemIsSelectable);
  7.     setAcceptHoverEvents(true);
  8.     m_Color = Qt::green;
  9.     setLineItem(startPoint,endPoint);
  10. }
  11. void BArrow::setLineItem(QPointF startP, QPointF endP)
  12. {
  13.     m_EndP = endP - startP;
  14.     CreatePointNodes();
  15. }
  16. void BArrow::setColor(QColor color)
  17. {
  18.    m_Color = color;
  19. }
  20. void BArrow::CreatePointNodes()
  21. {
  22.     //箭头直线与水平方向的夹角再加pi
  23.     float angle = atan2(m_EndP.y(), m_EndP.x()) + 3.1415926;
  24.     //这两个值需要根据实际场景的坐标大小进行调整,
  25.     float ExtRefArrowLenght = 4;//箭头末端大小的长度,
  26.     float ExtRefArrowDegrees = 1.047;//箭头末端顶角的一半
  27.     m_points[0] = m_EndP;
  28.      //求得箭头点1坐标
  29.     m_points[1].setX(m_EndP.x() + ExtRefArrowLenght * cos(angle - ExtRefArrowDegrees));
  30.     m_points[1].setY(m_EndP.y() + ExtRefArrowLenght * sin(angle - ExtRefArrowDegrees));
  31.     //求得箭头点2坐标
  32.     m_points[2].setX(m_EndP.x() + ExtRefArrowLenght * cos(angle + ExtRefArrowDegrees));
  33.     m_points[2].setY(m_EndP.y() + ExtRefArrowLenght * sin(angle + ExtRefArrowDegrees));
  34. }
  35. QRectF BArrow::boundingRect() const
  36. {
  37.     return QRectF(0, 0, m_EndP.x(), m_EndP.y());
  38. }
  39. void BArrow::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
  40. {
  41.     painter->setRenderHint(QPainter::Antialiasing, true);                   //设置反走样,防锯齿
  42.     QPen pen(m_Color, 2, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin);
  43.     QBrush brush(m_Color, Qt::SolidPattern);
  44.     painter->setPen(pen);
  45.     painter->setBrush(brush);
  46.     QLineF line(0, 0, m_EndP.x(), m_EndP.y());
  47.     painter->drawLine(line);
  48.     painter->drawPolygon(m_points, 3);
  49. }

5、函数调用

在Mainwindow中直接使用指针来调用
 

  1.     BConcentricCircle *m_conCircle = new BConcentricCircle(0, 0, 50, 80, BGraphicsItem::ItemType::Concentric_Circle);
  2.     m_scene->addItem(m_conCircle);
  3.     BArrow *m_Arrow = new BArrow(QPointF(0,0),QPointF(100,100));
  4.     m_Arrow->setPos(50,50);
  5.     m_Arrow->setColor(Qt::red);
  6.     m_scene->addItem(m_Arrow);

6、总结

这个自定义的Item构建本身并不难,主要是要了解QGraphicsView中的View、Scene、Item以及控件之间的坐标对应关系,有机会我也会针对这个进行详细的学习和回顾。本文中涉及了其他的框体创建未详细介绍,如果感兴趣可以查看代码自行了解,详细代码如下:

本节详细源码

 

 


 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/blog/article/detail/91756
推荐阅读
相关标签
  

闽ICP备14008679号