feat(view): 左下数据列表 + 对象树收到测线层(对齐原型, 增量2)

- 对象树 GS→TM(测线复选, UserRole+2=tmId); DS(采集批次)移出树, 入左下「数据真实显示栏」
  (QTabWidget 数据/文件; panels/DatasetListPanel)。findTm 按 tmId 查 TM。
- 中央 rebuildCentral 改遍历勾选的测线 → 渲染其 dd_section(可多条共存)。
- 树单击测线→填数据列表; 数据列表单击采集批次→loadDataset(数据详情+异常列表+属性,抽共享 lambda)。
- 启动自动选首个含 dd_section 的测线 + 首数据集(对齐原型默认载入态)。
- structure 取一次共享; app 构建干净; 待人工登录复核。
This commit is contained in:
gaozheng 2026-06-08 09:33:06 +08:00
parent 127e9a0b21
commit 50c4de4019
6 changed files with 194 additions and 74 deletions

View File

@ -13,7 +13,8 @@
桌面 app `geopro_desktop`: 桌面 app `geopro_desktop`:
- **真实登录**:LoginWindow(用户名/密码/图形验证码/记住)→ `verifyCodeCheck` → RSA 加密密码 → `login2` → token → 进工作台。**真机登录成功**;输入框样式已修(三态白底深字)。 - **真实登录**:LoginWindow(用户名/密码/图形验证码/记住)→ `verifyCodeCheck` → RSA 加密密码 → `login2` → token → 进工作台。**真机登录成功**;输入框样式已修(三态白底深字)。
- **工作台 ADS 三区 + 数据详情**: - **工作台 ADS 三区 + 数据详情**:
- **左 对象树**:GS→TM→DS,复选框勾选 → 控制中央显示哪些测线。 - **左上 对象显示栏**:GS→TM(测线),复选框勾选测线 → 控制中央显示哪些测线的 dd_section。
- **左下 数据真实显示栏**(对齐原型):单击测线 → 列其采集批次(数据集,tab 数据/文件);单击采集批次 → 数据详情+异常列表+属性。启动自动选首测线+首数据集。
- **中央 二维地图 / 三维视图**(两个**真内容**,非相机切换): - **中央 二维地图 / 三维视图**(两个**真内容**,非相机切换):
- 二维地图 = `MapLineActor`:测线 `lat/lon` 轨迹**红线**俯视(浅底),像地图。 - 二维地图 = `MapLineActor`:测线 `lat/lon` 轨迹**红线**俯视(浅底),像地图。
- 三维视图 = `CurtainActor`:沿测线的**竖直断面墙**(分段色带,z 纵向夸张×3,沿弯曲测线弯)。中央工具条**仅**「二维地图/三维视图」(对齐原型,无体素按钮)。 - 三维视图 = `CurtainActor`:沿测线的**竖直断面墙**(分段色带,z 纵向夸张×3,沿弯曲测线弯)。中央工具条**仅**「二维地图/三维视图」(对齐原型,无体素按钮)。
@ -76,7 +77,7 @@
9. render 仍部分内联在 main.cpp;可逐步抽到 view/controller。 9. render 仍部分内联在 main.cpp;可逐步抽到 view/controller。
10. **布局对齐原型**(权威参考 `http://prototype.geomative.cn/`;截图存 `.playwright-mcp/`)。**计划见 `plans/2026-06-08-m1-prototype-layout.md`(六面板 + view/ 抽取,增量序列)**。进度: 10. **布局对齐原型**(权威参考 `http://prototype.geomative.cn/`;截图存 `.playwright-mcp/`)。**计划见 `plans/2026-06-08-m1-prototype-layout.md`(六面板 + view/ 抽取,增量序列)**。进度:
- ✅ **增量1 右上「异常列表」**(2026-06-08,`panels/AnomalyListPanel`,与数据详情显隐联动;待人工复核)。 - ✅ **增量1 右上「异常列表」**(2026-06-08,`panels/AnomalyListPanel`,与数据详情显隐联动;待人工复核)。
- **增量2 左下「数据列表」**+ 对象树到 TM 层(DS 移出树入数据列表)。 - **增量2 左下「数据列表」+ 对象树到 TM 层**(2026-06-08,`panels/DatasetListPanel`;树 GS→TM 复选驱动中央, DS 移出树入数据列表 tab 数据/文件, DS 单击→详情+异常+属性, 启动自动选首测线/首数据集;待人工复核)。
- ⬜ **增量3 3D「视图详情」图层浮层**(体素的正确归宿;体素引擎已就绪,UI 待此接入)。 - ⬜ **增量3 3D「视图详情」图层浮层**(体素的正确归宿;体素引擎已就绪,UI 待此接入)。
- ⬜ **增量4 数据详情富工具条 + 电极标记 + 数值标签**;底图影像=DEM/底图任务。 - ⬜ **增量4 数据详情富工具条 + 电极标记 + 数值标签**;底图影像=DEM/底图任务。
- 架构:新面板抽到 `src/app/panels/`(暂随 app 编译,如 login/),控制 main.cpp 体量;后续可升 `src/view/` 库。 - 架构:新面板抽到 `src/app/panels/`(暂随 app 编译,如 login/),控制 main.cpp 体量;后续可升 `src/view/` 库。

View File

@ -58,7 +58,11 @@
- 放右上 dock与"属性"分上下或 tab。**人工复核**:单击数据集→右上列出 3 异常;眼睛切换隐藏对应虚线。 - 放右上 dock与"属性"分上下或 tab。**人工复核**:单击数据集→右上列出 3 异常;眼睛切换隐藏对应虚线。
- 提交 `feat(view): 右上异常列表面板 + 与数据详情异常显隐联动` - 提交 `feat(view): 右上异常列表面板 + 与数据详情异常显隐联动`
### 增量 2左下「数据列表」+ 树结构调整(树到 TMDS 入数据列表) ### 增量 2左下「数据列表」+ 树结构调整 ✅ 已完成(2026-06-08)
> `panels/DatasetListPanel`(populateDatasetList)。树 `populateTree` 改 GS→TM(测线复选,UserRole+2=tmId);
> `findTm` 按 tmId 查 TM。中央 rebuildCentral 改遍历勾选 TM→渲染其 dd_section。左下 dock = QTabWidget
> (数据 QListWidget / 文件占位)。树单击 TM→填数据列表;数据列表单击 DS→`loadDataset`(详情+异常+属性,
> 抽成共享 lambda)。启动自动选首含 dd_section 的测线+首数据集。app 构建净;**待人工复核**。**原计划存档**:
- repo.loadStructure 改为树到 **TM**GS→TMDS 不再进树;新增 `listDatasets(tmId)` 或复用结构。 - repo.loadStructure 改为树到 **TM**GS→TMDS 不再进树;新增 `listDatasets(tmId)` 或复用结构。
(当前样本仅 1 DS grid1 挂 ERT1多 TM/DS 为 mock 结构,先支持单条,预留多条。) (当前样本仅 1 DS grid1 挂 ERT1多 TM/DS 为 mock 结构,先支持单条,预留多条。)
- `src/view/panels/DatasetListPanel`tab 数据/文件;列选中 TM 的采集批次;单击 → 数据详情 + 属性。 - `src/view/panels/DatasetListPanel`tab 数据/文件;列选中 TM 的采集批次;单击 → 数据详情 + 属性。

View File

@ -15,7 +15,8 @@ find_package(nlohmann_json CONFIG REQUIRED)
add_executable(geopro_desktop WIN32 add_executable(geopro_desktop WIN32
main.cpp main.cpp
login/LoginWindow.cpp login/LoginWindow.cpp
panels/AnomalyListPanel.cpp) panels/AnomalyListPanel.cpp
panels/DatasetListPanel.cpp)
target_include_directories(geopro_desktop PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) target_include_directories(geopro_desktop PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})

View File

@ -1,5 +1,6 @@
// M1 工作台(视图重构 Task B正确产品模型。 // M1 工作台(视图重构 Task B正确产品模型。
// - 左 对象树GS→TM→DS复选框。勾选 dd_section → 在中央当前视图显示该数据集,可多条共存。 // - 左上 对象显示栏GS→TM(测线,复选框)。勾选测线 → 在中央显示其 dd_section可多条共存。
// - 左下 数据真实显示栏:单击测线 → 列其采集批次(数据集,tab 数据/文件)。单击采集批次 → 数据详情+异常+属性。
// - 中央「二维地图 / 三维视图」:两个互斥视图(内容不同,不是同一物体换相机)。 // - 中央「二维地图 / 三维视图」:两个互斥视图(内容不同,不是同一物体换相机)。
// 二维地图 = 对每个勾选数据集 buildSurveyLinelat/lon 红线俯视z=0+ applyTop2D浅底背景 // 二维地图 = 对每个勾选数据集 buildSurveyLinelat/lon 红线俯视z=0+ applyTop2D浅底背景
// 三维视图 = 对每个勾选数据集 buildCurtain竖直断面墙actor SetScale(1,1,3) 纵向夸张 + applyFree3D白底 // 三维视图 = 对每个勾选数据集 buildCurtain竖直断面墙actor SetScale(1,1,3) 纵向夸张 + applyFree3D白底
@ -28,6 +29,7 @@
#include <QListWidget> #include <QListWidget>
#include <QListWidgetItem> #include <QListWidgetItem>
#include <QSignalBlocker> #include <QSignalBlocker>
#include <QTabWidget>
#include <QMainWindow> #include <QMainWindow>
#include <QSurfaceFormat> #include <QSurfaceFormat>
#include <QToolBar> #include <QToolBar>
@ -47,6 +49,7 @@
#include "AuthService.hpp" #include "AuthService.hpp"
#include "login/LoginWindow.hpp" #include "login/LoginWindow.hpp"
#include "panels/AnomalyListPanel.hpp" #include "panels/AnomalyListPanel.hpp"
#include "panels/DatasetListPanel.hpp"
#include "CameraPreset.hpp" #include "CameraPreset.hpp"
#include "Scene.hpp" #include "Scene.hpp"
@ -70,13 +73,12 @@
namespace { namespace {
// 角色DS 项的 dd 类型存在 UserRole+1dsId 存在 UserRole // 角色:树 TM 项存 tmId(UserRole+2);数据列表 DS 项的 dsId/ddType 由 panels/DatasetListPanel 定义。
constexpr int kRoleDsId = Qt::UserRole; constexpr int kRoleTmId = Qt::UserRole + 2;
constexpr int kRoleDdType = Qt::UserRole + 1;
// 从对象结构树构建 QTreeWidgetGS → TM → DS 三层 // 从对象结构树构建 QTreeWidgetGS → TM 两层对齐原型DS=采集批次在左下「数据列表」,不进树)
// DS 项可勾选复选框勾选驱动该测线竖直帘面在中央场景显示UserRole 存 dsId、UserRole+1 存 ddType // TM(测线) 项可勾选(复选框):勾选驱动该测线的 dd_section 在中央场景显示UserRole+2 存 tmId
// 网格剖面dd_section默认勾选启动即显示帘面 // 含 dd_section 的测线默认勾选,启动即显示
void populateTree(QTreeWidget* tree, const std::vector<geopro::data::GsNode>& gss) void populateTree(QTreeWidget* tree, const std::vector<geopro::data::GsNode>& gss)
{ {
for (const auto& gs : gss) { for (const auto& gs : gss) {
@ -85,21 +87,27 @@ void populateTree(QTreeWidget* tree, const std::vector<geopro::data::GsNode>& gs
for (const auto& tm : gs.tms) { for (const auto& tm : gs.tms) {
auto* tmItem = new QTreeWidgetItem(gsItem); auto* tmItem = new QTreeWidgetItem(gsItem);
tmItem->setText(0, QString::fromStdString(tm.name)); tmItem->setText(0, QString::fromStdString(tm.name));
for (const auto& ds : tm.dss) { tmItem->setData(0, kRoleTmId, QString::fromStdString(tm.id));
auto* dsItem = new QTreeWidgetItem(tmItem); tmItem->setFlags(tmItem->flags() | Qt::ItemIsUserCheckable);
dsItem->setText(0, QString::fromStdString(ds.name)); const bool hasSection =
dsItem->setData(0, kRoleDsId, QString::fromStdString(ds.id)); std::any_of(tm.dss.begin(), tm.dss.end(),
dsItem->setData(0, kRoleDdType, QString::fromStdString(ds.ddType)); [](const geopro::data::DsNode& d) { return d.ddType == "dd_section"; });
dsItem->setFlags(dsItem->flags() | Qt::ItemIsUserCheckable); tmItem->setCheckState(0, hasSection ? Qt::Checked : Qt::Unchecked);
// 网格剖面默认勾选 → 启动即显帘面;其余默认不勾。
dsItem->setCheckState(
0, ds.ddType == "dd_section" ? Qt::Checked : Qt::Unchecked);
}
} }
} }
tree->expandAll(); tree->expandAll();
} }
// 在结构中按 tmId 查 TM找不到返回 nullptr。
const geopro::data::TmNode* findTm(const std::vector<geopro::data::GsNode>& gss,
const std::string& tmId)
{
for (const auto& gs : gss)
for (const auto& tm : gs.tms)
if (tm.id == tmId) return &tm;
return nullptr;
}
// 读取 RSA 公钥 PEM 全文(登录时密码加密用)。读不到返回空串,登录将报错。 // 读取 RSA 公钥 PEM 全文(登录时密码加密用)。读不到返回空串,登录将报错。
std::string readPem(const std::string& path) std::string readPem(const std::string& path)
{ {
@ -225,13 +233,27 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re
// 放在中央视图下方。 // 放在中央视图下方。
dockManager->addDockWidget(ads::BottomDockWidgetArea, detailDock, centerDockArea); dockManager->addDockWidget(ads::BottomDockWidgetArea, detailDock, centerDockArea);
// 左 dock对象树。 // 项目结构(GS→TM→DS):取一次共享,供树/中央/数据列表查 TM 的数据集。
auto structure = std::make_shared<std::vector<geopro::data::GsNode>>(repo.loadStructure());
// 左上 dock对象树(GS→TM,测线复选)。
auto* tree = new QTreeWidget(); auto* tree = new QTreeWidget();
tree->setHeaderLabel(QStringLiteral("对象")); tree->setHeaderLabel(QStringLiteral("对象显示栏"));
populateTree(tree, repo.loadStructure()); populateTree(tree, *structure);
auto* leftDock = new ads::CDockWidget(QStringLiteral("对象列表")); auto* leftDock = new ads::CDockWidget(QStringLiteral("对象显示栏"));
leftDock->setWidget(tree); leftDock->setWidget(tree);
dockManager->addDockWidget(ads::LeftDockWidgetArea, leftDock); auto* leftArea = dockManager->addDockWidget(ads::LeftDockWidgetArea, leftDock);
// 左下 dock数据真实显示栏(选中测线后列其采集批次=数据集;tab 数据/文件)。
auto* datasetTabs = new QTabWidget();
auto* datasetList = new QListWidget();
datasetList->setAlternatingRowColors(true);
datasetTabs->addTab(datasetList, QStringLiteral("数据"));
auto* fileList = new QListWidget(); // M1 文件 tab 占位
datasetTabs->addTab(fileList, QStringLiteral("文件"));
auto* datasetDock = new ads::CDockWidget(QStringLiteral("数据真实显示栏"));
datasetDock->setWidget(datasetTabs);
dockManager->addDockWidget(ads::BottomDockWidgetArea, datasetDock, leftArea);
// 右上 dock异常列表对齐原型颜色块 + 名称 + 位置/深/尺寸 + 勾选显隐,与数据详情异常联动)。 // 右上 dock异常列表对齐原型颜色块 + 名称 + 位置/深/尺寸 + 勾选显隐,与数据详情异常联动)。
auto* anomalyList = new QListWidget(); auto* anomalyList = new QListWidget();
@ -250,30 +272,19 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re
dockManager->addDockWidget(ads::BottomDockWidgetArea, propDock, rightArea); dockManager->addDockWidget(ads::BottomDockWidgetArea, propDock, rightArea);
// ── 中央视图重建(核心)───────────────────────────────────────────── // ── 中央视图重建(核心)─────────────────────────────────────────────
// 两个互斥视图按当前勾选集整体重建scene.clear() → 对每个勾选 dd_section 加对应 actor。 // 按勾选的测线(TM)整体重建scene.clear() → 对每个勾选 TM 的 dd_section 加对应 actor。
// 二维地图 = buildSurveyLine红线俯视浅底背景+ applyTop2D。 // 二维地图 = buildSurveyLine红线俯视浅底背景+ applyTop2D。
// 三维视图 = buildCurtain断面墙SetScale(1,1,kCurtainZScale) + applyFree3D白底 // 三维视图 = buildCurtain断面墙SetScale(1,1,kCurtainZScale) + applyFree3D白底
// frame 全局共享;切视图/勾选变化都调用此函数重建当前视图。 // frame/structure 全局共享;切视图/勾选变化都调用此函数重建当前视图。
auto rebuildCentral = [scene, rendererPtr, renderWindowPtr, viewMode, &repo, frame, tree]() { auto rebuildCentral = [scene, rendererPtr, renderWindowPtr, viewMode, &repo, frame, tree,
structure]() {
scene->clear(); scene->clear();
const bool is2D = (*viewMode == ViewMode::Map2D); const bool is2D = (*viewMode == ViewMode::Map2D);
rendererPtr->SetBackground(is2D ? 0.96 : 1.0, is2D ? 0.97 : 1.0, is2D ? 0.99 : 1.0); rendererPtr->SetBackground(is2D ? 0.96 : 1.0, is2D ? 0.97 : 1.0, is2D ? 0.99 : 1.0);
// 遍历对象树收集所有勾选的 dd_section逐个加入当前视图内容。 // 渲染单个 dd_section 数据集到当前视图。
QList<QTreeWidgetItem*> stack; auto renderSection = [&](const std::string& id) {
for (int i = 0; i < tree->topLevelItemCount(); ++i) stack.append(tree->topLevelItem(i));
while (!stack.isEmpty()) {
QTreeWidgetItem* cur = stack.takeFirst();
for (int i = 0; i < cur->childCount(); ++i) stack.append(cur->child(i));
const QString dsId = cur->data(0, kRoleDsId).toString();
if (dsId.isEmpty()) continue; // GS/TM 节点忽略
if (cur->checkState(0) != Qt::Checked) continue; // 仅显示勾选的
const QString ddType = cur->data(0, kRoleDdType).toString();
if (ddType != "dd_section") continue; // 当前仅支持剖面网格
const std::string id = dsId.toStdString();
const auto g = repo.loadGrid(id); const auto g = repo.loadGrid(id);
if (is2D) { if (is2D) {
auto line = geopro::render::buildSurveyLine(g, *frame); auto line = geopro::render::buildSurveyLine(g, *frame);
@ -286,6 +297,22 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re
scene->addActor(curtain); scene->addActor(curtain);
} }
} }
};
// 遍历对象树收集所有勾选的测线(TM),渲染其 dd_section 数据集(可多条共存)。
QList<QTreeWidgetItem*> stack;
for (int i = 0; i < tree->topLevelItemCount(); ++i) stack.append(tree->topLevelItem(i));
while (!stack.isEmpty()) {
QTreeWidgetItem* cur = stack.takeFirst();
for (int i = 0; i < cur->childCount(); ++i) stack.append(cur->child(i));
const QString tmId = cur->data(0, kRoleTmId).toString();
if (tmId.isEmpty()) continue; // GS 节点忽略
if (cur->checkState(0) != Qt::Checked) continue; // 仅显示勾选的测线
const auto* tm = findTm(*structure, tmId.toStdString());
if (!tm) continue;
for (const auto& ds : tm->dss)
if (ds.ddType == "dd_section") renderSection(ds.id);
} }
if (is2D) if (is2D)
@ -296,13 +323,22 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re
renderWindowPtr->Render(); renderWindowPtr->Render();
}; };
// 勾选/取消某 dd_section → 重建当前视图内容(勾的才显示;可多条共存)。 // 勾选/取消某测线(TM) → 重建当前视图内容(勾的才显示;可多条共存)。
QObject::connect(tree, &QTreeWidget::itemChanged, tree, QObject::connect(tree, &QTreeWidget::itemChanged, tree,
[rebuildCentral](QTreeWidgetItem* item, int) { [rebuildCentral](QTreeWidgetItem* item, int) {
if (item->data(0, kRoleDsId).toString().isEmpty()) return; // GS/TM 忽略 if (item->data(0, kRoleTmId).toString().isEmpty()) return; // GS 忽略
rebuildCentral(); rebuildCentral();
}); });
// 单击测线(TM) → 左下数据列表填充其采集批次(数据集)。
QObject::connect(tree, &QTreeWidget::itemClicked, tree,
[structure, datasetList](QTreeWidgetItem* item, int) {
const QString tmId = item->data(0, kRoleTmId).toString();
if (tmId.isEmpty()) return; // GS 节点无数据集
const auto* tm = findTm(*structure, tmId.toStdString());
if (tm) geopro::app::populateDatasetList(datasetList, tm->dss);
});
// ── 数据详情共享状态 + 重建 ────────────────────────────────────────── // ── 数据详情共享状态 + 重建 ──────────────────────────────────────────
// 当前选中数据集 id空=未选)与详情显示模式(反演剖面/原数据);切模式或换选中都重建。 // 当前选中数据集 id空=未选)与详情显示模式(反演剖面/原数据);切模式或换选中都重建。
auto currentDsId = std::make_shared<QString>(); auto currentDsId = std::make_shared<QString>();
@ -359,17 +395,10 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re
detailRenderWindowPtr->Render(); detailRenderWindowPtr->Render();
}; };
// ── 单击 DS → 记选中 + 重建数据详情 + 右侧属性(与勾选区分;不改帘面可见性)── // 加载某数据集到「数据详情 + 异常列表 + 属性」(数据列表单击与启动默认共用)。
QObject::connect( auto loadDataset = [&repo, propLabel, currentDsId, rebuildDetail, anomalyList, hiddenAnoms](
tree, &QTreeWidget::itemClicked, tree, const QString& dsId, const QString& name) {
[&repo, propLabel, currentDsId, rebuildDetail, anomalyList, hiddenAnoms]( if (dsId.isEmpty()) return;
QTreeWidgetItem* item, int) {
const QString dsId = item->data(0, kRoleDsId).toString();
if (dsId.isEmpty()) return; // GS/TM 节点无详情
const QString ddType = item->data(0, kRoleDdType).toString();
if (ddType != "dd_section") return;
const QString name = item->text(0);
*currentDsId = dsId; *currentDsId = dsId;
// 右上异常列表:按该数据集异常重填(默认全显);先清隐藏集再填,避免重建时阻塞信号回灌。 // 右上异常列表:按该数据集异常重填(默认全显);先清隐藏集再填,避免重建时阻塞信号回灌。
@ -389,6 +418,17 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re
"vmin / vmax: %4 / %5\n异常: %6 个") "vmin / vmax: %4 / %5\n异常: %6 个")
.arg(name).arg(g.nx()).arg(g.ny()).arg(g.vmin).arg(g.vmax) .arg(name).arg(g.nx()).arg(g.ny()).arg(g.vmin).arg(g.vmax)
.arg(anomalies.size())); .arg(anomalies.size()));
};
// ── 单击左下数据列表的采集批次(DS) → 加载到数据详情/异常/属性 ──
QObject::connect(datasetList, &QListWidget::itemClicked, datasetList,
[loadDataset](QListWidgetItem* item) {
const QString dsId = item->data(geopro::app::kDsIdRole).toString();
const QString ddType = item->data(geopro::app::kDsDdTypeRole).toString();
if (ddType != "dd_section") return; // 仅剖面网格有详情图
const QString name =
item->data(Qt::DisplayRole).toString().section('\n', 0, 0);
loadDataset(dsId, name);
}); });
// ── 异常列表勾选(显隐) → 更新隐藏集 → 重建数据详情 ── // ── 异常列表勾选(显隐) → 更新隐藏集 → 重建数据详情 ──
@ -431,9 +471,27 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re
rebuildCentral(); rebuildCentral();
}); });
// ── 启动默认dd_section 已勾选,但 itemChanged 在 connect 之前触发故未渲染。 // ── 启动默认:测线已勾选,但 itemChanged 在 connect 之前触发故未渲染;这里重建一次中央内容。
// 这里 connect 之后主动按默认视图(二维地图)重建一次中央内容。
rebuildCentral(); rebuildCentral();
// 启动默认:选第一个含 dd_section 的测线 → 填充数据列表 + 加载其首个 dd_section 详情(对齐原型)。
for (const auto& gs : *structure) {
const geopro::data::TmNode* picked = nullptr;
for (const auto& tm : gs.tms) {
const bool hasSection =
std::any_of(tm.dss.begin(), tm.dss.end(),
[](const geopro::data::DsNode& d) { return d.ddType == "dd_section"; });
if (hasSection) { picked = &tm; break; }
}
if (!picked) continue;
geopro::app::populateDatasetList(datasetList, picked->dss);
for (const auto& ds : picked->dss)
if (ds.ddType == "dd_section") {
loadDataset(QString::fromStdString(ds.id), QString::fromStdString(ds.name));
break;
}
break;
}
} }
} // namespace } // namespace

View File

@ -0,0 +1,37 @@
#include "panels/DatasetListPanel.hpp"
#include <QListWidget>
#include <QListWidgetItem>
#include <QString>
namespace geopro::app {
namespace {
// dd 类型 → 中文标注。
QString ddTypeLabel(const std::string& ddType)
{
if (ddType == "dd_section") return QStringLiteral("剖面网格");
if (ddType == "dd_voxel") return QStringLiteral("体素");
return QString::fromStdString(ddType);
}
} // namespace
void populateDatasetList(QListWidget* list, const std::vector<geopro::data::DsNode>& dss)
{
if (!list) return;
list->clear();
for (const auto& ds : dss) {
const QString name = QString::fromStdString(ds.name);
const QString label = ddTypeLabel(ds.ddType);
QString text = name;
if (!label.isEmpty()) text += QStringLiteral("\n%1").arg(label);
auto* item = new QListWidgetItem(text, list);
item->setData(kDsIdRole, QString::fromStdString(ds.id));
item->setData(kDsDdTypeRole, QString::fromStdString(ds.ddType));
}
}
} // namespace geopro::app

View File

@ -0,0 +1,19 @@
#pragma once
#include <vector>
#include "repo/RepoTypes.hpp"
class QListWidget;
namespace geopro::app {
// 数据列表条目角色(与 main.cpp 树一致Qt::UserRole=dsId、+1=ddType
constexpr int kDsIdRole = 0x0100; // Qt::UserRole
constexpr int kDsDdTypeRole = 0x0101; // Qt::UserRole + 1
// 用某测线(TM)的数据集(采集批次)填充 QListWidget对齐原型左下「数据真实显示栏」
// 每条目 = 名称 +ddType 标注UserRole 存 dsId、+1 存 ddType供单击驱动数据详情
// 清空旧条目后重填。
void populateDatasetList(QListWidget* list, const std::vector<geopro::data::DsNode>& dss);
} // namespace geopro::app