fix(ui): 筛选时间换 QComboBox(与装置同款) + 异常复选框驱动显隐(①)
- #3/#4 DateRangeEdit 重写为 QComboBox 子类(覆写 showPopup 弹双日历):外观与装置下拉完全一致 (同款原生下拉箭头/高度/边框),消除 QToolButton 方案的 popup「must be top level window」告警 - ① 创建异常后取消勾选仍渲染:异常行复选框现驱动显隐——itemChanged 对 dd_anomaly 发 anomalyVisibilityChanged→setAnomalyVisible;异常默认勾选=显示(新项默认勾,曾取消的保留); refreshAnomalies 按三维体段复选框设各异常可见性;异常创建回调改为先 refreshAnalysis 再 refreshAnomalies 构建:app 链接通过
This commit is contained in:
parent
2bd1c36579
commit
62b7cde5cd
|
|
@ -425,7 +425,7 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re
|
||||||
// 异常刷新渲染(#4b/4c):恒「全部显示」——旧三维分析栏的过滤档位 UI 已退役,新分段 tab 暂无档位
|
// 异常刷新渲染(#4b/4c):恒「全部显示」——旧三维分析栏的过滤档位 UI 已退役,新分段 tab 暂无档位
|
||||||
// 控件(功能缺失,待补)。异常列表由 refreshAnalysis 经 voxelTree 全量注入三维体段,此处只管渲染。
|
// 控件(功能缺失,待补)。异常列表由 refreshAnalysis 经 voxelTree 全量注入三维体段,此处只管渲染。
|
||||||
// (loadAnomalyTree 空 key=全部。mock 同步回调。)
|
// (loadAnomalyTree 空 key=全部。mock 同步回调。)
|
||||||
auto refreshAnomalies = [sceneView, scene3dRepo, renderWindowPtr]() {
|
auto refreshAnomalies = [sceneView, scene3dRepo, renderWindowPtr, drawer]() {
|
||||||
sceneView->clearAnomalies();
|
sceneView->clearAnomalies();
|
||||||
std::vector<geopro::core::Anomaly> set;
|
std::vector<geopro::core::Anomaly> set;
|
||||||
scene3dRepo->loadAnomalyTree(
|
scene3dRepo->loadAnomalyTree(
|
||||||
|
|
@ -437,6 +437,13 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re
|
||||||
},
|
},
|
||||||
[](const std::string&) {});
|
[](const std::string&) {});
|
||||||
for (const auto& a : set) sceneView->addAnomaly(a);
|
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 不刷新
|
renderWindowPtr->Render(); // 必须重绘:clear+addAnomaly 改了 prop,否则 VTK 不刷新
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -562,8 +569,8 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re
|
||||||
[sceneView, renderWindowPtr, refreshAnomalies, refreshAnalysis,
|
[sceneView, renderWindowPtr, refreshAnomalies, refreshAnalysis,
|
||||||
draftId](std::string) {
|
draftId](std::string) {
|
||||||
sceneView->removeAnomaly(draftId); // 撤草稿
|
sceneView->removeAnomaly(draftId); // 撤草稿
|
||||||
refreshAnomalies(); // 重渲染异常 actor
|
refreshAnalysis(); // 先:新异常进三维体段三级树(默认勾选=显示)
|
||||||
refreshAnalysis(); // 新异常进三维体段三级树(挂体/切片)
|
refreshAnomalies(); // 后:重渲染异常 actor 并按复选框设显隐
|
||||||
renderWindowPtr->Render();
|
renderWindowPtr->Render();
|
||||||
},
|
},
|
||||||
[&window](const std::string& m) {
|
[&window](const std::string& m) {
|
||||||
|
|
|
||||||
|
|
@ -86,7 +86,13 @@ CategorySection::CategorySection(const CategorySpec& spec, geopro::data::Dataset
|
||||||
list_->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
list_->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||||
list_->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
list_->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||||
applyDatasetCardDelegate(list_);
|
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) {
|
connect(list_, &QTreeWidget::itemDoubleClicked, this, [this](QTreeWidgetItem* it, int) {
|
||||||
const QString id = it->data(0, kDsIdRole).toString();
|
const QString id = it->data(0, kDsIdRole).toString();
|
||||||
if (id.isEmpty()) return;
|
if (id.isEmpty()) return;
|
||||||
|
|
@ -180,10 +186,13 @@ bool CategorySection::passesFilters(const DsRow& row) const {
|
||||||
void CategorySection::rebuildList() {
|
void CategorySection::rebuildList() {
|
||||||
// 增量保留:记住当前已勾选的 ds,重建后复原(仍存在的项保持勾选)。否则每次刷新(勾选对象/建体/
|
// 增量保留:记住当前已勾选的 ds,重建后复原(仍存在的项保持勾选)。否则每次刷新(勾选对象/建体/
|
||||||
// 存切片/建异常都会触发)清空全部勾选 → 渲染被重置,体验极差(用户反馈:必须增量更新)。
|
// 存切片/建异常都会触发)清空全部勾选 → 渲染被重置,体验极差(用户反馈:必须增量更新)。
|
||||||
std::set<std::string> wasChecked;
|
std::set<std::string> wasChecked, wasSeen; // wasSeen=重建前所有可勾选项(区分"新项" vs "曾取消")
|
||||||
for (QTreeWidgetItemIterator it(list_); *it; ++it)
|
for (QTreeWidgetItemIterator it(list_); *it; ++it) {
|
||||||
if ((*it)->checkState(0) == Qt::Checked)
|
if (!((*it)->flags() & Qt::ItemIsUserCheckable)) continue;
|
||||||
wasChecked.insert((*it)->data(0, kDsIdRole).toString().toStdString());
|
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;
|
std::vector<DsRow> filtered;
|
||||||
filtered.reserve(rows_.size());
|
filtered.reserve(rows_.size());
|
||||||
|
|
@ -236,7 +245,12 @@ void CategorySection::rebuildList() {
|
||||||
}
|
}
|
||||||
(*it)->setFlags((*it)->flags() | Qt::ItemIsUserCheckable);
|
(*it)->setFlags((*it)->flags() | Qt::ItemIsUserCheckable);
|
||||||
const std::string id = (*it)->data(0, kDsIdRole).toString().toStdString();
|
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),让体/切片/异常可见
|
list_->expandAll(); // 展开容器层级(项目根/GS/TM),让体/切片/异常可见
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ public:
|
||||||
void setStructure(const std::vector<geopro::data::StructNode>& nodes);
|
void setStructure(const std::vector<geopro::data::StructNode>& nodes);
|
||||||
void setDatasets(const std::vector<geopro::data::DsRow>& rows);
|
void setDatasets(const std::vector<geopro::data::DsRow>& rows);
|
||||||
void setChecked(const QString& dsId, bool on); // 按 dsId 勾选/取消(新建切片自动勾选等场景)
|
void setChecked(const QString& dsId, bool on); // 按 dsId 勾选/取消(新建切片自动勾选等场景)
|
||||||
|
QStringList checkedIds() const { return checkedDsIds(); } // 当前勾选 ds(异常显隐同步用)
|
||||||
const CategorySpec& spec() const { return spec_; }
|
const CategorySpec& spec() const { return spec_; }
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
|
|
||||||
|
|
@ -5,38 +5,20 @@
|
||||||
#include <QHBoxLayout>
|
#include <QHBoxLayout>
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
#include <QToolButton>
|
|
||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
|
|
||||||
#include "Theme.hpp"
|
#include "Theme.hpp"
|
||||||
|
|
||||||
namespace geopro::app {
|
namespace geopro::app {
|
||||||
|
|
||||||
DateRangeEdit::DateRangeEdit(QWidget* parent) : QWidget(parent) {
|
DateRangeEdit::DateRangeEdit(QWidget* parent) : QComboBox(parent) {
|
||||||
auto* lay = new QHBoxLayout(this);
|
addItem(QString()); // 单项承载显示文本(不展开列表,点击改为弹双日历)
|
||||||
lay->setContentsMargins(0, 0, 0, 0);
|
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
|
||||||
lay->setSpacing(0);
|
setToolTip(QStringLiteral("按采集时间筛选(起 ~ 止),可清空"));
|
||||||
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_);
|
|
||||||
updateText();
|
updateText();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DateRangeEdit::openPopup() {
|
void DateRangeEdit::showPopup() {
|
||||||
if (!popup_) {
|
if (!popup_) {
|
||||||
popup_ = new QFrame(this, Qt::Popup);
|
popup_ = new QFrame(this, Qt::Popup);
|
||||||
popup_->setFrameShape(QFrame::StyledPanel);
|
popup_->setFrameShape(QFrame::StyledPanel);
|
||||||
|
|
@ -69,7 +51,7 @@ void DateRangeEdit::openPopup() {
|
||||||
btns->addWidget(ok);
|
btns->addWidget(ok);
|
||||||
v->addLayout(btns);
|
v->addLayout(btns);
|
||||||
}
|
}
|
||||||
// 已有范围则定位到当前值,否则日历默认今天(不再 1752)。
|
// 已有范围则定位到当前值,否则日历默认今天。
|
||||||
if (from_.isValid()) fromCal_->setSelectedDate(from_);
|
if (from_.isValid()) fromCal_->setSelectedDate(from_);
|
||||||
if (to_.isValid()) toCal_->setSelectedDate(to_);
|
if (to_.isValid()) toCal_->setSelectedDate(to_);
|
||||||
popup_->adjustSize();
|
popup_->adjustSize();
|
||||||
|
|
@ -77,6 +59,10 @@ void DateRangeEdit::openPopup() {
|
||||||
popup_->show();
|
popup_->show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DateRangeEdit::hidePopup() {
|
||||||
|
if (popup_) popup_->hide(); // 我们用自绘双日历 popup,不走 QComboBox 列表
|
||||||
|
}
|
||||||
|
|
||||||
void DateRangeEdit::applyAndClose() {
|
void DateRangeEdit::applyAndClose() {
|
||||||
from_ = fromCal_->selectedDate();
|
from_ = fromCal_->selectedDate();
|
||||||
to_ = toCal_->selectedDate();
|
to_ = toCal_->selectedDate();
|
||||||
|
|
@ -96,12 +82,12 @@ void DateRangeEdit::clearAndClose() {
|
||||||
|
|
||||||
void DateRangeEdit::updateText() {
|
void DateRangeEdit::updateText() {
|
||||||
if (!from_.isValid() && !to_.isValid()) {
|
if (!from_.isValid() && !to_.isValid()) {
|
||||||
btn_->setText(QStringLiteral("全部时间")); // chevron 由 QSS background-image 绘制
|
setItemText(0, QStringLiteral("全部时间"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const QString f = from_.isValid() ? from_.toString(QStringLiteral("yyyy-MM-dd")) : QStringLiteral("不限");
|
const QString f = from_.isValid() ? from_.toString(QStringLiteral("yyyy-MM-dd")) : QStringLiteral("不限");
|
||||||
const QString t = to_.isValid() ? to_.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
|
} // namespace geopro::app
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,16 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
#include <QComboBox>
|
||||||
#include <QDate>
|
#include <QDate>
|
||||||
#include <QWidget>
|
|
||||||
|
|
||||||
class QToolButton;
|
|
||||||
class QCalendarWidget;
|
class QCalendarWidget;
|
||||||
class QFrame;
|
class QFrame;
|
||||||
|
|
||||||
namespace geopro::app {
|
namespace geopro::app {
|
||||||
|
|
||||||
// 日期范围选择器(spec 采集时间筛选):一个按钮显示「起 ~ 止」,点开弹双日历面板选起止;
|
// 日期范围选择器(spec 采集时间筛选):外观与普通 QComboBox **完全一致**(同款下拉箭头/高度/边框,
|
||||||
// 可清空(回到"全部时间");默认不限,日历定位今天(不再停在 1752)。
|
// 与装置类型下拉是同一种控件),但点开弹「双日历」选起止而非下拉列表。显示「起 ~ 止」/「全部时间」;
|
||||||
class DateRangeEdit : public QWidget {
|
// 可清空(回到"全部时间");默认不限,日历定位今天。
|
||||||
|
class DateRangeEdit : public QComboBox {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit DateRangeEdit(QWidget* parent = nullptr);
|
explicit DateRangeEdit(QWidget* parent = nullptr);
|
||||||
|
|
@ -18,16 +18,17 @@ public:
|
||||||
QDate from() const { return from_; } // 无效 QDate = 不限下限
|
QDate from() const { return from_; } // 无效 QDate = 不限下限
|
||||||
QDate to() const { return to_; } // 无效 QDate = 不限上限
|
QDate to() const { return to_; } // 无效 QDate = 不限上限
|
||||||
|
|
||||||
|
void showPopup() override; // 覆写:弹双日历面板(而非 QComboBox 默认列表)
|
||||||
|
void hidePopup() override;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void rangeChanged();
|
void rangeChanged();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void openPopup();
|
|
||||||
void applyAndClose();
|
void applyAndClose();
|
||||||
void clearAndClose();
|
void clearAndClose();
|
||||||
void updateText();
|
void updateText();
|
||||||
|
|
||||||
QToolButton* btn_ = nullptr;
|
|
||||||
QFrame* popup_ = nullptr;
|
QFrame* popup_ = nullptr;
|
||||||
QCalendarWidget* fromCal_ = nullptr;
|
QCalendarWidget* fromCal_ = nullptr;
|
||||||
QCalendarWidget* toCal_ = nullptr;
|
QCalendarWidget* toCal_ = nullptr;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue