From db797522e8842e3305f7b44882f91679813da433 Mon Sep 17 00:00:00 2001 From: anxinglong <2910824064@qq.com> Date: Wed, 15 Apr 2026 18:17:16 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E8=83=BD=E5=B3=B0=E6=8B=9F?= =?UTF-8?q?=E5=90=88=E5=88=86=E6=9E=90=E6=A1=86=E9=80=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../EnergyCountPeakFitView.cpp | 144 +++++++++++++++++- .../EnergyCountPeakFitView.h | 28 +++- src/EnergyCountPeakFitView/PlotRectItem.cpp | 42 +++++ src/EnergyCountPeakFitView/PlotRectItem.h | 28 ++++ src/src.pro | 2 + 5 files changed, 242 insertions(+), 2 deletions(-) create mode 100644 src/EnergyCountPeakFitView/PlotRectItem.cpp create mode 100644 src/EnergyCountPeakFitView/PlotRectItem.h diff --git a/src/EnergyCountPeakFitView/EnergyCountPeakFitView.cpp b/src/EnergyCountPeakFitView/EnergyCountPeakFitView.cpp index 4034865..682e5cb 100644 --- a/src/EnergyCountPeakFitView/EnergyCountPeakFitView.cpp +++ b/src/EnergyCountPeakFitView/EnergyCountPeakFitView.cpp @@ -15,6 +15,7 @@ #include #include #include +#include "PlotRectItem.h" EnergyCountPeakFitView::EnergyCountPeakFitView(QWidget *parent) @@ -33,6 +34,8 @@ EnergyCountPeakFitView::EnergyCountPeakFitView(QWidget *parent) EnergyCountPeakFitView::~EnergyCountPeakFitView() { LOG_DEBUG(QStringLiteral(u"%1析构.").arg(this->GetViewName())); + delete _rubberBand; + clearAllSelectionRects(); } void EnergyCountPeakFitView::InitViewWorkspace(const QString &project_name) @@ -79,8 +82,14 @@ void EnergyCountPeakFitView::setupPlot() // _plot->insertLegend(legend, QwtPlot::RightLegend); _plot->SetAxisDragScale(QwtPlot::xBottom, true); + + _data_selector = new CustomQwtPlotXaxisSelector(_plot->canvas()); _data_selector->setEnabled(false); + + // 启用鼠标追踪,并安装事件过滤器 + _plot->canvas()->setMouseTracking(true); + _plot->canvas()->installEventFilter(this); } void EnergyCountPeakFitView::setupMenu() @@ -101,6 +110,13 @@ void EnergyCountPeakFitView::setupMenu() QAction* action_plot_config = this->_menu->addAction(QStringLiteral(u"图表配置")); action_plot_config->setObjectName("plot_config"); connect(action_plot_config, &QAction::triggered, this, &EnergyCountPeakFitView::onActionPlotConfigure); + + this->_menu->addSeparator(); + QAction* action_clear_rect = this->_menu->addAction(QStringLiteral(u"清除框选标记")); + connect(action_clear_rect, &QAction::triggered, this, &EnergyCountPeakFitView::clearAllSelectionRects); + + QAction* action_hint = this->_menu->addAction(QStringLiteral(u"框选提示:按住 Ctrl 键并拖动左键")); + action_hint->setEnabled(false); } void EnergyCountPeakFitView::loadDataFromFile(const QString &data_name, const QString &filename) @@ -209,4 +225,130 @@ void EnergyCountPeakFitView::onActionCurveShowSetting() void EnergyCountPeakFitView::onActionPlotConfigure() { -} \ No newline at end of file +} + + + +void EnergyCountPeakFitView::clearAllSelectionRects() +{ + for (PlotRectItem* item : _selectionRectItems) { + if (item) { + item->detach(); + delete item; + } + } + _selectionRectItems.clear(); + _plot->replot(); +} + +bool EnergyCountPeakFitView::eventFilter(QObject* watched, QEvent* event) +{ + if (watched == _plot->canvas()) { + QMouseEvent* me = static_cast(event); + + // 按下 Ctrl+左键 开始框选 + if (event->type() == QEvent::MouseButtonPress && + me->button() == Qt::LeftButton && + (me->modifiers() & Qt::ControlModifier)) { + startSelection(me->pos()); + return true; + } + // 移动时更新橡皮筋 + else if (event->type() == QEvent::MouseMove && _isSelecting) { + updateSelection(me->pos()); + return true; + } + // 释放左键完成框选 + else if (event->type() == QEvent::MouseButtonRelease && + me->button() == Qt::LeftButton && _isSelecting) { + finishSelection(); + return true; + } + } + return MeasureAnalysisView::eventFilter(watched, event); +} + +void EnergyCountPeakFitView::startSelection(const QPoint& pos) +{ + _isSelecting = true; + _selectionStart = pos; + + if (!_rubberBand) { + _rubberBand = new QRubberBand(QRubberBand::Rectangle, _plot->canvas()); + } + _rubberBand->setGeometry(QRect(pos, QSize())); + _rubberBand->show(); +} + +void EnergyCountPeakFitView::updateSelection(const QPoint& pos) +{ + if (!_rubberBand) return; + // 计算矩形(归一化) + QRect rect = QRect(_selectionStart, pos).normalized(); + _rubberBand->setGeometry(rect); +} + +void EnergyCountPeakFitView::finishSelection() +{ + if (_rubberBand) { + QRect finalRect = _rubberBand->geometry(); + _rubberBand->hide(); + if (finalRect.width() > 2 && finalRect.height() > 2) { + QRectF plotRect = pixelRectToPlotRect(finalRect); + addSelectionRect(plotRect); // 直接添加,不清除旧的 + // TODO: 在此处添加峰拟合逻辑 + // ========== 新增拟合逻辑 ========== + QList curves = _plot->GetCurveList(); + if (curves.isEmpty()) return; + QwtPlotCurve* originalCurve = curves.first(); + + // 获取原始数据 + QVector origX, origY; + for (size_t i = 0; i < originalCurve->dataSize(); ++i) { + origX.push_back(originalCurve->sample(i).x()); + origY.push_back(originalCurve->sample(i).y()); + } + + // 提取框内的点 + QVector roiX, roiY; + for (int i = 0; i < origX.size(); ++i) { + if (origX[i] >= plotRect.left() && origX[i] <= plotRect.right() && + origY[i] >= plotRect.bottom() && origY[i] <= plotRect.top()) { + roiX.push_back(origX[i]); + roiY.push_back(origY[i]); + } + } + + if (roiX.size() >= 3) + { + + + } + } + } + + _isSelecting = false; +} + +QRectF EnergyCountPeakFitView::pixelRectToPlotRect(const QRect& pixelRect) const +{ + const QwtScaleMap xMap = _plot->canvasMap(QwtPlot::xBottom); + const QwtScaleMap yMap = _plot->canvasMap(QwtPlot::yLeft); + + double x1 = xMap.invTransform(pixelRect.left()); + double x2 = xMap.invTransform(pixelRect.right()); + double y1 = yMap.invTransform(pixelRect.top()); + double y2 = yMap.invTransform(pixelRect.bottom()); + + return QRectF(QPointF(x1, y1), QPointF(x2, y2)).normalized(); +} + +void EnergyCountPeakFitView::addSelectionRect(const QRectF &plotRect) +{ + PlotRectItem* rectItem = new PlotRectItem("Selection"); + rectItem->setAxes(QwtPlot::xBottom, QwtPlot::yLeft); + rectItem->setRect(plotRect); + rectItem->attach(_plot); + _selectionRectItems.append(rectItem); + _plot->replot(); +} diff --git a/src/EnergyCountPeakFitView/EnergyCountPeakFitView.h b/src/EnergyCountPeakFitView/EnergyCountPeakFitView.h index cf03b47..b98df50 100644 --- a/src/EnergyCountPeakFitView/EnergyCountPeakFitView.h +++ b/src/EnergyCountPeakFitView/EnergyCountPeakFitView.h @@ -4,11 +4,17 @@ #include #include #include +#include +#include +#include // 新增 +#include // 新增 +class PlotRectItem; // 前向声明 class QMenu; class CustomQwtPlot; class CustomQwtPlotXaxisSelector; - +class QwtPlotPicker; +class QwtPlotCurve; class EnergyCountPeakFitView : public MeasureAnalysisView { Q_OBJECT @@ -27,12 +33,32 @@ private: private slots: void onActionCurveShowSetting(); void onActionPlotConfigure(); + void clearAllSelectionRects(); +protected: + bool eventFilter(QObject* watched, QEvent* event) override; // 事件过滤器 + +private: + void startSelection(const QPoint& pos); + void updateSelection(const QPoint& pos); + void finishSelection(); + QRectF pixelRectToPlotRect(const QRect& pixelRect) const; + void addSelectionRect(const QRectF& plotRect); + private: CustomQwtPlot* _plot = nullptr; QMenu* _menu = nullptr; QDialog* _curve_show_setting_dlg = nullptr; CustomQwtPlotXaxisSelector* _data_selector = nullptr; + // 手动框选状态 + bool _isSelecting = false; + QPoint _selectionStart; + QRubberBand* _rubberBand = nullptr; // 原生橡皮筋 + QList _selectionRectItems; + + QwtPlotCurve* _fittedCurve = nullptr; // 显示拟合结果的曲线 + + }; #endif // ENERGYCOUNTPEAKFITVIEW_H diff --git a/src/EnergyCountPeakFitView/PlotRectItem.cpp b/src/EnergyCountPeakFitView/PlotRectItem.cpp new file mode 100644 index 0000000..3c686c5 --- /dev/null +++ b/src/EnergyCountPeakFitView/PlotRectItem.cpp @@ -0,0 +1,42 @@ +#include "PlotRectItem.h" +#include +#include +PlotRectItem::PlotRectItem(const QString &title) + : QwtPlotItem() { + setTitle(title); + setZ(1000); + m_pen = QPen(Qt::red, 2, Qt::SolidLine); + m_brush = QBrush(QColor(255, 0, 0, 30)); +} + +void PlotRectItem::setRect(const QRectF &rect) { + m_rect = rect; +} + +void PlotRectItem::setPen(const QPen &pen) { + m_pen = pen; +} + +void PlotRectItem::setBrush(const QBrush &brush) { + m_brush = brush; +} + +void PlotRectItem::draw(QPainter *painter, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &) const { + const int x1 = xMap.transform(m_rect.left()); + const int x2 = xMap.transform(m_rect.right()); + const int y1 = yMap.transform(m_rect.top()); + const int y2 = yMap.transform(m_rect.bottom()); + const QRect rect(QPoint(x1, y1), QPoint(x2, y2)); + + painter->save(); + painter->setPen(m_pen); + painter->setBrush(m_brush); + painter->drawRect(rect); + painter->restore(); +} + +QRectF PlotRectItem::boundingRect() const { + return m_rect; +} diff --git a/src/EnergyCountPeakFitView/PlotRectItem.h b/src/EnergyCountPeakFitView/PlotRectItem.h new file mode 100644 index 0000000..f49c087 --- /dev/null +++ b/src/EnergyCountPeakFitView/PlotRectItem.h @@ -0,0 +1,28 @@ +#ifndef PLOTRECTITEM_H +#define PLOTRECTITEM_H + +#include +#include +#include +#include +class PlotRectItem : public QwtPlotItem { +public: + PlotRectItem(const QString &title = QString()); + + void setRect(const QRectF &rect); + void setPen(const QPen &pen); + void setBrush(const QBrush &brush); + + virtual void draw(QPainter *painter, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect) const override; + + virtual QRectF boundingRect() const override; + +private: + QRectF m_rect; + QPen m_pen; + QBrush m_brush; +}; + +#endif // PLOTRECTITEM_H diff --git a/src/src.pro b/src/src.pro index 184b076..c50b172 100644 --- a/src/src.pro +++ b/src/src.pro @@ -74,6 +74,7 @@ SOURCES += \ DeviceParameterConfig/DeviceParameterConfig.cpp \ DeviceParameterConfig/DeviceParameterConfigList.cpp \ EnergyCountPeakFitView/EnergyCountPeakFitView.cpp \ + EnergyCountPeakFitView/PlotRectItem.cpp \ EnergyCountPlotView/EnergyCountPlotView.cpp \ EnergyScaleDataModel.cpp \ EnergyScaleForm.cpp \ @@ -119,6 +120,7 @@ HEADERS += \ DeviceParameterConfig/DeviceParameterConfig.h \ DeviceParameterConfig/DeviceParameterConfigList.h \ EnergyCountPeakFitView/EnergyCountPeakFitView.h \ + EnergyCountPeakFitView/PlotRectItem.h \ EnergyCountPlotView/EnergyCountPlotView.h \ EnergyScaleDataModel.h \ EnergyScaleForm.h \