feat/vtk-3d-view #7
|
|
@ -425,7 +425,7 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re
|
|||
// 异常刷新渲染(#4b/4c):恒「全部显示」——旧三维分析栏的过滤档位 UI 已退役,新分段 tab 暂无档位
|
||||
// 控件(功能缺失,待补)。异常列表由 refreshAnalysis 经 voxelTree 全量注入三维体段,此处只管渲染。
|
||||
// (loadAnomalyTree 空 key=全部。mock 同步回调。)
|
||||
auto refreshAnomalies = [sceneView, scene3dRepo, renderWindowPtr]() {
|
||||
auto refreshAnomalies = [sceneView, scene3dRepo, renderWindowPtr, drawer]() {
|
||||
sceneView->clearAnomalies();
|
||||
std::vector<geopro::core::Anomaly> set;
|
||||
scene3dRepo->loadAnomalyTree(
|
||||
|
|
@ -437,6 +437,13 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re
|
|||
},
|
||||
[](const std::string&) {});
|
||||
for (const auto& a : set) sceneView->addAnomaly(a);
|
||||
// 异常显隐跟随三维体段复选框:取消勾选的异常隐藏(修「创建异常后取消勾选仍渲染」)。
|
||||
// 须在树已含该异常后调用(创建回调里先 refreshAnalysis 再 refreshAnomalies)。
|
||||
if (auto* sec = drawer->analysisTab()->section("voxel")) {
|
||||
const QStringList checked = sec->checkedIds();
|
||||
for (const auto& a : set)
|
||||
sceneView->setAnomalyVisible(a.id, checked.contains(QString::fromStdString(a.id)));
|
||||
}
|
||||
renderWindowPtr->Render(); // 必须重绘:clear+addAnomaly 改了 prop,否则 VTK 不刷新
|
||||
};
|
||||
|
||||
|
|
@ -562,8 +569,8 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re
|
|||
[sceneView, renderWindowPtr, refreshAnomalies, refreshAnalysis,
|
||||
draftId](std::string) {
|
||||
sceneView->removeAnomaly(draftId); // 撤草稿
|
||||
refreshAnomalies(); // 重渲染异常 actor
|
||||
refreshAnalysis(); // 新异常进三维体段三级树(挂体/切片)
|
||||
refreshAnalysis(); // 先:新异常进三维体段三级树(默认勾选=显示)
|
||||
refreshAnomalies(); // 后:重渲染异常 actor 并按复选框设显隐
|
||||
renderWindowPtr->Render();
|
||||
},
|
||||
[&window](const std::string& m) {
|
||||
|
|
|
|||
|
|
@ -86,7 +86,13 @@ CategorySection::CategorySection(const CategorySpec& spec, geopro::data::Dataset
|
|||
list_->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
list_->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
applyDatasetCardDelegate(list_);
|
||||
connect(list_, &QTreeWidget::itemChanged, this, [this](QTreeWidgetItem*, int) { emitChecked(); });
|
||||
connect(list_, &QTreeWidget::itemChanged, this, [this](QTreeWidgetItem* it, int) {
|
||||
// 异常行复选框 = 该异常显隐(异常不进渲染勾选集,单独走 anomalyVisibilityChanged → setAnomalyVisible)。
|
||||
if (it && it->data(0, kDsDdCodeRole).toString() == QStringLiteral("dd_anomaly"))
|
||||
emit anomalyVisibilityChanged(it->data(0, kDsIdRole).toString(),
|
||||
it->checkState(0) == Qt::Checked);
|
||||
emitChecked();
|
||||
});
|
||||
connect(list_, &QTreeWidget::itemDoubleClicked, this, [this](QTreeWidgetItem* it, int) {
|
||||
const QString id = it->data(0, kDsIdRole).toString();
|
||||
if (id.isEmpty()) return;
|
||||
|
|
@ -180,10 +186,13 @@ bool CategorySection::passesFilters(const DsRow& row) const {
|
|||
void CategorySection::rebuildList() {
|
||||
// 增量保留:记住当前已勾选的 ds,重建后复原(仍存在的项保持勾选)。否则每次刷新(勾选对象/建体/
|
||||
// 存切片/建异常都会触发)清空全部勾选 → 渲染被重置,体验极差(用户反馈:必须增量更新)。
|
||||
std::set<std::string> wasChecked;
|
||||
for (QTreeWidgetItemIterator it(list_); *it; ++it)
|
||||
if ((*it)->checkState(0) == Qt::Checked)
|
||||
wasChecked.insert((*it)->data(0, kDsIdRole).toString().toStdString());
|
||||
std::set<std::string> wasChecked, wasSeen; // wasSeen=重建前所有可勾选项(区分"新项" vs "曾取消")
|
||||
for (QTreeWidgetItemIterator it(list_); *it; ++it) {
|
||||
if (!((*it)->flags() & Qt::ItemIsUserCheckable)) continue;
|
||||
const std::string id = (*it)->data(0, kDsIdRole).toString().toStdString();
|
||||
wasSeen.insert(id);
|
||||
if ((*it)->checkState(0) == Qt::Checked) wasChecked.insert(id);
|
||||
}
|
||||
|
||||
std::vector<DsRow> filtered;
|
||||
filtered.reserve(rows_.size());
|
||||
|
|
@ -236,7 +245,12 @@ void CategorySection::rebuildList() {
|
|||
}
|
||||
(*it)->setFlags((*it)->flags() | Qt::ItemIsUserCheckable);
|
||||
const std::string id = (*it)->data(0, kDsIdRole).toString().toStdString();
|
||||
(*it)->setCheckState(0, wasChecked.count(id) ? Qt::Checked : Qt::Unchecked); // 复原勾选
|
||||
const bool isAnomaly = (*it)->data(0, kDsDdCodeRole).toString() == QStringLiteral("dd_anomaly");
|
||||
// 复原勾选:曾勾选→勾;曾出现但未勾→不勾;新项→异常默认勾(显示),其余默认不勾。
|
||||
const Qt::CheckState st = wasChecked.count(id) ? Qt::Checked
|
||||
: wasSeen.count(id) ? Qt::Unchecked
|
||||
: (isAnomaly ? Qt::Checked : Qt::Unchecked);
|
||||
(*it)->setCheckState(0, st);
|
||||
}
|
||||
}
|
||||
list_->expandAll(); // 展开容器层级(项目根/GS/TM),让体/切片/异常可见
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ public:
|
|||
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 勾选/取消(新建切片自动勾选等场景)
|
||||
QStringList checkedIds() const { return checkedDsIds(); } // 当前勾选 ds(异常显隐同步用)
|
||||
const CategorySpec& spec() const { return spec_; }
|
||||
|
||||
signals:
|
||||
|
|
|
|||
|
|
@ -5,38 +5,20 @@
|
|||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QPushButton>
|
||||
#include <QToolButton>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#include "Theme.hpp"
|
||||
|
||||
namespace geopro::app {
|
||||
|
||||
DateRangeEdit::DateRangeEdit(QWidget* parent) : QWidget(parent) {
|
||||
auto* lay = new QHBoxLayout(this);
|
||||
lay->setContentsMargins(0, 0, 0, 0);
|
||||
lay->setSpacing(0);
|
||||
btn_ = new QToolButton(this);
|
||||
btn_->setToolButtonStyle(Qt::ToolButtonTextOnly);
|
||||
btn_->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
|
||||
btn_->setPopupMode(QToolButton::InstantPopup);
|
||||
btn_->setToolTip(QStringLiteral("按采集时间筛选(起 ~ 止),可清空"));
|
||||
// 外观与 QComboBox 完全一致(同高/同边框/同圆角/同内距)+ 右侧统一 SVG chevron(替代手写 ▾,
|
||||
// 原 ▾ 字符渲染粗糙且按钮比装置下拉矮)。右侧留 24px 放 chevron,文本左对齐。
|
||||
applyTokenizedStyleSheet(
|
||||
btn_, QStringLiteral(
|
||||
"QToolButton{background-color:{{bg/panel}};color:{{text/primary}};"
|
||||
"border:1px solid {{border/default}};border-radius:4px;"
|
||||
"padding:6px 22px 6px 8px;min-height:16px;text-align:left;"
|
||||
"background-image:url(:/icons/chevron-down.svg);background-repeat:no-repeat;"
|
||||
"background-origin:padding;background-position:right center;}"
|
||||
"QToolButton:hover{border-color:{{border/strong}};}"));
|
||||
connect(btn_, &QToolButton::clicked, this, &DateRangeEdit::openPopup);
|
||||
lay->addWidget(btn_);
|
||||
DateRangeEdit::DateRangeEdit(QWidget* parent) : QComboBox(parent) {
|
||||
addItem(QString()); // 单项承载显示文本(不展开列表,点击改为弹双日历)
|
||||
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
|
||||
setToolTip(QStringLiteral("按采集时间筛选(起 ~ 止),可清空"));
|
||||
updateText();
|
||||
}
|
||||
|
||||
void DateRangeEdit::openPopup() {
|
||||
void DateRangeEdit::showPopup() {
|
||||
if (!popup_) {
|
||||
popup_ = new QFrame(this, Qt::Popup);
|
||||
popup_->setFrameShape(QFrame::StyledPanel);
|
||||
|
|
@ -69,7 +51,7 @@ void DateRangeEdit::openPopup() {
|
|||
btns->addWidget(ok);
|
||||
v->addLayout(btns);
|
||||
}
|
||||
// 已有范围则定位到当前值,否则日历默认今天(不再 1752)。
|
||||
// 已有范围则定位到当前值,否则日历默认今天。
|
||||
if (from_.isValid()) fromCal_->setSelectedDate(from_);
|
||||
if (to_.isValid()) toCal_->setSelectedDate(to_);
|
||||
popup_->adjustSize();
|
||||
|
|
@ -77,6 +59,10 @@ void DateRangeEdit::openPopup() {
|
|||
popup_->show();
|
||||
}
|
||||
|
||||
void DateRangeEdit::hidePopup() {
|
||||
if (popup_) popup_->hide(); // 我们用自绘双日历 popup,不走 QComboBox 列表
|
||||
}
|
||||
|
||||
void DateRangeEdit::applyAndClose() {
|
||||
from_ = fromCal_->selectedDate();
|
||||
to_ = toCal_->selectedDate();
|
||||
|
|
@ -96,12 +82,12 @@ void DateRangeEdit::clearAndClose() {
|
|||
|
||||
void DateRangeEdit::updateText() {
|
||||
if (!from_.isValid() && !to_.isValid()) {
|
||||
btn_->setText(QStringLiteral("全部时间")); // chevron 由 QSS background-image 绘制
|
||||
setItemText(0, QStringLiteral("全部时间"));
|
||||
return;
|
||||
}
|
||||
const QString f = from_.isValid() ? from_.toString(QStringLiteral("yyyy-MM-dd")) : QStringLiteral("不限");
|
||||
const QString t = to_.isValid() ? to_.toString(QStringLiteral("yyyy-MM-dd")) : QStringLiteral("不限");
|
||||
btn_->setText(QStringLiteral("%1 ~ %2").arg(f, t));
|
||||
setItemText(0, QStringLiteral("%1 ~ %2").arg(f, t));
|
||||
}
|
||||
|
||||
} // namespace geopro::app
|
||||
|
|
|
|||
|
|
@ -1,16 +1,16 @@
|
|||
#pragma once
|
||||
#include <QComboBox>
|
||||
#include <QDate>
|
||||
#include <QWidget>
|
||||
|
||||
class QToolButton;
|
||||
class QCalendarWidget;
|
||||
class QFrame;
|
||||
|
||||
namespace geopro::app {
|
||||
|
||||
// 日期范围选择器(spec 采集时间筛选):一个按钮显示「起 ~ 止」,点开弹双日历面板选起止;
|
||||
// 可清空(回到"全部时间");默认不限,日历定位今天(不再停在 1752)。
|
||||
class DateRangeEdit : public QWidget {
|
||||
// 日期范围选择器(spec 采集时间筛选):外观与普通 QComboBox **完全一致**(同款下拉箭头/高度/边框,
|
||||
// 与装置类型下拉是同一种控件),但点开弹「双日历」选起止而非下拉列表。显示「起 ~ 止」/「全部时间」;
|
||||
// 可清空(回到"全部时间");默认不限,日历定位今天。
|
||||
class DateRangeEdit : public QComboBox {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit DateRangeEdit(QWidget* parent = nullptr);
|
||||
|
|
@ -18,16 +18,17 @@ public:
|
|||
QDate from() const { return from_; } // 无效 QDate = 不限下限
|
||||
QDate to() const { return to_; } // 无效 QDate = 不限上限
|
||||
|
||||
void showPopup() override; // 覆写:弹双日历面板(而非 QComboBox 默认列表)
|
||||
void hidePopup() override;
|
||||
|
||||
signals:
|
||||
void rangeChanged();
|
||||
|
||||
private:
|
||||
void openPopup();
|
||||
void applyAndClose();
|
||||
void clearAndClose();
|
||||
void updateText();
|
||||
|
||||
QToolButton* btn_ = nullptr;
|
||||
QFrame* popup_ = nullptr;
|
||||
QCalendarWidget* fromCal_ = nullptr;
|
||||
QCalendarWidget* toCal_ = nullptr;
|
||||
|
|
|
|||
Loading…
Reference in New Issue