#pragma once #include #include #include #include "interact/SlicePlaneMath.hpp" // geopro::render::interact::SliceAxis #include "repo/CategoryDescriptor.hpp" // geopro::data::CategoryDescriptor / OpKind / FilterKind #include "repo/RepoTypes.hpp" class QTreeWidget; class QTreeWidgetItem; class QComboBox; class QDateEdit; class QLabel; class QToolButton; class QTimer; class QWidget; namespace geopro::data { class DatasetFieldDictionary; } namespace geopro::app { class DateRangeEdit; class SectionIconBar; // 单个数据类型大类段(spec §7):段头(标题/折叠 + 段头图标条)+ 段体(折叠筛选行 + 可勾选数据树)。 // 段头图标条由 descriptor.operations(OpKind) 驱动;筛选行由 descriptor.filters(FilterKind) 驱动、默认折叠。 // 勾选数据行 = 渲染(帘面/体素/切片/二维面);GenerateVolume 图标据当前勾选源发 generateVolumeRequested。 class CategorySection : public QWidget { Q_OBJECT public: CategorySection(const geopro::data::CategoryDescriptor& desc, geopro::data::DatasetFieldDictionary* dict, QWidget* parent = nullptr); // 对象树同源的扁平 GS/TM 节点(段体容器分层用;Task 12 接入真实结构,当前仅存储)。 void setStructure(const std::vector& nodes); void setDatasets(const std::vector& rows); void setChecked(const QString& dsId, bool on); // 按 dsId 勾选/取消(新建切片自动勾选等场景) // 渲染中:该行复选框替换为等待 spinner(busy=true)/复原(false)。busy 期间保持勾选、动画由定时器驱动。 void setBusy(const QString& dsId, bool busy); void clearAllBusy(); // 撤回本段所有 spinner(失败兜底) void selectItem(const QString& dsId); // 程序化选中某行(VTK→list 反向联动);空/未找到=清选中 void clearSelection(); // 清本段树选中(信号阻断,不回发 datasetSelected);供跨段全列互斥用 QStringList checkedIds() const { return checkedDsIds(); } // 当前勾选 ds(异常显隐同步用) void refreshArrayFilter() { refreshArrayCombo(); } // 装置枚举异步加载后重填下拉 bool isExpanded() const; // 段头展开态(供外层按折叠状态重分配 stretch,实现"折叠向上收") void ensureExpanded(); // 展开本段(折叠则展开):滚动定位前确保目标行可见 QTreeWidget* listWidget() const { return list_; } // 段体树(外层滚动定位用) QTreeWidgetItem* itemFor(const QString& dsId) const; // 按 dsId 取行(无则 nullptr) // 该 ds 所在层级子树的全部 dsId(贴合坐标轴子树盒,spec §2/§3.2 决策 3):先归一到子树根 // (向上找最高非容器祖先=三维体行),再自根向下收集整棵子树(体+切片+异常)的非空 dsId。 // 故选中体/切片/异常都归到同一个「该体子树」盒。dsId 不在本段则返回空。 QStringList subtreeDsIds(const QString& dsId) const; bool hasRenderableRows() const; // 段体是否含可渲染数据行(非 container 容器节点),供单列动态显隐 protected: // 段体树 viewport 事件过滤:在默认处理改选之前记录「按下瞬间该行是否已选中」,供单击已选行的 toggle 取消判定。 bool eventFilter(QObject* obj, QEvent* ev) override; signals: void checkedDatasetsChanged(const QStringList& dsIds); // 数据行勾选=渲染 void collapsedChanged(); // 折叠/展开切换 → 外层 CategoryAnalysisTab 重排各段 stretch void generateVolumeRequested(const QString& dsTypeCode, const QStringList& sourceDsIds); // 段头「+新增三维体」(接收方按 sourceIds 解析类型) void planeZChanged(const QString& typeId, double z); // PlaneZ 滑块:整体升降该 2D 类型平面(z=绝对高程,米) void basemapKindChanged(const QString& typeId, int kind); // 底图弹窗:矢量平面(0)/无(1) void basemapOpacityChanged(const QString& typeId, double o); // 底图弹窗透明度滑块[0,1](防抖发射) void detailRequested(const QString& dsId, const QString& ddCode, const QString& name); // 右键「详情」=属性弹窗 // 双击某行(决策6/T4):适配相机到该 ds 子树空间范围 + 联动中下方详情面板(无详情页类型静默)。 // 与 detailRequested(右键属性弹窗)分开,使双击「只适配 + 图表联动」,三维体等无图表页时静默。 void datasetActivated(const QString& dsId, const QString& ddCode, const QString& name); void deleteDatasetRequested(const QString& dsId, const QString& ddCode); // 右键删除(切片/异常) // ── 三维体段右键操作(迁自旧 Column3DAnalysis,全接)── void sliceRequested(geopro::render::interact::SliceAxis axis, const QString& volumeDsId); // 体→生成切片(轴+目标体) void colorScaleRequested(const QString& dsId); // 体/切片→色阶 void radarGainModeRequested(const QString& dsId, int mode); // 雷达体→显示增益模式(0关/1AGC/2保幅tpow) void sliceSaveRequested(const QString& dsId); // 切片→保存位姿 void sliceSaveAsRequested(const QString& dsId); // 切片→另存 void sliceExportImageRequested(const QString& dsId); // 切片→导出图片 void sliceExportDatRequested(const QString& dsId); // 切片→导出 dat void anomalyVisibilityChanged(const QString& dsId, bool vis); // 异常→显示/隐藏 void datasetSelected(const QString& dsId, const QString& ddCode); // 树选中行→VTK 高亮联动(切片/异常) private: void showContextMenu(const QPoint& pos); // 段体树右键菜单(详情 + 删除) void showPlaneZPopup(QToolButton* host); // PlaneZ 图标:弹 z 值滑块 popup → planeZChanged void showBasemapPopup(QToolButton* host); // Basemap 图标:弹 矢量平面/无 + 透明度滑块 popup void rebuildList(); // 据 rows_(经装置/日期筛选)重建段体树并复原勾选 void refreshArrayCombo(); // 据当前 rows_ 重填装置类型下拉项(经字典 value→中文) void emitChecked(); // 收集勾选 → checkedDatasetsChanged QStringList checkedDsIds() const; bool passesFilters(const geopro::data::DsRow& row) const; // 装置类型 + 采集时间范围 geopro::data::CategoryDescriptor desc_; geopro::data::DatasetFieldDictionary* dict_ = nullptr; std::vector rows_; std::vector structure_; QToolButton* header_ = nullptr; // 折叠头(标题 + 箭头) SectionIconBar* iconBar_ = nullptr; // 段头响应式图标条(由 desc_.operations 装配) QWidget* body_ = nullptr; // 段体容器(折叠时隐藏) QWidget* filterRow_ = nullptr; // 筛选行容器(默认折叠,由 Filter 图标 toggle) QComboBox* arrayCombo_ = nullptr; // 装置类型筛选(仅 desc_.filters 含 ArrayType) DateRangeEdit* dateRange_ = nullptr; // 采集时间范围筛选(起止双日历,可清空) QTreeWidget* list_ = nullptr; QTimer* spinTimer_ = nullptr; // 驱动 busy 行 spinner 旋转(有 busy 行时运行) int spinAngle_ = 0; // 当前 spinner 角度(度) double lastPlaneZ_ = 0.0; // 上次 z 值滑块设定的平面高程(重开 popup 时回显,无则 0;直接平移故无防抖) int lastBasemapKind_ = 0; // 上次底图选择(0=矢量平面/1=无),重开 popup 时回显 int lastBasemapOpacity_ = 50; // 上次底图透明度(0–100,重开 popup 回显;默认 50) QTimer* basemapOpacityTimer_ = nullptr; // 底图透明度滑块发射防抖(透明度改动会触发瓦片重铺,仍需防抖) double pendingBasemapOpacity_ = 0.5; // 防抖待发的底图透明度[0,1](定时器到点发射 basemapOpacityChanged) // 单击已选行取消选中(toggle-off):eventFilter 于按下瞬间(默认改选前)记录被按行及其选中态;itemClicked 据此取消。 QTreeWidgetItem* pressedItem_ = nullptr; // 本次左键按下的行(itemAt(press),与 itemClicked 的行比对) bool pressedSelected_ = false; // 按下瞬间该行是否已选中(true 且非勾选框点击时,单击取消选中) bool checkToggledThisPress_ = false; // 本次按下是否切换了勾选框(点勾选框只改渲染,不取消选中) }; } // namespace geopro::app