#include "panels/chart/GridDataChartView.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include "PanelHeader.hpp" #include "panels/AnomalyTablePanel.hpp" #include "panels/DescriptionPanel.hpp" #include "panels/chart/ColorBarWidget.hpp" #include "panels/chart/ColorMapService.hpp" #include "panels/chart/ContourPlotItem.hpp" #include "panels/chart/LivePanner.hpp" namespace geopro::app { GridDataChartView::GridDataChartView(QWidget* parent) : QWidget(parent) { auto* lay = new QVBoxLayout(this); lay->setContentsMargins(0, 0, 0, 0); lay->setSpacing(0); // ---- 工具条 ---- auto* toolbar = new QWidget(this); auto* tbLay = new QHBoxLayout(toolbar); tbLay->setContentsMargins(4, 4, 4, 4); tbLay->setSpacing(4); auto* btnGrid = new QToolButton(toolbar); btnGrid->setText(QStringLiteral("网格")); auto* btnColorScale = new QToolButton(toolbar); btnColorScale->setText(QStringLiteral("色阶配置")); auto* btnWhiten = new QToolButton(toolbar); btnWhiten->setText(QStringLiteral("白化")); auto* btnFilter = new QToolButton(toolbar); btnFilter->setText(QStringLiteral("滤波处理")); auto* chkShowAnom = new QCheckBox(QStringLiteral("显示异常"), toolbar); chkShowAnom->setChecked(true); auto* chkShowContourLabel = new QCheckBox(QStringLiteral("显示等值线标注"), toolbar); chkShowContourLabel->setChecked(true); auto* chkContourTip = new QCheckBox(QStringLiteral("显示等值线提示信息"), toolbar); chkContourTip->setChecked(false); auto* lblSimplify = new QLabel(QStringLiteral("简化容差:"), toolbar); simplifySlider_ = new QSlider(Qt::Horizontal, toolbar); simplifySlider_->setRange(0, 100); simplifySlider_->setValue(50); simplifySlider_->setFixedWidth(80); simplifyValueLabel_ = new QLabel(QStringLiteral("0.5"), toolbar); simplifyValueLabel_->setFixedWidth(28); connect(simplifySlider_, &QSlider::valueChanged, this, [this](int v) { simplifyValueLabel_->setText(QString::number(v / 100.0, 'f', 1)); }); auto* btnAnomalyLabel = new QToolButton(toolbar); btnAnomalyLabel->setText(QStringLiteral("异常标注")); auto* btnAutoLabel = new QToolButton(toolbar); btnAutoLabel->setText(QStringLiteral("自动标注")); auto* btnSaveAs = new QToolButton(toolbar); btnSaveAs->setText(QStringLiteral("另存为")); tbLay->addWidget(btnGrid); tbLay->addWidget(btnColorScale); tbLay->addWidget(btnWhiten); tbLay->addWidget(btnFilter); tbLay->addWidget(chkShowAnom); tbLay->addWidget(chkShowContourLabel); tbLay->addWidget(chkContourTip); tbLay->addWidget(lblSimplify); tbLay->addWidget(simplifySlider_); tbLay->addWidget(simplifyValueLabel_); tbLay->addWidget(btnAnomalyLabel); tbLay->addWidget(btnAutoLabel); tbLay->addWidget(btnSaveAs); tbLay->addStretch(); lay->addWidget(toolbar); // ---- QwtPlot(仿 RawDataChartView,差异:x 轴在底部、不画过原点零线、网格线被填充覆盖故省)---- plot_ = new QwtPlot(this); plot_->setObjectName(QStringLiteral("gridPlotArea")); plot_->enableAxis(QwtPlot::xBottom, true); plot_->enableAxis(QwtPlot::xTop, false); plot_->enableAxis(QwtPlot::yLeft, true); // 白底浅色(对齐原版 web 图表)。 plot_->setCanvasBackground(QBrush(Qt::white)); plot_->setAutoFillBackground(true); { QPalette pal = plot_->palette(); pal.setColor(QPalette::Window, Qt::white); pal.setColor(QPalette::WindowText, QColor(90, 90, 90)); pal.setColor(QPalette::Text, QColor(90, 90, 90)); plot_->setPalette(pal); } // 交互:LivePanner 统一左键实时平移 + 滚轮缩放(消费滚轮事件,不冒泡触发滚动条)。 new LivePanner(plot_, QwtPlot::xBottom, QwtPlot::yLeft, this); plot_->setMinimumSize(0, 0); // 锁定 x:y 真实比尺(参考轴 xBottom,Expanding)。 rescaler_ = new QwtPlotRescaler(plot_->canvas(), QwtPlot::xBottom, QwtPlotRescaler::Expanding); rescaler_->setAspectRatio(1.0); rescaler_->setEnabled(true); // 图表区 = 绘图 + 色阶条(色阶条固定高 36)。 colorBar_ = new ColorBarWidget(this); colorBar_->setObjectName(QStringLiteral("gridColorScaleBar")); auto* chartArea = new QWidget(this); auto* chartLay = new QVBoxLayout(chartArea); chartLay->setContentsMargins(0, 0, 0, 0); chartLay->setSpacing(0); chartLay->addWidget(plot_, 1); chartLay->addWidget(colorBar_); // ---- 底部双页签(异常列表 / 描述)---- anomalyTable_ = new AnomalyTablePanel(this); descriptionPanel_ = new DescriptionPanel(this); const QVector bottomTabs = { {Glyph::Anomaly, QStringLiteral("异常列表"), anomalyTable_, true}, {Glyph::Property, QStringLiteral("描述"), descriptionPanel_, false}, }; auto tabbedBottom = buildTabbedPanel(bottomTabs, {}); // 图表区 | 底部表 竖直 QSplitter(可拖拽调整比例)。给定最小高度→分割器有最小尺寸。 chartArea->setMinimumHeight(280); tabbedBottom.container->setMinimumHeight(160); auto* splitter = new QSplitter(Qt::Vertical); splitter->addWidget(chartArea); splitter->addWidget(tabbedBottom.container); splitter->setStretchFactor(0, 3); splitter->setStretchFactor(1, 1); splitter->setChildrenCollapsible(false); // 页签内滚动:把(图表+异常)分割器放进 QScrollArea。dock 够高→分割器填满(可拖动调整); // dock 太矮→在页签内部出现竖滚动条(工具条/页签/标题固定),而非整个面板滚动。 auto* scroll = new QScrollArea(this); scroll->setWidgetResizable(true); scroll->setFrameShape(QFrame::NoFrame); scroll->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); scroll->setWidget(splitter); lay->addWidget(scroll, 1); // ---- 工具条开关 → 重建/重绘 ---- connect(chkShowAnom, &QCheckBox::toggled, this, [this](bool on) { showAnomalies_ = on; if (contourItem_) { contourItem_->setShowAnomalies(on); plot_->replot(); } }); connect(chkShowContourLabel, &QCheckBox::toggled, this, [this](bool on) { showLabels_ = on; if (contourItem_) { contourItem_->setShowLabels(on); plot_->replot(); } }); } GridDataChartView::~GridDataChartView() { // colorSvc_ 非 QObject、无 parent,需手动释放(contourItem_ 由 QwtPlot autoDelete 处理)。 delete colorSvc_; } void GridDataChartView::setData(const geopro::controller::DatasetDetailController::ChartData& d) { data_ = d; // 开页:仅把 anomalies 喂给底部异常列表;图表区待网格数据懒加载后填充。 anomalyTable_->setAnomalies(d.anomalies, {}, {}); } void GridDataChartView::setGridData(const geopro::core::Grid& grid, const geopro::core::ColorScale& gridScale, const std::vector& anoms) { grid_ = grid; gridScale_ = gridScale; anoms_ = anoms; hasGrid_ = true; // 重建色彩服务(旧 contourItem 已 detach/delete)。 delete colorSvc_; colorSvc_ = new ColorMapService(gridScale_); rebuildContour(); // 色阶条 + 底部异常表(懒加载结果含真实异常)。 colorBar_->setColorScale(gridScale_); anomalyTable_->setAnomalies(anoms_, {}, {}); } void GridDataChartView::rebuildContour() { if (!hasGrid_ || !colorSvc_) return; // 卸载旧项:QwtPlot 默认 autoDelete=true(析构时 delete 仍在 dict 的 item)。 // 必须先 detach()(从 dict 移除)再 delete,否则 QwtPlot 析构时会 double-free。 if (contourItem_) { contourItem_->detach(); delete contourItem_; contourItem_ = nullptr; } contourItem_ = new ContourPlotItem(); contourItem_->setData(grid_, colorSvc_, anoms_, /*showLines*/ true, showLabels_); contourItem_->setShowAnomalies(showAnomalies_); contourItem_->attach(plot_); // 轴范围 = 数据范围(x=距离、y=深度/高程)。 const QRectF bbox = contourItem_->boundingRect(); if (!bbox.isNull()) { plot_->setAxisScale(QwtPlot::xBottom, bbox.left(), bbox.right()); plot_->setAxisScale(QwtPlot::yLeft, bbox.top(), bbox.bottom()); } plot_->updateAxes(); if (rescaler_) rescaler_->rescale(); // 应用真实比尺 plot_->replot(); } } // namespace geopro::app