498 lines
21 KiB
C++
498 lines
21 KiB
C++
#include "dialogdetectionconfig.h"
|
||
#include "ui_dialogdetectionconfig.h"
|
||
|
||
#include <QComboBox>
|
||
#include <QDoubleValidator>
|
||
#include <QIntValidator>
|
||
#include <QLineEdit>
|
||
#include <QMessageBox>
|
||
#include <QPushButton>
|
||
#include <QVector>
|
||
|
||
#include <algorithm>
|
||
#include <cmath>
|
||
#include <cstring>
|
||
|
||
#include "HandEyeCalibWidget.h"
|
||
#include "PathManager.h"
|
||
#include "ScrewPositionPresenter.h"
|
||
#include "StyledMessageBox.h"
|
||
|
||
namespace {
|
||
|
||
bool IsIdentityMatrix(const double matrix[16])
|
||
{
|
||
for (int i = 0; i < 16; ++i) {
|
||
const double expected = (i / 4 == i % 4) ? 1.0 : 0.0;
|
||
if (std::fabs(matrix[i] - expected) > 1e-9) {
|
||
return false;
|
||
}
|
||
}
|
||
return true;
|
||
}
|
||
|
||
} // namespace
|
||
|
||
DialogDetectionConfig::DialogDetectionConfig(QWidget *parent)
|
||
: QDialog(parent)
|
||
, ui(new Ui::DialogDetectionConfig)
|
||
{
|
||
ui->setupUi(this);
|
||
setWindowTitle(QStringLiteral("检测对象配置"));
|
||
|
||
initNumericEditors();
|
||
initHandEyeCalibTab();
|
||
initNetworkConfigControls();
|
||
|
||
// 检测对象 ComboBox 选项(带枚举值作 userData)
|
||
ui->comboType->blockSignals(true);
|
||
ui->comboType->clear();
|
||
ui->comboType->addItem(detectionTypeLabel(DETECTION_TYPE_SCREW),
|
||
static_cast<int>(DETECTION_TYPE_SCREW));
|
||
ui->comboType->addItem(detectionTypeLabel(DETECTION_TYPE_TOOL_DISK),
|
||
static_cast<int>(DETECTION_TYPE_TOOL_DISK));
|
||
ui->comboType->blockSignals(false);
|
||
|
||
connect(ui->comboCamera, QOverload<int>::of(&QComboBox::currentIndexChanged),
|
||
this, &DialogDetectionConfig::onSelectionChanged);
|
||
connect(ui->comboType, QOverload<int>::of(&QComboBox::currentIndexChanged),
|
||
this, &DialogDetectionConfig::onSelectionChanged);
|
||
connect(ui->btnOk, &QPushButton::clicked, this, &DialogDetectionConfig::onOkClicked);
|
||
connect(ui->btnCancel, &QPushButton::clicked, this, &DialogDetectionConfig::onCancelClicked);
|
||
connect(ui->btnApply, &QPushButton::clicked, this, &DialogDetectionConfig::onApplyClicked);
|
||
connect(ui->btnReset, &QPushButton::clicked, this, &DialogDetectionConfig::onResetCurrentClicked);
|
||
}
|
||
|
||
DialogDetectionConfig::~DialogDetectionConfig()
|
||
{
|
||
delete ui;
|
||
}
|
||
|
||
void DialogDetectionConfig::SetPresenter(ScrewPositionPresenter* presenter)
|
||
{
|
||
m_presenter = presenter;
|
||
loadFromConfig();
|
||
loadNetworkConfig();
|
||
}
|
||
|
||
void DialogDetectionConfig::initNumericEditors()
|
||
{
|
||
auto setupDouble = [](QLineEdit* edit, double min, double max, int decimals) {
|
||
auto* validator = new QDoubleValidator(min, max, decimals, edit);
|
||
validator->setNotation(QDoubleValidator::StandardNotation);
|
||
edit->setValidator(validator);
|
||
};
|
||
auto setupInt = [](QLineEdit* edit, int min, int max) {
|
||
edit->setValidator(new QIntValidator(min, max, edit));
|
||
};
|
||
|
||
setupDouble(ui->spinRodDiameter, 1.0, 100.0, 6);
|
||
setupDouble(ui->spinCornerTh, 0.0, 180.0, 6);
|
||
setupDouble(ui->spinScale, 0.0, 200.0, 6);
|
||
setupDouble(ui->spinMinEndingGap, 0.0, 100.0, 6);
|
||
setupDouble(ui->spinMinEndingGapZ, 0.0, 100.0, 6);
|
||
setupDouble(ui->spinJumpCornerTh1, 0.0, 100.0, 6);
|
||
setupDouble(ui->spinJumpCornerTh2, 0.0, 180.0, 6);
|
||
setupDouble(ui->spinContinuityTh, 0.0, 100.0, 6);
|
||
setupDouble(ui->spinOutlierTh, 0.0, 50.0, 6);
|
||
setupInt (ui->spinMaxLineSkipNum, -1, 100);
|
||
setupDouble(ui->spinYDeviationMax, 0.0, 100.0, 6);
|
||
setupDouble(ui->spinMaxSkipDistance, 0.0, 100.0, 6);
|
||
setupDouble(ui->spinZDeviationMax, 0.0, 100.0, 6);
|
||
setupDouble(ui->spinMinLTypeTreeLen, 0.0, 1000.0, 6);
|
||
setupDouble(ui->spinMinVTypeTreeLen, 0.0, 1000.0, 6);
|
||
}
|
||
|
||
void DialogDetectionConfig::initHandEyeCalibTab()
|
||
{
|
||
if (!ui->verticalLayout_handEyeCalibHost || m_handEyeCalibWidget) {
|
||
return;
|
||
}
|
||
m_handEyeCalibWidget = new HandEyeCalibWidget(this);
|
||
m_handEyeCalibWidget->setMatrixEditable(true);
|
||
m_handEyeCalibWidget->setExtrinsicControlsVisible(true);
|
||
m_handEyeCalibWidget->setApproachOffsetVisible(true);
|
||
m_handEyeCalibWidget->setTargetOffsetVisible(true);
|
||
m_handEyeCalibWidget->setDefaultFilePath(PathManager::GetInstance().GetAppConfigDirectory());
|
||
ui->verticalLayout_handEyeCalibHost->addWidget(m_handEyeCalibWidget);
|
||
}
|
||
|
||
void DialogDetectionConfig::initNetworkConfigControls()
|
||
{
|
||
ui->comboPoseOutputOrder->clear();
|
||
ui->comboPoseOutputOrder->addItem(QStringLiteral("RX-RY-RZ"), 0);
|
||
ui->comboPoseOutputOrder->addItem(QStringLiteral("RX-RZ-RY"), 1);
|
||
ui->comboPoseOutputOrder->addItem(QStringLiteral("RY-RX-RZ"), 2);
|
||
ui->comboPoseOutputOrder->addItem(QStringLiteral("RY-RZ-RX"), 3);
|
||
ui->comboPoseOutputOrder->addItem(QStringLiteral("RZ-RX-RY"), 4);
|
||
ui->comboPoseOutputOrder->addItem(QStringLiteral("RZ-RY-RX"), 5);
|
||
|
||
ui->comboByteOrder->clear();
|
||
ui->comboByteOrder->addItem(QStringLiteral("大端序 (ABCD)"), 0);
|
||
ui->comboByteOrder->addItem(QStringLiteral("小端序 (DCBA)"), 1);
|
||
|
||
ui->editTcpPort->setValidator(new QIntValidator(1, 65535, ui->editTcpPort));
|
||
}
|
||
|
||
void DialogDetectionConfig::loadNetworkConfig()
|
||
{
|
||
if (!m_presenter || !m_presenter->GetConfigManager()) {
|
||
return;
|
||
}
|
||
|
||
const ConfigResult configResult = m_presenter->GetConfigManager()->GetConfigResult();
|
||
|
||
int idx = ui->comboPoseOutputOrder->findData(configResult.poseOutputOrder);
|
||
if (idx >= 0) {
|
||
ui->comboPoseOutputOrder->setCurrentIndex(idx);
|
||
}
|
||
|
||
idx = ui->comboByteOrder->findData(configResult.byteOrder);
|
||
if (idx >= 0) {
|
||
ui->comboByteOrder->setCurrentIndex(idx);
|
||
}
|
||
|
||
ui->editTcpPort->setText(QString::number(configResult.tcpPort));
|
||
}
|
||
|
||
void DialogDetectionConfig::rebuildCameraSelector()
|
||
{
|
||
if (!ui->comboCamera) return;
|
||
ui->comboCamera->blockSignals(true);
|
||
ui->comboCamera->clear();
|
||
for (int camIdx = 1; camIdx <= m_cameraCount; ++camIdx) {
|
||
QString name = QStringLiteral("相机 %1").arg(camIdx);
|
||
if (m_presenter && m_presenter->GetConfigManager()) {
|
||
const auto& cameras = m_presenter->GetConfigManager()->GetConfigResult().cameraList;
|
||
const int arrayIndex = camIdx - 1;
|
||
if (arrayIndex >= 0 && arrayIndex < static_cast<int>(cameras.size())) {
|
||
name = QString::fromStdString(cameras[arrayIndex].name);
|
||
}
|
||
}
|
||
ui->comboCamera->addItem(name, camIdx);
|
||
}
|
||
ui->comboCamera->blockSignals(false);
|
||
}
|
||
|
||
void DialogDetectionConfig::loadFromConfig()
|
||
{
|
||
if (!m_presenter || !m_presenter->GetConfigManager()) {
|
||
return;
|
||
}
|
||
|
||
const ConfigResult configResult = m_presenter->GetConfigManager()->GetConfigResult();
|
||
m_cameraCount = std::max(1, static_cast<int>(configResult.cameraList.size()));
|
||
|
||
// 把当前 ConfigResult 中所有 (相机, 对象) set 抓到 m_workingSets,
|
||
// 缺失的槽位用默认值兜底补齐。
|
||
m_workingSets.clear();
|
||
const DetectionType allTypes[2] = { DETECTION_TYPE_SCREW, DETECTION_TYPE_TOOL_DISK };
|
||
for (int camIdx = 1; camIdx <= m_cameraCount; ++camIdx) {
|
||
for (DetectionType t : allTypes) {
|
||
WorkingSet ws;
|
||
ws.cameraIndex = camIdx;
|
||
ws.detectionType = t;
|
||
ws.handEyeMatrix.cameraIndex = camIdx;
|
||
if (const auto* item = configResult.FindDetectionConfig(camIdx, t)) {
|
||
ws.handEyeMatrix = item->handEyeMatrix;
|
||
ws.handEyeMatrix.cameraIndex = camIdx;
|
||
ws.algorithmParams = item->algorithmParams;
|
||
}
|
||
m_workingSets.push_back(ws);
|
||
}
|
||
}
|
||
|
||
// 把 HandEyeCalibWidget 配置成「单条目」模式:只展示当前选中那一份。
|
||
// 之所以不让它显示 4 条目,是因为本对话框已经在外部提供 (相机, 对象) 选择器,
|
||
// 内嵌相机选择器会让用户不知道编辑的是 screw 还是 toolDisk。
|
||
QVector<HandEyeCalibCameraInfo> singleCam;
|
||
HandEyeCalibCameraInfo info;
|
||
info.cameraIndex = 1;
|
||
info.displayName = QStringLiteral("当前");
|
||
singleCam.append(info);
|
||
m_handEyeCalibWidget->setCameraList(singleCam);
|
||
|
||
rebuildCameraSelector();
|
||
|
||
// 默认选中 (相机 1, 螺杆)
|
||
ui->comboCamera->setCurrentIndex(0);
|
||
ui->comboType->setCurrentIndex(0);
|
||
m_currentLoadedIndex = -1;
|
||
loadWorkingSetToEditors(currentWorkingIndex());
|
||
}
|
||
|
||
int DialogDetectionConfig::findWorkingIndex(int cameraIndex, DetectionType type) const
|
||
{
|
||
for (size_t i = 0; i < m_workingSets.size(); ++i) {
|
||
if (m_workingSets[i].cameraIndex == cameraIndex
|
||
&& m_workingSets[i].detectionType == type) {
|
||
return static_cast<int>(i);
|
||
}
|
||
}
|
||
return -1;
|
||
}
|
||
|
||
int DialogDetectionConfig::currentWorkingIndex() const
|
||
{
|
||
if (!ui || !ui->comboCamera || !ui->comboType) return -1;
|
||
const int camIdx = ui->comboCamera->currentData().toInt();
|
||
const auto type = static_cast<DetectionType>(ui->comboType->currentData().toInt());
|
||
return findWorkingIndex(camIdx, type);
|
||
}
|
||
|
||
void DialogDetectionConfig::loadWorkingSetToEditors(int workingIndex)
|
||
{
|
||
if (workingIndex < 0 || workingIndex >= static_cast<int>(m_workingSets.size())) {
|
||
return;
|
||
}
|
||
const auto& ws = m_workingSets[workingIndex];
|
||
|
||
// 手眼控件单条目模式:始终用虚拟 cameraIndex=1
|
||
m_handEyeCalibWidget->setCalibData(1, ws.handEyeMatrix.matrix, !IsIdentityMatrix(ws.handEyeMatrix.matrix));
|
||
m_handEyeCalibWidget->setExtrinsicData(1,
|
||
ws.handEyeMatrix.eulerOrder,
|
||
ws.handEyeMatrix.rotX,
|
||
ws.handEyeMatrix.rotY,
|
||
ws.handEyeMatrix.rotZ,
|
||
ws.handEyeMatrix.approachOffset,
|
||
ws.handEyeMatrix.offsetX,
|
||
ws.handEyeMatrix.offsetY,
|
||
ws.handEyeMatrix.offsetZ);
|
||
|
||
const auto& algo = ws.algorithmParams;
|
||
setDoubleEditorText(ui->spinRodDiameter, algo.screwParam.rodDiameter);
|
||
ui->chkHorizonScan->setChecked(algo.screwParam.isHorizonScan);
|
||
setDoubleEditorText(ui->spinCornerTh, algo.cornerParam.cornerTh);
|
||
setDoubleEditorText(ui->spinScale, algo.cornerParam.scale);
|
||
setDoubleEditorText(ui->spinMinEndingGap, algo.cornerParam.minEndingGap);
|
||
setDoubleEditorText(ui->spinMinEndingGapZ, algo.cornerParam.minEndingGap_z);
|
||
setDoubleEditorText(ui->spinJumpCornerTh1, algo.cornerParam.jumpCornerTh_1);
|
||
setDoubleEditorText(ui->spinJumpCornerTh2, algo.cornerParam.jumpCornerTh_2);
|
||
setDoubleEditorText(ui->spinContinuityTh, algo.filterParam.continuityTh);
|
||
setDoubleEditorText(ui->spinOutlierTh, algo.filterParam.outlierTh);
|
||
setIntEditorText (ui->spinMaxLineSkipNum, algo.growParam.maxLineSkipNum);
|
||
setDoubleEditorText(ui->spinYDeviationMax, algo.growParam.yDeviation_max);
|
||
setDoubleEditorText(ui->spinMaxSkipDistance, algo.growParam.maxSkipDistance);
|
||
setDoubleEditorText(ui->spinZDeviationMax, algo.growParam.zDeviation_max);
|
||
setDoubleEditorText(ui->spinMinLTypeTreeLen, algo.growParam.minLTypeTreeLen);
|
||
setDoubleEditorText(ui->spinMinVTypeTreeLen, algo.growParam.minVTypeTreeLen);
|
||
|
||
m_currentLoadedIndex = workingIndex;
|
||
}
|
||
|
||
bool DialogDetectionConfig::commitCurrentEditorsToWorkingSet(QString* errMsg)
|
||
{
|
||
if (m_currentLoadedIndex < 0 || m_currentLoadedIndex >= static_cast<int>(m_workingSets.size())) {
|
||
return true;
|
||
}
|
||
auto& ws = m_workingSets[m_currentLoadedIndex];
|
||
|
||
// 手眼控件(虚拟 cameraIndex=1)
|
||
if (m_handEyeCalibWidget) {
|
||
double matrix[16];
|
||
bool isCalibrated = false;
|
||
if (m_handEyeCalibWidget->getCalibData(1, matrix, isCalibrated)) {
|
||
std::memcpy(ws.handEyeMatrix.matrix, matrix, sizeof(matrix));
|
||
}
|
||
int eulerOrder = ws.handEyeMatrix.eulerOrder;
|
||
double rotX = ws.handEyeMatrix.rotX, rotY = ws.handEyeMatrix.rotY, rotZ = ws.handEyeMatrix.rotZ;
|
||
double approachOffset = ws.handEyeMatrix.approachOffset;
|
||
double offX = ws.handEyeMatrix.offsetX, offY = ws.handEyeMatrix.offsetY, offZ = ws.handEyeMatrix.offsetZ;
|
||
if (m_handEyeCalibWidget->getExtrinsicData(1, eulerOrder, rotX, rotY, rotZ,
|
||
approachOffset, offX, offY, offZ)) {
|
||
ws.handEyeMatrix.eulerOrder = eulerOrder;
|
||
ws.handEyeMatrix.rotX = rotX;
|
||
ws.handEyeMatrix.rotY = rotY;
|
||
ws.handEyeMatrix.rotZ = rotZ;
|
||
ws.handEyeMatrix.approachOffset = approachOffset;
|
||
ws.handEyeMatrix.offsetX = offX;
|
||
ws.handEyeMatrix.offsetY = offY;
|
||
ws.handEyeMatrix.offsetZ = offZ;
|
||
}
|
||
ws.handEyeMatrix.cameraIndex = ws.cameraIndex;
|
||
}
|
||
|
||
auto fail = [&](const QString& m) {
|
||
if (errMsg) *errMsg = m;
|
||
return false;
|
||
};
|
||
auto& algo = ws.algorithmParams;
|
||
|
||
if (!tryGetDouble(ui->spinRodDiameter, QStringLiteral("螺杆直径"), algo.screwParam.rodDiameter)) return fail(QStringLiteral("螺杆直径"));
|
||
algo.screwParam.isHorizonScan = ui->chkHorizonScan->isChecked();
|
||
if (!tryGetDouble(ui->spinCornerTh, QStringLiteral("拐角阈值"), algo.cornerParam.cornerTh)) return fail(QStringLiteral("拐角阈值"));
|
||
if (!tryGetDouble(ui->spinScale, QStringLiteral("窗口比例因子"), algo.cornerParam.scale)) return fail(QStringLiteral("窗口比例因子"));
|
||
if (!tryGetDouble(ui->spinMinEndingGap, QStringLiteral("Y方向最小结束间隙"), algo.cornerParam.minEndingGap)) return fail(QStringLiteral("Y方向最小结束间隙"));
|
||
if (!tryGetDouble(ui->spinMinEndingGapZ, QStringLiteral("Z方向最小结束间隙"), algo.cornerParam.minEndingGap_z)) return fail(QStringLiteral("Z方向最小结束间隙"));
|
||
if (!tryGetDouble(ui->spinJumpCornerTh1, QStringLiteral("跳变拐角阈值1"), algo.cornerParam.jumpCornerTh_1)) return fail(QStringLiteral("跳变拐角阈值1"));
|
||
if (!tryGetDouble(ui->spinJumpCornerTh2, QStringLiteral("跳变拐角阈值2"), algo.cornerParam.jumpCornerTh_2)) return fail(QStringLiteral("跳变拐角阈值2"));
|
||
if (!tryGetDouble(ui->spinContinuityTh, QStringLiteral("连续性阈值"), algo.filterParam.continuityTh)) return fail(QStringLiteral("连续性阈值"));
|
||
if (!tryGetDouble(ui->spinOutlierTh, QStringLiteral("离群点阈值"), algo.filterParam.outlierTh)) return fail(QStringLiteral("离群点阈值"));
|
||
if (!tryGetInt (ui->spinMaxLineSkipNum, QStringLiteral("最大跳线数"), algo.growParam.maxLineSkipNum)) return fail(QStringLiteral("最大跳线数"));
|
||
if (!tryGetDouble(ui->spinYDeviationMax, QStringLiteral("Y偏差最大值"), algo.growParam.yDeviation_max)) return fail(QStringLiteral("Y偏差最大值"));
|
||
if (!tryGetDouble(ui->spinMaxSkipDistance, QStringLiteral("最大跳跃距离"), algo.growParam.maxSkipDistance)) return fail(QStringLiteral("最大跳跃距离"));
|
||
if (!tryGetDouble(ui->spinZDeviationMax, QStringLiteral("Z偏差最大值"), algo.growParam.zDeviation_max)) return fail(QStringLiteral("Z偏差最大值"));
|
||
if (!tryGetDouble(ui->spinMinLTypeTreeLen, QStringLiteral("L型树最小长度"), algo.growParam.minLTypeTreeLen)) return fail(QStringLiteral("L型树最小长度"));
|
||
if (!tryGetDouble(ui->spinMinVTypeTreeLen, QStringLiteral("V型树最小长度"), algo.growParam.minVTypeTreeLen)) return fail(QStringLiteral("V型树最小长度"));
|
||
|
||
return true;
|
||
}
|
||
|
||
bool DialogDetectionConfig::saveAllWorkingSetsToConfig()
|
||
{
|
||
if (!m_presenter || !m_presenter->GetConfigManager()) {
|
||
StyledMessageBox::warning(this, QStringLiteral("失败"),
|
||
QStringLiteral("ConfigManager 未初始化。"));
|
||
return false;
|
||
}
|
||
|
||
QString err;
|
||
if (!commitCurrentEditorsToWorkingSet(&err)) {
|
||
// tryGetDouble/tryGetInt 内部已经弹了具体错误框;这里不再重复
|
||
return false;
|
||
}
|
||
|
||
SystemConfig systemConfig = m_presenter->GetConfigManager()->GetConfig();
|
||
// 用 m_workingSets 全量覆盖 detectionConfigList
|
||
systemConfig.configResult.detectionConfigList.clear();
|
||
for (const auto& ws : m_workingSets) {
|
||
VrDetectionConfigSet item;
|
||
item.cameraIndex = ws.cameraIndex;
|
||
item.detectionType = ws.detectionType;
|
||
item.handEyeMatrix = ws.handEyeMatrix;
|
||
item.handEyeMatrix.cameraIndex = ws.cameraIndex;
|
||
item.algorithmParams = ws.algorithmParams;
|
||
systemConfig.configResult.detectionConfigList.push_back(item);
|
||
}
|
||
|
||
bool ok = false;
|
||
const int tcpPort = ui->editTcpPort->text().trimmed().toInt(&ok);
|
||
if (!ok || tcpPort <= 0 || tcpPort > 65535) {
|
||
StyledMessageBox::warning(this, QStringLiteral("错误"),
|
||
QStringLiteral("TCP端口必须在 1 到 65535 之间。"));
|
||
ui->editTcpPort->setFocus();
|
||
ui->editTcpPort->selectAll();
|
||
return false;
|
||
}
|
||
|
||
systemConfig.configResult.tcpPort = static_cast<uint16_t>(tcpPort);
|
||
systemConfig.configResult.poseOutputOrder = ui->comboPoseOutputOrder->currentData().toInt();
|
||
systemConfig.configResult.byteOrder = ui->comboByteOrder->currentData().toInt();
|
||
|
||
if (!m_presenter->GetConfigManager()->UpdateFullConfig(systemConfig)) {
|
||
StyledMessageBox::warning(this, QStringLiteral("失败"),
|
||
QStringLiteral("更新配置缓存失败。"));
|
||
return false;
|
||
}
|
||
|
||
const QString configPath = PathManager::GetInstance().GetConfigFilePath();
|
||
if (!m_presenter->GetConfigManager()->SaveConfigToFile(configPath.toStdString())) {
|
||
StyledMessageBox::warning(this, QStringLiteral("失败"),
|
||
QStringLiteral("保存配置文件失败。"));
|
||
return false;
|
||
}
|
||
|
||
m_presenter->OnConfigChanged(systemConfig.configResult);
|
||
return true;
|
||
}
|
||
|
||
// —— 槽函数 ——
|
||
|
||
void DialogDetectionConfig::onSelectionChanged()
|
||
{
|
||
// 切换前先提交当前编辑器内容到旧 set;失败则回滚 combo 选择。
|
||
QString err;
|
||
if (!commitCurrentEditorsToWorkingSet(&err)) {
|
||
// 用户编辑了非法值,重新加载旧 set,避免进入未定义状态
|
||
loadWorkingSetToEditors(m_currentLoadedIndex);
|
||
return;
|
||
}
|
||
const int newIndex = currentWorkingIndex();
|
||
loadWorkingSetToEditors(newIndex);
|
||
}
|
||
|
||
void DialogDetectionConfig::onOkClicked()
|
||
{
|
||
if (saveAllWorkingSetsToConfig()) {
|
||
accept();
|
||
}
|
||
}
|
||
|
||
void DialogDetectionConfig::onCancelClicked()
|
||
{
|
||
reject();
|
||
}
|
||
|
||
void DialogDetectionConfig::onApplyClicked()
|
||
{
|
||
if (saveAllWorkingSetsToConfig()) {
|
||
StyledMessageBox::information(this, QStringLiteral("提示"),
|
||
QStringLiteral("4 套配置已应用并写入 config.xml。"));
|
||
}
|
||
}
|
||
|
||
void DialogDetectionConfig::onResetCurrentClicked()
|
||
{
|
||
if (m_currentLoadedIndex < 0 || m_currentLoadedIndex >= static_cast<int>(m_workingSets.size())) {
|
||
return;
|
||
}
|
||
auto ret = StyledMessageBox::question(this, QStringLiteral("确认重置"),
|
||
QStringLiteral("确定要把当前 (相机, 对象) 配置重置为默认值吗?\n(仅影响内存中正在编辑的副本,确定/应用前不会落盘)"),
|
||
QMessageBox::Yes | QMessageBox::No);
|
||
if (ret != QMessageBox::Yes) return;
|
||
|
||
WorkingSet defaults;
|
||
defaults.cameraIndex = m_workingSets[m_currentLoadedIndex].cameraIndex;
|
||
defaults.detectionType = m_workingSets[m_currentLoadedIndex].detectionType;
|
||
defaults.handEyeMatrix.cameraIndex = defaults.cameraIndex;
|
||
m_workingSets[m_currentLoadedIndex] = defaults;
|
||
loadWorkingSetToEditors(m_currentLoadedIndex);
|
||
}
|
||
|
||
// —— 工具函数 ——
|
||
|
||
void DialogDetectionConfig::setDoubleEditorText(QLineEdit* edit, double value)
|
||
{
|
||
if (edit) edit->setText(QString::number(value, 'g', 12));
|
||
}
|
||
|
||
void DialogDetectionConfig::setIntEditorText(QLineEdit* edit, int value)
|
||
{
|
||
if (edit) edit->setText(QString::number(value));
|
||
}
|
||
|
||
bool DialogDetectionConfig::tryGetDouble(QLineEdit* edit, const QString& label, double& value)
|
||
{
|
||
bool ok = false;
|
||
value = edit->text().trimmed().toDouble(&ok);
|
||
if (!ok) {
|
||
StyledMessageBox::warning(this, QStringLiteral("错误"),
|
||
QStringLiteral("“%1” 输入的数值无效。").arg(label));
|
||
edit->setFocus();
|
||
edit->selectAll();
|
||
return false;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
bool DialogDetectionConfig::tryGetInt(QLineEdit* edit, const QString& label, int& value)
|
||
{
|
||
bool ok = false;
|
||
value = edit->text().trimmed().toInt(&ok);
|
||
if (!ok) {
|
||
StyledMessageBox::warning(this, QStringLiteral("错误"),
|
||
QStringLiteral("“%1” 输入的整数无效。").arg(label));
|
||
edit->setFocus();
|
||
edit->selectAll();
|
||
return false;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
QString DialogDetectionConfig::detectionTypeLabel(DetectionType type)
|
||
{
|
||
return (type == DETECTION_TYPE_TOOL_DISK)
|
||
? QStringLiteral("工具盘")
|
||
: QStringLiteral("螺杆");
|
||
}
|