feat(detail): 自动标注对话框补等值线预览图(I13 1:1)
右上补轻量 QwtPlot+ContourPlotItem 渲染反演网格等值面(复用 GridDataChartView 同款 渲染器与 ColorMapService);执行自动标注后 parseDatasetAnomalies 解析预演异常实时叠加, 删除预览行同步移除。构造改收 Grid+ColorScale(统计从 grid.values 算)。 build all 绿,336/336。
This commit is contained in:
parent
03805f4326
commit
0212fb5d2e
|
|
@ -22,8 +22,13 @@
|
|||
#include <QToolButton>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#include <qwt_plot.h>
|
||||
|
||||
#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<double> 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);
|
||||
|
||||
// ── 右:上统计 + 下预览表 ───────────────────────────────────────────────
|
||||
// ── 右:上(统计条 + 预览图) + 下预览表 ──────────────────────────────────
|
||||
// 对照原版右上 <ContourPreview>:等值面预览图为主、数据统计在上。
|
||||
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) {
|
||||
// 复刻原版 <ContourPreview>:用 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<ColorMapService>(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<AutoAnnotationDialog> 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() {
|
||||
|
|
|
|||
|
|
@ -2,8 +2,12 @@
|
|||
#include <QDialog>
|
||||
#include <QJsonArray>
|
||||
#include <QString>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#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<double> 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<double> gridValues_;
|
||||
geopro::core::Grid grid_; // 反演网格(预览图等值面 + 统计)
|
||||
geopro::core::ColorScale scale_; // 网格色阶(预览图取色)
|
||||
|
||||
// 预览图(复用 GridDataChartView 的 ContourPlotItem 渲染等值面 + 异常叠加)。
|
||||
// colorSvc_ 非 QObject 手动持有;previewItem_ 由 QwtPlot autoDelete,析构前 detach。
|
||||
QwtPlot* previewPlot_ = nullptr;
|
||||
std::unique_ptr<ColorMapService> colorSvc_;
|
||||
ContourPlotItem* previewItem_ = nullptr;
|
||||
|
||||
QVBoxLayout* ruleHost_ = nullptr;
|
||||
std::vector<RuleCard> rules_;
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue