目录
一、功能需求
二、实现效果
三、实现方法
3D测量软件中,需要在轮廓上进行二次编程,需要显示轮廓线,然后可以调节矩形框的范围的获取参数,如华汉的HyperShape3D软件,对轮廓的编程界面如下。
2个矩形框 + 1个轮廓
1个矩形框 + 1个轮廓
找到了一篇比较符合需求的博客,基于QChartView实现;
Qt Charts使用(重写QChartView,实现一些自定义功能)_讳疾忌医丶的博客-CSDN博客_qtcharts使用
该文中重写了paintEvent(QPaintEvent *event),绘制2个矩形框。然而当我需要添加更多的矩形框时,发现需要添加很多重复性的代码,扩展性极差。为此我在此基础上做了改进,QChartView是基于qt的Graphics/View框架实现的,因此我可以将矩形框封装成一个图元QGraphicsItem,然后在场景中添加图元。
关键代码如下
自定义QChartView
#ifndef MYCHARTS_H
#define MYCHARTS_H#include
#include
#include
#include
#include
#include "processpropertyeditor_global.h"QT_CHARTS_USE_NAMESPACEclass GraphicsRangeRectItem;
class PROCESSPROPERTYEDITORSHARED_EXPORT ContourChartView : public QChartView
{Q_OBJECT
public:enum ItemTheme{LightPink = 0, /* 浅粉红 */Violet, /* 紫罗兰 */SkyBlue, /* 天蓝色 */Cyan, /* 青色 */SeaGreen, /* 海洋绿 */Yellow, /* 纯黄 */Gold, /* 金 */LightGrey, /* 浅灰色 */};explicit ContourChartView(QWidget *parent = nullptr);GraphicsRangeRectItem *addItem(ItemTheme theme, const QPointF &topleft);void lineSeriesAppend(const QList &points);protected:void paintEvent(QPaintEvent *event) override;void mousePressEvent(QMouseEvent *event) override;void mouseMoveEvent(QMouseEvent *event) override;void mouseReleaseEvent(QMouseEvent *event) override;void contextMenuEvent(QContextMenuEvent *event) override;public:QMap m_colors;QLineSeries *m_pSeries;QChart *m_pChart;
};//
#include "ContourChartView.h"
#include "GraphicsRangeRectItem.h"#include
#include
#include ContourChartView::ContourChartView(QWidget *parent): QChartView(parent)
{int a = 100;m_colors.insert(ItemTheme::LightPink, QColor(255, 182, 193, a));m_colors.insert(ItemTheme::Violet, QColor(238, 130, 238, a));m_colors.insert(ItemTheme::SkyBlue, QColor(135, 206, 235, a));m_colors.insert(ItemTheme::Cyan, QColor(0, 255, 255, a));m_colors.insert(ItemTheme::SeaGreen, QColor(46, 139, 87, a));m_colors.insert(ItemTheme::Yellow, QColor(255, 255, 0, a));m_colors.insert(ItemTheme::Gold, QColor(255, 215, 0, a));m_colors.insert(ItemTheme::LightGrey, QColor(211, 211, 211, a));m_pSeries = new QLineSeries();m_pSeries->setUseOpenGL(true);m_pChart = new QChart();m_pChart->setTheme(QChart::ChartThemeDark);m_pChart->legend()->hide();this->setChart(m_pChart);this->setRenderHints(QPainter::Antialiasing);
}GraphicsRangeRectItem *ContourChartView::addItem(ContourChartView::ItemTheme theme,const QPointF &topleft)
{GraphicsRangeRectItem *rectItem = new GraphicsRangeRectItem;rectItem->setRect(QRectF(topleft, QSizeF(60, 700)));rectItem->setPen(QPen(QColor(205, 104, 57, 100)));rectItem->setBrush(m_colors.value(theme));rectItem->setFlags(QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsMovable);rectItem->setZValue(100);this->scene()->addItem(rectItem);return rectItem;
}void ContourChartView::lineSeriesAppend(const QList &points)
{QListseries = m_pChart->series();if(series.contains(m_pSeries)){m_pChart->removeSeries(m_pSeries);}m_pSeries->clear();m_pSeries->append(points);m_pChart->addSeries(m_pSeries);m_pChart->createDefaultAxes();
}void ContourChartView::paintEvent(QPaintEvent *event)
{QChartView::paintEvent(event);
}void ContourChartView::mousePressEvent(QMouseEvent *event)
{QChartView::mousePressEvent(event);
}void ContourChartView::mouseMoveEvent(QMouseEvent *event)
{QChartView::mouseMoveEvent(event);
}void ContourChartView::mouseReleaseEvent(QMouseEvent *event)
{QChartView::mouseReleaseEvent(event);
}void ContourChartView::contextMenuEvent(QContextMenuEvent *event)
{QMenu menu(this);menu.addAction(QStringLiteral("放大"), [ = ](){chart()->zoom(1.2);});menu.addAction(QStringLiteral("缩小"), [ = ](){chart()->zoom(0.8);});menu.addAction(QStringLiteral("还原"), [ = ](){chart()->zoomReset();});menu.exec(event->globalPos());
}
自定义矩形框图元
#ifndef GRAPHICSRANGERECTITEM_H
#define GRAPHICSRANGERECTITEM_H#include class GraphicsRangeRectItem: public QObject, public QGraphicsRectItem
{Q_OBJECTpublic:enum E_HandleFlag : int{Default = 0x00,AtLeft = 0x01,AtRight = 0x02,AtCenter = 0x03};explicit GraphicsRangeRectItem(QGraphicsItem *parent = Q_NULLPTR);~GraphicsRangeRectItem();signals:void stateChanged();protected:virtual void hoverMoveEvent(QGraphicsSceneHoverEvent *event) override;virtual void hoverLeaveEvent(QGraphicsSceneHoverEvent *event) override;virtual void mousePressEvent(QGraphicsSceneMouseEvent *event) override;virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override;virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override;private:E_HandleFlag handleAt(const QPointF &pos);private:QMap handleCursors;E_HandleFlag handleSelected = Default;const int MIN_BOX_WIDTH = 20;
};#endif // GRAPHICSRANGERECTITEM_H//#include "GraphicsRangeRectItem.h"
#include
#include
#include
#include
#include GraphicsRangeRectItem::GraphicsRangeRectItem(QGraphicsItem *parent): QGraphicsRectItem(parent)
{handleCursors[Default] = Qt::ArrowCursor;handleCursors[AtLeft] = Qt::SizeHorCursor;handleCursors[AtRight] = Qt::SizeHorCursor;this->setFlags(QGraphicsItem::ItemIsSelectable |QGraphicsItem::ItemIsFocusable);setAcceptHoverEvents(true);
}GraphicsRangeRectItem::~GraphicsRangeRectItem()
{}void GraphicsRangeRectItem::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
{if(this->isSelected()){E_HandleFlag handle = this->handleAt(event->pos());this->setCursor(QCursor(handleCursors.value(handle)));}QGraphicsRectItem::hoverMoveEvent(event);
}void GraphicsRangeRectItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
{this->setCursor(QCursor(Qt::ArrowCursor));QGraphicsRectItem::hoverLeaveEvent(event);
}void GraphicsRangeRectItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
{handleSelected = this->handleAt(event->pos());QGraphicsRectItem::mousePressEvent(event);
}void GraphicsRangeRectItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{switch (handleSelected){case E_HandleFlag::AtLeft:{QRectF oldRect = this->rect();/* 最小宽度限制 */if(oldRect.right() - event->pos().x() < MIN_BOX_WIDTH){return;}QRectF newRect = oldRect;newRect.setLeft(event->pos().x());this->setRect(newRect);update();}break;case E_HandleFlag::AtRight:{QRectF oldRect = this->rect();/* 最小宽度限制 */if(event->pos().x() - oldRect.left() < MIN_BOX_WIDTH){return;}QRectF newRect = oldRect;newRect.setRight(event->pos().x());this->setRect(newRect);update();}break;case E_HandleFlag::AtCenter:{QRectF oldRect = this->rect();/* 防止超出左右边界 */int leftLimit = 0;int rightLimit = this->scene()->views().at(0)->size().width();if(event->pos().x() <= leftLimit || event->pos().x() >= rightLimit){return;}QRectF newRect = oldRect;newRect.moveCenter(QPointF(event->pos().x(), oldRect.center().y()));this->setRect(newRect);update();}break;default:break;}
}void GraphicsRangeRectItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{QGraphicsItem::mouseReleaseEvent(event);handleSelected = Default;if(event->button() == Qt::LeftButton){emit stateChanged();}this->update();
}GraphicsRangeRectItem::E_HandleFlag GraphicsRangeRectItem::handleAt(const QPointF &pos)
{QRectF rect = this->rect();static const int check_radius = 3;if (std::abs(pos.x() - rect.right()) < check_radius){return E_HandleFlag::AtRight;}else if (std::abs(pos.x() - rect.left()) < check_radius){return E_HandleFlag::AtLeft;}else if(rect.contains(pos)){return E_HandleFlag::AtCenter;}return E_HandleFlag::Default;
}
使用
创建窗口
ContourChartView *graphicsView = new ContourChartView();
添加轮廓
graphicsView->lineSeriesAppend(const QList
&points); 添加矩形框
GraphicsRangeRectItem *rectItem1 = chartView()->addItem(ContourChartView::ItemTheme::LightPink, QPointF(100, 10));
rectItem1->setToolTip(QStringLiteral("基准对象"));
GraphicsRangeRectItem *rectItem2 = chartView()->addItem(ContourChartView::ItemTheme::Violet, QPointF(400, 10));
rectItem2->setToolTip(QStringLiteral("测量对象"));
坐标映射,需要将场景中的坐标转换到chart图表上的坐标值
connect(rectItem1, &GraphicsRangeRectItem::stateChanged, this, [ = ]()
{
double xl = chartView()->chart()->mapToValue(QPointF(rectItem1->rect().left(), 0)).x();
double xr = chartView()->chart()->mapToValue(QPointF(rectItem1->rect().right(), 0)).x();
});