feat(vtk): 数据集单栏 段按数据动态显隐 + 全空占位提示
This commit is contained in:
parent
72b5f6f25f
commit
286054720e
|
|
@ -1,6 +1,7 @@
|
||||||
#include "panels/columns/CategoryAnalysisTab.hpp"
|
#include "panels/columns/CategoryAnalysisTab.hpp"
|
||||||
|
|
||||||
#include <QAbstractItemView>
|
#include <QAbstractItemView>
|
||||||
|
#include <QLabel>
|
||||||
#include <QPointer>
|
#include <QPointer>
|
||||||
#include <QScrollArea>
|
#include <QScrollArea>
|
||||||
#include <QScrollBar>
|
#include <QScrollBar>
|
||||||
|
|
@ -10,6 +11,7 @@
|
||||||
|
|
||||||
#include "Theme.hpp"
|
#include "Theme.hpp"
|
||||||
#include "panels/columns/CategorySection.hpp"
|
#include "panels/columns/CategorySection.hpp"
|
||||||
|
#include "repo/CategoryDescriptor.hpp" // categoryCatalog(含 trajectory 段)
|
||||||
|
|
||||||
namespace geopro::app {
|
namespace geopro::app {
|
||||||
|
|
||||||
|
|
@ -44,6 +46,7 @@ CategoryAnalysisTab::CategoryAnalysisTab(geopro::data::DatasetFieldDictionary* d
|
||||||
[this, segId](const QStringList& ids) {
|
[this, segId](const QStringList& ids) {
|
||||||
checkedBySeg_[segId] = ids;
|
checkedBySeg_[segId] = ids;
|
||||||
recomputeCheckedUnion();
|
recomputeCheckedUnion();
|
||||||
|
updatePlaceholderAndVisibility(); // 勾选/段内容变化后段「有无行」可能变 → 刷新显隐
|
||||||
});
|
});
|
||||||
connect(sec, &CategorySection::generateVolumeRequested, this,
|
connect(sec, &CategorySection::generateVolumeRequested, this,
|
||||||
&CategoryAnalysisTab::generateVolumeRequested);
|
&CategoryAnalysisTab::generateVolumeRequested);
|
||||||
|
|
@ -72,6 +75,18 @@ CategoryAnalysisTab::CategoryAnalysisTab(geopro::data::DatasetFieldDictionary* d
|
||||||
// 尾部弹簧(末项):默认 0;全部段折叠时由 relayoutSections 置 1,吸收余量把段头顶到顶部。
|
// 尾部弹簧(末项):默认 0;全部段折叠时由 relayoutSections 置 1,吸收余量把段头顶到顶部。
|
||||||
col->addStretch(0);
|
col->addStretch(0);
|
||||||
scroll->setWidget(content);
|
scroll->setWidget(content);
|
||||||
|
|
||||||
|
// 全空占位:所有段都无可渲染数据行时显示,与 scroll 同级、互斥显隐(由 updatePlaceholderAndVisibility 切换)。
|
||||||
|
auto* placeholder = new QLabel(QStringLiteral("请在左侧对象树勾选测线 / 数据集"), this);
|
||||||
|
placeholder->setAlignment(Qt::AlignCenter);
|
||||||
|
placeholder->setWordWrap(true);
|
||||||
|
applyTokenizedStyleSheet(placeholder,
|
||||||
|
QStringLiteral("QLabel{color:{{text/tertiary}};padding:24px;}"));
|
||||||
|
outer->addWidget(placeholder, 0);
|
||||||
|
placeholder->hide();
|
||||||
|
placeholder_ = placeholder;
|
||||||
|
|
||||||
|
updatePlaceholderAndVisibility(); // 初始无数据 → 直接进入占位态
|
||||||
}
|
}
|
||||||
|
|
||||||
void CategoryAnalysisTab::relayoutSections() {
|
void CategoryAnalysisTab::relayoutSections() {
|
||||||
|
|
@ -86,14 +101,29 @@ void CategoryAnalysisTab::relayoutSections() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void CategoryAnalysisTab::setBuckets(const CategoryBuckets& b) {
|
void CategoryAnalysisTab::setBuckets(const CategoryBuckets& b) {
|
||||||
const auto& cfg = categoryConfigs();
|
// splitByCategory 现按 categoryCatalog() 分桶(含 trajectory 第 5 桶,自然分发到轨迹段)。
|
||||||
for (std::size_t i = 0; i < cfg.size() && i < b.segments.size(); ++i) {
|
// 注:trajectory 段在 Task C2 才加入构造,当前 section("trajectory") 返回 nullptr → guard 跳过。
|
||||||
|
const auto& cat = geopro::data::categoryCatalog();
|
||||||
|
for (std::size_t i = 0; i < cat.size() && i < b.segments.size(); ++i) {
|
||||||
// voxel(三维体) 段数据来自 mock voxelTree(体/切片/异常),由调用方单独 section("voxel")->setDatasets
|
// voxel(三维体) 段数据来自 mock voxelTree(体/切片/异常),由调用方单独 section("voxel")->setDatasets
|
||||||
// 注入;splitByCategory 对它永远是空桶,若在此用空桶覆盖会先清掉其勾选(随后重填但勾选已丢) →
|
// 注入;splitByCategory 对它永远是空桶,若在此用空桶覆盖会先清掉其勾选(随后重填但勾选已丢) →
|
||||||
// 表现为「创建切片/异常后体/切片选择被清空」(用户 issue 1)。故跳过 voxel,勿覆盖。
|
// 表现为「创建切片/异常后体/切片选择被清空」(用户 issue 1)。故跳过 voxel,勿覆盖。
|
||||||
if (cfg[i].id == "voxel") continue;
|
if (cat[i].id == "voxel") continue;
|
||||||
if (auto* sec = section(cfg[i].id)) sec->setDatasets(b.segments[i]);
|
if (auto* sec = section(cat[i].id)) sec->setDatasets(b.segments[i]);
|
||||||
}
|
}
|
||||||
|
updatePlaceholderAndVisibility(); // 分发后据各段有无数据刷新显隐 + 占位
|
||||||
|
}
|
||||||
|
|
||||||
|
void CategoryAnalysisTab::updatePlaceholderAndVisibility() {
|
||||||
|
bool anyVisible = false;
|
||||||
|
for (auto* sec : ordered_) {
|
||||||
|
const bool has = sec->hasRenderableRows();
|
||||||
|
sec->setVisible(has);
|
||||||
|
if (has) anyVisible = true;
|
||||||
|
}
|
||||||
|
if (scroll_) scroll_->setVisible(anyVisible);
|
||||||
|
if (placeholder_) placeholder_->setVisible(!anyVisible);
|
||||||
|
relayoutSections();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CategoryAnalysisTab::setStructure(const std::vector<geopro::data::StructNode>& nodes) {
|
void CategoryAnalysisTab::setStructure(const std::vector<geopro::data::StructNode>& nodes) {
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,7 @@ public:
|
||||||
void setItemBusy(const QString& dsId, bool busy); // 复选框↔等待 spinner 切换
|
void setItemBusy(const QString& dsId, bool busy); // 复选框↔等待 spinner 切换
|
||||||
void clearAllBusy(); // 撤回所有 spinner(失败兜底)
|
void clearAllBusy(); // 撤回所有 spinner(失败兜底)
|
||||||
void scrollItemToTop(const QString& dsId); // 把某行尽量滚到面板顶部(新建三维体后定位)
|
void scrollItemToTop(const QString& dsId); // 把某行尽量滚到面板顶部(新建三维体后定位)
|
||||||
|
void refreshVisibility() { updatePlaceholderAndVisibility(); } // 外部注入(如 voxel setDatasets)后刷新段显隐/占位
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void checkedDatasetsChanged(const QStringList& dsIds); // 5 段勾选并集
|
void checkedDatasetsChanged(const QStringList& dsIds); // 5 段勾选并集
|
||||||
|
|
@ -61,12 +62,15 @@ private:
|
||||||
// 据各段折叠态重排 stretch:折叠段=0(仅段头高)、展开段=1(吸收余量);全折叠时尾部弹簧吸收→段头顶到顶部。
|
// 据各段折叠态重排 stretch:折叠段=0(仅段头高)、展开段=1(吸收余量);全折叠时尾部弹簧吸收→段头顶到顶部。
|
||||||
// 仅在面板不产生滚动条(内容short于视口)时有可见效果——正是用户反馈的"折叠后停在原位中间"场景。
|
// 仅在面板不产生滚动条(内容short于视口)时有可见效果——正是用户反馈的"折叠后停在原位中间"场景。
|
||||||
void relayoutSections();
|
void relayoutSections();
|
||||||
|
// 据各段是否有可渲染数据行 显隐段;全空时显示占位提示、隐藏滚动区。数据变化(分发/勾选/注入)后调用。
|
||||||
|
void updatePlaceholderAndVisibility();
|
||||||
|
|
||||||
std::map<std::string, CategorySection*> sections_;
|
std::map<std::string, CategorySection*> sections_;
|
||||||
std::vector<CategorySection*> ordered_; // 按 categoryConfigs 顺序(relayout 遍历用)
|
std::vector<CategorySection*> ordered_; // 按 categoryConfigs 顺序(relayout 遍历用)
|
||||||
QScrollArea* scroll_ = nullptr; // 外层滚动区(scrollItemToTop 定位用)
|
QScrollArea* scroll_ = nullptr; // 外层滚动区(scrollItemToTop 定位用)
|
||||||
QWidget* content_ = nullptr; // 滚动内容容器(坐标映射用)
|
QWidget* content_ = nullptr; // 滚动内容容器(坐标映射用)
|
||||||
QVBoxLayout* col_ = nullptr; // 段堆叠布局(末项=尾部弹簧)
|
QVBoxLayout* col_ = nullptr; // 段堆叠布局(末项=尾部弹簧)
|
||||||
|
QWidget* placeholder_ = nullptr; // 全空时显示的占位提示(与 scroll_ 同级,互斥显隐)
|
||||||
std::map<std::string, QStringList> checkedBySeg_; // 各段当前勾选(合并成并集)
|
std::map<std::string, QStringList> checkedBySeg_; // 各段当前勾选(合并成并集)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -189,6 +189,15 @@ QTreeWidgetItem* CategorySection::itemFor(const QString& dsId) const {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CategorySection::hasRenderableRows() const {
|
||||||
|
if (!list_) return false;
|
||||||
|
// 数据行 = 非 container 的可勾选行;只有容器节点(分组)不算「有数据」。
|
||||||
|
for (QTreeWidgetItemIterator it(list_); *it; ++it)
|
||||||
|
if ((*it)->data(0, kDsDdCodeRole).toString() != QStringLiteral("container"))
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void CategorySection::setStructure(const std::vector<geopro::data::StructNode>& nodes) {
|
void CategorySection::setStructure(const std::vector<geopro::data::StructNode>& nodes) {
|
||||||
structure_ = nodes; // 容器分层(项目根/GS/TM→ds)在 Task 12 接入真实结构后据此构建。
|
structure_ = nodes; // 容器分层(项目根/GS/TM→ds)在 Task 12 接入真实结构后据此构建。
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,7 @@ public:
|
||||||
void ensureExpanded(); // 展开本段(折叠则展开):滚动定位前确保目标行可见
|
void ensureExpanded(); // 展开本段(折叠则展开):滚动定位前确保目标行可见
|
||||||
QTreeWidget* listWidget() const { return list_; } // 段体树(外层滚动定位用)
|
QTreeWidget* listWidget() const { return list_; } // 段体树(外层滚动定位用)
|
||||||
QTreeWidgetItem* itemFor(const QString& dsId) const; // 按 dsId 取行(无则 nullptr)
|
QTreeWidgetItem* itemFor(const QString& dsId) const; // 按 dsId 取行(无则 nullptr)
|
||||||
|
bool hasRenderableRows() const; // 段体是否含可渲染数据行(非 container 容器节点),供单列动态显隐
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void checkedDatasetsChanged(const QStringList& dsIds); // 数据行勾选=渲染
|
void checkedDatasetsChanged(const QStringList& dsIds); // 数据行勾选=渲染
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue