feat(app): 工作台接入真实导航(空间/项目/对象树/DS),中央渲染占位
This commit is contained in:
parent
6241eb3a7e
commit
405fb2ae4f
300
src/app/main.cpp
300
src/app/main.cpp
|
|
@ -60,6 +60,10 @@
|
|||
#include "PanelHeader.hpp"
|
||||
#include "Theme.hpp"
|
||||
#include "TopBar.hpp"
|
||||
#include "CentralScene.hpp"
|
||||
#include "WorkbenchNavController.hpp"
|
||||
#include "api/ApiProjectRepository.hpp"
|
||||
#include "panels/ObjectTreePanel.hpp"
|
||||
#include "login/LoginWindow.hpp"
|
||||
#include "panels/AnomalyListPanel.hpp"
|
||||
#include "panels/DatasetListPanel.hpp"
|
||||
|
|
@ -95,41 +99,6 @@
|
|||
|
||||
namespace {
|
||||
|
||||
// 角色:树 TM 项存 tmId(UserRole+2);数据列表 DS 项的 dsId/ddType 由 panels/DatasetListPanel 定义。
|
||||
constexpr int kRoleTmId = Qt::UserRole + 2;
|
||||
|
||||
// 从对象结构树构建 QTreeWidget:GS → TM 两层(对齐原型;DS=采集批次在左下「数据列表」,不进树)。
|
||||
// TM(测线) 项可勾选(复选框):勾选驱动该测线的 dd_section 在中央场景显示;UserRole+2 存 tmId。
|
||||
// 含 dd_section 的测线默认勾选,启动即显示。
|
||||
void populateTree(QTreeWidget* tree, const std::vector<geopro::data::GsNode>& gss)
|
||||
{
|
||||
for (const auto& gs : gss) {
|
||||
auto* gsItem = new QTreeWidgetItem(tree);
|
||||
gsItem->setText(0, QString::fromStdString(gs.name));
|
||||
for (const auto& tm : gs.tms) {
|
||||
auto* tmItem = new QTreeWidgetItem(gsItem);
|
||||
tmItem->setText(0, QString::fromStdString(tm.name));
|
||||
tmItem->setData(0, kRoleTmId, QString::fromStdString(tm.id));
|
||||
tmItem->setFlags(tmItem->flags() | Qt::ItemIsUserCheckable);
|
||||
const bool hasSection =
|
||||
std::any_of(tm.dss.begin(), tm.dss.end(),
|
||||
[](const geopro::data::DsNode& d) { return d.ddType == "dd_section"; });
|
||||
tmItem->setCheckState(0, hasSection ? Qt::Checked : Qt::Unchecked);
|
||||
}
|
||||
}
|
||||
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 全文(登录时密码加密用)。读不到返回空串,登录将报错。
|
||||
std::string readPem(const std::string& path)
|
||||
{
|
||||
|
|
@ -150,7 +119,7 @@ double median(std::vector<double> v)
|
|||
}
|
||||
|
||||
// 当前中央视图(默认二维地图)。二维地图=测线红线俯视;三维视图=断面墙。
|
||||
enum class ViewMode { Map2D, View3D };
|
||||
using geopro::app::ViewMode;
|
||||
|
||||
// 数据详情显示内容(默认网格数据)。网格数据=#18 banded;原数据=#17 散点(对齐原型命名)。
|
||||
enum class DetailMode { Section18, Scatter17 };
|
||||
|
|
@ -168,7 +137,8 @@ constexpr const char* kWgs84 = "EPSG:4326";
|
|||
|
||||
// 在给定 QMainWindow 上构建 M1 工作台。
|
||||
// repo 生命周期须覆盖到事件循环结束(由调用方保证)。
|
||||
void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& repo)
|
||||
void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& repo,
|
||||
geopro::controller::WorkbenchNavController& nav)
|
||||
{
|
||||
// ── 世界系:启动取一次 grid1 的 lat/lon,用中位数作 GeoLocalFrame 原点 ──
|
||||
// 全项目共享(shared_ptr 持有):所有帘面用同一 frame 投影,保证多条测线空间配准。
|
||||
|
|
@ -291,6 +261,11 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re
|
|||
chkTerrain->setEnabled(false); chkTerrain->setToolTip(tip);
|
||||
chkSlice->setEnabled(false); chkSlice->setToolTip(tip);
|
||||
}
|
||||
// 本轮中央不接真实派生层:体素/切片/地形勾选置灰,待下一轮接入对应数据源。
|
||||
for (QCheckBox* c : {chkVoxel, chkSlice, chkTerrain}) {
|
||||
c->setEnabled(false);
|
||||
c->setToolTip(QStringLiteral("(下一轮接入真实数据源)"));
|
||||
}
|
||||
layerLayout->addWidget(layerTitle);
|
||||
layerLayout->addWidget(chkCurtain);
|
||||
layerLayout->addWidget(chkVoxel);
|
||||
|
|
@ -351,29 +326,11 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re
|
|||
// 放在中央视图下方。
|
||||
dockManager->addDockWidget(ads::BottomDockWidgetArea, detailDock, centerDockArea);
|
||||
|
||||
// 项目结构(GS→TM→DS):取一次共享,供树/中央/数据列表查 TM 的数据集。
|
||||
auto structure = std::make_shared<std::vector<geopro::data::GsNode>>(repo.loadStructure());
|
||||
|
||||
// 左上 dock:对象树(GS→TM,测线复选)。表头交给自绘 PanelHeader,隐藏树自带列头(避免双标题)。
|
||||
auto* tree = new QTreeWidget();
|
||||
tree->setHeaderHidden(true);
|
||||
populateTree(tree, *structure);
|
||||
// 选中行高亮不覆盖左侧缩进/折叠箭头列:给 branch 设白底(与树底一致),并用生成的箭头图片
|
||||
// 保留展开/折叠图标(直接给 branch 设背景会触发 Qt 不再画默认箭头的陷阱)。
|
||||
{
|
||||
const QString openArrow = geopro::app::writeChevronIcon(true, QColor("#8A93A3"));
|
||||
const QString closedArrow = geopro::app::writeChevronIcon(false, QColor("#8A93A3"));
|
||||
tree->setStyleSheet(
|
||||
QStringLiteral(
|
||||
"QTreeView::branch { background: #FFFFFF; }"
|
||||
"QTreeView::branch:has-children:!has-siblings:closed,"
|
||||
"QTreeView::branch:closed:has-children:has-siblings { image: url(%1); }"
|
||||
"QTreeView::branch:open:has-children:!has-siblings,"
|
||||
"QTreeView::branch:open:has-children:has-siblings { image: url(%2); }")
|
||||
.arg(closedArrow, openArrow));
|
||||
}
|
||||
// 左上 dock:对象树(真实结构:项目根 → GS → TM)。被动视图,数据由控制器推送。
|
||||
auto* objectTree = new geopro::app::ObjectTreePanel();
|
||||
auto* leftDock = new ads::CDockWidget(QStringLiteral("对象显示栏"));
|
||||
leftDock->setWidget(wrapWithHeader(geopro::app::Glyph::Tree, QStringLiteral("对象显示栏"), tree,
|
||||
leftDock->setWidget(wrapWithHeader(geopro::app::Glyph::Tree, QStringLiteral("对象显示栏"),
|
||||
objectTree,
|
||||
{{geopro::app::Glyph::Plus, QStringLiteral("新建对象")}}));
|
||||
auto* leftArea = dockManager->addDockWidget(ads::LeftDockWidgetArea, leftDock);
|
||||
|
||||
|
|
@ -438,128 +395,14 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re
|
|||
if (auto* bar = area->titleBar()) bar->setVisible(false);
|
||||
}
|
||||
|
||||
// ── 中央视图重建(核心)─────────────────────────────────────────────
|
||||
// 按勾选的测线(TM)整体重建:scene.clear() → 对每个勾选 TM 的 dd_section 加对应 actor。
|
||||
// 二维地图 = buildSurveyLine(红线俯视,浅底背景)+ applyTop2D。
|
||||
// 三维视图 = buildCurtain(断面墙)SetScale(1,1,kCurtainZScale) + applyFree3D(白底)。
|
||||
// frame/structure 全局共享;切视图/勾选变化都调用此函数重建当前视图。
|
||||
auto rebuildCentral = [scene, rendererPtr, renderWindowPtr, viewMode, &repo, frame, tree,
|
||||
structure, showCurtain, showVoxel, showTerrain, showSlice, slicePlane,
|
||||
crs, refElev]() {
|
||||
// 先拆除上次的切片 widget(独立于 scene actor,须显式关闭),再按条件重建。
|
||||
if (*slicePlane) { (*slicePlane)->Off(); *slicePlane = nullptr; }
|
||||
scene->clear();
|
||||
|
||||
const bool is2D = (*viewMode == ViewMode::Map2D);
|
||||
rendererPtr->SetBackground(is2D ? 0.96 : 1.0, is2D ? 0.97 : 1.0, is2D ? 0.99 : 1.0);
|
||||
|
||||
// 渲染单个 dd_section 数据集:二维=测线线;三维=帘面(受「帘面」图层开关控制)。
|
||||
auto renderSection = [&](const std::string& id) {
|
||||
const auto g = repo.loadGrid(id);
|
||||
if (is2D) {
|
||||
auto line = geopro::render::buildSurveyLine(g, *frame);
|
||||
if (line) scene->addActor(line);
|
||||
} else if (*showCurtain) {
|
||||
const auto cs = repo.loadColorScale(id);
|
||||
auto curtain = geopro::render::buildCurtain(g, cs, *frame);
|
||||
if (curtain) {
|
||||
curtain->SetScale(1.0, 1.0, kCurtainZScale); // 纵向夸张成墙
|
||||
scene->addActor(curtain);
|
||||
}
|
||||
}
|
||||
// 中央编排已解耦到 CentralScene::rebuildCentralScene(数据驱动)。本轮空 sections → 空背景占位。
|
||||
// 下一轮:用真实 DS 数据构建 sections 调同一 helper 即复活。
|
||||
auto rebuildCentral = [scene, rendererPtr, renderWindowPtr, viewMode, showCurtain, frame]() {
|
||||
geopro::app::rebuildCentralScene(*scene, rendererPtr, renderWindowPtr, *viewMode,
|
||||
std::vector<geopro::app::SectionInput>{}, *showCurtain,
|
||||
*frame, kCurtainZScale);
|
||||
};
|
||||
|
||||
// 遍历对象树收集所有勾选的测线(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);
|
||||
}
|
||||
|
||||
// 三维「体素 / 切片」图层:两交叉测线散点经 CRS 配准 IDW 成体素。
|
||||
// 体素=GPU 体绘制(与帘面同纵向夸张);切片=vtkImagePlaneWidget 在体素 image 上交互拖切面。
|
||||
// 注:切片 widget 作用于 image 原始米坐标(无 actor 夸张),与夸张后的体绘制存在纵向比例差
|
||||
// (spec M-3 Z 基准统一待办);切片本身演示 dd_slice 交互正确。
|
||||
if (!is2D && (*showVoxel || *showSlice) && crs) {
|
||||
const auto profs = repo.loadVoxelScatters();
|
||||
const auto vcs = repo.loadScatterColorScale("grid1");
|
||||
// 纵向夸张烤进 image(zDisplayScale=kCurtainZScale),使体绘制/切片/帘面纵向一致。
|
||||
auto vr = geopro::render::buildVoxelFromScatters(profs, vcs, *crs, *frame, 1.0, 0.5, 2.0,
|
||||
4.0, kCurtainZScale);
|
||||
if (vr.valid()) {
|
||||
if (*showVoxel) {
|
||||
rendererPtr->AddVolume(vr.volume); // 夸张已烤进 image,无需 actor SetScale
|
||||
}
|
||||
vtkRenderWindowInteractor* interactor = renderWindowPtr->GetInteractor();
|
||||
if (*showSlice && interactor) {
|
||||
const std::vector<double> stops = vcs.stopValues();
|
||||
const double vmn = stops.size() >= 2 ? stops.front() : 0.0;
|
||||
const double vmx = stops.size() >= 2 ? stops.back() : 1.0;
|
||||
auto lut = geopro::render::buildLut(vcs, vmn, vmx, 256);
|
||||
int dims[3] = {1, 1, 1};
|
||||
vr.image->GetDimensions(dims);
|
||||
auto plane = vtkSmartPointer<vtkImagePlaneWidget>::New();
|
||||
plane->SetInteractor(interactor);
|
||||
plane->SetInputData(vr.image);
|
||||
plane->SetPlaneOrientationToXAxes();
|
||||
plane->SetSliceIndex(dims[0] / 2);
|
||||
plane->SetLookupTable(lut);
|
||||
plane->DisplayTextOn();
|
||||
// 左键拖动=移动切面(默认左键是取值光标十字,不直观);中键仍可取值。
|
||||
plane->SetLeftButtonAction(vtkImagePlaneWidget::VTK_SLICE_MOTION_ACTION);
|
||||
plane->SetMiddleButtonAction(vtkImagePlaneWidget::VTK_CURSOR_ACTION);
|
||||
plane->On();
|
||||
*slicePlane = plane;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 三维「地形」图层:GDAL 读 DEM(高程)+影像(EPSG:3857),重投影到世界系,warp 面 + 纹理。
|
||||
if (!is2D && *showTerrain && crs) {
|
||||
// zOffset=refElev 使地形落在测线地表高程附近(不按绝对高程浮空);zScale=1 真实起伏。
|
||||
auto terr = geopro::render::buildTerrain(repo.demPath(), repo.imagePath(), *frame,
|
||||
refElev, 1.0);
|
||||
if (terr) scene->addActor(terr);
|
||||
}
|
||||
|
||||
if (is2D)
|
||||
geopro::render::applyTop2D(rendererPtr);
|
||||
else
|
||||
geopro::render::applyFree3D(rendererPtr);
|
||||
rendererPtr->ResetCamera();
|
||||
renderWindowPtr->Render();
|
||||
};
|
||||
|
||||
// 勾选/取消某测线(TM) → 重建当前视图内容(勾的才显示;可多条共存)。
|
||||
QObject::connect(tree, &QTreeWidget::itemChanged, tree,
|
||||
[rebuildCentral](QTreeWidgetItem* item, int) {
|
||||
if (item->data(0, kRoleTmId).toString().isEmpty()) return; // GS 忽略
|
||||
rebuildCentral();
|
||||
});
|
||||
|
||||
// 单击测线(TM) → 左下数据列表填充其采集批次(数据集) + 动态标题 + 数据 Tab 数量。
|
||||
QObject::connect(tree, &QTreeWidget::itemClicked, tree,
|
||||
[structure, datasetList, datasetTitle, datasetTabs](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) return;
|
||||
geopro::app::populateDatasetList(datasetList, tm->dss);
|
||||
if (datasetTitle)
|
||||
datasetTitle->setText(QStringLiteral("数据集显示栏 · %1").arg(item->text(0)));
|
||||
datasetTabs->setTabText(
|
||||
0, QStringLiteral("数据 (%1)").arg(static_cast<int>(tm->dss.size())));
|
||||
});
|
||||
|
||||
// ── 数据详情共享状态 + 重建 ──────────────────────────────────────────
|
||||
// 当前选中数据集 id(空=未选)与详情显示模式(反演剖面/原数据);切模式或换选中都重建。
|
||||
auto currentDsId = std::make_shared<QString>();
|
||||
|
|
@ -656,15 +499,15 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re
|
|||
.arg(anomalies.size()));
|
||||
};
|
||||
|
||||
// ── 单击左下数据列表的采集批次(DS) → 加载到数据详情/异常/属性 ──
|
||||
// ── 单击左下数据列表的采集批次(DS) → 占位(真实剖面/反演渲染下一阶段接 dd 接口)──
|
||||
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; // 仅剖面网格有详情图
|
||||
[propLabel, detailRendererPtr, detailRenderWindowPtr](QListWidgetItem* item) {
|
||||
const QString name =
|
||||
item->data(Qt::DisplayRole).toString().section('\n', 0, 0);
|
||||
loadDataset(dsId, name);
|
||||
detailRendererPtr->RemoveAllViewProps();
|
||||
detailRenderWindowPtr->Render();
|
||||
propLabel->setText(QStringLiteral(
|
||||
"数据集: %1\n(该数据集的剖面/反演渲染将在下一阶段接入 dd 接口)").arg(name));
|
||||
});
|
||||
|
||||
// ── 异常列表勾选(显隐) → 更新隐藏集 → 重建数据详情 ──
|
||||
|
|
@ -755,45 +598,72 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re
|
|||
rebuildCentral();
|
||||
});
|
||||
|
||||
// ── 启动默认:测线已勾选,但 itemChanged 在 connect 之前触发故未渲染;这里重建一次中央内容。
|
||||
// ── 启动:建立一次空背景中央视图(真实 sections 数据由下一轮接入)。
|
||||
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);
|
||||
if (datasetTitle)
|
||||
datasetTitle->setText(
|
||||
QStringLiteral("数据集显示栏 · %1").arg(QString::fromStdString(picked->name)));
|
||||
datasetTabs->setTabText(
|
||||
0, QStringLiteral("数据 (%1)").arg(static_cast<int>(picked->dss.size())));
|
||||
for (const auto& ds : picked->dss)
|
||||
if (ds.ddType == "dd_section") {
|
||||
loadDataset(QString::fromStdString(ds.id), QString::fromStdString(ds.name));
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// 顶部应用区(静态视觉壳,对齐原型):上=菜单栏(视图/项目管理/业务工具/设备),
|
||||
// 下=工具条(工作空间切换 + 项目 + 帮助/通知/设置 + 用户)。纵向堆叠后挂到主窗口顶部。
|
||||
geopro::app::TopBar* topBar = nullptr;
|
||||
{
|
||||
auto* topChrome = new QWidget(&window);
|
||||
auto* topLayout = new QVBoxLayout(topChrome);
|
||||
topLayout->setContentsMargins(0, 0, 0, 0);
|
||||
topLayout->setSpacing(0);
|
||||
topLayout->addWidget(geopro::app::buildMenuBar(topChrome));
|
||||
topLayout->addWidget(new geopro::app::TopBar(topChrome));
|
||||
topBar = new geopro::app::TopBar(topChrome);
|
||||
topLayout->addWidget(topBar);
|
||||
window.setMenuWidget(topChrome);
|
||||
}
|
||||
|
||||
// ── 控制器 ↔ UI 信号接线(导航壳)──────────────────────────────────────
|
||||
QObject::connect(topBar, &geopro::app::TopBar::workspaceSwitchRequested, &nav,
|
||||
&geopro::controller::WorkbenchNavController::switchWorkspace);
|
||||
QObject::connect(topBar, &geopro::app::TopBar::projectSwitchRequested, &nav,
|
||||
&geopro::controller::WorkbenchNavController::switchProject);
|
||||
QObject::connect(objectTree, &geopro::app::ObjectTreePanel::tmClicked, &nav,
|
||||
&geopro::controller::WorkbenchNavController::selectTm);
|
||||
|
||||
QObject::connect(&nav, &geopro::controller::WorkbenchNavController::workspacesLoaded, topBar,
|
||||
[topBar](const std::vector<geopro::data::Workspace>& list, const QString& cur) {
|
||||
topBar->setWorkspaces(list, cur);
|
||||
});
|
||||
QObject::connect(&nav, &geopro::controller::WorkbenchNavController::projectsLoaded, topBar,
|
||||
[topBar](const std::vector<geopro::data::ProjectSummary>& list,
|
||||
const QString& cur) { topBar->setProjects(list, cur); });
|
||||
QObject::connect(&nav, &geopro::controller::WorkbenchNavController::structureLoaded, objectTree,
|
||||
[objectTree, datasetList, datasetTitle, datasetTabs](
|
||||
const QString& projectName,
|
||||
const std::vector<geopro::data::StructNode>& nodes) {
|
||||
objectTree->setStructure(projectName, nodes);
|
||||
datasetList->clear(); // 切项目清空 DS 列表
|
||||
if (datasetTitle) datasetTitle->setText(QStringLiteral("数据集显示栏"));
|
||||
datasetTabs->setTabText(0, QStringLiteral("数据"));
|
||||
});
|
||||
QObject::connect(&nav, &geopro::controller::WorkbenchNavController::datasetsLoaded, datasetList,
|
||||
[datasetList, datasetTitle, datasetTabs](
|
||||
const QString&, const std::vector<geopro::data::DsNode>& list) {
|
||||
geopro::app::populateDatasetList(datasetList, list);
|
||||
if (datasetTitle)
|
||||
datasetTitle->setText(QStringLiteral("数据集显示栏"));
|
||||
datasetTabs->setTabText(
|
||||
0, QStringLiteral("数据 (%1)").arg(static_cast<int>(list.size())));
|
||||
});
|
||||
QObject::connect(&nav, &geopro::controller::WorkbenchNavController::loadFailed, objectTree,
|
||||
[objectTree, &window](const QString& stage, const QString& msg) {
|
||||
if (stage == QStringLiteral("structure") ||
|
||||
stage == QStringLiteral("projects"))
|
||||
objectTree->showMessage(QStringLiteral("加载失败:%1").arg(msg));
|
||||
window.statusBar()->showMessage(
|
||||
QStringLiteral("加载失败(%1):%2").arg(stage, msg), 8000);
|
||||
});
|
||||
QObject::connect(&nav, &geopro::controller::WorkbenchNavController::busyChanged, &window,
|
||||
[](bool busy) {
|
||||
if (busy)
|
||||
QApplication::setOverrideCursor(Qt::WaitCursor);
|
||||
else
|
||||
QApplication::restoreOverrideCursor();
|
||||
});
|
||||
|
||||
// 底部状态栏:常驻显示坐标系与世界系原点(wayfinding:用户随时知道当前空间基准)。
|
||||
window.statusBar()->showMessage(
|
||||
QStringLiteral("就绪 | 坐标系 %1 | 世界系原点 %2, %3")
|
||||
|
|
@ -851,13 +721,19 @@ int main(int argc, char* argv[])
|
|||
geopro::data::LocalSampleRepository repo(
|
||||
"D:/Git/lanbingtech/geopro/docs/剖面网格数据的色阶数据2等文件/");
|
||||
|
||||
// 导航仓储 + 控制器(接口/逻辑层):用同一共享会话 ApiClient。
|
||||
geopro::data::ApiProjectRepository projectRepo(api);
|
||||
geopro::controller::WorkbenchNavController nav(projectRepo);
|
||||
|
||||
QMainWindow window;
|
||||
window.setWindowTitle(QStringLiteral("Geopro 3.0 — 项目分析视图 (M1)"));
|
||||
window.resize(1280, 800);
|
||||
window.setMinimumSize(1024, 680); // 防止停靠面板被压到不可用尺寸
|
||||
|
||||
buildWorkbench(window, repo);
|
||||
buildWorkbench(window, repo, nav);
|
||||
window.show();
|
||||
|
||||
nav.start(); // 进入工作台后拉真实 空间/项目/结构
|
||||
|
||||
return app.exec();
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue