From 0212fb5d2ed876cea76ea8bbbec4dd592d019de6 Mon Sep 17 00:00:00 2001 From: gaozheng Date: Tue, 23 Jun 2026 13:53:56 +0800 Subject: [PATCH] =?UTF-8?q?feat(detail):=20=E8=87=AA=E5=8A=A8=E6=A0=87?= =?UTF-8?q?=E6=B3=A8=E5=AF=B9=E8=AF=9D=E6=A1=86=E8=A1=A5=E7=AD=89=E5=80=BC?= =?UTF-8?q?=E7=BA=BF=E9=A2=84=E8=A7=88=E5=9B=BE(I13=201:1)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 右上补轻量 QwtPlot+ContourPlotItem 渲染反演网格等值面(复用 GridDataChartView 同款 渲染器与 ColorMapService);执行自动标注后 parseDatasetAnomalies 解析预演异常实时叠加, 删除预览行同步移除。构造改收 Grid+ColorScale(统计从 grid.values 算)。 build all 绿,336/336。 --- src/app/panels/chart/AutoAnnotationDialog.cpp | 75 +++++++++++++++++-- src/app/panels/chart/AutoAnnotationDialog.hpp | 27 ++++++- src/app/panels/chart/GridDataChartView.cpp | 4 +- 3 files changed, 94 insertions(+), 12 deletions(-) diff --git a/src/app/panels/chart/AutoAnnotationDialog.cpp b/src/app/panels/chart/AutoAnnotationDialog.cpp index 4227ad0..8cf06a9 100644 --- a/src/app/panels/chart/AutoAnnotationDialog.cpp +++ b/src/app/panels/chart/AutoAnnotationDialog.cpp @@ -22,8 +22,13 @@ #include #include +#include + #include "FormKit.hpp" #include "Theme.hpp" // scaledPx +#include "dto/DatasetChartDto.hpp" // parseDatasetAnomalies(JSON→Anomaly) +#include "panels/chart/ColorMapService.hpp" +#include "panels/chart/ContourPlotItem.hpp" #include "repo/IDatasetCommandRepository.hpp" namespace geopro::app { @@ -61,12 +66,14 @@ QString fmt2(bool valid, double x) { AutoAnnotationDialog::AutoAnnotationDialog(geopro::data::IDatasetCommandRepository* repo, QString dsObjectId, QString projectId, - std::vector gridValues, QWidget* parent) + const geopro::core::Grid& grid, + const geopro::core::ColorScale& scale, QWidget* parent) : QDialog(parent), repo_(repo), dsObjectId_(std::move(dsObjectId)), projectId_(std::move(projectId)), - gridValues_(std::move(gridValues)) { + grid_(grid), + scale_(scale) { setWindowTitle(QStringLiteral("自动标注")); setModal(true); resize(geopro::app::scaledPx(1400), geopro::app::scaledPx(600)); @@ -89,9 +96,11 @@ AutoAnnotationDialog::AutoAnnotationDialog(geopro::data::IDatasetCommandReposito leftWrap->setLayout(leftCol); split->addWidget(leftWrap, 35); - // ── 右:上统计 + 下预览表 ─────────────────────────────────────────────── + // ── 右:上(统计条 + 预览图) + 下预览表 ────────────────────────────────── + // 对照原版右上 :等值面预览图为主、数据统计在上。 auto* rightCol = new QVBoxLayout(); buildStatsBar(rightCol); + buildPreviewPlot(rightCol); detectedLabel_ = new QLabel(QStringLiteral("自动标注结果"), this); rightCol->addWidget(detectedLabel_); @@ -128,10 +137,19 @@ AutoAnnotationDialog::AutoAnnotationDialog(geopro::data::IDatasetCommandReposito loadExceptionTypes(); // 拉面异常类型 } +AutoAnnotationDialog::~AutoAnnotationDialog() { + // previewItem_ 由 QwtPlot autoDelete=true 处理,但析构顺序不确定:先 detach 再交还。 + if (previewItem_) { + previewItem_->detach(); + delete previewItem_; + previewItem_ = nullptr; + } + // colorSvc_ 为 unique_ptr,自动释放。 +} + void AutoAnnotationDialog::buildStatsBar(QVBoxLayout* into) { - // 数据统计(max/min/mean/median,从网格标量算)。预览图后置:当前以统计条替代, - // 后续如复用 ContourPlotItem 渲染异常预览图可在此区追加。 - const Stats s = computeStats(gridValues_); + // 数据统计(max/min/mean/median,从网格标量算)。 + const Stats s = computeStats(grid_.values()); auto* bar = new QFrame(this); bar->setFrameShape(QFrame::StyledPanel); auto* lay = new QHBoxLayout(bar); @@ -146,6 +164,49 @@ void AutoAnnotationDialog::buildStatsBar(QVBoxLayout* into) { into->addWidget(bar); } +void AutoAnnotationDialog::buildPreviewPlot(QVBoxLayout* into) { + // 复刻原版 :用 GridDataChartView 同款 ContourPlotItem 渲染当前网格等值面, + // 预览图为主区域;执行/删除时把预演异常实时叠加(refreshPreviewAnomalies)。 + previewPlot_ = new QwtPlot(this); + previewPlot_->setObjectName(QStringLiteral("autoAnnotPreview")); + previewPlot_->enableAxis(QwtPlot::xBottom, true); + previewPlot_->enableAxis(QwtPlot::xTop, false); + previewPlot_->enableAxis(QwtPlot::yLeft, true); + previewPlot_->setMinimumHeight(geopro::app::scaledPx(220)); + + // 网格 < 2×2 视为无可渲染数据:仅占位,不建等值面项。 + if (grid_.nx() < 2 || grid_.ny() < 2) { + into->addWidget(previewPlot_, 1); + return; + } + + colorSvc_ = std::make_unique(scale_); + previewItem_ = new ContourPlotItem(); + // 轻量预览:渲染等值面 + 等值线,关标注(小图标注过密);初始无异常。 + previewItem_->setData(grid_, colorSvc_.get(), {}, /*showLines=*/true, /*showLabels=*/false); + previewItem_->setShowAnomalies(true); + previewItem_->attach(previewPlot_); + + // 轴范围 = 数据范围(y 深度向下:上沿 ymax、下沿 ymin,与 GridDataChartView 一致)。 + const QRectF bbox = previewItem_->boundingRect(); + if (!bbox.isNull()) { + previewPlot_->setAxisScale(QwtPlot::xBottom, bbox.left(), bbox.right()); + previewPlot_->setAxisScale(QwtPlot::yLeft, bbox.top(), bbox.bottom()); + } + previewPlot_->replot(); + into->addWidget(previewPlot_, 1); +} + +void AutoAnnotationDialog::refreshPreviewAnomalies() { + // 把当前 previewExceptions_(execute 返回 / 删除后剩余)映射成 Anomaly 叠加到预览图。 + // 复用 dto::parseDatasetAnomalies(与正式异常同一 JSON 形态:location.coordinate + legend)。 + if (!previewItem_ || !colorSvc_) return; + const auto anoms = geopro::data::dto::parseDatasetAnomalies(previewExceptions_); + previewItem_->setData(grid_, colorSvc_.get(), anoms, /*showLines=*/true, /*showLabels=*/false); + previewItem_->setShowAnomalies(true); + if (previewPlot_) previewPlot_->replot(); +} + void AutoAnnotationDialog::loadExceptionTypes() { if (!repo_) return; QPointer self(this); @@ -362,6 +423,7 @@ void AutoAnnotationDialog::onExecute() { self->previewTable_->setCellWidget(i, 5, delBtn); } self->saveBtn_->setEnabled(!list.isEmpty()); + self->refreshPreviewAnomalies(); // 实时叠加预演异常到预览图 if (list.isEmpty()) QMessageBox::information(self, self->windowTitle(), QStringLiteral("暂未识别到异常")); }); @@ -397,6 +459,7 @@ void AutoAnnotationDialog::deletePreviewRow(int row) { detectedLabel_->setText( QStringLiteral("自动标注结果(共识别到 %1 个异常)").arg(previewExceptions_.size())); saveBtn_->setEnabled(!previewExceptions_.isEmpty()); + refreshPreviewAnomalies(); // 同步从预览图移除该异常 } void AutoAnnotationDialog::onSave() { diff --git a/src/app/panels/chart/AutoAnnotationDialog.hpp b/src/app/panels/chart/AutoAnnotationDialog.hpp index 62554b9..60f237b 100644 --- a/src/app/panels/chart/AutoAnnotationDialog.hpp +++ b/src/app/panels/chart/AutoAnnotationDialog.hpp @@ -2,8 +2,12 @@ #include #include #include +#include #include +#include "model/Field.hpp" // core::Grid(预览图等值面) +#include "model/ColorScale.hpp" // core::ColorScale(预览图色阶) + class QButtonGroup; class QComboBox; class QLineEdit; @@ -14,6 +18,7 @@ class QToolButton; class QLabel; class QVBoxLayout; class QWidget; +class QwtPlot; namespace geopro::data { class IDatasetCommandRepository; @@ -21,6 +26,9 @@ class IDatasetCommandRepository; namespace geopro::app { +class ColorMapService; +class ContourPlotItem; + // 自动标注对话框(I13,复刻原版 AutoAnnotationDialog,1400×600): // 左:规则卡片列表(标题「规则N」+ 折叠 + 删除;阈值模式 radio-button 数值/百分位、min/max、 // 最小点数、异常类型)+「添加规则」。 @@ -31,10 +39,12 @@ namespace geopro::app { class AutoAnnotationDialog : public QDialog { Q_OBJECT public: - // gridValues:网格标量(用于右上数据统计 max/min/mean/median);可空(统计显示 '-')。 + // grid/scale:当前反演网格 + 色阶,用于右上预览图(ContourPlotItem 渲染等值面) + // 及数据统计(max/min/mean/median 从 grid.values() 算)。 AutoAnnotationDialog(geopro::data::IDatasetCommandRepository* repo, QString dsObjectId, - QString projectId, std::vector gridValues, - QWidget* parent = nullptr); + QString projectId, const geopro::core::Grid& grid, + const geopro::core::ColorScale& scale, QWidget* parent = nullptr); + ~AutoAnnotationDialog() override; private: // 一条规则卡片的控件集合(卡片标题/折叠/删除 + 模式 radio + min/max + 最小点数 + 类型)。 @@ -50,6 +60,8 @@ private: }; void buildStatsBar(QVBoxLayout* into); // 右上统计条(max/min/mean/median) + void buildPreviewPlot(QVBoxLayout* into); // 右上预览图(QwtPlot + ContourPlotItem 等值面) + void refreshPreviewAnomalies(); // 把 previewExceptions_ 映射成 Anomaly 叠加到预览图并重绘 void loadExceptionTypes(); // 拉面异常类型(填充所有规则卡片下拉) void addRule(); // 加一条规则卡片 void removeRule(QWidget* frame); // 删除指定卡片(至少保留一条) @@ -63,7 +75,14 @@ private: geopro::data::IDatasetCommandRepository* repo_ = nullptr; QString dsObjectId_; QString projectId_; - std::vector gridValues_; + geopro::core::Grid grid_; // 反演网格(预览图等值面 + 统计) + geopro::core::ColorScale scale_; // 网格色阶(预览图取色) + + // 预览图(复用 GridDataChartView 的 ContourPlotItem 渲染等值面 + 异常叠加)。 + // colorSvc_ 非 QObject 手动持有;previewItem_ 由 QwtPlot autoDelete,析构前 detach。 + QwtPlot* previewPlot_ = nullptr; + std::unique_ptr colorSvc_; + ContourPlotItem* previewItem_ = nullptr; QVBoxLayout* ruleHost_ = nullptr; std::vector rules_; diff --git a/src/app/panels/chart/GridDataChartView.cpp b/src/app/panels/chart/GridDataChartView.cpp index c14a5d8..eb226a1 100644 --- a/src/app/panels/chart/GridDataChartView.cpp +++ b/src/app/panels/chart/GridDataChartView.cpp @@ -499,8 +499,8 @@ void GridDataChartView::openAutoAnnotation() { const QString dsId = dsIdGetter_ ? dsIdGetter_() : QString(); const QString projectId = projectIdGetter_ ? projectIdGetter_() : QString(); if (!cmdRepo_ || dsId.isEmpty()) { showNotImplemented(nullptr); return; } - // 透传网格标量用于右上数据统计(max/min/mean/median)。 - AutoAnnotationDialog dlg(cmdRepo_, dsId, projectId, grid_.values(), this); + // 透传网格 + 色阶:右上预览图(ContourPlotItem 等值面)+ 数据统计(max/min/mean/median)。 + AutoAnnotationDialog dlg(cmdRepo_, dsId, projectId, grid_, gridScale_, this); if (dlg.exec() == QDialog::Accepted) reloadGrid(); }