fix(vtk): 修创建切片/异常清空体·切片选择(issue1)+异常副标题补时间+新切片自动勾选

- issue1 真因:voxel(三维体)段数据来自 mock voxelTree,但 setBuckets 用 splitByCategory 的空桶
  (对象树 ds 里无 dd_voxel→必空)先 setDatasets(空) 清掉其勾选,随后 section("voxel")->setDatasets
  (voxelTree) 重填时勾选已丢→「创建切片/异常后体/切片选择被清空」。修:setBuckets 跳过 voxel 段
- 异常副标题没时间:saveAnomaly 没设 createTime(main 构建 Anomaly 也没设)→补当前时间(mock)
- 新切片自动勾选:CategorySection::setChecked(dsId) + 保存切片回调在 refreshAnalysis 后勾选新切片
  (恢复旧行为,避免刚存的切片因未勾被 syncSlices 隐藏)

未解(下轮):issue2 选第二个体时第一个体切片消失=单"当前体"模型固有限制(syncSlices 只显示
volumeDsId==currentVolumeDsId 的切片),需多体并发切片渲染改造

构建:app 链接通过
This commit is contained in:
gaozheng 2026-06-25 19:21:55 +08:00
parent 09a48d846b
commit 652b37b672
5 changed files with 22 additions and 3 deletions

View File

@ -608,10 +608,12 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re
if (!ok) return;
scene3dRepo->createSlice(
spec, name.isEmpty() ? std::string("切片") : name.toStdString(),
[interactionMgr, refreshAnalysis](std::string newId) {
[interactionMgr, refreshAnalysis, drawer](std::string newId) {
interactionMgr->tagSelectedSlice(newId); // 链接当前切片 → 新数据集(不重绘)
refreshAnalysis(); // 新行进列表(勾选集不变→不发多余信号)
// TODO新分段 tab 暂无 setItemChecked新切片列表默认未勾已渲染但列表不打勾待补
refreshAnalysis(); // 新行进列表
// 新切片自动勾选 → 列表打勾 + 保持渲染refreshAnalysis 已重建列表,故在其后勾选)。
if (auto* sec = drawer->analysisTab()->section("voxel"))
sec->setChecked(QString::fromStdString(newId), true);
},
[&window](const std::string& m) {
QMessageBox::warning(&window, QStringLiteral("保存切片"),

View File

@ -59,6 +59,10 @@ CategoryAnalysisTab::CategoryAnalysisTab(geopro::data::DatasetFieldDictionary* d
void CategoryAnalysisTab::setBuckets(const CategoryBuckets& b) {
const auto& cfg = categoryConfigs();
for (std::size_t i = 0; i < cfg.size() && i < b.segments.size(); ++i) {
// voxel(三维体) 段数据来自 mock voxelTree(体/切片/异常),由调用方单独 section("voxel")->setDatasets
// 注入splitByCategory 对它永远是空桶,若在此用空桶覆盖会先清掉其勾选(随后重填但勾选已丢) →
// 表现为「创建切片/异常后体/切片选择被清空」(用户 issue 1)。故跳过 voxel勿覆盖。
if (cfg[i].id == "voxel") continue;
if (auto* sec = section(cfg[i].id)) sec->setDatasets(b.segments[i]);
}
}

View File

@ -116,6 +116,15 @@ void CategorySection::setDatasets(const std::vector<DsRow>& rows) {
rebuildList();
}
void CategorySection::setChecked(const QString& dsId, bool on) {
for (QTreeWidgetItemIterator it(list_); *it; ++it)
if ((*it)->data(0, kDsIdRole).toString() == dsId &&
((*it)->flags() & Qt::ItemIsUserCheckable)) {
(*it)->setCheckState(0, on ? Qt::Checked : Qt::Unchecked); // 触发 itemChanged→emitChecked→渲染
return;
}
}
void CategorySection::refreshArrayCombo() {
if (!spec_.hasArrayTypeFilter || !arrayCombo_) return;
const QString prev = arrayCombo_->currentData().toString();

View File

@ -32,6 +32,7 @@ public:
// 对象树同源的扁平 GS/TM 节点段体容器分层用Task 12 接入真实结构,当前仅存储)。
void setStructure(const std::vector<geopro::data::StructNode>& nodes);
void setDatasets(const std::vector<geopro::data::DsRow>& rows);
void setChecked(const QString& dsId, bool on); // 按 dsId 勾选/取消(新建切片自动勾选等场景)
const CategorySpec& spec() const { return spec_; }
signals:

View File

@ -416,6 +416,9 @@ void Api3dRepository::saveAnomaly(const geopro::core::Anomaly& a,
if (id.empty()) id = "anomaly-" + std::to_string(++anomalyCounter_); // 新建 → 生成 id
geopro::core::Anomaly stored = a;
stored.id = id;
if (stored.createTime.empty()) // mock构建时未设创建时刻 → 补当前时间(副标题/列表显示)
stored.createTime =
QDateTime::currentDateTime().toString(QStringLiteral("yyyy-MM-dd HH:mm")).toStdString();
anomalies_[id] = StoredAnomaly{std::move(stored), screenshotPngPath};
onOk(id);
}