geopro/src/app/panels/chart/GridWizardDialog.cpp

337 lines
14 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "panels/chart/GridWizardDialog.hpp"
#include <cmath>
#include <utility>
#include <QComboBox>
#include "EmptyAwareComboBox.hpp"
#include <QDoubleSpinBox>
#include <QFrame>
#include <QGridLayout>
#include <QHBoxLayout>
#include <QLabel>
#include <QListWidget>
#include <QMessageBox>
#include <QPointer>
#include <QPushButton>
#include <QSpinBox>
#include <QStackedWidget>
#include <QVBoxLayout>
#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 int kStep1W = 500, kStep2W = 800; // 原版步骤 1/2 弹窗宽
constexpr int kParamLabelW = 60; // 原版 .param-group label min-width:60px
constexpr int kParamFieldW = 100; // 原版输入框宽 100px
// 配一个坐标 spinbox6 位小数,宽范围)。
QDoubleSpinBox* makeCoordSpin(QWidget* parent) {
auto* sp = new QDoubleSpinBox(parent);
sp->setRange(-kCoordRange, kCoordRange);
sp->setDecimals(6);
sp->setFixedWidth(kParamFieldW);
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
GridWizardDialog::GridWizardDialog(geopro::data::IDatasetCommandRepository* repo, QString dsId,
QWidget* parent)
: QDialog(parent), repo_(repo), dsId_(std::move(dsId)) {
setWindowTitle(QStringLiteral("网格配置")); // 原版 gridSetting
setModal(true);
setFixedWidth(kStep1W);
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);
buildStep1();
buildStep2();
// ── 底部按钮(上一步 / 确认(主) / 取消,原版步骤 2 三按钮)────────────
auto* btnLay = new QHBoxLayout();
btnLay->setSpacing(geopro::app::space::kMd);
btnLay->addStretch();
prevBtn_ = 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(okBtn_);
btnLay->addWidget(nextBtn_);
btnLay->addWidget(cancelBtn);
root->addLayout(btnLay);
prevBtn_->setVisible(false);
okBtn_->setVisible(false);
connect(cancelBtn, &QPushButton::clicked, this, &QDialog::reject);
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);
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 EmptyAwareComboBox(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() {
if (!repo_) return;
QPointer<GridWizardDialog> self(this);
repo_->listGridAlgorithm(dsId_, [self](bool ok, QJsonArray list, QString) {
if (!self || !ok) return;
self->algoList_->clear();
for (const QJsonValue& v : list) {
const QJsonObject o = v.toObject();
const QString name = o.value(QStringLiteral("scriptName")).toString();
const QString code = o.value(QStringLiteral("scriptCode")).toString();
auto* item = new QListWidgetItem(name, self->algoList_);
item->setData(Qt::UserRole, code);
}
if (self->algoList_->count() > 0) self->algoList_->setCurrentRow(0); // 默认首项
});
}
void GridWizardDialog::goToStep2() {
if (!algoList_->currentItem()) {
QMessageBox::warning(this, windowTitle(), QStringLiteral("请选择网格方法"));
return;
}
stack_->setCurrentIndex(1);
setFixedWidth(kStep2W);
nextBtn_->setVisible(false);
prevBtn_->setVisible(true);
okBtn_->setVisible(true);
loadParams();
}
void GridWizardDialog::loadParams() {
if (!repo_) return;
QPointer<GridWizardDialog> self(this);
repo_->getGridRawDataParams(dsId_, [self](bool ok, QJsonObject d, QString) {
if (!self || !ok) return;
self->xMin_->setValue(d.value(QStringLiteral("xmin")).toDouble());
self->xMax_->setValue(d.value(QStringLiteral("xmax")).toDouble());
self->yMin_->setValue(d.value(QStringLiteral("ymin")).toDouble());
self->yMax_->setValue(d.value(QStringLiteral("ymax")).toDouble());
self->vMin_->setValue(d.value(QStringLiteral("vmin")).toDouble());
self->vMax_->setValue(d.value(QStringLiteral("vmax")).toDouble());
// 原版 onMountedX点数固定 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); // 触发 calcYPoints
});
}
void GridWizardDialog::calcXInterval() {
const int n = xSize_->value();
const double range = xMax_->value() - xMin_->value();
if (n > 0 && range > 0) {
QSignalBlocker b(xSpacing_); // 防与 calcXPoints 互触发
xSpacing_->setValue(range / n);
}
}
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 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() {
if (!repo_ || !algoList_->currentItem()) { reject(); return; }
// 原版按 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;
p.dsObjectId = dsId_;
p.actionCode = algoList_->currentItem()->data(Qt::UserRole).toString();
p.xMin = xMin_->value(); p.xMax = xMax_->value();
p.yMin = yMin_->value(); p.yMax = yMax_->value();
p.vMin = vMin_->value(); p.vMax = vMax_->value();
p.xSize = xSize_->value(); p.ySize = ySize_->value();
p.xSpacing = xSpacing_->value(); p.ySpacing = ySpacing_->value();
p.logFormat = (saveFormat_->currentData().toString() == QStringLiteral("log"));
okBtn_->setEnabled(false);
QPointer<GridWizardDialog> self(this);
repo_->toGrid(buildGridToBody(p), [self](bool ok, QString msg) {
if (!self) return;
self->okBtn_->setEnabled(true);
if (ok) {
self->accept();
} else {
QMessageBox::warning(self, self->windowTitle(),
msg.isEmpty() ? QStringLiteral("网格化失败") : msg);
}
});
}
} // namespace geopro::app