From 4a1fecb149fa9bdfde0ae774a25e7ff08e7f1c49 Mon Sep 17 00:00:00 2001 From: gaozheng Date: Tue, 23 Jun 2026 11:31:14 +0800 Subject: [PATCH] =?UTF-8?q?fix(detail):=20inversion=20=E5=A4=84=E7=90=86?= =?UTF-8?q?=E7=B1=BB=E5=AF=B9=E8=AF=9D=E6=A1=86=E8=A7=86=E8=A7=89=E8=BF=94?= =?UTF-8?q?=E5=B7=A5=E5=AF=B9=E9=BD=90=E5=8E=9F=E7=89=88(=E7=99=BD?= =?UTF-8?q?=E5=8C=96/=E7=BD=91=E6=A0=BC=E5=8C=96/=E6=BB=A4=E6=B3=A2/?= =?UTF-8?q?=E5=8F=A6=E5=AD=98)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 之前用客户端 FormKit 外壳导致与原版 web modal 系统性不一致,以原版为准返工: - 白化:550px、第2项"白化文件"、边界扩展改文本框、确认/取消顺序、标签右对齐 - 网格化:步1 500/步2 800px、网格参数/数据值设置双分组栅格、"数据值保存为"、 补恢复默认值按钮 + 间距↔点数双向联动 + 分项校验 - 滤波:900px 左树右设置双卡片、"忽略"、矩阵行列表头 + 奇偶校验 - 另存为(Inversion):标题"另存为新的网格数据"、400px、默认名"网格数据1"、确认/取消 - 工具条:异常标注/自动标注/另存为、原数据另存为 右对齐 API 端点/请求体字段未动(已 1:1)。build all + test 324/324 绿。 --- src/app/panels/chart/FilterDialog.cpp | 252 ++++++++++++++------ src/app/panels/chart/FilterDialog.hpp | 9 + src/app/panels/chart/GridDataChartView.cpp | 3 +- src/app/panels/chart/GridWizardDialog.cpp | 265 +++++++++++++++------ src/app/panels/chart/GridWizardDialog.hpp | 10 +- src/app/panels/chart/RawDataChartView.cpp | 3 +- src/app/panels/chart/SaveAsDialog.cpp | 70 ++++-- src/app/panels/chart/WhiteningDialog.cpp | 113 +++++---- src/app/panels/chart/WhiteningDialog.hpp | 4 +- 9 files changed, 502 insertions(+), 227 deletions(-) diff --git a/src/app/panels/chart/FilterDialog.cpp b/src/app/panels/chart/FilterDialog.cpp index d4b6cea..43c2e25 100644 --- a/src/app/panels/chart/FilterDialog.cpp +++ b/src/app/panels/chart/FilterDialog.cpp @@ -3,7 +3,7 @@ #include #include -#include +#include #include #include #include @@ -20,16 +20,17 @@ #include #include -#include "FormKit.hpp" +#include "Theme.hpp" #include "panels/chart/InversionProcessOps.hpp" // buildFilterApplyBody / buildNewFilterBody #include "repo/IDatasetCommandRepository.hpp" namespace geopro::app { namespace { -constexpr double kFillRange = 1e9; +constexpr int kDialogW = 900; // 原版弹窗宽 900px constexpr int kMatrixMin = 1, kMatrixMax = 21; // 矩阵行列范围(对照原版 1~21) constexpr int kDefaultDim = 3; +constexpr int kSettingLabelW = 80; // 原版 .setting-label width:80px const char kDefaultCustomKey[] = "default-custom-filter"; // 默认自定义滤波器(不可删) const char kCustomGroupName[] = "自定义滤波器"; @@ -40,86 +41,58 @@ double cellValue(const QTableWidgetItem* it) { const double v = it->text().toDouble(&ok); 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 FilterDialog::FilterDialog(geopro::data::IDatasetCommandRepository* repo, QString dsId, QString projectId, QWidget* parent) : QDialog(parent), repo_(repo), dsId_(std::move(dsId)), projectId_(std::move(projectId)) { - setWindowTitle(QStringLiteral("滤波处理")); + setWindowTitle(QStringLiteral("滤波设置")); // 原版 filterSetting 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(); + body->setSpacing(geopro::app::space::kLg); root->addLayout(body, 1); - // ── 左:滤波器树 + 增删按钮 ───────────────────────────────────────── - auto* leftLay = new QVBoxLayout(); - 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); + buildLeft(body); + buildRight(body); - // ── 右:配置面板 ──────────────────────────────────────────────────── - 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); - - // 底部按钮。 + // 底部按钮(原版三按钮各 ~30%:保存设置(主,左)/确认(主,中)/取消(右))。 auto* btnLay = new QHBoxLayout(); - btnLay->addStretch(); - auto* cancelBtn = new QPushButton(QStringLiteral("取消"), this); - okBtn_ = new QPushButton(QStringLiteral("应用"), this); + btnLay->setSpacing(geopro::app::space::kMd); + auto* saveSettingBtn = new QPushButton(QStringLiteral("保存设置"), this); + okBtn_ = new QPushButton(QStringLiteral("确认"), this); okBtn_->setDefault(true); - btnLay->addWidget(cancelBtn); - btnLay->addWidget(okBtn_); + auto* cancelBtn = new QPushButton(QStringLiteral("取消"), this); + btnLay->addWidget(saveSettingBtn, 30); + btnLay->addStretch(2); + btnLay->addWidget(okBtn_, 30); + btnLay->addStretch(2); + btnLay->addWidget(cancelBtn, 30); root->addLayout(btnLay); resizeMatrix(); // 默认 3x3 中心 1 @@ -127,14 +100,16 @@ FilterDialog::FilterDialog(geopro::data::IDatasetCommandRepository* repo, QStrin connect(cancelBtn, &QPushButton::clicked, this, &QDialog::reject); connect(okBtn_, &QPushButton::clicked, this, &FilterDialog::onConfirm); - connect(addBtn, &QPushButton::clicked, this, &FilterDialog::saveCustomFilter); - connect(delBtn, &QPushButton::clicked, this, &FilterDialog::deleteSelectedFilter); + connect(saveSettingBtn, &QPushButton::clicked, this, &FilterDialog::saveCustomFilter); connect(tree_, &QTreeWidget::itemSelectionChanged, this, &FilterDialog::onTreeSelectionChanged); + // 行/列:仅奇数允许,偶数弹警告并回退旧值(对照原版 watch rows/cols)。 + prevRows_ = kDefaultDim; + prevCols_ = kDefaultDim; connect(rows_, QOverload::of(&QSpinBox::valueChanged), this, - [this](int) { resizeMatrix(); }); + [this](int v) { onDimChanged(rows_, v, prevRows_, QStringLiteral("行")); }); connect(cols_, QOverload::of(&QSpinBox::valueChanged), this, - [this](int) { resizeMatrix(); }); + [this](int v) { onDimChanged(cols_, v, prevCols_, QStringLiteral("列")); }); // 数据边缘/无数据点:仅「填充」启用对应值输入框(对照原版 v-if=filling)。 connect(dataEdge_, QOverload::of(&QComboBox::currentIndexChanged), this, [this](int) { dataEdgeValue_->setEnabled(dataEdge_->currentData().toString() == @@ -149,6 +124,125 @@ FilterDialog::FilterDialog(geopro::data::IDatasetCommandRepository* repo, QStrin 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-row:label(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() { if (!repo_) return; QPointer self(this); @@ -226,6 +320,12 @@ void FilterDialog::resizeMatrix() { std::vector> old = readMatrix(); matrix_->setRowCount(r); 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 j = 0; j < c; ++j) { auto* it = matrix_->item(i, j); @@ -272,8 +372,8 @@ void FilterDialog::saveCustomFilter() { return; } bool ok = false; - const QString name = QInputDialog::getText(this, QStringLiteral("另存为新自定义滤波器"), - QStringLiteral("名称:"), QLineEdit::Normal, + const QString name = QInputDialog::getText(this, QStringLiteral("保存为新的自定义滤波器"), + QStringLiteral("请输入滤波器名称"), QLineEdit::Normal, QStringLiteral("自定义滤波器1"), &ok); if (!ok || name.trimmed().isEmpty()) return; QPointer self(this); diff --git a/src/app/panels/chart/FilterDialog.hpp b/src/app/panels/chart/FilterDialog.hpp index 9eb3e2c..d4aa442 100644 --- a/src/app/panels/chart/FilterDialog.hpp +++ b/src/app/panels/chart/FilterDialog.hpp @@ -13,6 +13,8 @@ class QTreeWidget; class QTreeWidgetItem; class QTableWidget; class QLineEdit; +class QHBoxLayout; +class QWidget; namespace geopro::data { class IDatasetCommandRepository; @@ -31,6 +33,11 @@ public: QWidget* parent = nullptr); 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 buildTree(); // 由 flatItems_ 建树 void onTreeSelectionChanged(); // 选中叶节点 → 右侧回填 @@ -56,6 +63,8 @@ private: QSpinBox* filterTimes_ = nullptr; QSpinBox* rows_ = nullptr; QSpinBox* cols_ = nullptr; + int prevRows_ = 3; // 奇偶校验回退用(上一合法行数) + int prevCols_ = 3; // 上一合法列数 QTableWidget* matrix_ = nullptr; QPushButton* okBtn_ = nullptr; diff --git a/src/app/panels/chart/GridDataChartView.cpp b/src/app/panels/chart/GridDataChartView.cpp index 0fd677e..3621335 100644 --- a/src/app/panels/chart/GridDataChartView.cpp +++ b/src/app/panels/chart/GridDataChartView.cpp @@ -122,10 +122,11 @@ GridDataChartView::GridDataChartView(QWidget* parent) : QWidget(parent) { tbLay->addWidget(lblSimplify); tbLay->addWidget(simplifySlider_); tbLay->addWidget(simplifyValueLabel_); + // 原版 .right-buttons margin-left:auto:异常标注/自动标注/另存为 右对齐。 + tbLay->addStretch(); tbLay->addWidget(btnAnomalyLabel); tbLay->addWidget(btnAutoLabel); tbLay->addWidget(btnSaveAs); - tbLay->addStretch(); lay->addWidget(toolbar); diff --git a/src/app/panels/chart/GridWizardDialog.cpp b/src/app/panels/chart/GridWizardDialog.cpp index 2ba01fc..1d128ff 100644 --- a/src/app/panels/chart/GridWizardDialog.cpp +++ b/src/app/panels/chart/GridWizardDialog.cpp @@ -1,10 +1,12 @@ #include "panels/chart/GridWizardDialog.hpp" +#include #include #include #include -#include +#include +#include #include #include #include @@ -15,96 +17,85 @@ #include #include -#include "FormKit.hpp" +#include "Theme.hpp" #include "panels/chart/InversionProcessOps.hpp" // buildGridToBody #include "repo/IDatasetCommandRepository.hpp" namespace geopro::app { namespace { -constexpr double kCoordRange = 1e9; // 坐标范围(足够宽) -constexpr int kSizeMin = 1, kSizeMax = 300; // 点数范围(对照原版 1~300) -constexpr int kDefaultXSize = 100; // 默认点数(原版 xPoints 默认 100) +constexpr double kCoordRange = 1e9; // 坐标范围(足够宽) +constexpr int kSizeMin = 1, kSizeMax = 300; // 点数范围(对照原版 1~300) +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 // 配一个坐标 spinbox(6 位小数,宽范围)。 QDoubleSpinBox* makeCoordSpin(QWidget* parent) { auto* sp = new QDoubleSpinBox(parent); sp->setRange(-kCoordRange, kCoordRange); sp->setDecimals(6); + sp->setFixedWidth(kParamFieldW); return sp; } + +// 分组卡片标题(原版 .section-title:14px 半粗 + 标题下 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 GridWizardDialog::GridWizardDialog(geopro::data::IDatasetCommandRepository* repo, QString dsId, QWidget* parent) : QDialog(parent), repo_(repo), dsId_(std::move(dsId)) { - setWindowTitle(QStringLiteral("网格化")); + setWindowTitle(QStringLiteral("网格配置")); // 原版 gridSetting 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); root->addWidget(stack_, 1); - // ── 步骤 1:算法选择(单选列表)────────────────────────────────────── - auto* page1 = new QWidget(this); - auto* p1Lay = new QVBoxLayout(page1); - p1Lay->addWidget(new QLabel(QStringLiteral("请选择网格方法:"), page1)); - algoList_ = new QListWidget(page1); - p1Lay->addWidget(algoList_, 1); - stack_->addWidget(page1); + buildStep1(); + buildStep2(); - // ── 步骤 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); - - // ── 底部按钮(上一步 / 下一步 / 确认 / 取消)──────────────────────── + // ── 底部按钮(上一步 / 确认(主) / 取消,原版步骤 2 三按钮)──────────── auto* btnLay = new QHBoxLayout(); + btnLay->setSpacing(geopro::app::space::kMd); btnLay->addStretch(); prevBtn_ = new QPushButton(QStringLiteral("上一步"), this); - nextBtn_ = new QPushButton(QStringLiteral("下一步"), this); okBtn_ = new QPushButton(QStringLiteral("确认"), this); + okBtn_->setDefault(true); + nextBtn_ = new QPushButton(QStringLiteral("下一步"), this); auto* cancelBtn = new QPushButton(QStringLiteral("取消"), this); btnLay->addWidget(prevBtn_); - btnLay->addWidget(nextBtn_); btnLay->addWidget(okBtn_); + btnLay->addWidget(nextBtn_); btnLay->addWidget(cancelBtn); root->addLayout(btnLay); prevBtn_->setVisible(false); @@ -114,18 +105,108 @@ GridWizardDialog::GridWizardDialog(geopro::data::IDatasetCommandRepository* repo connect(nextBtn_, &QPushButton::clicked, this, &GridWizardDialog::goToStep2); connect(prevBtn_, &QPushButton::clicked, this, [this]() { stack_->setCurrentIndex(0); + setFixedWidth(kStep1W); prevBtn_->setVisible(false); okBtn_->setVisible(false); nextBtn_->setVisible(true); }); connect(okBtn_, &QPushButton::clicked, this, &GridWizardDialog::onConfirm); - // 点数变化 → 重算间距(原版 calculateXInterval/calculateYInterval)。 - connect(xSize_, QOverload::of(&QSpinBox::valueChanged), this, - [this](int) { recalcXSpacing(); }); - connect(ySize_, QOverload::of(&QSpinBox::valueChanged), this, - [this](int) { recalcYSpacing(); }); 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::of(&QDoubleSpinBox::valueChanged), this, + [this](double) { calcXInterval(); }); + connect(xMin_, QOverload::of(&QDoubleSpinBox::valueChanged), this, + [this](double) { calcXInterval(); }); + connect(xSize_, QOverload::of(&QSpinBox::valueChanged), this, + [this](int) { calcXInterval(); }); + connect(xSpacing_, QOverload::of(&QDoubleSpinBox::valueChanged), this, + [this](double) { calcXPoints(); }); + connect(yMax_, QOverload::of(&QDoubleSpinBox::valueChanged), this, + [this](double) { calcYInterval(); }); + connect(yMin_, QOverload::of(&QDoubleSpinBox::valueChanged), this, + [this](double) { calcYInterval(); }); + connect(ySize_, QOverload::of(&QSpinBox::valueChanged), this, + [this](int) { calcYInterval(); }); + connect(ySpacing_, QOverload::of(&QDoubleSpinBox::valueChanged), this, + [this](double) { calcYPoints(); }); + connect(restoreBtn, &QPushButton::clicked, this, &GridWizardDialog::loadParams); +} + void GridWizardDialog::loadAlgorithms() { if (!repo_) return; QPointer self(this); @@ -149,6 +230,7 @@ void GridWizardDialog::goToStep2() { return; } stack_->setCurrentIndex(1); + setFixedWidth(kStep2W); nextBtn_->setVisible(false); prevBtn_->setVisible(true); okBtn_->setVisible(true); @@ -166,28 +248,63 @@ void GridWizardDialog::loadParams() { self->yMax_->setValue(d.value(QStringLiteral("ymax")).toDouble()); self->vMin_->setValue(d.value(QStringLiteral("vmin")).toDouble()); self->vMax_->setValue(d.value(QStringLiteral("vmax")).toDouble()); - // 初始间距 = (xmax-xmin)/xSize,y 间距同 x(原版 onMounted 逻辑)。 - self->recalcXSpacing(); + // 原版 onMounted:X点数固定 100 → 算 X间距 → Y间距=X间距 → Y点数=ceil。 + self->xSize_->setValue(kDefaultXSize); + self->calcXInterval(); 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(); - 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(std::lround(range / iv))); // 原版 round + } +} + +void GridWizardDialog::calcYInterval() { 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(std::ceil(range / iv))); // 原版 ceil(与 X 的 round 不同) + } } void GridWizardDialog::onConfirm() { if (!repo_ || !algoList_->currentItem()) { reject(); return; } - if (xMax_->value() < xMin_->value() || yMax_->value() < yMin_->value() || - vMax_->value() < vMin_->value()) { - QMessageBox::warning(this, windowTitle(), QStringLiteral("最大值不能小于最小值")); + // 原版按 Xmax/Ymax/数据值分别提示。 + if (xMax_->value() < xMin_->value()) { + 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; } GridToParams p; diff --git a/src/app/panels/chart/GridWizardDialog.hpp b/src/app/panels/chart/GridWizardDialog.hpp index 6d27991..bde55b5 100644 --- a/src/app/panels/chart/GridWizardDialog.hpp +++ b/src/app/panels/chart/GridWizardDialog.hpp @@ -27,11 +27,15 @@ public: QWidget* parent = nullptr); private: + void buildStep1(); // 步骤 1:算法选择列表 + void buildStep2(); // 步骤 2:网格参数 + 数据值设置(两分组卡片) void loadAlgorithms(); // 步骤 1:拉算法列表 - void loadParams(); // 步骤 2:拉 x/y/v 默认参数 + void loadParams(); // 步骤 2:拉 x/y/v 默认参数(兼「恢复默认值」) void goToStep2(); // 下一步(校验算法已选) - void recalcXSpacing(); // (xMax-xMin)/xSize → xSpacing(间距已有值时) - void recalcYSpacing(); // (yMax-yMin)/ySize → ySpacing + void calcXInterval(); // Xmax/Xmin/X点数变 → X间距=(xMax-xMin)/xSize + 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 geopro::data::IDatasetCommandRepository* repo_ = nullptr; diff --git a/src/app/panels/chart/RawDataChartView.cpp b/src/app/panels/chart/RawDataChartView.cpp index db2f899..27c78ab 100644 --- a/src/app/panels/chart/RawDataChartView.cpp +++ b/src/app/panels/chart/RawDataChartView.cpp @@ -87,8 +87,9 @@ RawDataChartView::RawDataChartView(QWidget* parent) : QWidget(parent) { tbLay->addWidget(btnColorScale); tbLay->addWidget(lblCurrentChart); tbLay->addWidget(chartTypeCombo_); - tbLay->addWidget(btnSaveAs); + // 原版 .right-buttons margin-left:auto:另存为 右对齐。 tbLay->addStretch(); + tbLay->addWidget(btnSaveAs); // 反演原数据默认工具条交互(O1 网格 / O2 色阶配置 / O3 另存为)。 connect(btnGrid, &QToolButton::clicked, this, [this, btnGrid]() { openGridWizard(btnGrid); }); diff --git a/src/app/panels/chart/SaveAsDialog.cpp b/src/app/panels/chart/SaveAsDialog.cpp index af9b0bd..6afd3ee 100644 --- a/src/app/panels/chart/SaveAsDialog.cpp +++ b/src/app/panels/chart/SaveAsDialog.cpp @@ -3,7 +3,6 @@ #include #include -#include #include #include #include @@ -13,49 +12,68 @@ #include #include -#include "FormKit.hpp" +#include "Theme.hpp" #include "panels/chart/ScatterDataOps.hpp" // buildSaveRawDataBody(纯组装,便于单测) #include "repo/IDatasetCommandRepository.hpp" 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, QWidget* parent) : QDialog(parent), mode_(mode), repo_(repo), dsId_(std::move(dsId)) { - setWindowTitle(QStringLiteral("数据另存为")); 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); - auto* cardLay = formkit::cardBody(card); + if (mode_ == Mode::Inversion) { + // ── 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 { + // ── RawData(measurement):新增/覆盖 + 名称(保持既有逻辑,不在本批返工范围)── + setWindowTitle(QStringLiteral("数据另存为")); - if (mode_ == Mode::RawData) { - // 新增/覆盖单选(复刻原版 a-radio-group:1=新增 0=覆盖)。 auto* opLay = new QHBoxLayout(); auto* rbNew = new QRadioButton(QStringLiteral("新增"), this); auto* rbOverwrite = new QRadioButton(QStringLiteral("覆盖"), this); opGroup_ = new QButtonGroup(this); opGroup_->addButton(rbNew, 1); opGroup_->addButton(rbOverwrite, 0); - rbNew->setChecked(true); // 默认新增(与原版 dataStored 初值一致) + rbNew->setChecked(true); // 默认新增 opLay->addWidget(rbNew); opLay->addWidget(rbOverwrite); opLay->addStretch(); - cardLay->addLayout(opLay); - } + root->addLayout(opLay); - // 名称行:RawData 仅新增可见;Inversion 始终可见。 - auto* nameForm = formkit::makeEditForm(); - nameLabel_ = formkit::editLabel(QStringLiteral("数据名称"), this); - nameEdit_ = new QLineEdit(this); - formkit::capField(nameEdit_); - nameForm->addRow(nameLabel_, nameEdit_); - cardLay->addLayout(nameForm); - root->addWidget(card); + auto* nameRow = new QHBoxLayout(); + nameRow->setSpacing(geopro::app::space::kMd); + nameLabel_ = new QLabel(QStringLiteral("数据名称"), this); + nameLabel_->setMinimumWidth(kLabelW); + nameEdit_ = new QLineEdit(this); + nameRow->addWidget(nameLabel_); + nameRow->addWidget(nameEdit_, 1); + root->addLayout(nameRow); - if (mode_ == Mode::RawData && opGroup_) { - // 切到覆盖隐藏名称框,切回新增显示(复刻原版 v-show=dataStored===1)。 + // 切到覆盖隐藏名称框,切回新增显示。 connect(opGroup_, QOverload::of(&QButtonGroup::idClicked), this, [this](int id) { const bool isNew = (id == 1); nameLabel_->setVisible(isNew); @@ -63,13 +81,15 @@ SaveAsDialog::SaveAsDialog(Mode mode, geopro::data::IDatasetCommandRepository* r }); } + // 底部按钮:原版右对齐,确认(主,左)/取消(右)。 auto* btnLay = new QHBoxLayout(); + btnLay->setSpacing(geopro::app::space::kMd); btnLay->addStretch(); - auto* cancelBtn = new QPushButton(QStringLiteral("取消"), this); - okBtn_ = new QPushButton(QStringLiteral("确定"), this); + okBtn_ = new QPushButton(QStringLiteral("确认"), this); okBtn_->setDefault(true); - btnLay->addWidget(cancelBtn); + auto* cancelBtn = new QPushButton(QStringLiteral("取消"), this); btnLay->addWidget(okBtn_); + btnLay->addWidget(cancelBtn); root->addLayout(btnLay); connect(cancelBtn, &QPushButton::clicked, this, &QDialog::reject); @@ -84,7 +104,7 @@ void SaveAsDialog::onConfirm() { const bool needName = (mode_ == Mode::Inversion) || (operationType == 1); const QString name = nameEdit_->text().trimmed(); if (needName && name.isEmpty()) { - QMessageBox::warning(this, windowTitle(), QStringLiteral("请输入数据名称")); + QMessageBox::warning(this, windowTitle(), QStringLiteral("请输入名称")); return; } diff --git a/src/app/panels/chart/WhiteningDialog.cpp b/src/app/panels/chart/WhiteningDialog.cpp index e18b8d9..c83c3a7 100644 --- a/src/app/panels/chart/WhiteningDialog.cpp +++ b/src/app/panels/chart/WhiteningDialog.cpp @@ -4,10 +4,9 @@ #include #include -#include -#include #include #include +#include #include #include #include @@ -15,14 +14,34 @@ #include #include -#include "FormKit.hpp" +#include "Theme.hpp" #include "panels/chart/InversionProcessOps.hpp" // buildWhitenBody #include "repo/IDatasetCommandRepository.hpp" namespace geopro::app { 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-item:flex 行(标签右对齐 + 控件占 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 WhiteningDialog::WhiteningDialog(geopro::data::IDatasetCommandRepository* repo, QString dsId, @@ -32,37 +51,37 @@ WhiteningDialog::WhiteningDialog(geopro::data::IDatasetCommandRepository* repo, dsId_(std::move(dsId)), projectId_(std::move(projectId)), tmObjectId_(std::move(tmObjectId)) { - setWindowTitle(QStringLiteral("白化")); + setWindowTitle(QStringLiteral("白化配置")); // 原版 whiteningSetting 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)。 - auto* methodLay = formkit::makeEditForm(); + // 白化方式下拉(原版 3 项,数值对照 whiteningMethod 1/2/3)。 methodCombo_ = new QComboBox(this); methodCombo_->addItem(QStringLiteral("数据边界自动白化"), 1); - methodCombo_->addItem(QStringLiteral("白化模板"), 2); + methodCombo_->addItem(QStringLiteral("白化文件"), 2); methodCombo_->addItem(QStringLiteral("模型白化"), 3); - formkit::capField(methodCombo_); - methodLay->addRow(formkit::editLabel(QStringLiteral("白化方式")), methodCombo_); - root->addLayout(methodLay); + root->addLayout(optionRow(QStringLiteral("白化方式"), methodCombo_, this)); stack_ = new QStackedWidget(this); - root->addWidget(stack_, 1); + root->addWidget(stack_); - // ── 方式 1:数据边界自动白化 ──────────────────────────────────────── + // ── 方式 1:数据边界自动白化(边界扩展文本框 + 内/外白化单选)──────────── auto* page1 = new QWidget(this); - auto* p1 = formkit::makeEditForm(); - page1->setLayout(p1); - extension_ = new QDoubleSpinBox(page1); - extension_->setRange(-kExtRange, kExtRange); - extension_->setDecimals(3); - formkit::capField(extension_); - p1->addRow(formkit::editLabel(QStringLiteral("白化边界扩展")), extension_); - auto* typeRow = new QHBoxLayout(); - auto* rbOuter = new QRadioButton(QStringLiteral("外部白化"), page1); - auto* rbInner = new QRadioButton(QStringLiteral("内部白化"), page1); + auto* p1 = new QVBoxLayout(page1); + p1->setContentsMargins(0, 0, 0, 0); + p1->setSpacing(geopro::app::space::kMd); + extension_ = new QLineEdit(QStringLiteral("0"), page1); // 原版 AInput,默认 "0" + p1->addLayout(optionRow(QStringLiteral("白化边界扩展"), extension_, page1)); + auto* typeWrap = new QWidget(page1); + auto* typeRow = new QHBoxLayout(typeWrap); + typeRow->setContentsMargins(0, 0, 0, 0); + auto* rbOuter = new QRadioButton(QStringLiteral("外部白化"), typeWrap); + auto* rbInner = new QRadioButton(QStringLiteral("内部白化"), typeWrap); rbOuter->setChecked(true); whiteningType_ = new QButtonGroup(this); whiteningType_->addButton(rbOuter, 0); @@ -70,25 +89,26 @@ WhiteningDialog::WhiteningDialog(geopro::data::IDatasetCommandRepository* repo, typeRow->addWidget(rbOuter); typeRow->addWidget(rbInner); typeRow->addStretch(); - p1->addRow(formkit::editLabel(QStringLiteral("白化")), typeRow); + p1->addLayout(optionRow(QStringLiteral("白化"), typeWrap, page1)); stack_->addWidget(page1); - // ── 方式 2:白化模板(选文件)────────────────────────────────────── + // ── 方式 2:白化文件(选文件)────────────────────────────────────── auto* page2 = new QWidget(this); - auto* p2 = formkit::makeEditForm(); - page2->setLayout(p2); + auto* p2 = new QVBoxLayout(page2); + p2->setContentsMargins(0, 0, 0, 0); fileCombo_ = new QComboBox(page2); - formkit::capField(fileCombo_); - p2->addRow(formkit::editLabel(QStringLiteral("选择白化文件")), fileCombo_); + p2->addLayout(optionRow(QStringLiteral("选择白化文件"), fileCombo_, page2)); stack_->addWidget(page2); // ── 方式 3:模型白化(梯形/矩形)─────────────────────────────────── auto* page3 = new QWidget(this); - auto* p3 = formkit::makeEditForm(); - page3->setLayout(p3); - auto* subRow = new QHBoxLayout(); - auto* rbTrap = new QRadioButton(QStringLiteral("梯形白化"), page3); - auto* rbRect = new QRadioButton(QStringLiteral("矩形白化"), page3); + auto* p3 = new QVBoxLayout(page3); + p3->setContentsMargins(0, 0, 0, 0); + auto* subWrap = new QWidget(page3); + auto* subRow = new QHBoxLayout(subWrap); + subRow->setContentsMargins(0, 0, 0, 0); + auto* rbTrap = new QRadioButton(QStringLiteral("梯形白化"), subWrap); + auto* rbRect = new QRadioButton(QStringLiteral("矩形白化"), subWrap); rbTrap->setChecked(true); modelSubType_ = new QButtonGroup(this); modelSubType_->addButton(rbTrap, 2); // 2 梯形 @@ -96,17 +116,20 @@ WhiteningDialog::WhiteningDialog(geopro::data::IDatasetCommandRepository* repo, subRow->addWidget(rbTrap); subRow->addWidget(rbRect); subRow->addStretch(); - p3->addRow(formkit::editLabel(QStringLiteral("白化")), subRow); + p3->addLayout(optionRow(QStringLiteral("白化"), subWrap, page3)); stack_->addWidget(page3); - // 底部按钮。 + Q_UNUSED(kCtrlRatio); + + // 底部按钮:原版 justify-content:space-between,确认(主,左)/取消(右) 各 45%。 auto* btnLay = new QHBoxLayout(); - btnLay->addStretch(); - auto* cancelBtn = new QPushButton(QStringLiteral("取消"), this); - okBtn_ = new QPushButton(QStringLiteral("确定"), this); + btnLay->setSpacing(geopro::app::space::kMd); + okBtn_ = new QPushButton(QStringLiteral("确认"), this); okBtn_->setDefault(true); - btnLay->addWidget(cancelBtn); - btnLay->addWidget(okBtn_); + auto* cancelBtn = new QPushButton(QStringLiteral("取消"), this); + btnLay->addWidget(okBtn_, 45); + btnLay->addStretch(10); + btnLay->addWidget(cancelBtn, 45); root->addLayout(btnLay); connect(cancelBtn, &QPushButton::clicked, this, &QDialog::reject); @@ -144,11 +167,11 @@ void WhiteningDialog::onConfirm() { p.dsObjectId = dsId_; p.whiteningMethod = methodCombo_->currentData().toInt(); if (p.whiteningMethod == 1) { - p.boundaryExtension = extension_->value(); + p.boundaryExtension = extension_->text().toDouble(); // 原版 Number(extension)||0 p.whiteningType = whiteningType_->checkedId(); } else if (p.whiteningMethod == 2) { if (fileCombo_->currentIndex() < 0) { - QMessageBox::warning(this, windowTitle(), QStringLiteral("请选择白化文件")); + QMessageBox::warning(this, windowTitle(), QStringLiteral("请选择一个白化文件")); return; } p.whitenedDataId = fileCombo_->currentData().toString(); diff --git a/src/app/panels/chart/WhiteningDialog.hpp b/src/app/panels/chart/WhiteningDialog.hpp index 624b9a0..8996774 100644 --- a/src/app/panels/chart/WhiteningDialog.hpp +++ b/src/app/panels/chart/WhiteningDialog.hpp @@ -3,7 +3,7 @@ #include class QComboBox; -class QDoubleSpinBox; +class QLineEdit; class QButtonGroup; class QStackedWidget; class QPushButton; @@ -38,7 +38,7 @@ private: QComboBox* methodCombo_ = nullptr; QStackedWidget* stack_ = nullptr; // 方式 1: - QDoubleSpinBox* extension_ = nullptr; + QLineEdit* extension_ = nullptr; // 原版 AInput 文本框(默认 "0") QButtonGroup* whiteningType_ = nullptr; // 0 外部 / 1 内部 // 方式 2: QComboBox* fileCombo_ = nullptr;