feat(ui): GridDataChartView 接入 QwtPlot+ContourPlotItem(真实比尺,白底,色阶条)

plotArea 占位换 QwtPlot(仿 RawDataChartView: 白底浅 palette, x 轴 xBottom/y 轴 yLeft,
LivePanner 实时平移, Magnifier setWheelFactor(1/0.9), QwtPlotRescaler ref=xBottom
aspect=1 真实比尺; 网格线/过原点零线省去, 被填充覆盖)。色阶占位换 ColorBarWidget。
setGridData: 建 ColorMapService(gridScale) + ContourPlotItem.setData → attach,
按数据 bbox setAxisScale + rescale + replot, 更新色阶条与底部异常表。
工具条「显示异常/显示等值线标注」复选框 → 切 ContourPlotItem 开关并 replot。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
gaozheng 2026-06-11 17:00:36 +08:00
parent 32e0aaec28
commit f14902cdd4
2 changed files with 140 additions and 23 deletions

View File

@ -7,9 +7,18 @@
#include <QToolButton> #include <QToolButton>
#include <QVBoxLayout> #include <QVBoxLayout>
#include <qwt_plot.h>
#include <qwt_plot_grid.h>
#include <qwt_plot_magnifier.h>
#include <qwt_plot_rescaler.h>
#include "PanelHeader.hpp" #include "PanelHeader.hpp"
#include "panels/AnomalyTablePanel.hpp" #include "panels/AnomalyTablePanel.hpp"
#include "panels/DescriptionPanel.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 { namespace geopro::app {
@ -85,16 +94,42 @@ GridDataChartView::GridDataChartView(QWidget* parent) : QWidget(parent) {
lay->addWidget(toolbar); lay->addWidget(toolbar);
// ---- 图表占位区stretch ---- // ---- QwtPlot仿 RawDataChartView差异x 轴在底部、不画过原点零线、网格线被填充覆盖故省)----
plotArea_ = new QWidget(this); plot_ = new QwtPlot(this);
plotArea_->setObjectName(QStringLiteral("gridPlotArea")); plot_->setObjectName(QStringLiteral("gridPlotArea"));
lay->addWidget(plotArea_, 1); plot_->enableAxis(QwtPlot::xBottom, true);
plot_->enableAxis(QwtPlot::xTop, false);
plot_->enableAxis(QwtPlot::yLeft, true);
// ---- 色阶占位(固定高 36px ---- // 白底浅色(对齐原版 web 图表)。
auto* colorScaleBar = new QWidget(this); plot_->setCanvasBackground(QBrush(Qt::white));
colorScaleBar->setObjectName(QStringLiteral("gridColorScaleBar")); plot_->setAutoFillBackground(true);
colorScaleBar->setFixedHeight(36); {
lay->addWidget(colorScaleBar); 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左键实时平移x 底轴 + y 左轴)+ Magnifier滚轮缩放反向使上滚=放大)。
new LivePanner(plot_, QwtPlot::xBottom, QwtPlot::yLeft, this);
auto* magnifier = new QwtPlotMagnifier(plot_->canvas());
magnifier->setWheelFactor(1.0 / 0.9);
plot_->setMinimumSize(0, 0);
// 锁定 x:y 真实比尺(参考轴 xBottomExpanding
rescaler_ = new QwtPlotRescaler(plot_->canvas(), QwtPlot::xBottom, QwtPlotRescaler::Expanding);
rescaler_->setAspectRatio(1.0);
rescaler_->setEnabled(true);
lay->addWidget(plot_, 1);
// ---- 独立色阶条(固定高 36px----
colorBar_ = new ColorBarWidget(this);
colorBar_->setObjectName(QStringLiteral("gridColorScaleBar"));
lay->addWidget(colorBar_);
// ---- 底部双页签(异常列表 / 描述),固定高约 200px ---- // ---- 底部双页签(异常列表 / 描述),固定高约 200px ----
anomalyTable_ = new AnomalyTablePanel(this); anomalyTable_ = new AnomalyTablePanel(this);
@ -109,12 +144,67 @@ GridDataChartView::GridDataChartView(QWidget* parent) : QWidget(parent) {
auto* bottomContainer = tabbedBottom.container; auto* bottomContainer = tabbedBottom.container;
bottomContainer->setFixedHeight(200); bottomContainer->setFixedHeight(200);
lay->addWidget(bottomContainer); lay->addWidget(bottomContainer);
// ---- 工具条开关 → 重建/重绘 ----
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(); }
});
} }
void GridDataChartView::setData(const geopro::controller::DatasetDetailController::ChartData& d) { void GridDataChartView::setData(const geopro::controller::DatasetDetailController::ChartData& d) {
data_ = d; data_ = d;
// 步骤1只把 anomalies 喂给底部异常列表;图表区留空。 // 开页:仅把 anomalies 喂给底部异常列表;图表区待网格数据懒加载后填充
anomalyTable_->setAnomalies(d.anomalies, {}, {}); anomalyTable_->setAnomalies(d.anomalies, {}, {});
} }
void GridDataChartView::setGridData(const geopro::core::Grid& grid,
const geopro::core::ColorScale& gridScale,
const std::vector<geopro::core::Anomaly>& 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 不拥有 item
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 } // namespace geopro::app

View File

@ -1,38 +1,65 @@
#pragma once #pragma once
#include <vector>
#include <QWidget> #include <QWidget>
#include "DatasetDetailController.hpp" #include "DatasetDetailController.hpp"
#include "model/Anomaly.hpp"
#include "model/ColorScale.hpp"
#include "model/Field.hpp"
class QSlider; class QSlider;
class QLabel; class QLabel;
class QwtPlot;
class QwtPlotRescaler;
namespace geopro::app { namespace geopro::app {
class AnomalyTablePanel; class AnomalyTablePanel;
class DescriptionPanel; class DescriptionPanel;
class ColorBarWidget;
class ColorMapService;
class ContourPlotItem;
// 网格数据图表视图步骤1骨架工具条 + 图表占位 + 色阶占位 + 底部双页签(异常列表/描述)。 // 网格数据图表视图:工具条 + QwtPlot白底 + 真实比尺 + 实时平移/滚轮缩放x 轴在底部)
// 图表渲染由后续步骤填入 plotArea()。 // + 独立色阶条 + 底部双页签(异常列表/描述)。
// 填充走 ContourPlotItem 栅格热力图 + 矢量等值线 + 标注 + 异常叠加。
class GridDataChartView : public QWidget { class GridDataChartView : public QWidget {
Q_OBJECT Q_OBJECT
public: public:
explicit GridDataChartView(QWidget* parent = nullptr); explicit GridDataChartView(QWidget* parent = nullptr);
// 步骤1把 anomalies 喂给底部异常列表;图表区留空 // 开页时调用:仅喂底部异常列表(网格数据随网格页激活懒加载)
void setData(const geopro::controller::DatasetDetailController::ChartData& d); void setData(const geopro::controller::DatasetDetailController::ChartData& d);
// 供后续步骤填充 Qwt 画布的占位区域。 // 网格数据到达(懒加载结果):建色彩服务 + ContourPlotItem挂到 QwtPlot更新色阶条/异常表。
QWidget* plotArea() const { return plotArea_; } void setGridData(const geopro::core::Grid& grid, const geopro::core::ColorScale& gridScale,
const std::vector<geopro::core::Anomaly>& anoms);
// 供后续步骤联动异常叠加。
AnomalyTablePanel* anomalyTable() const { return anomalyTable_; }
private: private:
void rebuildContour(); // 按当前显隐开关重建并重绘 ContourPlotItem
geopro::controller::DatasetDetailController::ChartData data_; geopro::controller::DatasetDetailController::ChartData data_;
QWidget* plotArea_;
AnomalyTablePanel* anomalyTable_; QwtPlot* plot_ = nullptr;
DescriptionPanel* descriptionPanel_; QwtPlotRescaler* rescaler_ = nullptr;
QSlider* simplifySlider_; ColorBarWidget* colorBar_ = nullptr;
QLabel* simplifyValueLabel_; AnomalyTablePanel* anomalyTable_ = nullptr;
DescriptionPanel* descriptionPanel_ = nullptr;
QSlider* simplifySlider_ = nullptr;
QLabel* simplifyValueLabel_ = nullptr;
// 渲染状态
ColorMapService* colorSvc_ = nullptr; // heapsetGridData 重建
ContourPlotItem* contourItem_ = nullptr;
geopro::core::Grid grid_{1, 1};
geopro::core::ColorScale gridScale_;
std::vector<geopro::core::Anomaly> anoms_;
bool hasGrid_ = false;
// 工具条显隐开关
bool showAnomalies_ = true;
bool showLabels_ = true;
}; };
} // namespace geopro::app } // namespace geopro::app