赞
踩
在视觉项目开发过程中碰到了图像显示和ROI矩形框或其他框体的显示的需求,最早我在开发过程中直接将Halcon的显示窗口直接贴在Qt的控件上,这样就省去了图像转换后再绘图的操作(Halcon具有独特的图像格式HObject),但是Halcon没有图层的概念,只有create_drawing_object_circle这些算子可以使用,但这些在图像实时刷新的时候比较耗时且也没有图层可以操作(Win环境实时效果还行,Linux下较难实现实时效果),采用Qpixmap显示在UI端,并使用QGraphicsItem来实现自定义的图形显示需求,效果比使用Halcon窗口显示要好很多,本篇就如何实现自定义的QGraphicsItem开发实现各种图形的显示进行展开。
目前根据需求,给出了如下图所示的图形的自定义效果,可以根据需要创建不同形状的图形框:
首先在创建的同心圆构造类里,有中心点,两个圆半径,以及两个圆上的Edge点(用于拖动改变圆大小),其类的定义如下
- // 同心圆
- class BConcentricCircle : public BCircle
- {
- public:
- BConcentricCircle(qreal x, qreal y, qreal radius1, qreal radius2, ItemType type);
- enum { Type = 22};
- int type() const
- {
- return Type;
- }
- void updateOtherRadius();
- void setAnotherEdge(QPointF p);
-
- protected:
- virtual QRectF boundingRect() const override;
-
- virtual void paint(QPainter *painter,
- const QStyleOptionGraphicsItem *option,
- QWidget *widget) override;
-
- virtual void contextMenuEvent(QGraphicsSceneContextMenuEvent *event) override;
-
- public:
- QPointF m_another_edge;
- qreal m_another_radius;
- };
同心圆的策略是在圆的基础上再画一个圆,所以在其构造函数中要先去定义同心圆的几个点-圆心、圆上边缘点。
- BConcentricCircle::BConcentricCircle(qreal x, qreal y, qreal radius1, qreal radius2, ItemType type)
- : BCircle(x, y, radius1, type), m_another_edge(x+radius2*sqrt(2)/2, y+radius2*sqrt(2)/2)
- {
- BPointItem *point = new BPointItem(this, m_another_edge, BPointItem::Special);
- point->setParentItem(this);
- m_pointList.append(point);
- m_pointList.setRandColor();
-
- updateOtherRadius();
- }
由构造函数可知,同心圆是由一个圆和另一个圆组成,其包含BCircle(x, y, radius1, type),再以圆心和m_another_edge(x+radius2*sqrt(2)/2, y+radius2*sqrt(2)/2)去画另一个圆。其他部分实现如下:
- void BConcentricCircle::updateOtherRadius()
- {
- m_another_radius = sqrt(pow(m_center.x() - m_another_edge.x(), 2) +
- pow(m_center.y() - m_another_edge.y(), 2));
- }
-
- void BConcentricCircle::setAnotherEdge(QPointF p)
- {
- m_another_edge = p;
- }
-
- QRectF BConcentricCircle::boundingRect() const
- {
- qreal temp = m_radius > m_another_radius ? m_radius : m_another_radius;
- return QRectF(m_center.x() - temp, m_center.y() - temp, temp * 2, temp * 2);
- }
-
- void BConcentricCircle::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
- {
- painter->setPen(this->pen());
- painter->setBrush(this->brush());
-
- QRectF ret(m_center.x() - m_another_radius, m_center.y() - m_another_radius, m_another_radius * 2, m_another_radius * 2);
- painter->drawEllipse(ret);
-
- BCircle::paint(painter, option, widget);
- }
创建同心圆时为我们创建了3个点,一个圆心点,一个内圆边缘点,一个外圆边缘点,拖动圆心点时,可实现Item整体的移动,而拖动内圆或外圆上的点时可以改变圆的大小。这里是创建一个点的类来实现拖动改变的效果。
- class BPointItem : public QObject, public QAbstractGraphicsShapeItem
- {
- Q_OBJECT
-
- public:
- enum PointType {
- Center = 0, // 中心点
- Edge, // 边缘点(可拖动改变图形的形状、大小)
- Special // 特殊功能点
- };
-
- BPointItem(QAbstractGraphicsShapeItem* parent, QPointF p, PointType type);
-
- QPointF getPoint() { return m_point; }
- void setPoint(QPointF p) { m_point = p; }
-
- protected:
- virtual QRectF boundingRect() const override;
-
- virtual void paint(QPainter *painter,
- const QStyleOptionGraphicsItem *option,
- QWidget *widget) override;
-
- virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override;
-
- private:
- QPointF m_point;
- PointType m_type;
- };
其中Paint函数用于画点,其实现函数如下:
- void BPointItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
- {
- Q_UNUSED(option);
- Q_UNUSED(widget);
- painter->setPen(this->pen());
- painter->setBrush(this->brush());
- this->setPos(m_point);
-
- switch (m_type) {
- case Center:
- painter->drawEllipse(-4, -4, 8, 8);
- break;
- case Edge:
- painter->drawRect(QRectF(-4, -4, 8, 8));
- break;
- case Special:
- painter->drawRect(QRectF(-4, -4, 8, 8));
- break;
- default: break;
- }
- }
然后通过mouseMoveEvent事件函数修改边缘点位置后驱动同心圆类里的Paint函数进行重绘
- void BPointItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
- {
- if ( event->buttons() == Qt::LeftButton ) {
- qreal dx = event->scenePos().x() - event->lastScenePos().x();
- qreal dy = event->scenePos().y() - event->lastScenePos().y();
-
- BGraphicsItem* item = static_cast<BGraphicsItem *>(this->parentItem());
- BGraphicsItem::ItemType itemType = item->getType();
-
- switch (m_type) {
- case Center: {
- item->moveBy(dx, dy);
- this->scene()->update();
- } break;
- case Edge: {
- switch (itemType) {
- case BGraphicsItem::ItemType::Ellipse: {
- BEllipse *ellipse = dynamic_cast<BEllipse *>(item);
- ellipse->setEdge(m_point);
- } break;
- case BGraphicsItem::ItemType::Circle: {
- BCircle *circle = dynamic_cast<BCircle *>(item);
- circle->setEdge(m_point);
- circle->updateRadius();
- } break;
- case BGraphicsItem::ItemType::Concentric_Circle: {
- BCircle *circle = dynamic_cast<BCircle *>(item);
- circle->setEdge(m_point);
- circle->updateRadius();
- } break;
- case BGraphicsItem::ItemType::Pie: {
- BPie *pie = dynamic_cast<BPie *>(item);
- pie->setEdge(m_point);
- pie->updateRadius();
- pie->updateAngle();
- } break;
- case BGraphicsItem::ItemType::Chord: {
- BChord *chord = dynamic_cast<BChord *>(item);
- chord->setEdge(m_point);
- chord->updateRadius();
- chord->updateEndAngle();
- } break;
- case BGraphicsItem::ItemType::Rectangle: {
- BRectangle *rectangle = dynamic_cast<BRectangle *>(item);
- rectangle->setEdge(m_point);
- } break;
- default: break;
- }
- } break;
- default: break;
- }
这里基本操作跟步骤3差不多,这里我在使用的时候不需要修改箭头直线的长短,所以我只进行了箭头的创建类:
- //箭头
- class BArrow : public QGraphicsItem
- {
- public:
- BArrow();
- BArrow(QPointF startPoint,QPointF endPoint);
- void setLineItem(QPointF startP, QPointF endP);
- void setColor(QColor color);
- enum { Type = 33};
- int type() const
- {
- return Type;
- }
- protected:
- virtual QRectF boundingRect() const override;
-
- virtual void paint(QPainter *painter,
- const QStyleOptionGraphicsItem *option,
- QWidget *widget) override;
- private:
- void CreatePointNodes(void);
- private:
- QPointF m_EndP;
- QPointF m_points[3]; //保存箭头的顶点
- QColor m_Color; //设置箭头颜色
- };
这里我们通过起点和终点来创建一个箭头直线,使用setLineItem函数来修改箭头直线的尺寸,使用setColor函数来设置箭头的颜色。实现代码如下:
- BArrow::BArrow()
- {
-
- }
-
- BArrow::BArrow(QPointF startPoint, QPointF endPoint)
- {
-
- setFlag(ItemIsSelectable);
- setAcceptHoverEvents(true);
-
- m_Color = Qt::green;
- setLineItem(startPoint,endPoint);
-
- }
-
- void BArrow::setLineItem(QPointF startP, QPointF endP)
- {
- m_EndP = endP - startP;
-
- CreatePointNodes();
- }
-
- void BArrow::setColor(QColor color)
- {
- m_Color = color;
- }
-
- void BArrow::CreatePointNodes()
- {
- //箭头直线与水平方向的夹角再加pi
- float angle = atan2(m_EndP.y(), m_EndP.x()) + 3.1415926;
- //这两个值需要根据实际场景的坐标大小进行调整,
- float ExtRefArrowLenght = 4;//箭头末端大小的长度,
- float ExtRefArrowDegrees = 1.047;//箭头末端顶角的一半
-
- m_points[0] = m_EndP;
- //求得箭头点1坐标
- m_points[1].setX(m_EndP.x() + ExtRefArrowLenght * cos(angle - ExtRefArrowDegrees));
- m_points[1].setY(m_EndP.y() + ExtRefArrowLenght * sin(angle - ExtRefArrowDegrees));
- //求得箭头点2坐标
- m_points[2].setX(m_EndP.x() + ExtRefArrowLenght * cos(angle + ExtRefArrowDegrees));
- m_points[2].setY(m_EndP.y() + ExtRefArrowLenght * sin(angle + ExtRefArrowDegrees));
-
-
- }
-
- QRectF BArrow::boundingRect() const
- {
- return QRectF(0, 0, m_EndP.x(), m_EndP.y());
- }
-
-
- void BArrow::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
- {
- painter->setRenderHint(QPainter::Antialiasing, true); //设置反走样,防锯齿
- QPen pen(m_Color, 2, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin);
- QBrush brush(m_Color, Qt::SolidPattern);
-
- painter->setPen(pen);
- painter->setBrush(brush);
-
- QLineF line(0, 0, m_EndP.x(), m_EndP.y());
- painter->drawLine(line);
-
- painter->drawPolygon(m_points, 3);
- }
在Mainwindow中直接使用指针来调用
- BConcentricCircle *m_conCircle = new BConcentricCircle(0, 0, 50, 80, BGraphicsItem::ItemType::Concentric_Circle);
- m_scene->addItem(m_conCircle);
-
- BArrow *m_Arrow = new BArrow(QPointF(0,0),QPointF(100,100));
- m_Arrow->setPos(50,50);
- m_Arrow->setColor(Qt::red);
- m_scene->addItem(m_Arrow);
这个自定义的Item构建本身并不难,主要是要了解QGraphicsView中的View、Scene、Item以及控件之间的坐标对应关系,有机会我也会针对这个进行详细的学习和回顾。本文中涉及了其他的框体创建未详细介绍,如果感兴趣可以查看代码自行了解,详细代码如下:
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。