feat(vtk): 异常属性对话框(#4c-3, R83)-双击异常列表弹只读属性
- AnomalyPropertiesDialog:名称/类型/标记类型/归属三维体/异常体/顶点世界坐标/备注,只读 - Column3DAnalysis:留存 anomalies_,双击 itemDoubleClicked 按 id 回查发 anomalyPropertiesRequested - main:接线打开对话框 - 截图字段:模型/端点无,不展示(保存对话框截图为mock未持久化) 编译绿(build.bat app);用户实测通过。#4 异常功能收口。
This commit is contained in:
parent
f1309240a4
commit
c83f63a8f5
|
|
@ -0,0 +1,74 @@
|
||||||
|
#include "AnomalyPropertiesDialog.hpp"
|
||||||
|
|
||||||
|
#include <QDialogButtonBox>
|
||||||
|
#include <QFormLayout>
|
||||||
|
#include <QLabel>
|
||||||
|
#include <QPlainTextEdit>
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
|
||||||
|
namespace geopro::app {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
QString markTypeLabel(geopro::core::AnomalyMarkType t) {
|
||||||
|
switch (t) {
|
||||||
|
case geopro::core::AnomalyMarkType::Point: return QStringLiteral("点");
|
||||||
|
case geopro::core::AnomalyMarkType::Polyline: return QStringLiteral("折线");
|
||||||
|
case geopro::core::AnomalyMarkType::Polygon: return QStringLiteral("多边形");
|
||||||
|
}
|
||||||
|
return QStringLiteral("—");
|
||||||
|
}
|
||||||
|
|
||||||
|
QString orDash(const std::string& s) {
|
||||||
|
return s.empty() ? QStringLiteral("—") : QString::fromStdString(s);
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
AnomalyPropertiesDialog::AnomalyPropertiesDialog(const geopro::core::Anomaly& a, QWidget* parent)
|
||||||
|
: QDialog(parent) {
|
||||||
|
setWindowTitle(QStringLiteral("异常属性"));
|
||||||
|
setModal(true);
|
||||||
|
|
||||||
|
auto* root = new QVBoxLayout(this);
|
||||||
|
|
||||||
|
auto* form = new QFormLayout();
|
||||||
|
form->addRow(QStringLiteral("名称"), new QLabel(orDash(a.name)));
|
||||||
|
form->addRow(QStringLiteral("类型"), new QLabel(orDash(a.typeName)));
|
||||||
|
form->addRow(QStringLiteral("标记类型"), new QLabel(markTypeLabel(a.markType)));
|
||||||
|
form->addRow(QStringLiteral("归属三维体"), new QLabel(orDash(a.volumeDsId)));
|
||||||
|
form->addRow(QStringLiteral("异常体"),
|
||||||
|
new QLabel(a.consortiumId.empty() ? QStringLiteral("(未分组)")
|
||||||
|
: QString::fromStdString(a.consortiumId)));
|
||||||
|
root->addLayout(form);
|
||||||
|
|
||||||
|
// 顶点世界坐标(只读列表,x/y/z 每行一个点)。
|
||||||
|
root->addWidget(new QLabel(QStringLiteral("顶点坐标(%1 个)").arg(a.worldPts.size())));
|
||||||
|
auto* pts = new QPlainTextEdit();
|
||||||
|
pts->setReadOnly(true);
|
||||||
|
pts->setFixedHeight(120);
|
||||||
|
QString text;
|
||||||
|
for (std::size_t i = 0; i < a.worldPts.size(); ++i) {
|
||||||
|
const auto& p = a.worldPts[i];
|
||||||
|
text += QStringLiteral("%1: (%2, %3, %4)\n")
|
||||||
|
.arg(i + 1)
|
||||||
|
.arg(p.x, 0, 'f', 2)
|
||||||
|
.arg(p.y, 0, 'f', 2)
|
||||||
|
.arg(p.z, 0, 'f', 2);
|
||||||
|
}
|
||||||
|
pts->setPlainText(text);
|
||||||
|
root->addWidget(pts);
|
||||||
|
|
||||||
|
// 备注(只读)。
|
||||||
|
root->addWidget(new QLabel(QStringLiteral("备注")));
|
||||||
|
auto* remark = new QPlainTextEdit();
|
||||||
|
remark->setReadOnly(true);
|
||||||
|
remark->setFixedHeight(60);
|
||||||
|
remark->setPlainText(QString::fromStdString(a.remark));
|
||||||
|
root->addWidget(remark);
|
||||||
|
|
||||||
|
auto* buttons = new QDialogButtonBox(QDialogButtonBox::Close);
|
||||||
|
connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||||
|
connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
||||||
|
root->addWidget(buttons);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace geopro::app
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
#pragma once
|
||||||
|
#include <QDialog>
|
||||||
|
|
||||||
|
#include "model/Anomaly.hpp"
|
||||||
|
|
||||||
|
namespace geopro::app {
|
||||||
|
|
||||||
|
// 异常属性对话框(#4c-3,需求 R83):双击异常列表项弹出,只读展示选中异常的
|
||||||
|
// 名称/类型/标记类型/备注/归属三维体/异常体分组/顶点世界坐标。
|
||||||
|
// 截图:模型与异常端点均无截图字段(保存对话框的截图仅为 mock 预览、未持久化),故不展示。
|
||||||
|
class AnomalyPropertiesDialog : public QDialog {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
AnomalyPropertiesDialog(const geopro::core::Anomaly& a, QWidget* parent = nullptr);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace geopro::app
|
||||||
|
|
@ -65,6 +65,7 @@ add_executable(geopro_desktop WIN32
|
||||||
ImportDatasetDialog.cpp
|
ImportDatasetDialog.cpp
|
||||||
ExportDatasetDialog.cpp
|
ExportDatasetDialog.cpp
|
||||||
AnomalySaveDialog.cpp
|
AnomalySaveDialog.cpp
|
||||||
|
AnomalyPropertiesDialog.cpp
|
||||||
SettingsDialog.cpp
|
SettingsDialog.cpp
|
||||||
SliceExport.cpp
|
SliceExport.cpp
|
||||||
VolumeParamsDialog.cpp
|
VolumeParamsDialog.cpp
|
||||||
|
|
|
||||||
|
|
@ -98,6 +98,7 @@
|
||||||
#include "PanelHeader.hpp"
|
#include "PanelHeader.hpp"
|
||||||
#include "Theme.hpp"
|
#include "Theme.hpp"
|
||||||
#include "AnomalySaveDialog.hpp"
|
#include "AnomalySaveDialog.hpp"
|
||||||
|
#include "AnomalyPropertiesDialog.hpp"
|
||||||
#include "SettingsDialog.hpp"
|
#include "SettingsDialog.hpp"
|
||||||
#include "SliceExport.hpp"
|
#include "SliceExport.hpp"
|
||||||
#include "TopBar.hpp"
|
#include "TopBar.hpp"
|
||||||
|
|
@ -785,6 +786,12 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re
|
||||||
[sceneView](const QString& id) {
|
[sceneView](const QString& id) {
|
||||||
sceneView->setSelectedAnomaly(id.toStdString());
|
sceneView->setSelectedAnomaly(id.toStdString());
|
||||||
});
|
});
|
||||||
|
// 双击异常 → 只读属性对话框(R83,名称/类型/标记/归属/坐标/备注)。
|
||||||
|
QObject::connect(ca, &geopro::app::Column3DAnalysis::anomalyPropertiesRequested, &window,
|
||||||
|
[&window](const geopro::core::Anomaly& a) {
|
||||||
|
geopro::app::AnomalyPropertiesDialog dlg(a, &window);
|
||||||
|
dlg.exec();
|
||||||
|
});
|
||||||
// 删除异常 → 删 mock + 刷新渲染/列表。
|
// 删除异常 → 删 mock + 刷新渲染/列表。
|
||||||
QObject::connect(ca, &geopro::app::Column3DAnalysis::anomalyDeleteRequested, &window,
|
QObject::connect(ca, &geopro::app::Column3DAnalysis::anomalyDeleteRequested, &window,
|
||||||
[scene3dRepo, refreshAnomalies](const QString& id) {
|
[scene3dRepo, refreshAnomalies](const QString& id) {
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,17 @@ Column3DAnalysis::Column3DAnalysis(QWidget* parent) : QWidget(parent) {
|
||||||
[this](QTreeWidgetItem* cur, QTreeWidgetItem*) {
|
[this](QTreeWidgetItem* cur, QTreeWidgetItem*) {
|
||||||
if (cur != nullptr) emit anomalySelected(cur->data(0, kDsIdRole).toString());
|
if (cur != nullptr) emit anomalySelected(cur->data(0, kDsIdRole).toString());
|
||||||
});
|
});
|
||||||
|
// 双击异常项 → 属性对话框(R83):按 id 回查当前集合发出整条异常。
|
||||||
|
connect(anomalyTree_, &QTreeWidget::itemDoubleClicked, this,
|
||||||
|
[this](QTreeWidgetItem* it, int) {
|
||||||
|
if (it == nullptr) return;
|
||||||
|
const QString id = it->data(0, kDsIdRole).toString();
|
||||||
|
for (const auto& a : anomalies_)
|
||||||
|
if (QString::fromStdString(a.id) == id) {
|
||||||
|
emit anomalyPropertiesRequested(a);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
auto* anomGroup = new QGroupBox(QStringLiteral("异常"));
|
auto* anomGroup = new QGroupBox(QStringLiteral("异常"));
|
||||||
auto* gv = new QVBoxLayout(anomGroup);
|
auto* gv = new QVBoxLayout(anomGroup);
|
||||||
|
|
@ -92,6 +103,7 @@ int Column3DAnalysis::anomalyFilterMode() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Column3DAnalysis::setAnomalies(const std::vector<geopro::core::Anomaly>& anoms) {
|
void Column3DAnalysis::setAnomalies(const std::vector<geopro::core::Anomaly>& anoms) {
|
||||||
|
anomalies_ = anoms; // 留存供双击查属性(R83)
|
||||||
QSignalBlocker block(anomalyTree_); // 填充不触发 visibilityChanged
|
QSignalBlocker block(anomalyTree_); // 填充不触发 visibilityChanged
|
||||||
anomalyTree_->clear();
|
anomalyTree_->clear();
|
||||||
for (const auto& a : anoms) {
|
for (const auto& a : anoms) {
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,7 @@ signals:
|
||||||
void anomalyDisplayFilterChanged(int mode); // 过滤档位 0..3
|
void anomalyDisplayFilterChanged(int mode); // 过滤档位 0..3
|
||||||
void anomalySelected(const QString& anomalyId); // 列表选中→VTK 高亮
|
void anomalySelected(const QString& anomalyId); // 列表选中→VTK 高亮
|
||||||
void anomalyDeleteRequested(const QString& anomalyId); // 右键删除
|
void anomalyDeleteRequested(const QString& anomalyId); // 右键删除
|
||||||
|
void anomalyPropertiesRequested(const geopro::core::Anomaly& a); // 双击→属性对话框(R83)
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void onContextMenu(const QPoint& pos);
|
void onContextMenu(const QPoint& pos);
|
||||||
|
|
@ -48,6 +49,7 @@ private:
|
||||||
QTreeWidget* tree_ = nullptr;
|
QTreeWidget* tree_ = nullptr;
|
||||||
QTreeWidget* anomalyTree_ = nullptr;
|
QTreeWidget* anomalyTree_ = nullptr;
|
||||||
QComboBox* anomalyFilter_ = nullptr;
|
QComboBox* anomalyFilter_ = nullptr;
|
||||||
|
std::vector<geopro::core::Anomaly> anomalies_; // 当前展示集合(双击查属性按 id 回查)
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace geopro::app
|
} // namespace geopro::app
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue