feat(ui): 网格数据懒加载接线(控制器 gridReady + 页签首次激活触发)

DatasetDetailController 新增 GridData 内嵌结构 + loadGridData slot
(busy 守卫, 仅 dd_inversion_data; 拉 rows+色阶type2+异常) + gridReady 信号。
buildTabbedPanel 暴露 tabGroup 供调用方监听页签切换。
DatasetDetailPage 在「网格数据」页签首次激活且未加载时 emit gridDataNeeded;
setGridData 转发给 GridDataChartView 并标记已加载(切 ds 重置)。
DatasetDetailPanel 透传 gridDataNeeded / 按 dsId 路由 setGridData。
main.cpp 接线 gridDataNeeded->loadGridData, gridReady->setGridData。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
gaozheng 2026-06-11 17:00:07 +08:00
parent e54a28e571
commit bac0a198ff
9 changed files with 86 additions and 3 deletions

View File

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

View File

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

View File

@ -508,6 +508,14 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re
detailPanel, [detailPanel](const QString& 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,
[&window](const QString& dsId, const QString& msg) {
window.statusBar()->showMessage(

View File

@ -1,5 +1,6 @@
#include "panels/DatasetDetailPage.hpp"
#include <QButtonGroup>
#include <QVBoxLayout>
#include "Glyphs.hpp"
@ -9,6 +10,10 @@
namespace geopro::app {
namespace {
constexpr int kGridTabIndex = 1; // 「网格数据」页签在 tabs 中的索引
}
DatasetDetailPage::DatasetDetailPage(QWidget* parent) : QWidget(parent) {
auto* lay = new QVBoxLayout(this);
lay->setContentsMargins(0, 0, 0, 0);
@ -27,12 +32,29 @@ DatasetDetailPage::DatasetDetailPage(QWidget* parent) : QWidget(parent) {
auto tabbedPanel = buildTabbedPanel(tabs, actions);
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) {
dsId_ = d.dsId;
ddCode_ = d.ddCode;
gridRequested_ = false; // 新数据集 → 网格数据需重新按需加载
rawView_->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

View File

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

View File

@ -20,10 +20,19 @@ DatasetDetailPage* DatasetDetailPanel::pageFor(const QString& dsId) const {
void DatasetDetailPanel::openOrUpdate(const geopro::controller::DatasetDetailController::ChartData& d) {
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);
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) {
if (auto* p = pageFor(dsId)) setCurrentWidget(p);
}

View File

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

View File

@ -21,12 +21,23 @@ public:
geopro::core::ColorScale gridScale;
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);
public slots:
void openDataset(const QString& dsId, const QString& ddCode); // 双击=新建/聚焦页
void focusDataset(const QString& dsId); // 单击=聚焦已开页
// 网格数据懒加载网格页首次激活时调用rows 服务端网格化 1-4s故不随 openDataset 拉)。
void loadGridData(const QString& dsId, const QString& ddCode);
signals:
void chartReady(const ChartData& data);
void gridReady(const GridData& data);
void focusRequested(const QString& dsId);
void loadFailed(const QString& dsId, const QString& message);
private: