feat/dataset-detail-chart #5
|
|
@ -508,6 +508,11 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re
|
|||
detailPanel, [detailPanel](const QString& dsId) {
|
||||
detailPanel->focusDataset(dsId);
|
||||
});
|
||||
QObject::connect(&detailCtrl, &geopro::controller::DatasetDetailController::loadFailed, &window,
|
||||
[&window](const QString& dsId, const QString& msg) {
|
||||
window.statusBar()->showMessage(
|
||||
QStringLiteral("数据集 %1 加载失败:%2").arg(dsId, msg), 5000);
|
||||
});
|
||||
|
||||
// ── 详情面板切 Tab → 反向高亮数据集列表对应行 ──
|
||||
QObject::connect(detailPanel, &geopro::app::DatasetDetailPanel::activeDatasetChanged,
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ namespace geopro::app {
|
|||
|
||||
DatasetDetailPanel::DatasetDetailPanel(QWidget* parent) : QTabWidget(parent) {
|
||||
setTabsClosable(true);
|
||||
connect(this, &QTabWidget::tabCloseRequested, this, [this](int i) { delete widget(i); });
|
||||
connect(this, &QTabWidget::tabCloseRequested, this, [this](int i) { widget(i)->deleteLater(); });
|
||||
connect(this, &QTabWidget::currentChanged, this, [this](int i) {
|
||||
if (auto* p = qobject_cast<DatasetDetailPage*>(widget(i)))
|
||||
emit activeDatasetChanged(p->dsId());
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
#include "panels/chart/DatasetChartView.hpp"
|
||||
#include <algorithm>
|
||||
#include <QGraphicsScene>
|
||||
#include <QGraphicsPathItem>
|
||||
#include <QGraphicsRectItem>
|
||||
|
|
@ -24,13 +25,18 @@ DatasetChartView::DatasetChartView(QWidget* parent)
|
|||
}
|
||||
|
||||
void DatasetChartView::clearChart() {
|
||||
// scene_->clear() 删除场景中的所有 item(含 anomalyItems_ 所指向的对象);
|
||||
// anomalyItems_.clear() 随后清空向量中的悬空指针。
|
||||
// 二者必须成对、顺序不可颠倒:若先 clear() 后仍持有指针,rebuildAnomalyItems
|
||||
// 中的 delete 会对已释放对象 double-free;若先清向量、后继续操作旧指针同样 UB。
|
||||
scene_->clear(); anomalyItems_.clear();
|
||||
}
|
||||
|
||||
void DatasetChartView::showScatter(const geopro::core::ScatterField& f, const geopro::core::ColorScale& cs) {
|
||||
clearChart();
|
||||
const double sz = 0.6; // 方块边长(数据单位,近似 web 方点)
|
||||
for (size_t i = 0; i < f.v.size(); ++i) {
|
||||
const size_t n = std::min({f.x.size(), f.y.size(), f.v.size()});
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
auto* r = scene_->addRect(f.x[i] - sz / 2, f.y[i] - sz / 2, sz, sz,
|
||||
QPen(Qt::white, 0), QBrush(toQ(cs.colorAt(f.v[i]))));
|
||||
r->setPen(QPen(Qt::white, 0)); // 白描边
|
||||
|
|
@ -81,6 +87,16 @@ void DatasetChartView::rebuildAnomalyItems() {
|
|||
for (int i = 0; i < static_cast<int>(anomalies_.size()); ++i) {
|
||||
if (hidden_.count(i)) continue;
|
||||
const auto& a = anomalies_[i];
|
||||
if (a.localPts.empty()) continue;
|
||||
if (static_cast<int>(a.markType) == 1) { // 点:小方块标记
|
||||
const double s = 0.8;
|
||||
auto* dot = scene_->addRect(a.localPts[0].x - s/2, a.localPts[0].y - s/2, s, s,
|
||||
QPen(QColor(QString::fromStdString(a.lineColor)), 0),
|
||||
QBrush(QColor(QString::fromStdString(a.lineColor))));
|
||||
dot->setZValue(2);
|
||||
anomalyItems_.push_back(dot);
|
||||
continue;
|
||||
}
|
||||
if (a.localPts.size() < 2) continue;
|
||||
QPainterPath path; path.moveTo(a.localPts[0].x, a.localPts[0].y);
|
||||
for (size_t k = 1; k < a.localPts.size(); ++k) path.lineTo(a.localPts[k].x, a.localPts[k].y);
|
||||
|
|
|
|||
|
|
@ -7,6 +7,13 @@ DatasetDetailController::DatasetDetailController(data::IDatasetRepository& repo,
|
|||
: QObject(parent), repo_(repo) {}
|
||||
|
||||
void DatasetDetailController::openDataset(const QString& dsId, const QString& ddCode) {
|
||||
if (busy_) return; // 防重入(同步网络期间 QEventLoop 可重入)
|
||||
busy_ = true;
|
||||
if (ddCode != QLatin1String("dd_inversion_data")) { // 首版仅支持 ERT 反演
|
||||
busy_ = false;
|
||||
emit loadFailed(dsId, QStringLiteral("暂不支持该数据类型的预览"));
|
||||
return;
|
||||
}
|
||||
const std::string id = dsId.toStdString();
|
||||
try {
|
||||
ChartData d;
|
||||
|
|
@ -17,8 +24,10 @@ void DatasetDetailController::openDataset(const QString& dsId, const QString& dd
|
|||
d.grid = repo_.loadGrid(id);
|
||||
d.gridScale = repo_.loadColorScale(id);
|
||||
d.anomalies = repo_.loadAnomalies(id);
|
||||
busy_ = false;
|
||||
emit chartReady(d);
|
||||
} catch (const std::exception& e) {
|
||||
busy_ = false;
|
||||
emit loadFailed(dsId, QString::fromStdString(e.what()));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,5 +31,6 @@ signals:
|
|||
void loadFailed(const QString& dsId, const QString& message);
|
||||
private:
|
||||
data::IDatasetRepository& repo_;
|
||||
bool busy_ = false;
|
||||
};
|
||||
} // namespace geopro::controller
|
||||
|
|
|
|||
|
|
@ -130,7 +130,7 @@ ContourBandsResult buildContourBands(const Grid& g, const ColorScale& cs, const
|
|||
banded->SetInputConnection(surf->GetOutputPort());
|
||||
banded->SetNumberOfContours(static_cast<int>(stops.size()));
|
||||
for (int i = 0; i < static_cast<int>(stops.size()); ++i) banded->SetValue(i, stops[i]);
|
||||
banded->GenerateContourEdgesOn();
|
||||
if (opt.makeLines) banded->GenerateContourEdgesOn();
|
||||
banded->SetScalarModeToValue();
|
||||
banded->Update();
|
||||
|
||||
|
|
@ -152,21 +152,23 @@ ContourBandsResult buildContourBands(const Grid& g, const ColorScale& cs, const
|
|||
out.bands.push_back(std::move(bp));
|
||||
}
|
||||
|
||||
// port1:等值线(polylines)+ DP 简化。
|
||||
vtkPolyData* edges = banded->GetContourEdgesOutput();
|
||||
if (edges) {
|
||||
edges->BuildCells();
|
||||
const vtkIdType nLines = edges->GetNumberOfCells();
|
||||
for (vtkIdType c = 0; c < nLines; ++c) {
|
||||
vtkCell* cell = edges->GetCell(c);
|
||||
vtkPoints* cp = cell->GetPoints();
|
||||
ContourLine cl; cl.level = 0.0;
|
||||
for (vtkIdType p = 0; p < cp->GetNumberOfPoints(); ++p) {
|
||||
double xyz[3]; cp->GetPoint(p, xyz);
|
||||
cl.pts.push_back(Vec2{xyz[0], xyz[1]});
|
||||
// port1:等值线(polylines)+ DP 简化。仅 makeLines=true 时生成(否则不提取)。
|
||||
if (opt.makeLines) {
|
||||
vtkPolyData* edges = banded->GetContourEdgesOutput();
|
||||
if (edges) {
|
||||
edges->BuildCells();
|
||||
const vtkIdType nLines = edges->GetNumberOfCells();
|
||||
for (vtkIdType c = 0; c < nLines; ++c) {
|
||||
vtkCell* cell = edges->GetCell(c);
|
||||
vtkPoints* cp = cell->GetPoints();
|
||||
ContourLine cl; cl.level = 0.0;
|
||||
for (vtkIdType p = 0; p < cp->GetNumberOfPoints(); ++p) {
|
||||
double xyz[3]; cp->GetPoint(p, xyz);
|
||||
cl.pts.push_back(Vec2{xyz[0], xyz[1]});
|
||||
}
|
||||
simplifyInPlace(cl.pts, opt.simplifyTol);
|
||||
if (cl.pts.size() >= 2) out.lines.push_back(std::move(cl));
|
||||
}
|
||||
simplifyInPlace(cl.pts, opt.simplifyTol);
|
||||
if (cl.pts.size() >= 2) out.lines.push_back(std::move(cl));
|
||||
}
|
||||
}
|
||||
return out;
|
||||
|
|
|
|||
Loading…
Reference in New Issue