diff --git a/src/app/CMakeLists.txt b/src/app/CMakeLists.txt index cbca01e..2d06c4a 100644 --- a/src/app/CMakeLists.txt +++ b/src/app/CMakeLists.txt @@ -52,6 +52,7 @@ add_executable(geopro_desktop WIN32 panels/chart/ScatterHoverTip.cpp panels/columns/Column2DDataset.cpp panels/columns/Column3DDataset.cpp + panels/columns/Column3DAnalysis.cpp panels/AnomalyTablePanel.cpp panels/LoadingOverlay.cpp panels/DatasetDetailPage.cpp diff --git a/src/app/panels/columns/Column3DAnalysis.cpp b/src/app/panels/columns/Column3DAnalysis.cpp new file mode 100644 index 0000000..4032c3e --- /dev/null +++ b/src/app/panels/columns/Column3DAnalysis.cpp @@ -0,0 +1,84 @@ +#include "panels/columns/Column3DAnalysis.hpp" + +#include +#include +#include +#include +#include + +#include "Theme.hpp" +#include "panels/DatasetListPanel.hpp" + +namespace geopro::app { + +Column3DAnalysis::Column3DAnalysis(QWidget* parent) : QWidget(parent) { + auto* root = new QVBoxLayout(this); + root->setContentsMargins(space::kMd, space::kMd, space::kMd, space::kMd); + root->setSpacing(space::kMd); + + tree_ = new QTreeWidget(); + tree_->setHeaderHidden(true); + tree_->setRootIsDecorated(true); + applyDatasetCardDelegate(tree_); + tree_->setContextMenuPolicy(Qt::CustomContextMenu); + + connect(tree_, &QTreeWidget::customContextMenuRequested, this, &Column3DAnalysis::onContextMenu); + + connect(tree_, &QTreeWidget::itemChanged, this, [this](QTreeWidgetItem*, int) { + QStringList ids; + for (QTreeWidgetItemIterator it(tree_); *it; ++it) { + if ((*it)->checkState(0) == Qt::Checked) + ids << (*it)->data(0, kDsIdRole).toString(); + } + emit checkedItemsChanged(ids); + }); + + root->addWidget(tree_, 1); +} + +void Column3DAnalysis::setDatasets(const std::vector& rows) { + populateDatasetList(tree_, rows, /*append=*/false); + for (QTreeWidgetItemIterator it(tree_); *it; ++it) { + (*it)->setFlags((*it)->flags() | Qt::ItemIsUserCheckable); + if ((*it)->checkState(0) != Qt::Checked) + (*it)->setCheckState(0, Qt::Unchecked); + } +} + +void Column3DAnalysis::onContextMenu(const QPoint& pos) { + QTreeWidgetItem* it = tree_->itemAt(pos); + if (!it) return; + + const QString dsId = it->data(0, kDsIdRole).toString(); + const QString ddCode = it->data(0, kDsDdCodeRole).toString(); + const QString name = it->data(0, kDsNameRole).toString(); + const bool isSlice = (ddCode == QStringLiteral("dd_slice")); + + QMenu menu(this); + if (!isSlice) { + // 三维体数据集:切片▸(上下/前后/左右/任意) / 色阶 / 显示·隐藏 / 数据详情 + QMenu* sub = menu.addMenu(QStringLiteral("切片")); + using SA = geopro::render::interact::SliceAxis; + sub->addAction(QStringLiteral("上下"), this, [this]{ emit sliceRequested(SA::UpDown); }); + sub->addAction(QStringLiteral("前后"), this, [this]{ emit sliceRequested(SA::FrontBack); }); + sub->addAction(QStringLiteral("左右"), this, [this]{ emit sliceRequested(SA::LeftRight); }); + sub->addAction(QStringLiteral("任意"), this, [this]{ emit sliceRequested(SA::Oblique); }); + menu.addAction(QStringLiteral("色阶"), this, [this, dsId]{ emit colorScaleRequested(dsId); }); + menu.addAction(QStringLiteral("显示 / 隐藏"), this, [this, dsId]{ emit visibilityToggled(dsId); }); + menu.addAction(QStringLiteral("数据详情"), this, [this, dsId, ddCode, name]{ emit detailRequested(dsId, ddCode, name); }); + } else { + // 切片数据集:保存/保存为/导出/删除 — 色阶/显示·隐藏/数据详情 + menu.addAction(QStringLiteral("保存"), this, [this, dsId]{ emit sliceSaveRequested(dsId); }); + menu.addAction(QStringLiteral("保存为"), this, [this, dsId]{ emit sliceSaveAsRequested(dsId); }); + menu.addAction(QStringLiteral("导出"), this, [this, dsId]{ emit sliceExportRequested(dsId); }); + menu.addAction(QStringLiteral("删除"), this, [this, dsId]{ emit sliceDeleteRequested(dsId); }); + menu.addSeparator(); + menu.addAction(QStringLiteral("色阶"), this, [this, dsId]{ emit colorScaleRequested(dsId); }); + menu.addAction(QStringLiteral("显示 / 隐藏"), this, [this, dsId]{ emit visibilityToggled(dsId); }); + menu.addAction(QStringLiteral("数据详情"), this, [this, dsId, ddCode, name]{ emit detailRequested(dsId, ddCode, name); }); + } + + menu.exec(tree_->viewport()->mapToGlobal(pos)); +} + +} // namespace geopro::app diff --git a/src/app/panels/columns/Column3DAnalysis.hpp b/src/app/panels/columns/Column3DAnalysis.hpp new file mode 100644 index 0000000..0119626 --- /dev/null +++ b/src/app/panels/columns/Column3DAnalysis.hpp @@ -0,0 +1,36 @@ +#pragma once +#include +#include +#include +#include "repo/RepoTypes.hpp" +#include "interact/SlicePlaneMath.hpp" // SliceAxis + +class QTreeWidget; +class QPoint; + +namespace geopro::app { + +// 三维分析栏:对象→三维体模型→切片 树 + 三维体/切片两套右键菜单。 +class Column3DAnalysis : public QWidget { + Q_OBJECT +public: + explicit Column3DAnalysis(QWidget* parent = nullptr); + void setDatasets(const std::vector& rows); // Analysis 维度(三维体/切片) + +signals: + void sliceRequested(geopro::render::interact::SliceAxis axis); // 三维体右键 切片▸(上下/前后/左右/任意) + void colorScaleRequested(const QString& dsId); + void visibilityToggled(const QString& dsId); + void detailRequested(const QString& dsId, const QString& ddCode, const QString& name); + void sliceSaveRequested(const QString& dsId); + void sliceSaveAsRequested(const QString& dsId); + void sliceExportRequested(const QString& dsId); + void sliceDeleteRequested(const QString& dsId); + void checkedItemsChanged(const QStringList& dsIds); + +private: + void onContextMenu(const QPoint& pos); + QTreeWidget* tree_ = nullptr; +}; + +} // namespace geopro::app