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
10 changed files with 102 additions and 25 deletions
Showing only changes of commit 3dfe8b54f5 - Show all commits

View File

@ -97,14 +97,16 @@ ColorScaleConfigDialog::ColorScaleConfigDialog(const geopro::core::ColorScale& i
double vmax, std::vector<double> samples, double vmax, std::vector<double> samples,
const ContourLineConfig& lineInit, const ContourLineConfig& lineInit,
geopro::data::IColorTemplateRepository* tplRepo, geopro::data::IColorTemplateRepository* tplRepo,
QString projectId, QWidget* parent) QString projectId, QString lvlTemplateId,
QWidget* parent)
: QDialog(parent), : QDialog(parent),
vmin_(vmin), vmin_(vmin),
vmax_(vmax), vmax_(vmax),
samples_(std::move(samples)), samples_(std::move(samples)),
lineCfg_(lineInit), lineCfg_(lineInit),
tplRepo_(tplRepo), tplRepo_(tplRepo),
projectId_(std::move(projectId)) { projectId_(std::move(projectId)),
lvlTemplateId_(std::move(lvlTemplateId)) {
setWindowTitle(QStringLiteral("色阶配置")); setWindowTitle(QStringLiteral("色阶配置"));
setModal(true); setModal(true);
resize(560, 420); resize(560, 420);
@ -433,11 +435,31 @@ void ColorScaleConfigDialog::loadColorBar(
void ColorScaleConfigDialog::onSaveOther() { void ColorScaleConfigDialog::onSaveOther() {
if (tplRepo_ == nullptr || projectId_.isEmpty()) return; if (tplRepo_ == nullptr || projectId_.isEmpty()) return;
bool ok = false;
const QString name = QInputDialog::getText(this, QStringLiteral("另存模板配置"), // 自定义另存为弹窗(复刻 handleSaveOther名称输入 + 覆盖复选框。
QStringLiteral("模板名称:"), QLineEdit::Normal, // 「覆盖」仅当有来源模板 idlvlTemplateId_ 非空)时可勾选,对照原版 props.data.lvlTemplateId。
QStringLiteral("等值线配置.lvl"), &ok); QDialog askDlg(this);
if (!ok || name.trimmed().isEmpty()) return; askDlg.setWindowTitle(QStringLiteral("另存模板配置"));
askDlg.setModal(true);
auto* askRoot = new QVBoxLayout(&askDlg);
auto* nameRow = new QHBoxLayout();
nameRow->addWidget(new QLabel(QStringLiteral("模板名称:"), &askDlg));
auto* nameEdit = new QLineEdit(QStringLiteral("等值线配置.lvl"), &askDlg);
nameRow->addWidget(nameEdit, 1);
askRoot->addLayout(nameRow);
auto* overwriteCheck = new QCheckBox(QStringLiteral("覆盖原模板"), &askDlg);
overwriteCheck->setEnabled(!lvlTemplateId_.isEmpty()); // 无来源模板 → 禁用覆盖
askRoot->addWidget(overwriteCheck);
auto* askBtns = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, &askDlg);
askBtns->button(QDialogButtonBox::Ok)->setText(QStringLiteral("应用"));
askBtns->button(QDialogButtonBox::Cancel)->setText(QStringLiteral("取消"));
connect(askBtns, &QDialogButtonBox::accepted, &askDlg, &QDialog::accept);
connect(askBtns, &QDialogButtonBox::rejected, &askDlg, &QDialog::reject);
askRoot->addWidget(askBtns);
if (askDlg.exec() != QDialog::Accepted) return;
const QString name = nameEdit->text().trimmed();
if (name.isEmpty()) return;
const bool overwrite = overwriteCheck->isChecked() && !lvlTemplateId_.isEmpty();
// 组装 properties复刻 handleSaveOther // 组装 properties复刻 handleSaveOther
QJsonArray colorBar; QJsonArray colorBar;
@ -457,18 +479,22 @@ void ColorScaleConfigDialog::onSaveOther() {
{QStringLiteral("colorBar"), colorBar}}; {QStringLiteral("colorBar"), colorBar}};
// 走仓储传输;回调里用 QPointer 守卫 this模态对话框可能已关 // 走仓储传输;回调里用 QPointer 守卫 this模态对话框可能已关
// 勾选覆盖 → PUT 更新来源模板updateLvlTemplate否则 → POST 新建saveLvlTemplate
QPointer<ColorScaleConfigDialog> self(this); QPointer<ColorScaleConfigDialog> self(this);
tplRepo_->saveLvlTemplate(projectId_, name.trimmed(), properties, auto onDone = [self, overwrite](bool ok, QString msg) {
[self](bool ok, QString msg) { if (!self) return;
if (!self) return; if (ok)
if (ok) QMessageBox::information(
QMessageBox::information(self, QStringLiteral("另存"), self, QStringLiteral("另存"),
QStringLiteral("另存成功。")); overwrite ? QStringLiteral("更新成功。") : QStringLiteral("另存成功。"));
else else
QMessageBox::warning( QMessageBox::warning(self, QStringLiteral("另存"),
self, QStringLiteral("另存"), QStringLiteral("另存失败:%1").arg(msg));
QStringLiteral("另存失败:%1").arg(msg)); };
}); if (overwrite)
tplRepo_->updateLvlTemplate(lvlTemplateId_, name, properties, std::move(onDone));
else
tplRepo_->saveLvlTemplate(projectId_, name, properties, std::move(onDone));
} }
void ColorScaleConfigDialog::onOpen() { void ColorScaleConfigDialog::onOpen() {

View File

@ -29,12 +29,15 @@ public:
// init当前色阶升序断点填表vmin/vmax数据原始范围层级/颜色子对话框 + 新增外推用); // init当前色阶升序断点填表vmin/vmax数据原始范围层级/颜色子对话框 + 新增外推用);
// samples数据原始标量等积分层 + 颜色编辑器直方图用,空则等积退化为线性); // samples数据原始标量等积分层 + 颜色编辑器直方图用,空则等积退化为线性);
// lineInit线形/标注初值2D 传当前态3D 用默认); // lineInit线形/标注初值2D 传当前态3D 用默认);
// tplRepo/projectIdlvl 模板库仓储句柄(可空 → 另存为/打开 禁用)。 // tplRepo/projectIdlvl 模板库仓储句柄(可空 → 另存为/打开 禁用);
// lvlTemplateId当前色阶来源模板 id可空对照原版 props.data.lvlTemplateId
// 非空时「另存为」弹窗的「覆盖」复选框可勾选 → 走 PUT 更新该模板3D/无模板场景不传即可。
ColorScaleConfigDialog(const geopro::core::ColorScale& init, double vmin, double vmax, ColorScaleConfigDialog(const geopro::core::ColorScale& init, double vmin, double vmax,
std::vector<double> samples = {}, std::vector<double> samples = {},
const ContourLineConfig& lineInit = {}, const ContourLineConfig& lineInit = {},
geopro::data::IColorTemplateRepository* tplRepo = nullptr, geopro::data::IColorTemplateRepository* tplRepo = nullptr,
QString projectId = {}, QWidget* parent = nullptr); QString projectId = {}, QString lvlTemplateId = {},
QWidget* parent = nullptr);
// 由表格当前断点装配的新色阶(按层级升序 addStop // 由表格当前断点装配的新色阶(按层级升序 addStop
geopro::core::ColorScale colorScale() const; geopro::core::ColorScale colorScale() const;
@ -74,6 +77,7 @@ private:
geopro::data::IColorTemplateRepository* tplRepo_ = nullptr; // lvl 模板库仓储(可空) geopro::data::IColorTemplateRepository* tplRepo_ = nullptr; // lvl 模板库仓储(可空)
QString projectId_; QString projectId_;
QString lvlTemplateId_; // 当前色阶来源模板 id可空 → 另存为弹窗禁用「覆盖」)
// 随子对话框更新、写入另存为 properties复刻原版透传字段 // 随子对话框更新、写入另存为 properties复刻原版透传字段
QString lvlSchemeType_ = QStringLiteral("normal"); QString lvlSchemeType_ = QStringLiteral("normal");
int logLinesCount_ = 8; int logLinesCount_ = 8;

View File

@ -807,10 +807,11 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re
} }
// 3D 无等值线,线形/标注配置忽略(用默认);仅取色阶应用。 // 3D 无等值线,线形/标注配置忽略(用默认);仅取色阶应用。
// 「另存为/打开」与「新建色阶/配色方案」走色阶模板仓储projectId 取当前项目。 // 「另存为/打开」与「新建色阶/配色方案」走色阶模板仓储projectId 取当前项目。
// 3D 体无来源 lvl 模板 → lvlTemplateId 传空(覆盖复选框禁用,行为不变)。
geopro::app::ColorScaleConfigDialog dlg( geopro::app::ColorScaleConfigDialog dlg(
sceneView->currentColorScale(), sceneView->currentVmin(), sceneView->currentColorScale(), sceneView->currentVmin(),
sceneView->currentVmax(), std::move(samples), {}, &colorTplRepo, sceneView->currentVmax(), std::move(samples), {}, &colorTplRepo,
nav.currentProjectId(), &window); nav.currentProjectId(), QString(), &window);
if (dlg.exec() == QDialog::Accepted) if (dlg.exec() == QDialog::Accepted)
sceneCtrl->setVolumeColorScale(dsId, dlg.colorScale()); sceneCtrl->setVolumeColorScale(dsId, dlg.colorScale());
}); });

View File

@ -23,6 +23,8 @@ std::unique_ptr<IDetailView> makeDetailView(controller::ViewKind kind, QWidget*
auto* raw = new RawDataChartView(parent); auto* raw = new RawDataChartView(parent);
// 注入反演命令仓储 + dsId/projectId 取值回调measurement 反演运算/生成视电阻率)。 // 注入反演命令仓储 + dsId/projectId 取值回调measurement 反演运算/生成视电阻率)。
raw->setCommandRepo(cmdRepo, dsIdGetter, projectIdGetter); raw->setCommandRepo(cmdRepo, dsIdGetter, projectIdGetter);
// 注入色阶模板仓储(散点「色阶配置」编辑器另存为/打开/覆盖用projectId 复用上面的 getter
raw->setColorTemplateRepo(colorTplRepo);
return std::unique_ptr<IDetailView>(raw); return std::unique_ptr<IDetailView>(raw);
} }
case controller::ViewKind::FilledContour: { case controller::ViewKind::FilledContour: {

View File

@ -318,8 +318,9 @@ void GridDataChartView::openColorScaleEditor() {
// projectId 在打开时取一次(随项目切换生效);无 getter 退化为空 → 后端按钮禁用。 // projectId 在打开时取一次(随项目切换生效);无 getter 退化为空 → 后端按钮禁用。
const QString projectId = projectIdGetter_ ? projectIdGetter_() : QString(); const QString projectId = projectIdGetter_ ? projectIdGetter_() : QString();
// 网格剖面载荷ContourPayload无 templateId 字段 → lvlTemplateId 传空(覆盖复选框禁用)。
ColorScaleConfigDialog dlg(gridScale_, grid_.vmin, grid_.vmax, std::move(samples), lineCfg_, ColorScaleConfigDialog dlg(gridScale_, grid_.vmin, grid_.vmax, std::move(samples), lineCfg_,
tplRepo_, projectId, this); tplRepo_, projectId, QString(), this);
if (dlg.exec() != QDialog::Accepted) return; if (dlg.exec() != QDialog::Accepted) return;
gridScale_ = dlg.colorScale(); gridScale_ = dlg.colorScale();

View File

@ -352,6 +352,10 @@ void RawDataChartView::setCommandRepo(geopro::data::IDatasetCommandRepository* r
projectIdGetter_ = std::move(projectIdGetter); projectIdGetter_ = std::move(projectIdGetter);
} }
void RawDataChartView::setColorTemplateRepo(geopro::data::IColorTemplateRepository* repo) {
colorTplRepo_ = repo;
}
void RawDataChartView::openInversionDialog(bool apparentResistivity, QWidget* anchor) { void RawDataChartView::openInversionDialog(bool apparentResistivity, QWidget* anchor) {
// 无仓储/无 dsId 取值回调 → 退化占位(与未注入时一致)。 // 无仓储/无 dsId 取值回调 → 退化占位(与未注入时一致)。
const QString dsId = dsIdGetter_ ? dsIdGetter_() : QString(); const QString dsId = dsIdGetter_ ? dsIdGetter_() : QString();
@ -388,7 +392,10 @@ void RawDataChartView::openInversionColorScale(QWidget* anchor) {
} }
if (vMin > vMax) { vMin = 0.0; vMax = 1.0; } if (vMin > vMax) { vMin = 0.0; vMax = 1.0; }
std::vector<double> samples = data_.scatter.v; std::vector<double> samples = data_.scatter.v;
ColorScaleConfigDialog dlg(data_.scale, vMin, vMax, std::move(samples), {}, nullptr, {}, this); // 接通色阶模板库:注入仓储 + 当前 projectId + 载荷 templateId另存为/打开/覆盖 可用)。
ColorScaleConfigDialog dlg(data_.scale, vMin, vMax, std::move(samples), {}, colorTplRepo_,
projectIdGetter_ ? projectIdGetter_() : QString(), data_.templateId,
this);
if (dlg.exec() != QDialog::Accepted) return; if (dlg.exec() != QDialog::Accepted) return;
// 本地重建上色重绘。 // 本地重建上色重绘。
@ -565,8 +572,10 @@ void RawDataChartView::openScatterColorScale(QWidget* anchor) {
if (vMin > vMax) { vMin = 0.0; vMax = 1.0; } if (vMin > vMax) { vMin = 0.0; vMax = 1.0; }
std::vector<double> samples = data_.scatter.v; // 直方图/等积分层用原始标量 std::vector<double> samples = data_.scatter.v; // 直方图/等积分层用原始标量
// 散点无独立 lvl 模板仓储(视图只持命令仓储)→ tplRepo 传空(另存为/打开 禁用)。 // 接通色阶模板库:注入仓储 + 当前 projectId + 载荷 templateId另存为/打开/覆盖 可用)。
ColorScaleConfigDialog dlg(data_.scale, vMin, vMax, std::move(samples), {}, nullptr, {}, this); ColorScaleConfigDialog dlg(data_.scale, vMin, vMax, std::move(samples), {}, colorTplRepo_,
projectIdGetter_ ? projectIdGetter_() : QString(), data_.templateId,
this);
if (dlg.exec() != QDialog::Accepted) return; if (dlg.exec() != QDialog::Accepted) return;
// 本地重建 colorSvc_ 重绘散点M8 即时生效)。 // 本地重建 colorSvc_ 重绘散点M8 即时生效)。

View File

@ -15,6 +15,7 @@ class QwtPlotRescaler;
namespace geopro::data { namespace geopro::data {
class IDatasetCommandRepository; class IDatasetCommandRepository;
class IColorTemplateRepository;
} }
namespace geopro::app { namespace geopro::app {
@ -47,6 +48,10 @@ public:
std::function<QString()> dsIdGetter, std::function<QString()> dsIdGetter,
std::function<QString()> projectIdGetter); std::function<QString()> projectIdGetter);
// 注入色阶模板仓储(散点「色阶配置」编辑器「另存为/打开/覆盖」用projectId 复用
// setCommandRepo 注入的 projectIdGetter_。可传空 → 编辑器后端按钮禁用。
void setColorTemplateRepo(geopro::data::IColorTemplateRepository* repo);
protected: protected:
// 信息模式M13下捕获画布点击找最近散点显示属性。其余事件不消费。 // 信息模式M13下捕获画布点击找最近散点显示属性。其余事件不消费。
bool eventFilter(QObject* obj, QEvent* ev) override; bool eventFilter(QObject* obj, QEvent* ev) override;
@ -123,6 +128,8 @@ private:
geopro::data::IDatasetCommandRepository* cmdRepo_ = nullptr; geopro::data::IDatasetCommandRepository* cmdRepo_ = nullptr;
std::function<QString()> dsIdGetter_; std::function<QString()> dsIdGetter_;
std::function<QString()> projectIdGetter_; std::function<QString()> projectIdGetter_;
// 色阶模板仓储(注入;空则编辑器「另存为/打开」禁用)。
geopro::data::IColorTemplateRepository* colorTplRepo_ = nullptr;
}; };
} // namespace geopro::app } // namespace geopro::app

View File

@ -35,6 +35,24 @@ void ApiColorTemplateRepository::saveLvlTemplate(const QString& projectId,
}); });
} }
void ApiColorTemplateRepository::updateLvlTemplate(const QString& id,
const QString& templateName,
const QJsonObject& properties,
std::function<void(bool, QString)> cb) {
QJsonObject body{{QStringLiteral("id"), id},
{QStringLiteral("templateName"), templateName},
{QStringLiteral("properties"), properties}};
auto* call = api_.putJsonAsync(QStringLiteral("/business/lvlTemplate"), body);
if (call == nullptr) {
if (cb) cb(false, QStringLiteral("请求创建失败"));
return;
}
QObject::connect(call, &net::IApiCall::finished, call,
[cb = std::move(cb)](const net::ApiResponse& resp) {
if (cb) cb(isOk(resp), resp.msg);
});
}
void ApiColorTemplateRepository::listLvlTemplates( void ApiColorTemplateRepository::listLvlTemplates(
const QString& projectId, std::function<void(bool, QJsonArray, QString)> cb) { const QString& projectId, std::function<void(bool, QJsonArray, QString)> cb) {
QJsonObject body{{QStringLiteral("projectId"), projectId}, QJsonObject body{{QStringLiteral("projectId"), projectId},

View File

@ -15,6 +15,9 @@ public:
void saveLvlTemplate(const QString& projectId, const QString& templateName, void saveLvlTemplate(const QString& projectId, const QString& templateName,
const QJsonObject& properties, const QJsonObject& properties,
std::function<void(bool ok, QString msg)> cb) override; std::function<void(bool ok, QString msg)> cb) override;
void updateLvlTemplate(const QString& id, const QString& templateName,
const QJsonObject& properties,
std::function<void(bool ok, QString msg)> cb) override;
void listLvlTemplates(const QString& projectId, void listLvlTemplates(const QString& projectId,
std::function<void(bool ok, QJsonArray list, QString msg)> cb) override; std::function<void(bool ok, QJsonArray list, QString msg)> cb) override;
void newClrScheme(const QString& projectId, const QJsonObject& properties, void newClrScheme(const QString& projectId, const QJsonObject& properties,

View File

@ -19,6 +19,12 @@ public:
const QJsonObject& properties, const QJsonObject& properties,
std::function<void(bool ok, QString msg)> cb) = 0; std::function<void(bool ok, QString msg)> cb) = 0;
// 覆盖更新 lvl 模板PUT /business/lvlTemplatebody {id, templateName, properties})。
// 复刻原版 updateLvlTemplate另存为弹窗勾选「覆盖」时调用id=当前模板 lvlTemplateId
virtual void updateLvlTemplate(const QString& id, const QString& templateName,
const QJsonObject& properties,
std::function<void(bool ok, QString msg)> cb) = 0;
// 列 lvl 模板POST /business/lvlTemplate/pagepageNo=1,pageSize=1000 // 列 lvl 模板POST /business/lvlTemplate/pagepageNo=1,pageSize=1000
// 回调 list = 响应 data.list 数组(每项含 templateName/properties // 回调 list = 响应 data.list 数组(每项含 templateName/properties
virtual void listLvlTemplates(const QString& projectId, virtual void listLvlTemplates(const QString& projectId,