feat/vtk-3d-view #7

Merged
gaozheng merged 301 commits from feat/vtk-3d-view into main 2026-06-27 18:43:52 +08:00
9 changed files with 502 additions and 227 deletions
Showing only changes of commit 4a1fecb149 - Show all commits

View File

@ -3,7 +3,7 @@
#include <utility> #include <utility>
#include <QComboBox> #include <QComboBox>
#include <QFormLayout> #include <QFrame>
#include <QHBoxLayout> #include <QHBoxLayout>
#include <QHeaderView> #include <QHeaderView>
#include <QHash> #include <QHash>
@ -20,16 +20,17 @@
#include <QTreeWidget> #include <QTreeWidget>
#include <QVBoxLayout> #include <QVBoxLayout>
#include "FormKit.hpp" #include "Theme.hpp"
#include "panels/chart/InversionProcessOps.hpp" // buildFilterApplyBody / buildNewFilterBody #include "panels/chart/InversionProcessOps.hpp" // buildFilterApplyBody / buildNewFilterBody
#include "repo/IDatasetCommandRepository.hpp" #include "repo/IDatasetCommandRepository.hpp"
namespace geopro::app { namespace geopro::app {
namespace { namespace {
constexpr double kFillRange = 1e9; constexpr int kDialogW = 900; // 原版弹窗宽 900px
constexpr int kMatrixMin = 1, kMatrixMax = 21; // 矩阵行列范围(对照原版 1~21 constexpr int kMatrixMin = 1, kMatrixMax = 21; // 矩阵行列范围(对照原版 1~21
constexpr int kDefaultDim = 3; constexpr int kDefaultDim = 3;
constexpr int kSettingLabelW = 80; // 原版 .setting-label width:80px
const char kDefaultCustomKey[] = "default-custom-filter"; // 默认自定义滤波器(不可删) const char kDefaultCustomKey[] = "default-custom-filter"; // 默认自定义滤波器(不可删)
const char kCustomGroupName[] = "自定义滤波器"; const char kCustomGroupName[] = "自定义滤波器";
@ -40,86 +41,58 @@ double cellValue(const QTableWidgetItem* it) {
const double v = it->text().toDouble(&ok); const double v = it->text().toDouble(&ok);
return ok ? v : 0.0; return ok ? v : 0.0;
} }
// 原版分组小标题14px 半粗 + 标题下 1px divider
void addSpecTitle(QVBoxLayout* into, const QString& title, QWidget* parent) {
auto* lbl = new QLabel(title, parent);
auto f = lbl->font();
f.setBold(true);
lbl->setFont(f);
into->addWidget(lbl);
auto* line = new QFrame(parent);
line->setFrameShape(QFrame::HLine);
line->setFrameShadow(QFrame::Plain);
into->addWidget(line);
}
// 原版带边框卡片1px 边框 + 圆角 + 内距)。
QFrame* cardFrame(QWidget* parent) {
auto* card = new QFrame(parent);
card->setFrameShape(QFrame::StyledPanel);
return card;
}
} // namespace } // namespace
FilterDialog::FilterDialog(geopro::data::IDatasetCommandRepository* repo, QString dsId, FilterDialog::FilterDialog(geopro::data::IDatasetCommandRepository* repo, QString dsId,
QString projectId, QWidget* parent) QString projectId, QWidget* parent)
: QDialog(parent), repo_(repo), dsId_(std::move(dsId)), projectId_(std::move(projectId)) { : QDialog(parent), repo_(repo), dsId_(std::move(dsId)), projectId_(std::move(projectId)) {
setWindowTitle(QStringLiteral("滤波处理")); setWindowTitle(QStringLiteral("滤波设置")); // 原版 filterSetting
setModal(true); setModal(true);
resize(820, 520); setFixedWidth(kDialogW);
auto* root = formkit::dialogRoot(this); auto* root = new QVBoxLayout(this);
root->setContentsMargins(geopro::app::space::kLg, geopro::app::space::kLg,
geopro::app::space::kLg, geopro::app::space::kLg);
root->setSpacing(geopro::app::space::kMd);
auto* body = new QHBoxLayout(); auto* body = new QHBoxLayout();
body->setSpacing(geopro::app::space::kLg);
root->addLayout(body, 1); root->addLayout(body, 1);
// ── 左:滤波器树 + 增删按钮 ───────────────────────────────────────── buildLeft(body);
auto* leftLay = new QVBoxLayout(); buildRight(body);
tree_ = new QTreeWidget(this);
tree_->setHeaderHidden(true);
leftLay->addWidget(tree_, 1);
auto* treeBtnLay = new QHBoxLayout();
auto* addBtn = new QPushButton(QStringLiteral("另存为"), this);
auto* delBtn = new QPushButton(QStringLiteral("删除"), this);
treeBtnLay->addWidget(addBtn);
treeBtnLay->addWidget(delBtn);
leftLay->addLayout(treeBtnLay);
body->addLayout(leftLay, 1);
// ── 右:配置面板 ──────────────────────────────────────────────────── // 底部按钮(原版三按钮各 ~30%:保存设置(主,左)/确认(主,中)/取消(右))。
auto* rightLay = new QVBoxLayout();
auto* form = formkit::makeEditForm();
dataEdge_ = new QComboBox(this);
dataEdge_->addItem(QStringLiteral("设为无效点"), QStringLiteral("whitening"));
dataEdge_->addItem(QStringLiteral("忽略"), QStringLiteral("skip"));
dataEdge_->addItem(QStringLiteral("复制边缘点"), QStringLiteral("edgePoint"));
dataEdge_->addItem(QStringLiteral("填充"), QStringLiteral("filling"));
dataEdgeValue_ = new QLineEdit(this);
dataEdgeValue_->setEnabled(false);
noDataPoints_ = new QComboBox(this);
noDataPoints_->addItem(QStringLiteral("扩展"), QStringLiteral("expansion"));
noDataPoints_->addItem(QStringLiteral("保留"), QStringLiteral("retain"));
noDataPoints_->addItem(QStringLiteral("跳过"), QStringLiteral("skip"));
noDataPoints_->addItem(QStringLiteral("填充"), QStringLiteral("filling"));
noDataPoints_->setCurrentIndex(3); // 默认填充(对照原版)
noDataValue_ = new QLineEdit(this);
filterTimes_ = new QSpinBox(this);
filterTimes_->setRange(1, 10);
rows_ = new QSpinBox(this);
rows_->setRange(kMatrixMin, kMatrixMax);
rows_->setValue(kDefaultDim);
cols_ = new QSpinBox(this);
cols_->setRange(kMatrixMin, kMatrixMax);
cols_->setValue(kDefaultDim);
formkit::capField(dataEdge_);
formkit::capField(dataEdgeValue_);
formkit::capField(noDataPoints_);
formkit::capField(noDataValue_);
formkit::capField(filterTimes_);
formkit::capField(rows_);
formkit::capField(cols_);
form->addRow(formkit::editLabel(QStringLiteral("数据边缘:")), dataEdge_);
form->addRow(formkit::editLabel(QStringLiteral("数据边缘值:")), dataEdgeValue_);
form->addRow(formkit::editLabel(QStringLiteral("无数据点:")), noDataPoints_);
form->addRow(formkit::editLabel(QStringLiteral("无数据点值:")), noDataValue_);
form->addRow(formkit::editLabel(QStringLiteral("滤波次数:")), filterTimes_);
form->addRow(formkit::editLabel(QStringLiteral("行:")), rows_);
form->addRow(formkit::editLabel(QStringLiteral("列:")), cols_);
rightLay->addLayout(form);
matrix_ = new QTableWidget(kDefaultDim, kDefaultDim, this);
matrix_->horizontalHeader()->setVisible(false);
matrix_->verticalHeader()->setVisible(false);
rightLay->addWidget(matrix_, 1);
body->addLayout(rightLay, 2);
// 底部按钮。
auto* btnLay = new QHBoxLayout(); auto* btnLay = new QHBoxLayout();
btnLay->addStretch(); btnLay->setSpacing(geopro::app::space::kMd);
auto* cancelBtn = new QPushButton(QStringLiteral("取消"), this); auto* saveSettingBtn = new QPushButton(QStringLiteral("保存设置"), this);
okBtn_ = new QPushButton(QStringLiteral("应用"), this); okBtn_ = new QPushButton(QStringLiteral("确认"), this);
okBtn_->setDefault(true); okBtn_->setDefault(true);
btnLay->addWidget(cancelBtn); auto* cancelBtn = new QPushButton(QStringLiteral("取消"), this);
btnLay->addWidget(okBtn_); btnLay->addWidget(saveSettingBtn, 30);
btnLay->addStretch(2);
btnLay->addWidget(okBtn_, 30);
btnLay->addStretch(2);
btnLay->addWidget(cancelBtn, 30);
root->addLayout(btnLay); root->addLayout(btnLay);
resizeMatrix(); // 默认 3x3 中心 1 resizeMatrix(); // 默认 3x3 中心 1
@ -127,14 +100,16 @@ FilterDialog::FilterDialog(geopro::data::IDatasetCommandRepository* repo, QStrin
connect(cancelBtn, &QPushButton::clicked, this, &QDialog::reject); connect(cancelBtn, &QPushButton::clicked, this, &QDialog::reject);
connect(okBtn_, &QPushButton::clicked, this, &FilterDialog::onConfirm); connect(okBtn_, &QPushButton::clicked, this, &FilterDialog::onConfirm);
connect(addBtn, &QPushButton::clicked, this, &FilterDialog::saveCustomFilter); connect(saveSettingBtn, &QPushButton::clicked, this, &FilterDialog::saveCustomFilter);
connect(delBtn, &QPushButton::clicked, this, &FilterDialog::deleteSelectedFilter);
connect(tree_, &QTreeWidget::itemSelectionChanged, this, connect(tree_, &QTreeWidget::itemSelectionChanged, this,
&FilterDialog::onTreeSelectionChanged); &FilterDialog::onTreeSelectionChanged);
// 行/列:仅奇数允许,偶数弹警告并回退旧值(对照原版 watch rows/cols
prevRows_ = kDefaultDim;
prevCols_ = kDefaultDim;
connect(rows_, QOverload<int>::of(&QSpinBox::valueChanged), this, connect(rows_, QOverload<int>::of(&QSpinBox::valueChanged), this,
[this](int) { resizeMatrix(); }); [this](int v) { onDimChanged(rows_, v, prevRows_, QStringLiteral("")); });
connect(cols_, QOverload<int>::of(&QSpinBox::valueChanged), this, connect(cols_, QOverload<int>::of(&QSpinBox::valueChanged), this,
[this](int) { resizeMatrix(); }); [this](int v) { onDimChanged(cols_, v, prevCols_, QStringLiteral("")); });
// 数据边缘/无数据点:仅「填充」启用对应值输入框(对照原版 v-if=filling // 数据边缘/无数据点:仅「填充」启用对应值输入框(对照原版 v-if=filling
connect(dataEdge_, QOverload<int>::of(&QComboBox::currentIndexChanged), this, [this](int) { connect(dataEdge_, QOverload<int>::of(&QComboBox::currentIndexChanged), this, [this](int) {
dataEdgeValue_->setEnabled(dataEdge_->currentData().toString() == dataEdgeValue_->setEnabled(dataEdge_->currentData().toString() ==
@ -149,6 +124,125 @@ FilterDialog::FilterDialog(geopro::data::IDatasetCommandRepository* repo, QStrin
loadFilters(); loadFilters();
} }
void FilterDialog::buildLeft(QHBoxLayout* body) {
auto* card = cardFrame(this);
card->setMinimumWidth(270); // 原版左卡片 min-width:270px
auto* leftLay = new QVBoxLayout(card);
leftLay->setContentsMargins(geopro::app::space::kLg, geopro::app::space::kLg,
geopro::app::space::kLg, geopro::app::space::kLg);
auto* title = new QLabel(QStringLiteral("滤波方式:"), card); // 原版 filterType:
auto tf = title->font();
tf.setBold(true);
title->setFont(tf);
leftLay->addWidget(title);
tree_ = new QTreeWidget(card);
tree_->setHeaderHidden(true);
leftLay->addWidget(tree_, 1);
auto* treeBtnLay = new QHBoxLayout();
auto* addBtn = new QPushButton(QStringLiteral("另存为"), card);
auto* delBtn = new QPushButton(QStringLiteral("删除"), card);
treeBtnLay->addWidget(addBtn);
treeBtnLay->addWidget(delBtn);
leftLay->addLayout(treeBtnLay);
body->addWidget(card, 3); // 左 ~30%
connect(addBtn, &QPushButton::clicked, this, &FilterDialog::saveCustomFilter);
connect(delBtn, &QPushButton::clicked, this, &FilterDialog::deleteSelectedFilter);
}
void FilterDialog::buildRight(QHBoxLayout* body) {
auto* card = cardFrame(this);
auto* rightLay = new QVBoxLayout(card);
rightLay->setContentsMargins(geopro::app::space::kLg, geopro::app::space::kLg,
geopro::app::space::kLg, geopro::app::space::kLg);
rightLay->setSpacing(geopro::app::space::kMd);
// 滤波设置分组。
addSpecTitle(rightLay, QStringLiteral("滤波设置"), card);
dataEdge_ = new QComboBox(card);
dataEdge_->addItem(QStringLiteral("设置为无效点"), QStringLiteral("whitening"));
dataEdge_->addItem(QStringLiteral("忽略"), QStringLiteral("skip"));
dataEdge_->addItem(QStringLiteral("复制边缘点"), QStringLiteral("edgePoint"));
dataEdge_->addItem(QStringLiteral("填充"), QStringLiteral("filling"));
dataEdgeValue_ = new QLineEdit(card);
dataEdgeValue_->setEnabled(false);
noDataPoints_ = new QComboBox(card);
noDataPoints_->addItem(QStringLiteral("扩展"), QStringLiteral("expansion"));
noDataPoints_->addItem(QStringLiteral("保留"), QStringLiteral("retain"));
noDataPoints_->addItem(QStringLiteral("忽略"), QStringLiteral("skip")); // 原版 skip → 忽略
noDataPoints_->addItem(QStringLiteral("填充"), QStringLiteral("filling"));
noDataPoints_->setCurrentIndex(3); // 默认填充(对照原版)
noDataValue_ = new QLineEdit(card);
filterTimes_ = new QSpinBox(card);
filterTimes_->setRange(1, 10);
rightLay->addLayout(settingRow(QStringLiteral("数据边缘:"), dataEdge_,
QStringLiteral("值:"), dataEdgeValue_, card));
rightLay->addLayout(settingRow(QStringLiteral("无数据点:"), noDataPoints_,
QStringLiteral("值:"), noDataValue_, card));
rightLay->addLayout(settingRow(QStringLiteral("滤波次数:"), filterTimes_,
QString(), nullptr, card));
// 滤波器规格分组。
addSpecTitle(rightLay, QStringLiteral("滤波器规格"), card);
rows_ = new QSpinBox(card);
rows_->setRange(kMatrixMin, kMatrixMax);
rows_->setValue(kDefaultDim);
cols_ = new QSpinBox(card);
cols_->setRange(kMatrixMin, kMatrixMax);
cols_->setValue(kDefaultDim);
auto* specsRow = new QHBoxLayout();
auto* rowLbl = new QLabel(QStringLiteral("行:"), card);
rowLbl->setMinimumWidth(30);
specsRow->addWidget(rowLbl);
specsRow->addWidget(rows_);
specsRow->addSpacing(geopro::app::space::kLg);
auto* colLbl = new QLabel(QStringLiteral("列:"), card);
colLbl->setMinimumWidth(30);
specsRow->addWidget(colLbl);
specsRow->addWidget(cols_);
specsRow->addStretch();
rightLay->addLayout(specsRow);
matrix_ = new QTableWidget(kDefaultDim, kDefaultDim, card);
matrix_->horizontalHeader()->setVisible(true); // 原版矩阵带行列号表头
matrix_->verticalHeader()->setVisible(true);
rightLay->addWidget(matrix_, 1);
body->addWidget(card, 6); // 右 ~60%
}
// 原版 .setting-rowlabel(80px) + 主控件(30%) [+ 右侧 label「值:」+ 值框(30%)]。
QHBoxLayout* FilterDialog::settingRow(const QString& label, QWidget* main, const QString& valLabel,
QWidget* valField, QWidget* parent) {
auto* row = new QHBoxLayout();
row->setSpacing(geopro::app::space::kMd);
auto* lbl = new QLabel(label, parent);
lbl->setMinimumWidth(kSettingLabelW);
row->addWidget(lbl);
row->addWidget(main, 3);
if (valField) {
row->addSpacing(geopro::app::space::kLg);
row->addWidget(new QLabel(valLabel, parent));
row->addWidget(valField, 3);
} else {
row->addStretch(4);
}
return row;
}
void FilterDialog::onDimChanged(QSpinBox* box, int newVal, int& prev, const QString& which) {
if (newVal % 2 == 0) { // 偶数 → 警告并回退(对照原版 watch 回退旧值)
QMessageBox::warning(
this, windowTitle(),
QStringLiteral("滤波矩阵%1数必须为奇数以确保有唯一的中心点").arg(which));
QSignalBlocker b(box);
box->setValue(prev);
return;
}
prev = newVal;
resizeMatrix();
}
void FilterDialog::loadFilters() { void FilterDialog::loadFilters() {
if (!repo_) return; if (!repo_) return;
QPointer<FilterDialog> self(this); QPointer<FilterDialog> self(this);
@ -226,6 +320,12 @@ void FilterDialog::resizeMatrix() {
std::vector<std::vector<double>> old = readMatrix(); std::vector<std::vector<double>> old = readMatrix();
matrix_->setRowCount(r); matrix_->setRowCount(r);
matrix_->setColumnCount(c); matrix_->setColumnCount(c);
// 行列号表头1..n
QStringList hh, vh;
for (int j = 0; j < c; ++j) hh << QString::number(j + 1);
for (int i = 0; i < r; ++i) vh << QString::number(i + 1);
matrix_->setHorizontalHeaderLabels(hh);
matrix_->setVerticalHeaderLabels(vh);
for (int i = 0; i < r; ++i) for (int i = 0; i < r; ++i)
for (int j = 0; j < c; ++j) { for (int j = 0; j < c; ++j) {
auto* it = matrix_->item(i, j); auto* it = matrix_->item(i, j);
@ -272,8 +372,8 @@ void FilterDialog::saveCustomFilter() {
return; return;
} }
bool ok = false; bool ok = false;
const QString name = QInputDialog::getText(this, QStringLiteral("另存为新自定义滤波器"), const QString name = QInputDialog::getText(this, QStringLiteral("保存为新的自定义滤波器"),
QStringLiteral("名称"), QLineEdit::Normal, QStringLiteral("请输入滤波器名称"), QLineEdit::Normal,
QStringLiteral("自定义滤波器1"), &ok); QStringLiteral("自定义滤波器1"), &ok);
if (!ok || name.trimmed().isEmpty()) return; if (!ok || name.trimmed().isEmpty()) return;
QPointer<FilterDialog> self(this); QPointer<FilterDialog> self(this);

View File

@ -13,6 +13,8 @@ class QTreeWidget;
class QTreeWidgetItem; class QTreeWidgetItem;
class QTableWidget; class QTableWidget;
class QLineEdit; class QLineEdit;
class QHBoxLayout;
class QWidget;
namespace geopro::data { namespace geopro::data {
class IDatasetCommandRepository; class IDatasetCommandRepository;
@ -31,6 +33,11 @@ public:
QWidget* parent = nullptr); QWidget* parent = nullptr);
private: private:
void buildLeft(QHBoxLayout* body); // 左:滤波方式树卡片
void buildRight(QHBoxLayout* body); // 右:滤波设置 + 滤波器规格卡片
QHBoxLayout* settingRow(const QString& label, QWidget* main, const QString& valLabel,
QWidget* valField, QWidget* parent); // 原版 .setting-row
void onDimChanged(QSpinBox* box, int newVal, int& prev, const QString& which); // 奇偶校验
void loadFilters(); // 拉滤波器树 void loadFilters(); // 拉滤波器树
void buildTree(); // 由 flatItems_ 建树 void buildTree(); // 由 flatItems_ 建树
void onTreeSelectionChanged(); // 选中叶节点 → 右侧回填 void onTreeSelectionChanged(); // 选中叶节点 → 右侧回填
@ -56,6 +63,8 @@ private:
QSpinBox* filterTimes_ = nullptr; QSpinBox* filterTimes_ = nullptr;
QSpinBox* rows_ = nullptr; QSpinBox* rows_ = nullptr;
QSpinBox* cols_ = nullptr; QSpinBox* cols_ = nullptr;
int prevRows_ = 3; // 奇偶校验回退用(上一合法行数)
int prevCols_ = 3; // 上一合法列数
QTableWidget* matrix_ = nullptr; QTableWidget* matrix_ = nullptr;
QPushButton* okBtn_ = nullptr; QPushButton* okBtn_ = nullptr;

View File

@ -122,10 +122,11 @@ GridDataChartView::GridDataChartView(QWidget* parent) : QWidget(parent) {
tbLay->addWidget(lblSimplify); tbLay->addWidget(lblSimplify);
tbLay->addWidget(simplifySlider_); tbLay->addWidget(simplifySlider_);
tbLay->addWidget(simplifyValueLabel_); tbLay->addWidget(simplifyValueLabel_);
// 原版 .right-buttons margin-left:auto异常标注/自动标注/另存为 右对齐。
tbLay->addStretch();
tbLay->addWidget(btnAnomalyLabel); tbLay->addWidget(btnAnomalyLabel);
tbLay->addWidget(btnAutoLabel); tbLay->addWidget(btnAutoLabel);
tbLay->addWidget(btnSaveAs); tbLay->addWidget(btnSaveAs);
tbLay->addStretch();
lay->addWidget(toolbar); lay->addWidget(toolbar);

View File

@ -1,10 +1,12 @@
#include "panels/chart/GridWizardDialog.hpp" #include "panels/chart/GridWizardDialog.hpp"
#include <cmath>
#include <utility> #include <utility>
#include <QComboBox> #include <QComboBox>
#include <QDoubleSpinBox> #include <QDoubleSpinBox>
#include <QFormLayout> #include <QFrame>
#include <QGridLayout>
#include <QHBoxLayout> #include <QHBoxLayout>
#include <QLabel> #include <QLabel>
#include <QListWidget> #include <QListWidget>
@ -15,7 +17,7 @@
#include <QStackedWidget> #include <QStackedWidget>
#include <QVBoxLayout> #include <QVBoxLayout>
#include "FormKit.hpp" #include "Theme.hpp"
#include "panels/chart/InversionProcessOps.hpp" // buildGridToBody #include "panels/chart/InversionProcessOps.hpp" // buildGridToBody
#include "repo/IDatasetCommandRepository.hpp" #include "repo/IDatasetCommandRepository.hpp"
@ -25,86 +27,75 @@ namespace {
constexpr double kCoordRange = 1e9; // 坐标范围(足够宽) constexpr double kCoordRange = 1e9; // 坐标范围(足够宽)
constexpr int kSizeMin = 1, kSizeMax = 300; // 点数范围(对照原版 1~300 constexpr int kSizeMin = 1, kSizeMax = 300; // 点数范围(对照原版 1~300
constexpr int kDefaultXSize = 100; // 默认点数(原版 xPoints 默认 100 constexpr int kDefaultXSize = 100; // 默认点数(原版 xPoints 默认 100
constexpr int kStep1W = 500, kStep2W = 800; // 原版步骤 1/2 弹窗宽
constexpr int kParamLabelW = 60; // 原版 .param-group label min-width:60px
constexpr int kParamFieldW = 100; // 原版输入框宽 100px
// 配一个坐标 spinbox6 位小数,宽范围)。 // 配一个坐标 spinbox6 位小数,宽范围)。
QDoubleSpinBox* makeCoordSpin(QWidget* parent) { QDoubleSpinBox* makeCoordSpin(QWidget* parent) {
auto* sp = new QDoubleSpinBox(parent); auto* sp = new QDoubleSpinBox(parent);
sp->setRange(-kCoordRange, kCoordRange); sp->setRange(-kCoordRange, kCoordRange);
sp->setDecimals(6); sp->setDecimals(6);
sp->setFixedWidth(kParamFieldW);
return sp; return sp;
} }
// 分组卡片标题(原版 .section-title14px 半粗 + 标题下 1px divider
void addSectionTitle(QVBoxLayout* into, const QString& title, QWidget* parent) {
auto* lbl = new QLabel(title, parent);
auto f = lbl->font();
f.setBold(true);
lbl->setFont(f);
into->addWidget(lbl);
auto* line = new QFrame(parent);
line->setFrameShape(QFrame::HLine);
line->setFrameShadow(QFrame::Plain);
into->addWidget(line);
}
// 原版 .param-group定宽右标签 + 紧随输入框,多个并排成一行栅格。
void addParamCell(QGridLayout* grid, int row, int col, const QString& label, QWidget* field,
QWidget* parent) {
auto* cell = new QHBoxLayout();
cell->setSpacing(geopro::app::space::kSm);
auto* lbl = new QLabel(label, parent);
lbl->setMinimumWidth(kParamLabelW);
lbl->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
cell->addWidget(lbl);
cell->addWidget(field);
grid->addLayout(cell, row, col);
}
} // namespace } // namespace
GridWizardDialog::GridWizardDialog(geopro::data::IDatasetCommandRepository* repo, QString dsId, GridWizardDialog::GridWizardDialog(geopro::data::IDatasetCommandRepository* repo, QString dsId,
QWidget* parent) QWidget* parent)
: QDialog(parent), repo_(repo), dsId_(std::move(dsId)) { : QDialog(parent), repo_(repo), dsId_(std::move(dsId)) {
setWindowTitle(QStringLiteral("网格化")); setWindowTitle(QStringLiteral("网格配置")); // 原版 gridSetting
setModal(true); setModal(true);
resize(560, 420); setFixedWidth(kStep1W);
auto* root = formkit::dialogRoot(this); auto* root = new QVBoxLayout(this);
root->setContentsMargins(geopro::app::space::kLg, geopro::app::space::kLg,
geopro::app::space::kLg, geopro::app::space::kLg);
root->setSpacing(geopro::app::space::kMd);
stack_ = new QStackedWidget(this); stack_ = new QStackedWidget(this);
root->addWidget(stack_, 1); root->addWidget(stack_, 1);
// ── 步骤 1算法选择单选列表────────────────────────────────────── buildStep1();
auto* page1 = new QWidget(this); buildStep2();
auto* p1Lay = new QVBoxLayout(page1);
p1Lay->addWidget(new QLabel(QStringLiteral("请选择网格方法:"), page1));
algoList_ = new QListWidget(page1);
p1Lay->addWidget(algoList_, 1);
stack_->addWidget(page1);
// ── 步骤 2网格参数 + 数据值设置 ──────────────────────────────────── // ── 底部按钮(上一步 / 确认(主) / 取消,原版步骤 2 三按钮)────────────
auto* page2 = new QWidget(this);
auto* p2Lay = new QVBoxLayout(page2);
xMin_ = makeCoordSpin(page2); xMax_ = makeCoordSpin(page2);
yMin_ = makeCoordSpin(page2); yMax_ = makeCoordSpin(page2);
vMin_ = makeCoordSpin(page2); vMax_ = makeCoordSpin(page2);
xSize_ = new QSpinBox(page2); xSize_->setRange(kSizeMin, kSizeMax); xSize_->setValue(kDefaultXSize);
ySize_ = new QSpinBox(page2); ySize_->setRange(kSizeMin, kSizeMax); ySize_->setValue(kDefaultXSize);
xSpacing_ = makeCoordSpin(page2); xSpacing_->setRange(0.0, kCoordRange);
ySpacing_ = makeCoordSpin(page2); ySpacing_->setRange(0.0, kCoordRange);
saveFormat_ = new QComboBox(page2);
saveFormat_->addItem(QStringLiteral("线性"), QStringLiteral("linear"));
saveFormat_->addItem(QStringLiteral("对数"), QStringLiteral("log"));
formkit::capField(xMin_);
formkit::capField(xMax_);
formkit::capField(xSpacing_);
formkit::capField(yMin_);
formkit::capField(yMax_);
formkit::capField(ySpacing_);
formkit::capField(vMin_);
formkit::capField(vMax_);
formkit::capField(xSize_);
formkit::capField(ySize_);
formkit::capField(saveFormat_);
auto* form = formkit::makeEditForm();
form->addRow(formkit::editLabel(QStringLiteral("Xmin")), xMin_);
form->addRow(formkit::editLabel(QStringLiteral("Xmax")), xMax_);
form->addRow(formkit::editLabel(QStringLiteral("X点数")), xSize_);
form->addRow(formkit::editLabel(QStringLiteral("X间距")), xSpacing_);
form->addRow(formkit::editLabel(QStringLiteral("Ymin")), yMin_);
form->addRow(formkit::editLabel(QStringLiteral("Ymax")), yMax_);
form->addRow(formkit::editLabel(QStringLiteral("Y点数")), ySize_);
form->addRow(formkit::editLabel(QStringLiteral("Y间距")), ySpacing_);
form->addRow(formkit::editLabel(QStringLiteral("数据值min")), vMin_);
form->addRow(formkit::editLabel(QStringLiteral("数据值max")), vMax_);
form->addRow(formkit::editLabel(QStringLiteral("保存格式:")), saveFormat_);
p2Lay->addLayout(form);
stack_->addWidget(page2);
// ── 底部按钮(上一步 / 下一步 / 确认 / 取消)────────────────────────
auto* btnLay = new QHBoxLayout(); auto* btnLay = new QHBoxLayout();
btnLay->setSpacing(geopro::app::space::kMd);
btnLay->addStretch(); btnLay->addStretch();
prevBtn_ = new QPushButton(QStringLiteral("上一步"), this); prevBtn_ = new QPushButton(QStringLiteral("上一步"), this);
nextBtn_ = new QPushButton(QStringLiteral("下一步"), this);
okBtn_ = new QPushButton(QStringLiteral("确认"), this); okBtn_ = new QPushButton(QStringLiteral("确认"), this);
okBtn_->setDefault(true);
nextBtn_ = new QPushButton(QStringLiteral("下一步"), this);
auto* cancelBtn = new QPushButton(QStringLiteral("取消"), this); auto* cancelBtn = new QPushButton(QStringLiteral("取消"), this);
btnLay->addWidget(prevBtn_); btnLay->addWidget(prevBtn_);
btnLay->addWidget(nextBtn_);
btnLay->addWidget(okBtn_); btnLay->addWidget(okBtn_);
btnLay->addWidget(nextBtn_);
btnLay->addWidget(cancelBtn); btnLay->addWidget(cancelBtn);
root->addLayout(btnLay); root->addLayout(btnLay);
prevBtn_->setVisible(false); prevBtn_->setVisible(false);
@ -114,18 +105,108 @@ GridWizardDialog::GridWizardDialog(geopro::data::IDatasetCommandRepository* repo
connect(nextBtn_, &QPushButton::clicked, this, &GridWizardDialog::goToStep2); connect(nextBtn_, &QPushButton::clicked, this, &GridWizardDialog::goToStep2);
connect(prevBtn_, &QPushButton::clicked, this, [this]() { connect(prevBtn_, &QPushButton::clicked, this, [this]() {
stack_->setCurrentIndex(0); stack_->setCurrentIndex(0);
setFixedWidth(kStep1W);
prevBtn_->setVisible(false); okBtn_->setVisible(false); nextBtn_->setVisible(true); prevBtn_->setVisible(false); okBtn_->setVisible(false); nextBtn_->setVisible(true);
}); });
connect(okBtn_, &QPushButton::clicked, this, &GridWizardDialog::onConfirm); connect(okBtn_, &QPushButton::clicked, this, &GridWizardDialog::onConfirm);
// 点数变化 → 重算间距(原版 calculateXInterval/calculateYInterval
connect(xSize_, QOverload<int>::of(&QSpinBox::valueChanged), this,
[this](int) { recalcXSpacing(); });
connect(ySize_, QOverload<int>::of(&QSpinBox::valueChanged), this,
[this](int) { recalcYSpacing(); });
loadAlgorithms(); loadAlgorithms();
} }
void GridWizardDialog::buildStep1() {
auto* page1 = new QWidget(this);
auto* p1Lay = new QVBoxLayout(page1);
p1Lay->setContentsMargins(0, 0, 0, 0);
p1Lay->setSpacing(geopro::app::space::kMd);
p1Lay->addWidget(new QLabel(QStringLiteral("请选择网格方法:"), page1));
algoList_ = new QListWidget(page1);
p1Lay->addWidget(algoList_, 1);
stack_->addWidget(page1);
}
void GridWizardDialog::buildStep2() {
auto* page2 = new QWidget(this);
auto* p2Lay = new QVBoxLayout(page2);
p2Lay->setContentsMargins(0, 0, 0, 0);
p2Lay->setSpacing(geopro::app::space::kLg);
xMin_ = makeCoordSpin(page2); xMax_ = makeCoordSpin(page2);
yMin_ = makeCoordSpin(page2); yMax_ = makeCoordSpin(page2);
vMin_ = makeCoordSpin(page2); vMax_ = makeCoordSpin(page2);
xSize_ = new QSpinBox(page2); xSize_->setRange(kSizeMin, kSizeMax);
xSize_->setValue(kDefaultXSize); xSize_->setFixedWidth(100);
ySize_ = new QSpinBox(page2); ySize_->setRange(kSizeMin, kSizeMax);
ySize_->setValue(kDefaultXSize); ySize_->setFixedWidth(100);
xSpacing_ = makeCoordSpin(page2); xSpacing_->setRange(0.0, kCoordRange);
ySpacing_ = makeCoordSpin(page2); ySpacing_->setRange(0.0, kCoordRange);
// 分组 1网格参数栅格Xmax→Xmin→X间距→X点数 同行Y 同理)。
addSectionTitle(p2Lay, QStringLiteral("网格参数"), page2);
auto* grid = new QGridLayout();
grid->setHorizontalSpacing(geopro::app::space::kMd);
grid->setVerticalSpacing(geopro::app::space::kMd);
addParamCell(grid, 0, 0, QStringLiteral("Xmax"), xMax_, page2);
addParamCell(grid, 0, 1, QStringLiteral("Xmin"), xMin_, page2);
addParamCell(grid, 0, 2, QStringLiteral("X间距"), xSpacing_, page2);
addParamCell(grid, 0, 3, QStringLiteral("X点数"), xSize_, page2);
addParamCell(grid, 1, 0, QStringLiteral("Ymax"), yMax_, page2);
addParamCell(grid, 1, 1, QStringLiteral("Ymin"), yMin_, page2);
addParamCell(grid, 1, 2, QStringLiteral("Y间距"), ySpacing_, page2);
addParamCell(grid, 1, 3, QStringLiteral("Y点数"), ySize_, page2);
grid->setColumnStretch(4, 1);
p2Lay->addLayout(grid);
// 分组 2数据值设置数据值max/min + 恢复默认值 + 数据值保存为)。
addSectionTitle(p2Lay, QStringLiteral("数据值设置"), page2);
saveFormat_ = new QComboBox(page2);
saveFormat_->addItem(QStringLiteral("线性"), QStringLiteral("linear"));
saveFormat_->addItem(QStringLiteral("对数"), QStringLiteral("log"));
saveFormat_->setFixedWidth(120);
auto* restoreBtn = new QPushButton(QStringLiteral("恢复默认值"), page2);
auto* dv = new QGridLayout();
dv->setHorizontalSpacing(geopro::app::space::kLg);
dv->setVerticalSpacing(geopro::app::space::kMd);
auto* vmaxLbl = new QLabel(QStringLiteral("数据值max"), page2);
vmaxLbl->setMinimumWidth(kParamLabelW + 20);
vmaxLbl->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
dv->addWidget(vmaxLbl, 0, 0);
dv->addWidget(vMax_, 0, 1);
dv->addWidget(restoreBtn, 0, 3);
auto* vminLbl = new QLabel(QStringLiteral("数据值min"), page2);
vminLbl->setMinimumWidth(kParamLabelW + 20);
vminLbl->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
dv->addWidget(vminLbl, 1, 0);
dv->addWidget(vMin_, 1, 1);
auto* fmtLbl = new QLabel(QStringLiteral("数据值保存为"), page2); // 原版 saveFormat
fmtLbl->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
dv->addWidget(fmtLbl, 1, 2);
dv->addWidget(saveFormat_, 1, 3);
dv->setColumnStretch(4, 1);
p2Lay->addLayout(dv);
p2Lay->addStretch();
stack_->addWidget(page2);
// 联动Xmax/Xmin/X点数变 → 反算 X间距(round)X间距变 → 反算 X点数(round)。
connect(xMax_, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this,
[this](double) { calcXInterval(); });
connect(xMin_, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this,
[this](double) { calcXInterval(); });
connect(xSize_, QOverload<int>::of(&QSpinBox::valueChanged), this,
[this](int) { calcXInterval(); });
connect(xSpacing_, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this,
[this](double) { calcXPoints(); });
connect(yMax_, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this,
[this](double) { calcYInterval(); });
connect(yMin_, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this,
[this](double) { calcYInterval(); });
connect(ySize_, QOverload<int>::of(&QSpinBox::valueChanged), this,
[this](int) { calcYInterval(); });
connect(ySpacing_, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this,
[this](double) { calcYPoints(); });
connect(restoreBtn, &QPushButton::clicked, this, &GridWizardDialog::loadParams);
}
void GridWizardDialog::loadAlgorithms() { void GridWizardDialog::loadAlgorithms() {
if (!repo_) return; if (!repo_) return;
QPointer<GridWizardDialog> self(this); QPointer<GridWizardDialog> self(this);
@ -149,6 +230,7 @@ void GridWizardDialog::goToStep2() {
return; return;
} }
stack_->setCurrentIndex(1); stack_->setCurrentIndex(1);
setFixedWidth(kStep2W);
nextBtn_->setVisible(false); nextBtn_->setVisible(false);
prevBtn_->setVisible(true); prevBtn_->setVisible(true);
okBtn_->setVisible(true); okBtn_->setVisible(true);
@ -166,28 +248,63 @@ void GridWizardDialog::loadParams() {
self->yMax_->setValue(d.value(QStringLiteral("ymax")).toDouble()); self->yMax_->setValue(d.value(QStringLiteral("ymax")).toDouble());
self->vMin_->setValue(d.value(QStringLiteral("vmin")).toDouble()); self->vMin_->setValue(d.value(QStringLiteral("vmin")).toDouble());
self->vMax_->setValue(d.value(QStringLiteral("vmax")).toDouble()); self->vMax_->setValue(d.value(QStringLiteral("vmax")).toDouble());
// 初始间距 = (xmax-xmin)/xSizey 间距同 x原版 onMounted 逻辑)。 // 原版 onMountedX点数固定 100 → 算 X间距 → Y间距=X间距 → Y点数=ceil。
self->recalcXSpacing(); self->xSize_->setValue(kDefaultXSize);
self->calcXInterval();
const double dx = self->xSpacing_->value(); const double dx = self->xSpacing_->value();
if (dx > 0) self->ySpacing_->setValue(dx); if (dx > 0) self->ySpacing_->setValue(dx); // 触发 calcYPoints
}); });
} }
void GridWizardDialog::recalcXSpacing() { void GridWizardDialog::calcXInterval() {
const int n = xSize_->value(); const int n = xSize_->value();
if (n > 0) xSpacing_->setValue((xMax_->value() - xMin_->value()) / n); const double range = xMax_->value() - xMin_->value();
if (n > 0 && range > 0) {
QSignalBlocker b(xSpacing_); // 防与 calcXPoints 互触发
xSpacing_->setValue(range / n);
}
} }
void GridWizardDialog::recalcYSpacing() { void GridWizardDialog::calcXPoints() {
const double iv = xSpacing_->value();
const double range = xMax_->value() - xMin_->value();
if (iv > 0 && range > 0) {
QSignalBlocker b(xSize_);
xSize_->setValue(static_cast<int>(std::lround(range / iv))); // 原版 round
}
}
void GridWizardDialog::calcYInterval() {
const int n = ySize_->value(); const int n = ySize_->value();
if (n > 0) ySpacing_->setValue((yMax_->value() - yMin_->value()) / n); const double range = yMax_->value() - yMin_->value();
if (n > 0 && range > 0) {
QSignalBlocker b(ySpacing_);
ySpacing_->setValue(range / n);
}
}
void GridWizardDialog::calcYPoints() {
const double iv = ySpacing_->value();
const double range = yMax_->value() - yMin_->value();
if (iv > 0 && range > 0) {
QSignalBlocker b(ySize_);
ySize_->setValue(static_cast<int>(std::ceil(range / iv))); // 原版 ceil与 X 的 round 不同)
}
} }
void GridWizardDialog::onConfirm() { void GridWizardDialog::onConfirm() {
if (!repo_ || !algoList_->currentItem()) { reject(); return; } if (!repo_ || !algoList_->currentItem()) { reject(); return; }
if (xMax_->value() < xMin_->value() || yMax_->value() < yMin_->value() || // 原版按 Xmax/Ymax/数据值分别提示。
vMax_->value() < vMin_->value()) { if (xMax_->value() < xMin_->value()) {
QMessageBox::warning(this, windowTitle(), QStringLiteral("最大值不能小于最小值")); QMessageBox::warning(this, windowTitle(), QStringLiteral("Xmax不能小于Xmin"));
return;
}
if (yMax_->value() < yMin_->value()) {
QMessageBox::warning(this, windowTitle(), QStringLiteral("Ymax不能小于Ymin"));
return;
}
if (vMax_->value() < vMin_->value()) {
QMessageBox::warning(this, windowTitle(), QStringLiteral("数据值max不能小于数据值min"));
return; return;
} }
GridToParams p; GridToParams p;

View File

@ -27,11 +27,15 @@ public:
QWidget* parent = nullptr); QWidget* parent = nullptr);
private: private:
void buildStep1(); // 步骤 1算法选择列表
void buildStep2(); // 步骤 2网格参数 + 数据值设置(两分组卡片)
void loadAlgorithms(); // 步骤 1拉算法列表 void loadAlgorithms(); // 步骤 1拉算法列表
void loadParams(); // 步骤 2拉 x/y/v 默认参数 void loadParams(); // 步骤 2拉 x/y/v 默认参数(兼「恢复默认值」)
void goToStep2(); // 下一步(校验算法已选) void goToStep2(); // 下一步(校验算法已选)
void recalcXSpacing(); // (xMax-xMin)/xSize → xSpacing间距已有值时 void calcXInterval(); // Xmax/Xmin/X点数变 → X间距=(xMax-xMin)/xSize
void recalcYSpacing(); // (yMax-yMin)/ySize → ySpacing void calcXPoints(); // X间距变 → X点数=round((xMax-xMin)/xSpacing)
void calcYInterval(); // Ymax/Ymin/Y点数变 → Y间距=(yMax-yMin)/ySize
void calcYPoints(); // Y间距变 → Y点数=ceil((yMax-yMin)/ySpacing)
void onConfirm(); // 确认 → toGrid void onConfirm(); // 确认 → toGrid
geopro::data::IDatasetCommandRepository* repo_ = nullptr; geopro::data::IDatasetCommandRepository* repo_ = nullptr;

View File

@ -87,8 +87,9 @@ RawDataChartView::RawDataChartView(QWidget* parent) : QWidget(parent) {
tbLay->addWidget(btnColorScale); tbLay->addWidget(btnColorScale);
tbLay->addWidget(lblCurrentChart); tbLay->addWidget(lblCurrentChart);
tbLay->addWidget(chartTypeCombo_); tbLay->addWidget(chartTypeCombo_);
tbLay->addWidget(btnSaveAs); // 原版 .right-buttons margin-left:auto另存为 右对齐。
tbLay->addStretch(); tbLay->addStretch();
tbLay->addWidget(btnSaveAs);
// 反演原数据默认工具条交互O1 网格 / O2 色阶配置 / O3 另存为)。 // 反演原数据默认工具条交互O1 网格 / O2 色阶配置 / O3 另存为)。
connect(btnGrid, &QToolButton::clicked, this, [this, btnGrid]() { openGridWizard(btnGrid); }); connect(btnGrid, &QToolButton::clicked, this, [this, btnGrid]() { openGridWizard(btnGrid); });

View File

@ -3,7 +3,6 @@
#include <utility> #include <utility>
#include <QButtonGroup> #include <QButtonGroup>
#include <QFormLayout>
#include <QHBoxLayout> #include <QHBoxLayout>
#include <QLabel> #include <QLabel>
#include <QLineEdit> #include <QLineEdit>
@ -13,49 +12,68 @@
#include <QRadioButton> #include <QRadioButton>
#include <QVBoxLayout> #include <QVBoxLayout>
#include "FormKit.hpp" #include "Theme.hpp"
#include "panels/chart/ScatterDataOps.hpp" // buildSaveRawDataBody纯组装便于单测 #include "panels/chart/ScatterDataOps.hpp" // buildSaveRawDataBody纯组装便于单测
#include "repo/IDatasetCommandRepository.hpp" #include "repo/IDatasetCommandRepository.hpp"
namespace geopro::app { namespace geopro::app {
namespace {
constexpr int kInversionW = 400; // 原版 inversion 另存为弹窗宽 400px
constexpr int kLabelW = 60; // 原版 .label width:60px
} // namespace
SaveAsDialog::SaveAsDialog(Mode mode, geopro::data::IDatasetCommandRepository* repo, QString dsId, SaveAsDialog::SaveAsDialog(Mode mode, geopro::data::IDatasetCommandRepository* repo, QString dsId,
QWidget* parent) QWidget* parent)
: QDialog(parent), mode_(mode), repo_(repo), dsId_(std::move(dsId)) { : QDialog(parent), mode_(mode), repo_(repo), dsId_(std::move(dsId)) {
setWindowTitle(QStringLiteral("数据另存为"));
setModal(true); setModal(true);
auto* root = formkit::dialogRoot(this); auto* root = new QVBoxLayout(this);
root->setContentsMargins(geopro::app::space::kLg, geopro::app::space::kLg,
geopro::app::space::kLg, geopro::app::space::kLg);
root->setSpacing(geopro::app::space::kMd);
auto* card = formkit::formCard(this); if (mode_ == Mode::Inversion) {
auto* cardLay = formkit::cardBody(card); // ── inversion原版「另存为新的网格数据」400px仅名称行 ──
setWindowTitle(QStringLiteral("另存为新的网格数据"));
setFixedWidth(kInversionW);
auto* nameRow = new QHBoxLayout();
nameRow->setSpacing(geopro::app::space::kMd);
nameLabel_ = new QLabel(QStringLiteral("名称:"), this); // 原版 label「名称:」
nameLabel_->setMinimumWidth(kLabelW);
nameEdit_ = new QLineEdit(this);
nameEdit_->setPlaceholderText(QStringLiteral("请输入名称"));
nameEdit_->setText(QStringLiteral("网格数据1")); // 原版默认值
nameRow->addWidget(nameLabel_);
nameRow->addWidget(nameEdit_, 1);
root->addLayout(nameRow);
} else {
// ── RawDatameasurement新增/覆盖 + 名称(保持既有逻辑,不在本批返工范围)──
setWindowTitle(QStringLiteral("数据另存为"));
if (mode_ == Mode::RawData) {
// 新增/覆盖单选(复刻原版 a-radio-group1=新增 0=覆盖)。
auto* opLay = new QHBoxLayout(); auto* opLay = new QHBoxLayout();
auto* rbNew = new QRadioButton(QStringLiteral("新增"), this); auto* rbNew = new QRadioButton(QStringLiteral("新增"), this);
auto* rbOverwrite = new QRadioButton(QStringLiteral("覆盖"), this); auto* rbOverwrite = new QRadioButton(QStringLiteral("覆盖"), this);
opGroup_ = new QButtonGroup(this); opGroup_ = new QButtonGroup(this);
opGroup_->addButton(rbNew, 1); opGroup_->addButton(rbNew, 1);
opGroup_->addButton(rbOverwrite, 0); opGroup_->addButton(rbOverwrite, 0);
rbNew->setChecked(true); // 默认新增(与原版 dataStored 初值一致) rbNew->setChecked(true); // 默认新增
opLay->addWidget(rbNew); opLay->addWidget(rbNew);
opLay->addWidget(rbOverwrite); opLay->addWidget(rbOverwrite);
opLay->addStretch(); opLay->addStretch();
cardLay->addLayout(opLay); root->addLayout(opLay);
}
// 名称行RawData 仅新增可见Inversion 始终可见。 auto* nameRow = new QHBoxLayout();
auto* nameForm = formkit::makeEditForm(); nameRow->setSpacing(geopro::app::space::kMd);
nameLabel_ = formkit::editLabel(QStringLiteral("数据名称"), this); nameLabel_ = new QLabel(QStringLiteral("数据名称"), this);
nameLabel_->setMinimumWidth(kLabelW);
nameEdit_ = new QLineEdit(this); nameEdit_ = new QLineEdit(this);
formkit::capField(nameEdit_); nameRow->addWidget(nameLabel_);
nameForm->addRow(nameLabel_, nameEdit_); nameRow->addWidget(nameEdit_, 1);
cardLay->addLayout(nameForm); root->addLayout(nameRow);
root->addWidget(card);
if (mode_ == Mode::RawData && opGroup_) { // 切到覆盖隐藏名称框,切回新增显示。
// 切到覆盖隐藏名称框,切回新增显示(复刻原版 v-show=dataStored===1
connect(opGroup_, QOverload<int>::of(&QButtonGroup::idClicked), this, [this](int id) { connect(opGroup_, QOverload<int>::of(&QButtonGroup::idClicked), this, [this](int id) {
const bool isNew = (id == 1); const bool isNew = (id == 1);
nameLabel_->setVisible(isNew); nameLabel_->setVisible(isNew);
@ -63,13 +81,15 @@ SaveAsDialog::SaveAsDialog(Mode mode, geopro::data::IDatasetCommandRepository* r
}); });
} }
// 底部按钮:原版右对齐,确认(主,左)/取消(右)。
auto* btnLay = new QHBoxLayout(); auto* btnLay = new QHBoxLayout();
btnLay->setSpacing(geopro::app::space::kMd);
btnLay->addStretch(); btnLay->addStretch();
auto* cancelBtn = new QPushButton(QStringLiteral("取消"), this); okBtn_ = new QPushButton(QStringLiteral("确认"), this);
okBtn_ = new QPushButton(QStringLiteral("确定"), this);
okBtn_->setDefault(true); okBtn_->setDefault(true);
btnLay->addWidget(cancelBtn); auto* cancelBtn = new QPushButton(QStringLiteral("取消"), this);
btnLay->addWidget(okBtn_); btnLay->addWidget(okBtn_);
btnLay->addWidget(cancelBtn);
root->addLayout(btnLay); root->addLayout(btnLay);
connect(cancelBtn, &QPushButton::clicked, this, &QDialog::reject); connect(cancelBtn, &QPushButton::clicked, this, &QDialog::reject);
@ -84,7 +104,7 @@ void SaveAsDialog::onConfirm() {
const bool needName = (mode_ == Mode::Inversion) || (operationType == 1); const bool needName = (mode_ == Mode::Inversion) || (operationType == 1);
const QString name = nameEdit_->text().trimmed(); const QString name = nameEdit_->text().trimmed();
if (needName && name.isEmpty()) { if (needName && name.isEmpty()) {
QMessageBox::warning(this, windowTitle(), QStringLiteral("请输入数据名称")); QMessageBox::warning(this, windowTitle(), QStringLiteral("请输入名称"));
return; return;
} }

View File

@ -4,10 +4,9 @@
#include <QButtonGroup> #include <QButtonGroup>
#include <QComboBox> #include <QComboBox>
#include <QDoubleSpinBox>
#include <QFormLayout>
#include <QHBoxLayout> #include <QHBoxLayout>
#include <QLabel> #include <QLabel>
#include <QLineEdit>
#include <QMessageBox> #include <QMessageBox>
#include <QPointer> #include <QPointer>
#include <QPushButton> #include <QPushButton>
@ -15,14 +14,34 @@
#include <QStackedWidget> #include <QStackedWidget>
#include <QVBoxLayout> #include <QVBoxLayout>
#include "FormKit.hpp" #include "Theme.hpp"
#include "panels/chart/InversionProcessOps.hpp" // buildWhitenBody #include "panels/chart/InversionProcessOps.hpp" // buildWhitenBody
#include "repo/IDatasetCommandRepository.hpp" #include "repo/IDatasetCommandRepository.hpp"
namespace geopro::app { namespace geopro::app {
namespace { namespace {
constexpr double kExtRange = 1e6; // 边界扩展范围 constexpr int kDialogW = 550; // 原版弹窗宽 550px
constexpr int kLabelMinW = 120; // 原版 .field-label min-width:120px 右对齐
constexpr double kCtrlRatio = 0.6; // 原版控件宽 60%
// 原版 .field-label定宽右对齐标签。
QLabel* fieldLabel(const QString& text, QWidget* parent) {
auto* lbl = new QLabel(text, parent);
lbl->setMinimumWidth(kLabelMinW);
lbl->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
return lbl;
}
// 原版 .option-itemflex 行(标签右对齐 + 控件占 60%)。控件外裹一层以 60/40 拉伸。
QHBoxLayout* optionRow(const QString& label, QWidget* ctrl, QWidget* parent) {
auto* row = new QHBoxLayout();
row->setSpacing(geopro::app::space::kLg);
row->addWidget(fieldLabel(label, parent));
row->addWidget(ctrl, 6); // 控件 60%
row->addStretch(4); // 余 40% 留白
return row;
}
} // namespace } // namespace
WhiteningDialog::WhiteningDialog(geopro::data::IDatasetCommandRepository* repo, QString dsId, WhiteningDialog::WhiteningDialog(geopro::data::IDatasetCommandRepository* repo, QString dsId,
@ -32,37 +51,37 @@ WhiteningDialog::WhiteningDialog(geopro::data::IDatasetCommandRepository* repo,
dsId_(std::move(dsId)), dsId_(std::move(dsId)),
projectId_(std::move(projectId)), projectId_(std::move(projectId)),
tmObjectId_(std::move(tmObjectId)) { tmObjectId_(std::move(tmObjectId)) {
setWindowTitle(QStringLiteral("白化")); setWindowTitle(QStringLiteral("白化配置")); // 原版 whiteningSetting
setModal(true); setModal(true);
resize(460, 280); setFixedWidth(kDialogW);
auto* root = formkit::dialogRoot(this); auto* root = new QVBoxLayout(this);
root->setContentsMargins(geopro::app::space::kLg, geopro::app::space::kLg,
geopro::app::space::kLg, geopro::app::space::kLg);
root->setSpacing(geopro::app::space::kMd);
// 白化方式下拉(数值对照原版 whiteningMethod 1/2/3 // 白化方式下拉(原版 3 项,数值对照 whiteningMethod 1/2/3
auto* methodLay = formkit::makeEditForm();
methodCombo_ = new QComboBox(this); methodCombo_ = new QComboBox(this);
methodCombo_->addItem(QStringLiteral("数据边界自动白化"), 1); methodCombo_->addItem(QStringLiteral("数据边界自动白化"), 1);
methodCombo_->addItem(QStringLiteral("白化模板"), 2); methodCombo_->addItem(QStringLiteral("白化文件"), 2);
methodCombo_->addItem(QStringLiteral("模型白化"), 3); methodCombo_->addItem(QStringLiteral("模型白化"), 3);
formkit::capField(methodCombo_); root->addLayout(optionRow(QStringLiteral("白化方式"), methodCombo_, this));
methodLay->addRow(formkit::editLabel(QStringLiteral("白化方式")), methodCombo_);
root->addLayout(methodLay);
stack_ = new QStackedWidget(this); stack_ = new QStackedWidget(this);
root->addWidget(stack_, 1); root->addWidget(stack_);
// ── 方式 1数据边界自动白化 ──────────────────────────────────────── // ── 方式 1数据边界自动白化(边界扩展文本框 + 内/外白化单选)────────────
auto* page1 = new QWidget(this); auto* page1 = new QWidget(this);
auto* p1 = formkit::makeEditForm(); auto* p1 = new QVBoxLayout(page1);
page1->setLayout(p1); p1->setContentsMargins(0, 0, 0, 0);
extension_ = new QDoubleSpinBox(page1); p1->setSpacing(geopro::app::space::kMd);
extension_->setRange(-kExtRange, kExtRange); extension_ = new QLineEdit(QStringLiteral("0"), page1); // 原版 AInput默认 "0"
extension_->setDecimals(3); p1->addLayout(optionRow(QStringLiteral("白化边界扩展"), extension_, page1));
formkit::capField(extension_); auto* typeWrap = new QWidget(page1);
p1->addRow(formkit::editLabel(QStringLiteral("白化边界扩展")), extension_); auto* typeRow = new QHBoxLayout(typeWrap);
auto* typeRow = new QHBoxLayout(); typeRow->setContentsMargins(0, 0, 0, 0);
auto* rbOuter = new QRadioButton(QStringLiteral("外部白化"), page1); auto* rbOuter = new QRadioButton(QStringLiteral("外部白化"), typeWrap);
auto* rbInner = new QRadioButton(QStringLiteral("内部白化"), page1); auto* rbInner = new QRadioButton(QStringLiteral("内部白化"), typeWrap);
rbOuter->setChecked(true); rbOuter->setChecked(true);
whiteningType_ = new QButtonGroup(this); whiteningType_ = new QButtonGroup(this);
whiteningType_->addButton(rbOuter, 0); whiteningType_->addButton(rbOuter, 0);
@ -70,25 +89,26 @@ WhiteningDialog::WhiteningDialog(geopro::data::IDatasetCommandRepository* repo,
typeRow->addWidget(rbOuter); typeRow->addWidget(rbOuter);
typeRow->addWidget(rbInner); typeRow->addWidget(rbInner);
typeRow->addStretch(); typeRow->addStretch();
p1->addRow(formkit::editLabel(QStringLiteral("白化")), typeRow); p1->addLayout(optionRow(QStringLiteral("白化"), typeWrap, page1));
stack_->addWidget(page1); stack_->addWidget(page1);
// ── 方式 2白化模板(选文件)────────────────────────────────────── // ── 方式 2白化文件(选文件)──────────────────────────────────────
auto* page2 = new QWidget(this); auto* page2 = new QWidget(this);
auto* p2 = formkit::makeEditForm(); auto* p2 = new QVBoxLayout(page2);
page2->setLayout(p2); p2->setContentsMargins(0, 0, 0, 0);
fileCombo_ = new QComboBox(page2); fileCombo_ = new QComboBox(page2);
formkit::capField(fileCombo_); p2->addLayout(optionRow(QStringLiteral("选择白化文件"), fileCombo_, page2));
p2->addRow(formkit::editLabel(QStringLiteral("选择白化文件")), fileCombo_);
stack_->addWidget(page2); stack_->addWidget(page2);
// ── 方式 3模型白化梯形/矩形)─────────────────────────────────── // ── 方式 3模型白化梯形/矩形)───────────────────────────────────
auto* page3 = new QWidget(this); auto* page3 = new QWidget(this);
auto* p3 = formkit::makeEditForm(); auto* p3 = new QVBoxLayout(page3);
page3->setLayout(p3); p3->setContentsMargins(0, 0, 0, 0);
auto* subRow = new QHBoxLayout(); auto* subWrap = new QWidget(page3);
auto* rbTrap = new QRadioButton(QStringLiteral("梯形白化"), page3); auto* subRow = new QHBoxLayout(subWrap);
auto* rbRect = new QRadioButton(QStringLiteral("矩形白化"), page3); subRow->setContentsMargins(0, 0, 0, 0);
auto* rbTrap = new QRadioButton(QStringLiteral("梯形白化"), subWrap);
auto* rbRect = new QRadioButton(QStringLiteral("矩形白化"), subWrap);
rbTrap->setChecked(true); rbTrap->setChecked(true);
modelSubType_ = new QButtonGroup(this); modelSubType_ = new QButtonGroup(this);
modelSubType_->addButton(rbTrap, 2); // 2 梯形 modelSubType_->addButton(rbTrap, 2); // 2 梯形
@ -96,17 +116,20 @@ WhiteningDialog::WhiteningDialog(geopro::data::IDatasetCommandRepository* repo,
subRow->addWidget(rbTrap); subRow->addWidget(rbTrap);
subRow->addWidget(rbRect); subRow->addWidget(rbRect);
subRow->addStretch(); subRow->addStretch();
p3->addRow(formkit::editLabel(QStringLiteral("白化")), subRow); p3->addLayout(optionRow(QStringLiteral("白化"), subWrap, page3));
stack_->addWidget(page3); stack_->addWidget(page3);
// 底部按钮。 Q_UNUSED(kCtrlRatio);
// 底部按钮:原版 justify-content:space-between确认(主,左)/取消(右) 各 45%。
auto* btnLay = new QHBoxLayout(); auto* btnLay = new QHBoxLayout();
btnLay->addStretch(); btnLay->setSpacing(geopro::app::space::kMd);
auto* cancelBtn = new QPushButton(QStringLiteral("取消"), this); okBtn_ = new QPushButton(QStringLiteral("确认"), this);
okBtn_ = new QPushButton(QStringLiteral("确定"), this);
okBtn_->setDefault(true); okBtn_->setDefault(true);
btnLay->addWidget(cancelBtn); auto* cancelBtn = new QPushButton(QStringLiteral("取消"), this);
btnLay->addWidget(okBtn_); btnLay->addWidget(okBtn_, 45);
btnLay->addStretch(10);
btnLay->addWidget(cancelBtn, 45);
root->addLayout(btnLay); root->addLayout(btnLay);
connect(cancelBtn, &QPushButton::clicked, this, &QDialog::reject); connect(cancelBtn, &QPushButton::clicked, this, &QDialog::reject);
@ -144,11 +167,11 @@ void WhiteningDialog::onConfirm() {
p.dsObjectId = dsId_; p.dsObjectId = dsId_;
p.whiteningMethod = methodCombo_->currentData().toInt(); p.whiteningMethod = methodCombo_->currentData().toInt();
if (p.whiteningMethod == 1) { if (p.whiteningMethod == 1) {
p.boundaryExtension = extension_->value(); p.boundaryExtension = extension_->text().toDouble(); // 原版 Number(extension)||0
p.whiteningType = whiteningType_->checkedId(); p.whiteningType = whiteningType_->checkedId();
} else if (p.whiteningMethod == 2) { } else if (p.whiteningMethod == 2) {
if (fileCombo_->currentIndex() < 0) { if (fileCombo_->currentIndex() < 0) {
QMessageBox::warning(this, windowTitle(), QStringLiteral("请选择白化文件")); QMessageBox::warning(this, windowTitle(), QStringLiteral("请选择一个白化文件"));
return; return;
} }
p.whitenedDataId = fileCombo_->currentData().toString(); p.whitenedDataId = fileCombo_->currentData().toString();

View File

@ -3,7 +3,7 @@
#include <QString> #include <QString>
class QComboBox; class QComboBox;
class QDoubleSpinBox; class QLineEdit;
class QButtonGroup; class QButtonGroup;
class QStackedWidget; class QStackedWidget;
class QPushButton; class QPushButton;
@ -38,7 +38,7 @@ private:
QComboBox* methodCombo_ = nullptr; QComboBox* methodCombo_ = nullptr;
QStackedWidget* stack_ = nullptr; QStackedWidget* stack_ = nullptr;
// 方式 1 // 方式 1
QDoubleSpinBox* extension_ = nullptr; QLineEdit* extension_ = nullptr; // 原版 AInput 文本框(默认 "0"
QButtonGroup* whiteningType_ = nullptr; // 0 外部 / 1 内部 QButtonGroup* whiteningType_ = nullptr; // 0 外部 / 1 内部
// 方式 2 // 方式 2
QComboBox* fileCombo_ = nullptr; QComboBox* fileCombo_ = nullptr;