feat/dataset-detail-chart #5

Merged
gaozheng merged 74 commits from feat/dataset-detail-chart into main 2026-06-13 17:30:37 +08:00
9 changed files with 86 additions and 3 deletions
Showing only changes of commit bac0a198ff - Show all commits

View File

@ -125,6 +125,7 @@ TabbedPanel buildTabbedPanel(const QVector<PanelTab>& tabs, const QVector<Header
TabbedPanel result; TabbedPanel result;
result.container = box; result.container = box;
result.tabGroup = group;
for (int i = 0; i < tabs.size(); ++i) { for (int i = 0; i < tabs.size(); ++i) {
const PanelTab& t = tabs[i]; const PanelTab& t = tabs[i];

View File

@ -15,6 +15,7 @@
class QWidget; class QWidget;
class QLabel; class QLabel;
class QToolButton; class QToolButton;
class QButtonGroup;
namespace geopro::app { namespace geopro::app {
@ -34,10 +35,12 @@ struct PanelTab {
bool hasBadge; bool hasBadge;
}; };
// 带 Tab 表头的面板构建结果:容器 + 各 Tab 的徽标标签(无徽标处为 nullptr供动态更新 // 带 Tab 表头的面板构建结果:容器 + 各 Tab 的徽标标签(无徽标处为 nullptr供动态更新
// + Tab 互斥按钮组idClicked(int),供调用方监听页签切换做懒加载等)。
struct TabbedPanel { struct TabbedPanel {
QWidget* container; QWidget* container;
QVector<QLabel*> badges; QVector<QLabel*> badges;
QButtonGroup* tabGroup = nullptr;
}; };
// 构建带 Tab 表头的面板(首个 Tab 默认激活;点击 Tab 切换下方堆叠内容)。 // 构建带 Tab 表头的面板(首个 Tab 默认激活;点击 Tab 切换下方堆叠内容)。

View File

@ -508,6 +508,14 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re
detailPanel, [detailPanel](const QString& dsId) { detailPanel, [detailPanel](const QString& dsId) {
detailPanel->focusDataset(dsId); detailPanel->focusDataset(dsId);
}); });
// ── 网格数据懒加载:网格页首次激活 → 拉 rows+色阶type2+异常 → 回填对应页 ──
QObject::connect(detailPanel, &geopro::app::DatasetDetailPanel::gridDataNeeded, &detailCtrl,
&geopro::controller::DatasetDetailController::loadGridData);
QObject::connect(
&detailCtrl, &geopro::controller::DatasetDetailController::gridReady, detailPanel,
[detailPanel](const geopro::controller::DatasetDetailController::GridData& d) {
detailPanel->setGridData(d);
});
QObject::connect(&detailCtrl, &geopro::controller::DatasetDetailController::loadFailed, &window, QObject::connect(&detailCtrl, &geopro::controller::DatasetDetailController::loadFailed, &window,
[&window](const QString& dsId, const QString& msg) { [&window](const QString& dsId, const QString& msg) {
window.statusBar()->showMessage( window.statusBar()->showMessage(

View File

@ -1,5 +1,6 @@
#include "panels/DatasetDetailPage.hpp" #include "panels/DatasetDetailPage.hpp"
#include <QButtonGroup>
#include <QVBoxLayout> #include <QVBoxLayout>
#include "Glyphs.hpp" #include "Glyphs.hpp"
@ -9,6 +10,10 @@
namespace geopro::app { namespace geopro::app {
namespace {
constexpr int kGridTabIndex = 1; // 「网格数据」页签在 tabs 中的索引
}
DatasetDetailPage::DatasetDetailPage(QWidget* parent) : QWidget(parent) { DatasetDetailPage::DatasetDetailPage(QWidget* parent) : QWidget(parent) {
auto* lay = new QVBoxLayout(this); auto* lay = new QVBoxLayout(this);
lay->setContentsMargins(0, 0, 0, 0); lay->setContentsMargins(0, 0, 0, 0);
@ -27,12 +32,29 @@ DatasetDetailPage::DatasetDetailPage(QWidget* parent) : QWidget(parent) {
auto tabbedPanel = buildTabbedPanel(tabs, actions); auto tabbedPanel = buildTabbedPanel(tabs, actions);
lay->addWidget(tabbedPanel.container); lay->addWidget(tabbedPanel.container);
// 「网格数据」页签首次激活 → 懒加载网格数据rows 服务端网格化慢,故不随开页同步拉)。
if (tabbedPanel.tabGroup) {
connect(tabbedPanel.tabGroup, &QButtonGroup::idClicked, this, [this](int idx) {
if (idx != kGridTabIndex || gridRequested_ || dsId_.isEmpty()) return;
gridRequested_ = true;
emit gridDataNeeded(dsId_, ddCode_);
});
}
} }
void DatasetDetailPage::setData(const geopro::controller::DatasetDetailController::ChartData& d) { void DatasetDetailPage::setData(const geopro::controller::DatasetDetailController::ChartData& d) {
dsId_ = d.dsId; dsId_ = d.dsId;
ddCode_ = d.ddCode;
gridRequested_ = false; // 新数据集 → 网格数据需重新按需加载
rawView_->setData(d); rawView_->setData(d);
gridView_->setData(d); gridView_->setData(d);
} }
void DatasetDetailPage::setGridData(
const geopro::controller::DatasetDetailController::GridData& d) {
gridRequested_ = true; // 已加载,切回网格页不再重复请求
gridView_->setGridData(d.grid, d.gridScale, d.anomalies);
}
} // namespace geopro::app } // namespace geopro::app

View File

@ -14,9 +14,16 @@ class DatasetDetailPage : public QWidget {
public: public:
explicit DatasetDetailPage(QWidget* parent = nullptr); explicit DatasetDetailPage(QWidget* parent = nullptr);
void setData(const geopro::controller::DatasetDetailController::ChartData& d); void setData(const geopro::controller::DatasetDetailController::ChartData& d);
// 网格数据到达(懒加载结果)→ 下发给 GridDataChartView 并标记已加载。
void setGridData(const geopro::controller::DatasetDetailController::GridData& d);
QString dsId() const { return dsId_; } QString dsId() const { return dsId_; }
signals:
// 「网格数据」页签首次激活且本页网格数据未加载 → 请求懒加载。
void gridDataNeeded(const QString& dsId, const QString& ddCode);
private: private:
QString dsId_; QString dsId_;
QString ddCode_;
bool gridRequested_ = false; // 已请求过(避免重复发信号)
RawDataChartView* rawView_; RawDataChartView* rawView_;
GridDataChartView* gridView_; GridDataChartView* gridView_;
}; };

View File

@ -20,10 +20,19 @@ DatasetDetailPage* DatasetDetailPanel::pageFor(const QString& dsId) const {
void DatasetDetailPanel::openOrUpdate(const geopro::controller::DatasetDetailController::ChartData& d) { void DatasetDetailPanel::openOrUpdate(const geopro::controller::DatasetDetailController::ChartData& d) {
auto* p = pageFor(d.dsId); auto* p = pageFor(d.dsId);
if (!p) { p = new DatasetDetailPage(this); addTab(p, d.dsId); } // 标题后续可换 ds 名 if (!p) {
p = new DatasetDetailPage(this);
addTab(p, d.dsId); // 标题后续可换 ds 名
// 页内「网格数据」页签首次激活 → 冒泡为面板信号(外部接控制器懒加载)。
connect(p, &DatasetDetailPage::gridDataNeeded, this, &DatasetDetailPanel::gridDataNeeded);
}
p->setData(d); p->setData(d);
setCurrentWidget(p); setCurrentWidget(p);
} }
void DatasetDetailPanel::setGridData(const geopro::controller::DatasetDetailController::GridData& d) {
if (auto* p = pageFor(d.dsId)) p->setGridData(d);
}
void DatasetDetailPanel::focusDataset(const QString& dsId) { void DatasetDetailPanel::focusDataset(const QString& dsId) {
if (auto* p = pageFor(dsId)) setCurrentWidget(p); if (auto* p = pageFor(dsId)) setCurrentWidget(p);
} }

View File

@ -10,9 +10,11 @@ class DatasetDetailPanel : public QTabWidget {
public: public:
explicit DatasetDetailPanel(QWidget* parent = nullptr); explicit DatasetDetailPanel(QWidget* parent = nullptr);
void openOrUpdate(const geopro::controller::DatasetDetailController::ChartData& d); // 双击/数据到达 void openOrUpdate(const geopro::controller::DatasetDetailController::ChartData& d); // 双击/数据到达
void setGridData(const geopro::controller::DatasetDetailController::GridData& d); // 网格数据懒加载到达
void focusDataset(const QString& dsId); // 单击聚焦已开页 void focusDataset(const QString& dsId); // 单击聚焦已开页
signals: signals:
void activeDatasetChanged(const QString& dsId); // 反向联动数据集列表 void activeDatasetChanged(const QString& dsId); // 反向联动数据集列表
void gridDataNeeded(const QString& dsId, const QString& ddCode); // 网格页首次激活 → 请求懒加载
private: private:
DatasetDetailPage* pageFor(const QString& dsId) const; DatasetDetailPage* pageFor(const QString& dsId) const;
}; };

View File

@ -31,5 +31,25 @@ void DatasetDetailController::openDataset(const QString& dsId, const QString& dd
} }
} }
void DatasetDetailController::loadGridData(const QString& dsId, const QString& ddCode) {
if (busy_) return; // 防重入(同步网络期间 QEventLoop 可重入)
if (ddCode != QLatin1String("dd_inversion_data")) return; // 仅 ERT 反演有网格数据
busy_ = true;
const std::string id = dsId.toStdString();
try {
GridData d;
d.dsId = dsId;
// 网格数据rows(服务端网格化,慢) + 色阶 type2 + 异常 queryException。
d.grid = repo_.loadGrid(id);
d.gridScale = repo_.loadColorScale(id);
d.anomalies = repo_.loadAnomalies(id);
busy_ = false;
emit gridReady(d);
} catch (const std::exception& e) {
busy_ = false;
emit loadFailed(dsId, QString::fromStdString(e.what()));
}
}
void DatasetDetailController::focusDataset(const QString& dsId) { emit focusRequested(dsId); } void DatasetDetailController::focusDataset(const QString& dsId) { emit focusRequested(dsId); }
} // namespace geopro::controller } // namespace geopro::controller

View File

@ -21,12 +21,23 @@ public:
geopro::core::ColorScale gridScale; geopro::core::ColorScale gridScale;
std::vector<geopro::core::Anomaly> anomalies; std::vector<geopro::core::Anomaly> anomalies;
}; };
// 网格数据inversion/rows + 色阶 type2 + 异常):随「网格数据」页签首次激活按需懒加载。
struct GridData {
QString dsId;
geopro::core::Grid grid{1, 1}; // Grid 无默认构造占位值初始化loadGridData 会覆盖
geopro::core::ColorScale gridScale;
std::vector<geopro::core::Anomaly> anomalies;
};
explicit DatasetDetailController(data::IDatasetRepository& repo, QObject* parent = nullptr); explicit DatasetDetailController(data::IDatasetRepository& repo, QObject* parent = nullptr);
public slots: public slots:
void openDataset(const QString& dsId, const QString& ddCode); // 双击=新建/聚焦页 void openDataset(const QString& dsId, const QString& ddCode); // 双击=新建/聚焦页
void focusDataset(const QString& dsId); // 单击=聚焦已开页 void focusDataset(const QString& dsId); // 单击=聚焦已开页
// 网格数据懒加载网格页首次激活时调用rows 服务端网格化 1-4s故不随 openDataset 拉)。
void loadGridData(const QString& dsId, const QString& ddCode);
signals: signals:
void chartReady(const ChartData& data); void chartReady(const ChartData& data);
void gridReady(const GridData& data);
void focusRequested(const QString& dsId); void focusRequested(const QString& dsId);
void loadFailed(const QString& dsId, const QString& message); void loadFailed(const QString& dsId, const QString& message);
private: private: