chore(ela): ElaWidgetTools 评估 spike + 全面迁移计划 + 构建 TEMP 兜底

- spike/ela: 隔离 demo 验证 ElaWindow + ADS 内嵌 + QVTK + 明暗切换(Qt6.11.1/MSVC 构建通过)
- CMakeLists: FetchContent 引入 ElaWidgetTools(fork,SOURCE_SUBDIR 仅编库) + 挂 spike
- build.bat: TEMP/TMP 重定向到 D: 构建目录,规避 C: 盘满导致的 LNK1108
- docs: 全面 Ela 化迁移计划(P0-P4 + 控件映射表 + 风险登记)
This commit is contained in:
gaozheng 2026-06-09 21:23:14 +08:00
parent 1a9fb72cf0
commit 6df2c4832c
5 changed files with 242 additions and 0 deletions

View File

@ -63,7 +63,21 @@ FetchContent_Declare(qtkeychain
GIT_TAG v0.14.0)
FetchContent_MakeAvailable(qtkeychain)
# ElaWidgetTools spike feat/elawidgettools Fluent UI for QWidget
# RainbowCandyX fork Qt6.10+ 6.11 SOURCE_SUBDIR
# /PySide bindingsMIT static DLL
# find_package(Qt6 Widgets/WidgetsPrivate) .qrc(靠全局 AUTORCC)
set(ELAWIDGETTOOLS_BUILD_STATIC_LIB ON CACHE BOOL "" FORCE)
FetchContent_Declare(elawidgettools
GIT_REPOSITORY https://github.com/RainbowCandyX/ElaWidgetTools.git
GIT_TAG main
SOURCE_SUBDIR ElaWidgetTools)
FetchContent_MakeAvailable(elawidgettools)
add_subdirectory(src)
# ElaWidgetTools spike demo geopro_desktop
add_subdirectory(spike/ela)
enable_testing()
add_subdirectory(tests)

View File

@ -19,6 +19,12 @@ set "ROOT=%~dp0"
set "BUILDDIR=%ROOT%build\release"
set "PRESET=msvc-release"
REM 把临时目录指向 D: 的构建目录,规避 C: 盘满导致链接器写 %TEMP% 失败(LNK1108)。
REM 仅作用于本次构建(setlocal 作用域),不污染用户 shell。注意仍建议尽快清理 C: 盘。
set "TEMP=%BUILDDIR%\tmp"
set "TMP=%BUILDDIR%\tmp"
if not exist "%TEMP%" mkdir "%TEMP%"
REM --- locate Visual Studio (vswhere lives at a fixed path) ---
set "VSWHERE=%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe"
if not exist "%VSWHERE%" (

View File

@ -0,0 +1,83 @@
# geopro_desktop → ElaWidgetTools (Fluent) 迁移计划
**分支**`feat/elawidgettools` **日期**2026-06-09 **决策**:全面 Ela 化(最彻底),支持明/暗主题。
**前置评估已完成**spike(`spike/ela/`) 证明 ElaWidgetTools(RainbowCandyX fork) 可用官方 Qt 6.11.1 + MSVC 经 FetchContent 构建ElaWindow + ADS 内嵌 + QVTK 渲染均可行ElaTheme 明暗切换可用,但**只自动覆盖 Ela\* 控件与 ElaWindow 外壳**,标准 QWidget/ADS/VTK 需手工主题联动。
---
## 0. 硬前提(动手前必须满足)
- **P0-a 清理 C: 盘**:当前 C: 0 GB 可用,链接器写 `%TEMP%` 失败(`LNK1108`)。迁移需大量构建验证。需用户清理 C:;我同时把 `TEMP/TMP→D:` 兜底固化进 `build.bat`,避免反复手动重定向。
- **P0-b 验证靠用户**:每阶段我构建通过后,用户运行 + 截图,我据反馈迭代(登录门槛 + GUI我无法目视
- **P0-c 回退保障**:全程保留 `GEOPRO_UI_SHELL=classic|ela` 环境变量开关,可在「现 QMainWindow 壳」与「ElaWindow 壳」间切换,便于 A/B 与回退;迁移稳定后再移除。
## 1. 依赖固化P0 工程)
- ElaWidgetTools 从 spike 提升为**正式依赖**`FetchContent` 的 `GIT_TAG``main` **钉到具体 commit**(可复现)。
- **静/动态**先静态MIT省 DLL若遇静态资源字体/SVG 图标 .qrc被剥离导致图标缺失改动态`ELAWIDGETTOOLS_BUILD_STATIC_LIB OFF` + DLL 随 `TARGET_RUNTIME_DLLS` 拷贝)。
- **插件部署**:把 `platforms / styles / imageformats / iconengines`(含 SVG 图标用的 `qsvg`/`qsvgicon`)部署接进 `geopro_desktop` 的 post-build今天 spike 是手动拷的要正式化windeployqt 会被 ADS 的 DLL 依赖卡住,改为显式 copy Qt plugins
## 2. P1 — 换壳(带开关)
- 新建 `ElaShellWindow`(继承 `ElaWindow`)或在 `main()` 分支构造。把现有 `buildWorkbench()` 产出的中心内容ADS `CDockManager` + 工具条)作为 ElaWindow 的一个 page/central content 挂入(`addPageNode` / `setCentralCustomWidget`)。
- `eApp->init()``QApplication` 后调用;保留高 DPI 与 QVTK surface format 设置顺序。
- **dock 持久化注意**ElaWindow 接管后,`restoreState` 后重隐藏 ADS 标题栏的时序修复(已在主分支)需在新壳下复核。
- **验收**`GEOPRO_UI_SHELL=ela` 启动 → 登录 → 工作台ADS 停靠可拖动;中央/详情 VTK 正常;导航/标题栏 Fluent 外观。截图确认。
## 3. P2 — 主题桥(明/暗覆盖所有非 Ela 面)
- 新建 `ThemeBridge`:监听 `ElaTheme::themeModeChanged`,把明/暗同步到:
1. **全局 QSS**:把 `Theme.cpp``kStyleSheet` 拆成「明」与「暗」两版(用已有 `type/space/radius/semantic` 令牌派生暗色盘),按主题切换。
2. **ADS 停靠区**`CDockManager::setStyleSheet` 明/暗两套。
3. **VTK 背景**:中央 + 详情 renderer 背景随主题切深/浅底并 `Render()`
4. **内联样式面板**PanelHeader / TopBar / LoginWindow / main 浮层 的内联 QSS 改为「跟随主题」(去硬编码色,引用桥提供的明/暗令牌)。
- **暗色盘设计**:在 `Theme.hpp` 增加暗色语义surface/ink/border/accent 的暗版保持品牌蓝在暗底的可读性与对比度≥4.5:1
- **验收**:明/暗一键切换,全界面(外壳+停靠+面板+VTK协调一致、无残留亮/暗块;对比度达标。截图明、暗各一。
## 4. P3 — 全面控件 Ela 化(工作量主体)
逐面替换标准控件为 `Ela*` 等价物,"白嫖"明暗与 Fluent 观感。映射(精确 Ela 类名在实施时按头文件确认):
| 现状 | → Ela 等价 | 所在 |
|---|---|---|
| QPushButton | ElaPushButton | LoginWindow / 各处 |
| QLineEdit | ElaLineEdit | LoginWindow / ProjectListDialog 过滤 |
| QCheckBox | ElaCheckBox | LoginWindow / 图层浮层 / 异常列表 |
| QComboBox | ElaComboBox | ProjectListDialog / 全局 |
| QLabel(文本) | ElaText | 各处文本/标题 |
| QToolButton(Tab/操作) | ElaToolButton / ElaIconButton | PanelHeader / TopBar |
| QMenuBar / QMenu | ElaMenuBar / ElaMenu | TopBar |
| QTreeWidget | ElaTreeView(+model) 或保留+主题联动 | ObjectTreePanel |
| QListWidget | ElaListView(+model) 或保留+联动 | Dataset/Anomaly 面板 |
| QTableWidget | ElaTableView 或保留+联动 | ProjectListDialog |
| QProgressBar | ElaProgressBar | 全局 |
| QStatusBar | ElaStatusBar 或保留+联动 | main |
| QTabWidget/分段 | ElaTabWidget / ElaToggleSwitch | PanelHeader 数据/文件 |
| QDialog(登录) | ElaWidget/ElaWindow 风格弹窗 | LoginWindow |
| **保留(无替代)** | QVTKOpenGLStereoWidget、ADS CDockManager | 中央/详情/停靠 |
- 树/列表/表若用 Ela 的 View 需改 model成本高可分两步先保留 widget 版做主题联动,后续再评估换 View。
- 登录窗重做为 Fluent 风格(沿用现有令牌与文案)。
## 5. P4 — 收尾
- 插件部署正式化、ElaWidgetTools 版本锁定、静态资源核验。
- 去掉 `GEOPRO_UI_SHELL` 过渡开关(确认稳定后)。
- 开源声明ElaWidgetTools(MIT)、ADS(LGPLv2.1)、Qt(LGPL) NOTICE 归集。
- 回归:登录、项目切换、对象树、数据集/文件分页、异常、VTK 各视图、dock 持久化。
## 风险登记
| 风险 | 缓解 |
|---|---|
| Qt 6.11 Windows Popup 渲染(作者红旗) | spike 已初验P1 重点复核菜单/下拉/提示;必要时打 fork 的条件补丁 |
| ADS 在 ElaWindow 内主题/交互异常 | spike 已验内嵌P2 专门做 ADS 明暗 QSS |
| Ela View 需 model 重写(树/列表/表) | 分步:先 widget 版主题联动,再评估换 View |
| 静态库资源剥离(图标/字体缺失) | 改动态库 |
| 我无法目视 | 每阶段用户运行+截图验收 |
| C: 满导致构建反复失败 | 清理 C: + TEMP→D: 固化进 build.bat |
| 大重构回归 | 全程 env 开关可回退;主分支零影响 |
## 执行顺序
P0 → P1验收→ P2验收→ P3按面板分批每批验收→ P4。每步构建通过后由用户运行+截图确认再进下一步。

18
spike/ela/CMakeLists.txt Normal file
View File

@ -0,0 +1,18 @@
# ElaWidgetTools spike demo exe feat/elawidgettools
# geopro_desktop ElaWidgetTools(Fluent ) + ADS + VTK
add_executable(geopro_ela_spike WIN32 main.cpp)
target_link_libraries(geopro_ela_spike PRIVATE
Qt6::Core Qt6::Gui Qt6::Widgets
ElaWidgetTools
ads::qt6advanceddocking
${VTK_LIBRARIES})
vtk_module_autoinit(TARGETS geopro_ela_spike MODULES ${VTK_LIBRARIES})
if(WIN32)
add_custom_command(TARGET geopro_ela_spike POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
$<TARGET_RUNTIME_DLLS:geopro_ela_spike> $<TARGET_FILE_DIR:geopro_ela_spike>
COMMAND_EXPAND_LISTS)
endif()

121
spike/ela/main.cpp Normal file
View File

@ -0,0 +1,121 @@
// ElaWidgetTools 评估 spike隔离 demo不属于产品 app仅 feat/elawidgettools 分支评估用)。
// 一锤定音验证四件事:
// ① 用你们官方 Qt 6.11.1 + MSVC 经 FetchContent 能否构建 ElaWidgetTools(RainbowCandyX fork)
// ② ElaWindow 的 Fluent 观感在你们机器上渲染是否正常(重点看 Qt6.11 的 Popup/弹窗);
// ③ Qt Advanced Docking System(ADS) 能否内嵌进 ElaWindow
// ④ QVTKOpenGLStereoWidget 视口在 ElaWindow + ADS 内能否正常渲染。
// 结论决定是否值得对真实 app 做外壳重构。
#include <QApplication>
#include <QLabel>
#include <QSurfaceFormat>
#include <QVBoxLayout>
#include <QWidget>
#include "ElaApplication.h"
#include "ElaDef.h"
#include "ElaPushButton.h"
#include "ElaText.h"
#include "ElaTheme.h"
#include "ElaWindow.h"
#include <DockManager.h>
#include <DockWidget.h>
#include <QVTKOpenGLStereoWidget.h>
#include <vtkActor.h>
#include <vtkConeSource.h>
#include <vtkGenericOpenGLRenderWindow.h>
#include <vtkNew.h>
#include <vtkPolyDataMapper.h>
#include <vtkProperty.h>
#include <vtkRenderer.h>
int main(int argc, char* argv[])
{
QApplication::setHighDpiScaleFactorRoundingPolicy(
Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
QSurfaceFormat::setDefaultFormat(QVTKOpenGLStereoWidget::defaultFormat());
QApplication app(argc, argv);
eApp->init(); // ElaApplication 初始化Fluent 主题/字体/动画基建)
ElaWindow window;
window.setWindowTitle(QStringLiteral("ElaWidgetTools Spike — Fluent + ADS + VTK"));
window.resize(1200, 760);
// 「工作台」页内嵌 ADS 停靠管理器:验证 ADS 能否在 ElaWindow 内正常工作。
auto* dockHost = new QWidget;
auto* hostLay = new QVBoxLayout(dockHost);
hostLay->setContentsMargins(0, 0, 0, 0);
auto* dockManager = new ads::CDockManager(dockHost);
hostLay->addWidget(dockManager);
// dock1Fluent 控件样例看观感ElaText / ElaPushButton 与标准控件对照)。
auto* sample = new QWidget;
sample->setObjectName(QStringLiteral("sampleHost"));
auto* sLay = new QVBoxLayout(sample);
sLay->setContentsMargins(16, 16, 16, 16);
sLay->setSpacing(12);
sLay->addWidget(new ElaText(QStringLiteral("ElaText —— Fluent 文本"), sample));
sLay->addWidget(new ElaPushButton(QStringLiteral("ElaPushButton 主操作"), sample));
sLay->addWidget(new QLabel(QStringLiteral("(对照)标准 QLabel"), sample));
// 浅/深主题切换ElaWidgetTools 内置 ElaTheme运行期一键切换整套 Fluent 主题。
auto* themeBtn = new ElaPushButton(QStringLiteral("切换 浅色 / 深色"), sample);
QObject::connect(themeBtn, &QPushButton::clicked, themeBtn, [] {
eTheme->setThemeMode(eTheme->getThemeMode() == ElaThemeType::Light ? ElaThemeType::Dark
: ElaThemeType::Light);
});
sLay->addWidget(themeBtn);
sLay->addStretch();
auto* d1 = new ads::CDockWidget(QStringLiteral("Fluent 控件"));
d1->setWidget(sample);
dockManager->addDockWidget(ads::LeftDockWidgetArea, d1);
// dock2QVTK 视口(蓝色锥体),验证 VTK 在 ElaWindow + ADS 内渲染。
auto* vtkWidget = new QVTKOpenGLStereoWidget;
vtkNew<vtkGenericOpenGLRenderWindow> renderWindow;
vtkNew<vtkRenderer> renderer;
renderer->SetBackground(1.0, 1.0, 1.0);
vtkWidget->setRenderWindow(renderWindow);
renderWindow->AddRenderer(renderer);
vtkNew<vtkConeSource> cone;
cone->SetResolution(48);
vtkNew<vtkPolyDataMapper> mapper;
mapper->SetInputConnection(cone->GetOutputPort());
vtkNew<vtkActor> actor;
actor->SetMapper(mapper);
actor->GetProperty()->SetColor(0.18, 0.42, 0.71); // 品牌蓝 #2D6CB5 近似
renderer->AddActor(actor);
renderer->ResetCamera();
auto* d2 = new ads::CDockWidget(QStringLiteral("VTK 视口(锥体)"));
d2->setWidget(vtkWidget);
dockManager->addDockWidget(ads::RightDockWidgetArea, d2);
// 关键发现演示ADS 停靠区与普通 QWidget 不会自动跟随 ElaTheme只有 Ela* 控件与
// ElaWindow 外壳跟随)。这里手动把「停靠区背景 + 普通容器 + VTK 背景」同步到当前主题,
// 并监听 ElaTheme::themeModeChanged。这段「同步」正是真集成时要为每个非 Ela 面板付出的成本。
auto* rendererPtr = renderer.Get();
auto* rwPtr = renderWindow.Get();
auto applyContentTheme = [dockManager, sample, rendererPtr, rwPtr](ElaThemeType::ThemeMode mode) {
const bool dark = (mode == ElaThemeType::Dark);
const QString bg = dark ? QStringLiteral("#1E1F22") : QStringLiteral("#FFFFFF");
const QString fg = dark ? QStringLiteral("#E3E3E3") : QStringLiteral("#1F2A3D");
dockManager->setStyleSheet(
QStringLiteral("ads--CDockAreaWidget, ads--CDockContainerWidget { background:%1; }")
.arg(bg));
sample->setStyleSheet(
QStringLiteral("#sampleHost { background:%1; } #sampleHost QLabel { color:%2; }")
.arg(bg, fg));
rendererPtr->SetBackground(dark ? 0.11 : 1.0, dark ? 0.12 : 1.0, dark ? 0.14 : 1.0);
rwPtr->Render();
};
QObject::connect(eTheme, &ElaTheme::themeModeChanged, dockManager,
[applyContentTheme](ElaThemeType::ThemeMode m) { applyContentTheme(m); });
applyContentTheme(eTheme->getThemeMode()); // 初始同步当前主题
window.addPageNode(QStringLiteral("工作台"), dockHost);
window.show();
return app.exec();
}