From 6acdd1944820d999b230dc777f7564ae6559ec2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=B0=E4=BB=94?= Date: Sat, 20 Jun 2026 23:50:04 +0800 Subject: [PATCH] =?UTF-8?q?=E5=85=AC=E5=85=B1=E7=9A=84=E5=B7=A5=E5=85=B7?= =?UTF-8?q?=E6=9C=AB=E7=AB=AF=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AppUtils/UICommon/Inc/ToolExtrinsicWidget.h | 49 +++++ AppUtils/UICommon/Src/DeviceStatusWidget.cpp | 4 +- AppUtils/UICommon/Src/ToolExtrinsicWidget.cpp | 208 ++++++++++++++++++ AppUtils/UICommon/UICommon.pro | 6 +- 4 files changed, 263 insertions(+), 4 deletions(-) create mode 100644 AppUtils/UICommon/Inc/ToolExtrinsicWidget.h create mode 100644 AppUtils/UICommon/Src/ToolExtrinsicWidget.cpp diff --git a/AppUtils/UICommon/Inc/ToolExtrinsicWidget.h b/AppUtils/UICommon/Inc/ToolExtrinsicWidget.h new file mode 100644 index 00000000..ad99c306 --- /dev/null +++ b/AppUtils/UICommon/Inc/ToolExtrinsicWidget.h @@ -0,0 +1,49 @@ +#ifndef TOOLEXTRINSICWIDGET_H +#define TOOLEXTRINSICWIDGET_H + +#include +#include +#include +#include +#include + +/** + * @brief 工具外参编辑控件(从 HandEyeCalibWidget 中抽取 m_groupExtrinsic) + * + * 纯 QWidget,代码构建 UI(无 .ui 文件),可嵌入任意 tab/layout。 + * 包含:欧拉角顺序 + 姿态调整 + 目标点偏移。 + */ +class ToolExtrinsicWidget : public QWidget +{ + Q_OBJECT + +public: + explicit ToolExtrinsicWidget(QWidget *parent = nullptr); + ~ToolExtrinsicWidget(); + + void setData(int eulerOrder, + double rotX, double rotY, double rotZ, + double offsetX, double offsetY, double offsetZ); + + void getData(int& eulerOrder, + double& rotX, double& rotY, double& rotZ, + double& offsetX, double& offsetY, double& offsetZ) const; + + void setTargetOffsetVisible(bool visible); + +private: + void setupUI(); + void initEulerOrderComboBox(); + + QGroupBox* m_groupExtrinsic; + QComboBox* m_comboEulerOrder; + QLineEdit* m_editRotX; + QLineEdit* m_editRotY; + QLineEdit* m_editRotZ; + QLabel* m_labelTargetOffset; + QLineEdit* m_editOffsetX; + QLineEdit* m_editOffsetY; + QLineEdit* m_editOffsetZ; +}; + +#endif // TOOLEXTRINSICWIDGET_H diff --git a/AppUtils/UICommon/Src/DeviceStatusWidget.cpp b/AppUtils/UICommon/Src/DeviceStatusWidget.cpp index 8e079949..1609f6f7 100644 --- a/AppUtils/UICommon/Src/DeviceStatusWidget.cpp +++ b/AppUtils/UICommon/Src/DeviceStatusWidget.cpp @@ -264,7 +264,7 @@ void DeviceStatusWidget::setCameraCount(int cameraCount, bool horizontal) } // 横向布局时不需要调整布局 - if (!horizontal) { + if (!horizontal || 1 == m_cameraCount) { updateLayout(); } } else { @@ -292,7 +292,7 @@ void DeviceStatusWidget::setCameraCount(int cameraCount, bool horizontal) } // 横向布局时不需要调整布局 - if (!horizontal) { + if (!horizontal || 1 == m_cameraCount) { updateLayout(); } }, Qt::QueuedConnection); diff --git a/AppUtils/UICommon/Src/ToolExtrinsicWidget.cpp b/AppUtils/UICommon/Src/ToolExtrinsicWidget.cpp new file mode 100644 index 00000000..75a042f4 --- /dev/null +++ b/AppUtils/UICommon/Src/ToolExtrinsicWidget.cpp @@ -0,0 +1,208 @@ +#include "ToolExtrinsicWidget.h" + +#include +#include +#include +#include + +ToolExtrinsicWidget::ToolExtrinsicWidget(QWidget *parent) + : QWidget(parent) + , m_groupExtrinsic(nullptr) + , m_comboEulerOrder(nullptr) + , m_editRotX(nullptr) + , m_editRotY(nullptr) + , m_editRotZ(nullptr) + , m_labelTargetOffset(nullptr) + , m_editOffsetX(nullptr) + , m_editOffsetY(nullptr) + , m_editOffsetZ(nullptr) +{ + setupUI(); +} + +ToolExtrinsicWidget::~ToolExtrinsicWidget() +{ +} + +void ToolExtrinsicWidget::setData(int eulerOrder, + double rotX, double rotY, double rotZ, + double offsetX, double offsetY, double offsetZ) +{ + if (m_comboEulerOrder) { + int idx = m_comboEulerOrder->findData(eulerOrder); + if (idx < 0) idx = 0; + m_comboEulerOrder->setCurrentIndex(idx); + } + if (m_editRotX) m_editRotX->setText(QString::number(rotX, 'f', 6)); + if (m_editRotY) m_editRotY->setText(QString::number(rotY, 'f', 6)); + if (m_editRotZ) m_editRotZ->setText(QString::number(rotZ, 'f', 6)); + if (m_editOffsetX) m_editOffsetX->setText(QString::number(offsetX, 'f', 3)); + if (m_editOffsetY) m_editOffsetY->setText(QString::number(offsetY, 'f', 3)); + if (m_editOffsetZ) m_editOffsetZ->setText(QString::number(offsetZ, 'f', 3)); +} + +void ToolExtrinsicWidget::getData(int& eulerOrder, + double& rotX, double& rotY, double& rotZ, + double& offsetX, double& offsetY, double& offsetZ) const +{ + eulerOrder = m_comboEulerOrder ? m_comboEulerOrder->currentData().toInt() : 11; + rotX = m_editRotX ? m_editRotX->text().trimmed().toDouble() : 0.0; + rotY = m_editRotY ? m_editRotY->text().trimmed().toDouble() : 0.0; + rotZ = m_editRotZ ? m_editRotZ->text().trimmed().toDouble() : 0.0; + offsetX = m_editOffsetX ? m_editOffsetX->text().trimmed().toDouble() : 0.0; + offsetY = m_editOffsetY ? m_editOffsetY->text().trimmed().toDouble() : 0.0; + offsetZ = m_editOffsetZ ? m_editOffsetZ->text().trimmed().toDouble() : 0.0; +} + +void ToolExtrinsicWidget::setTargetOffsetVisible(bool visible) +{ + if (m_labelTargetOffset) m_labelTargetOffset->setVisible(visible); + if (m_editOffsetX) m_editOffsetX->setVisible(visible); + if (m_editOffsetY) m_editOffsetY->setVisible(visible); + if (m_editOffsetZ) m_editOffsetZ->setVisible(visible); +} + +void ToolExtrinsicWidget::initEulerOrderComboBox() +{ + if (!m_comboEulerOrder) return; + m_comboEulerOrder->addItem(QString::fromUtf8("RZ-RY-RX (外旋ZYX)"), 11); + m_comboEulerOrder->addItem(QString::fromUtf8("RX-RY-RZ (外旋XYZ)"), 10); + m_comboEulerOrder->addItem(QString::fromUtf8("RZ-RX-RY (外旋ZXY)"), 12); + m_comboEulerOrder->addItem(QString::fromUtf8("RY-RX-RZ (外旋YXZ)"), 13); + m_comboEulerOrder->addItem(QString::fromUtf8("RY-RZ-RX (外旋YZX)"), 14); + m_comboEulerOrder->addItem(QString::fromUtf8("RX-RZ-RY (外旋XZY)"), 15); + m_comboEulerOrder->addItem(QString::fromUtf8("RZ-RY-RX (内旋ZYX)"), 1); + m_comboEulerOrder->addItem(QString::fromUtf8("RX-RY-RZ (内旋XYZ)"), 0); + m_comboEulerOrder->addItem(QString::fromUtf8("RZ-RX-RY (内旋ZXY)"), 2); + m_comboEulerOrder->addItem(QString::fromUtf8("RY-RX-RZ (内旋YXZ)"), 3); + m_comboEulerOrder->addItem(QString::fromUtf8("RY-RZ-RX (内旋YZX)"), 4); + m_comboEulerOrder->addItem(QString::fromUtf8("RX-RZ-RY (内旋XZY)"), 5); + m_comboEulerOrder->setCurrentIndex(0); +} + +void ToolExtrinsicWidget::setupUI() +{ + const QString labelStyle = "color: rgb(221, 225, 233);"; + const QString editStyle = + "QLineEdit { color: rgb(221, 225, 233); background-color: rgb(47, 48, 52); " + "border: 1px solid rgb(70, 72, 78); padding: 4px; }"; + const QString comboStyle = + "QComboBox { color: rgb(221, 225, 233); background-color: rgb(47, 48, 52); " + "border: 1px solid rgb(70, 72, 78); padding: 6px; }" + "QComboBox QAbstractItemView { color: rgb(221, 225, 233); background-color: rgb(47, 48, 52); " + "selection-background-color: rgb(70, 100, 150); }"; + const QString groupStyle = + "QGroupBox { color: rgb(221, 225, 233); border: 1px solid rgb(100, 100, 100); " + "border-radius: 4px; margin-top: 12px; padding-top: 8px; }" + "QGroupBox::title { subcontrol-origin: margin; left: 10px; padding: 0 3px 0 3px; }"; + + QFont font; + font.setPointSize(14); + + QVBoxLayout* mainLayout = new QVBoxLayout(this); + mainLayout->setContentsMargins(20, 10, 20, 10); + + m_groupExtrinsic = new QGroupBox(QString::fromUtf8("工具参数"), this); + m_groupExtrinsic->setFont(font); + m_groupExtrinsic->setStyleSheet(groupStyle); + + QFormLayout* form = new QFormLayout(m_groupExtrinsic); + form->setSpacing(8); + + m_comboEulerOrder = new QComboBox(this); + m_comboEulerOrder->setFont(font); + m_comboEulerOrder->setStyleSheet(comboStyle); + initEulerOrderComboBox(); + QLabel* labelEuler = new QLabel(QString::fromUtf8("欧拉角旋转顺序:"), this); + labelEuler->setFont(font); + labelEuler->setStyleSheet(labelStyle); + form->addRow(labelEuler, m_comboEulerOrder); + + auto* validatorX = new QDoubleValidator(-360.0, 360.0, 6, this); + validatorX->setNotation(QDoubleValidator::StandardNotation); + auto* validatorY = new QDoubleValidator(-360.0, 360.0, 6, this); + validatorY->setNotation(QDoubleValidator::StandardNotation); + auto* validatorZ = new QDoubleValidator(-360.0, 360.0, 6, this); + validatorZ->setNotation(QDoubleValidator::StandardNotation); + + m_editRotX = new QLineEdit(this); + m_editRotX->setFont(font); + m_editRotX->setStyleSheet(editStyle); + m_editRotX->setValidator(validatorX); + + m_editRotY = new QLineEdit(this); + m_editRotY->setFont(font); + m_editRotY->setStyleSheet(editStyle); + m_editRotY->setValidator(validatorY); + + m_editRotZ = new QLineEdit(this); + m_editRotZ->setFont(font); + m_editRotZ->setStyleSheet(editStyle); + m_editRotZ->setValidator(validatorZ); + + auto makeAxisLabel = [&](const QString& text) { + QLabel* label = new QLabel(text, this); + label->setFont(font); + label->setStyleSheet(labelStyle); + return label; + }; + + QWidget* rotationRow = new QWidget(this); + QHBoxLayout* rotationLayout = new QHBoxLayout(rotationRow); + rotationLayout->setContentsMargins(0, 0, 0, 0); + rotationLayout->setSpacing(8); + rotationLayout->addWidget(makeAxisLabel(QString::fromUtf8("X:"))); + rotationLayout->addWidget(m_editRotX, 1); + rotationLayout->addWidget(makeAxisLabel(QString::fromUtf8("Y:"))); + rotationLayout->addWidget(m_editRotY, 1); + rotationLayout->addWidget(makeAxisLabel(QString::fromUtf8("Z:"))); + rotationLayout->addWidget(m_editRotZ, 1); + + QLabel* labelRotation = new QLabel(QString::fromUtf8("姿态调整 (°):"), this); + labelRotation->setFont(font); + labelRotation->setStyleSheet(labelStyle); + form->addRow(labelRotation, rotationRow); + + auto* validatorTargetOffset = new QDoubleValidator(-10000.0, 10000.0, 3, this); + validatorTargetOffset->setNotation(QDoubleValidator::StandardNotation); + + m_editOffsetX = new QLineEdit(this); + m_editOffsetX->setFont(font); + m_editOffsetX->setStyleSheet(editStyle); + m_editOffsetX->setValidator(validatorTargetOffset); + + m_editOffsetY = new QLineEdit(this); + m_editOffsetY->setFont(font); + m_editOffsetY->setStyleSheet(editStyle); + m_editOffsetY->setValidator(validatorTargetOffset); + + m_editOffsetZ = new QLineEdit(this); + m_editOffsetZ->setFont(font); + m_editOffsetZ->setStyleSheet(editStyle); + m_editOffsetZ->setValidator(validatorTargetOffset); + + QWidget* targetOffsetRow = new QWidget(this); + QHBoxLayout* targetOffsetLayout = new QHBoxLayout(targetOffsetRow); + targetOffsetLayout->setContentsMargins(0, 0, 0, 0); + targetOffsetLayout->setSpacing(8); + targetOffsetLayout->addWidget(makeAxisLabel(QString::fromUtf8("X:"))); + targetOffsetLayout->addWidget(m_editOffsetX, 1); + targetOffsetLayout->addWidget(makeAxisLabel(QString::fromUtf8("Y:"))); + targetOffsetLayout->addWidget(m_editOffsetY, 1); + targetOffsetLayout->addWidget(makeAxisLabel(QString::fromUtf8("Z:"))); + targetOffsetLayout->addWidget(m_editOffsetZ, 1); + + m_labelTargetOffset = new QLabel(QString::fromUtf8("目标点偏移 (mm):"), this); + m_labelTargetOffset->setFont(font); + m_labelTargetOffset->setStyleSheet(labelStyle); + form->addRow(m_labelTargetOffset, targetOffsetRow); + + m_labelTargetOffset->setVisible(false); + m_editOffsetX->setVisible(false); + m_editOffsetY->setVisible(false); + m_editOffsetZ->setVisible(false); + + mainLayout->addWidget(m_groupExtrinsic); + + setData(11, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0); +} diff --git a/AppUtils/UICommon/UICommon.pro b/AppUtils/UICommon/UICommon.pro index c90bb1de..c7c8e05a 100644 --- a/AppUtils/UICommon/UICommon.pro +++ b/AppUtils/UICommon/UICommon.pro @@ -37,7 +37,8 @@ HEADERS += \ Inc/XyzRpyItem.h \ Inc/ResultListLayoutHelper.h \ Inc/AboutDialog.h \ - Inc/NetworkConfigWidget.h + Inc/NetworkConfigWidget.h \ + Inc/ToolExtrinsicWidget.h SOURCES += \ Src/StyledMessageBox.cpp \ @@ -53,7 +54,8 @@ SOURCES += \ Src/XyzRpyItem.cpp \ Src/ResultListLayoutHelper.cpp \ Src/AboutDialog.cpp \ - Src/NetworkConfigWidget.cpp + Src/NetworkConfigWidget.cpp \ + Src/ToolExtrinsicWidget.cpp FORMS += \ DeviceStatusWidget.ui \