GrabBag/App/GrabBag/GrabBagApp/dialogconfigtree.cpp

1065 lines
42 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 "dialogconfigtree.h"
#include "ui_dialogconfigtree.h"
#include "dialogcameralevel.h"
#include "GrabBagPresenter.h"
#include "HandEyeCalibMatrixLoader.h"
#include "StyledMessageBox.h"
#include <QInputDialog>
#include <QFileDialog>
#include <QFont>
#include <QColor>
#include <QPalette>
#include <QComboBox>
enum TreeItemType {
TYPE_ROOT = 0,
TYPE_WORKPOS = 1,
TYPE_CAMERA = 2,
TYPE_PACKAGE = 3
};
DialogConfigTree::DialogConfigTree(ConfigResult* configResult,
const std::vector<std::pair<std::string, IVrEyeDevice*>>& deviceList,
GrabBagPresenter* presenter,
QWidget *parent) :
QDialog(parent),
ui(new Ui::DialogConfigTree),
m_configResult(configResult),
m_deviceList(deviceList),
m_presenter(presenter)
{
ui->setupUi(this);
// setWindowFlags(Qt::FramelessWindowHint);
// 设置树形控件支持取消选中
ui->treeWidget->setSelectionMode(QAbstractItemView::SingleSelection);
// 设置选中项的背景颜色(浅蓝色)
QPalette palette = ui->treeWidget->palette();
palette.setColor(QPalette::Highlight, QColor(135, 206, 235)); // 浅蓝色
palette.setColor(QPalette::HighlightedText, Qt::black);
ui->treeWidget->setPalette(palette);
connect(ui->treeWidget, &QTreeWidget::itemClicked, this, &DialogConfigTree::onTreeItemClicked);
connect(ui->btnAdd, &QPushButton::clicked, this, &DialogConfigTree::onAddClicked);
connect(ui->btnDelete, &QPushButton::clicked, this, &DialogConfigTree::onDeleteClicked);
connect(ui->btnSetDefault, &QPushButton::clicked, this, &DialogConfigTree::onSetDefaultClicked);
connect(ui->btnSave, &QPushButton::clicked, this, &DialogConfigTree::onSaveClicked);
connect(ui->btnCancel, &QPushButton::clicked, this, &DialogConfigTree::onCancelClicked);
// 连接相机页面的按钮
connect(ui->btnDoLevelCalib, &QPushButton::clicked, this, &DialogConfigTree::onCameraLevelClicked);
connect(ui->btnHeSave, &QPushButton::clicked, this, &DialogConfigTree::onHandEyeCalibSaveClicked);
connect(ui->btnHeLoadFromFile, &QPushButton::clicked, this, &DialogConfigTree::onHandEyeCalibLoadClicked);
// 连接工位配置按钮
connect(ui->btnUpdateWpName, &QPushButton::clicked, this, &DialogConfigTree::onUpdateWpNameClicked);
// 连接包裹配置按钮
connect(ui->btnSavePkgParam, &QPushButton::clicked, this, &DialogConfigTree::onSavePkgParamClicked);
initTree();
}
DialogConfigTree::~DialogConfigTree()
{
delete ui;
}
void DialogConfigTree::initTree()
{
ui->treeWidget->clear();
// 使用 ConfigResult 中的默认ID
std::string defaultWpId = m_configResult->defaultWorkPositionId;
std::string defaultCamId = m_configResult->defaultCameraId;
std::string defaultPkgId = m_configResult->defaultPackageId;
// 构建三级树:工位 -> 相机 -> 包裹
for (auto& wp : m_configResult->workPositions) {
QTreeWidgetItem* wpItem = new QTreeWidgetItem(ui->treeWidget);
QString wpName = QString::fromStdString(wp.name);
// 检查该工位是否是默认工位
if (wp.id == defaultWpId) {
wpName += " [默认]";
}
wpItem->setText(0, wpName);
wpItem->setData(0, Qt::UserRole, TYPE_WORKPOS);
wpItem->setData(0, Qt::UserRole + 1, QString::fromStdString(wp.id));
// 相机节点
for (auto& cam : wp.cameras) {
QTreeWidgetItem* camItem = new QTreeWidgetItem(wpItem);
// 显示相机名称
QString camName = QString::fromStdString(cam.cameraName);
// 检查该相机是否是默认相机
if (cam.id == defaultCamId && wp.id == defaultWpId) {
camName += " [默认]";
}
camItem->setText(0, camName);
camItem->setData(0, Qt::UserRole, TYPE_CAMERA);
camItem->setData(0, Qt::UserRole + 1, QString::fromStdString(wp.id)); // 工位ID
camItem->setData(0, Qt::UserRole + 2, QString::fromStdString(cam.id)); // 相机ID
// 包裹节点
for (auto& pkg : cam.packages) {
QTreeWidgetItem* pkgItem = new QTreeWidgetItem(camItem);
// 从包裹ID中提取数字编号格式pkg_1 -> 1
QString pkgIdStr = QString::fromStdString(pkg.id);
QString pkgNumber = pkgIdStr;
if (pkgIdStr.startsWith("pkg_")) {
pkgNumber = pkgIdStr.mid(4); // 提取 "pkg_" 后面的数字
}
// 显示格式:包裹名称 (ID编号)
QString pkgName = QString("%1 (%2)")
.arg(QString::fromStdString(pkg.name))
.arg(pkgNumber);
if (pkg.id == defaultPkgId && cam.id == defaultCamId && wp.id == defaultWpId) {
pkgName += " [默认]";
}
pkgItem->setText(0, pkgName);
pkgItem->setData(0, Qt::UserRole, TYPE_PACKAGE);
pkgItem->setData(0, Qt::UserRole + 1, QString::fromStdString(wp.id)); // 工位ID
pkgItem->setData(0, Qt::UserRole + 2, QString::fromStdString(cam.id)); // 相机ID
pkgItem->setData(0, Qt::UserRole + 3, QString::fromStdString(pkg.id)); // 包裹ID
}
}
}
ui->treeWidget->expandAll();
// 默认不选中任何项
ui->treeWidget->clearSelection();
m_currentItem = nullptr;
updateConfigPanel(nullptr);
}
void DialogConfigTree::onTreeItemClicked(QTreeWidgetItem* item, int column)
{
Q_UNUSED(column);
// 检查是否点击的是当前已选中的项,如果是则取消选中
if (m_currentItem == item) {
// 再次点击同一项,取消选中
ui->treeWidget->clearSelection();
m_currentItem = nullptr;
updateConfigPanel(nullptr);
} else {
// 点击了不同的项,保存当前配置并选中新项
saveCurrentConfig();
m_currentItem = item;
// 确保项被选中并显示背景颜色
ui->treeWidget->setCurrentItem(item);
updateConfigPanel(item);
}
}
void DialogConfigTree::updateConfigPanel(QTreeWidgetItem* item)
{
if (!item) {
ui->stackedWidget->setCurrentWidget(ui->emptyPage);
m_currentWorkPos = nullptr;
m_currentCamera = nullptr;
m_currentPackage = nullptr;
return;
}
int type = item->data(0, Qt::UserRole).toInt();
if (type == TYPE_WORKPOS) {
QString wpId = item->data(0, Qt::UserRole + 1).toString();
for (auto& wp : m_configResult->workPositions) {
if (wp.id == wpId.toStdString()) {
showWorkPositionConfig(&wp);
m_currentWorkPos = &wp;
m_currentCamera = nullptr;
m_currentPackage = nullptr;
break;
}
}
} else if (type == TYPE_CAMERA) {
QString wpId = item->data(0, Qt::UserRole + 1).toString();
QString camId = item->data(0, Qt::UserRole + 2).toString();
for (auto& wp : m_configResult->workPositions) {
if (wp.id == wpId.toStdString()) {
CameraConfig* cam = wp.GetCamera(camId.toStdString());
if (cam) {
// 确保相机参数已初始化
if (!cam->planeCalibParam.cameraIndex) {
cam->planeCalibParam.cameraIndex = cam->cameraIndex;
cam->planeCalibParam.cameraName = cam->cameraName;
cam->planeCalibParam.isCalibrated = false;
for (int i = 0; i < 9; i++) {
cam->planeCalibParam.planeCalib[i] = (i % 4 == 0) ? 1.0 : 0.0;
cam->planeCalibParam.invRMatrix[i] = (i % 4 == 0) ? 1.0 : 0.0;
}
}
if (!cam->handEyeCalibParam.cameraIndex) {
cam->handEyeCalibParam.cameraIndex = cam->cameraIndex;
cam->handEyeCalibParam.cameraName = cam->cameraName;
cam->handEyeCalibParam.isCalibrated = false;
for (int i = 0; i < 16; i++) {
cam->handEyeCalibParam.transformMatrix[i] = (i % 5 == 0) ? 1.0 : 0.0;
}
}
showCameraConfig(&wp, cam);
m_currentWorkPos = &wp;
m_currentCamera = cam;
m_currentPackage = nullptr;
}
break;
}
}
} else if (type == TYPE_PACKAGE) {
QString wpId = item->data(0, Qt::UserRole + 1).toString();
QString camId = item->data(0, Qt::UserRole + 2).toString();
QString pkgId = item->data(0, Qt::UserRole + 3).toString();
for (auto& wp : m_configResult->workPositions) {
if (wp.id == wpId.toStdString()) {
CameraConfig* cam = wp.GetCamera(camId.toStdString());
if (cam) {
PackageTypeConfig* pkg = cam->GetPackage(pkgId.toStdString());
if (pkg) {
showPackageConfig(&wp, cam, pkg);
m_currentWorkPos = &wp;
m_currentCamera = cam;
m_currentPackage = pkg;
}
}
break;
}
}
} else {
ui->stackedWidget->setCurrentWidget(ui->emptyPage);
m_currentWorkPos = nullptr;
m_currentCamera = nullptr;
m_currentPackage = nullptr;
}
}
void DialogConfigTree::showWorkPositionConfig(const WorkPositionConfig* config)
{
ui->editWpName->setText(QString::fromStdString(config->name));
ui->stackedWidget->setCurrentWidget(ui->workPosPage);
}
void DialogConfigTree::showCameraConfig(const WorkPositionConfig* wpConfig, const CameraConfig* camConfig)
{
Q_UNUSED(wpConfig);
// 显示相机名称
ui->editCameraName->setText(QString::fromStdString(camConfig->cameraName));
// 从ConfigResult的cameraList中查找相机IP地址
QString cameraIP = "未配置";
if (m_configResult) {
for (const auto& camera : m_configResult->cameraList) {
if (camera.name == camConfig->cameraName) {
cameraIP = QString::fromStdString(camera.ip);
break;
}
}
}
// 显示相机IP
ui->editCameraIP->setText(cameraIP);
// 显示调平状态
if (camConfig->planeCalibParam.isCalibrated) {
ui->labelLevelStatus->setText("标定状态: 已标定");
} else {
ui->labelLevelStatus->setText("标定状态: 未标定");
}
// 显示手眼标定状态
if (camConfig->handEyeCalibParam.isCalibrated) {
ui->labelHandEyeStatus->setText("标定状态: 已标定");
} else {
ui->labelHandEyeStatus->setText("标定状态: 未标定");
}
// 显示手眼标定矩阵
const double* matrix = camConfig->handEyeCalibParam.transformMatrix;
ui->edit_he_m00->setText(QString::number(matrix[0], 'f', 6));
ui->edit_he_m01->setText(QString::number(matrix[1], 'f', 6));
ui->edit_he_m02->setText(QString::number(matrix[2], 'f', 6));
ui->edit_he_m03->setText(QString::number(matrix[3], 'f', 6));
ui->edit_he_m10->setText(QString::number(matrix[4], 'f', 6));
ui->edit_he_m11->setText(QString::number(matrix[5], 'f', 6));
ui->edit_he_m12->setText(QString::number(matrix[6], 'f', 6));
ui->edit_he_m13->setText(QString::number(matrix[7], 'f', 6));
ui->edit_he_m20->setText(QString::number(matrix[8], 'f', 6));
ui->edit_he_m21->setText(QString::number(matrix[9], 'f', 6));
ui->edit_he_m22->setText(QString::number(matrix[10], 'f', 6));
ui->edit_he_m23->setText(QString::number(matrix[11], 'f', 6));
ui->edit_he_m30->setText(QString::number(matrix[12], 'f', 6));
ui->edit_he_m31->setText(QString::number(matrix[13], 'f', 6));
ui->edit_he_m32->setText(QString::number(matrix[14], 'f', 6));
ui->edit_he_m33->setText(QString::number(matrix[15], 'f', 6));
ui->stackedWidget->setCurrentWidget(ui->cameraPage);
}
void DialogConfigTree::showPackageConfig(const WorkPositionConfig* wpConfig,
const CameraConfig* camConfig,
const PackageTypeConfig* pkgConfig)
{
Q_UNUSED(wpConfig);
// 包裹名称
ui->editPkgName->setText(QString::fromStdString(pkgConfig->name));
// 包裹尺寸参数 - 使用ConfigResult中的默认值
if (pkgConfig->bagParam.bagL == 0.0 && pkgConfig->bagParam.bagW == 0.0 && pkgConfig->bagParam.bagH == 0.0) {
// 未初始化使用ConfigResult中的默认值
ui->spinBagL->setValue(m_configResult->algorithmParams.bagParam.bagL);
ui->spinBagW->setValue(m_configResult->algorithmParams.bagParam.bagW);
ui->spinBagH->setValue(m_configResult->algorithmParams.bagParam.bagH);
} else {
ui->spinBagL->setValue(pkgConfig->bagParam.bagL);
ui->spinBagW->setValue(pkgConfig->bagParam.bagW);
ui->spinBagH->setValue(pkgConfig->bagParam.bagH);
}
// 垛尺寸参数 - 使用ConfigResult中的默认值
if (pkgConfig->pileParam.pileL == 0.0 && pkgConfig->pileParam.pileW == 0.0 && pkgConfig->pileParam.pileH == 0.0) {
// 未初始化使用ConfigResult中的默认值
ui->spinPileL->setValue(m_configResult->algorithmParams.pileParam.pileL);
ui->spinPileW->setValue(m_configResult->algorithmParams.pileParam.pileW);
ui->spinPileH->setValue(m_configResult->algorithmParams.pileParam.pileH);
} else {
ui->spinPileL->setValue(pkgConfig->pileParam.pileL);
ui->spinPileW->setValue(pkgConfig->pileParam.pileW);
ui->spinPileH->setValue(pkgConfig->pileParam.pileH);
}
// 相机参数(如果存在)
const VrCameraParams* camParams = pkgConfig->GetCameraParam(camConfig->cameraIndex);
if (camParams) {
ui->spinExposure->setValue(camParams->exposure);
ui->spinGain->setValue(camParams->gain);
ui->spinFrameRate->setValue(camParams->frameRate);
ui->spinSwingSpeed->setValue(camParams->swingSpeed);
ui->spinSwingStartAngle->setValue(camParams->swingStartAngle);
ui->spinSwingStopAngle->setValue(camParams->swingStopAngle);
} else {
// 使用VrCameraParams结构的默认值
VrCameraParams defaultParams;
ui->spinExposure->setValue(defaultParams.exposure);
ui->spinGain->setValue(defaultParams.gain);
ui->spinFrameRate->setValue(defaultParams.frameRate);
ui->spinSwingSpeed->setValue(defaultParams.swingSpeed);
ui->spinSwingStartAngle->setValue(defaultParams.swingStartAngle);
ui->spinSwingStopAngle->setValue(defaultParams.swingStopAngle);
}
ui->stackedWidget->setCurrentWidget(ui->packagePage);
}
void DialogConfigTree::saveCurrentConfig()
{
if (!m_currentItem) return;
int type = m_currentItem->data(0, Qt::UserRole).toInt();
// 验证指针有效性 - 检查指针是否仍然指向有效的对象
bool isWorkPosValid = false;
bool isCameraValid = false;
bool isPackageValid = false;
if (type == TYPE_WORKPOS && m_currentWorkPos) {
for (const auto& wp : m_configResult->workPositions) {
if (&wp == m_currentWorkPos) {
isWorkPosValid = true;
break;
}
}
if (isWorkPosValid) {
saveWorkPositionConfig(m_currentWorkPos);
}
} else if (type == TYPE_CAMERA && m_currentWorkPos && m_currentCamera) {
// 验证工位有效性
for (const auto& wp : m_configResult->workPositions) {
if (&wp == m_currentWorkPos) {
isWorkPosValid = true;
// 再验证相机有效性
for (const auto& cam : wp.cameras) {
if (&cam == m_currentCamera) {
isCameraValid = true;
break;
}
}
break;
}
}
if (isCameraValid) {
saveCameraConfig(m_currentWorkPos, m_currentCamera);
}
} else if (type == TYPE_PACKAGE && m_currentWorkPos && m_currentCamera && m_currentPackage) {
// 验证工位、相机、包裹有效性
for (const auto& wp : m_configResult->workPositions) {
if (&wp == m_currentWorkPos) {
isWorkPosValid = true;
for (const auto& cam : wp.cameras) {
if (&cam == m_currentCamera) {
isCameraValid = true;
for (const auto& pkg : cam.packages) {
if (&pkg == m_currentPackage) {
isPackageValid = true;
break;
}
}
break;
}
}
break;
}
}
if (isPackageValid) {
savePackageConfig(m_currentWorkPos, m_currentCamera, m_currentPackage);
}
}
}
void DialogConfigTree::saveWorkPositionConfig(WorkPositionConfig* config)
{
config->name = ui->editWpName->text().toStdString();
}
void DialogConfigTree::saveCameraConfig(WorkPositionConfig* wpConfig, CameraConfig* camConfig)
{
Q_UNUSED(wpConfig);
Q_UNUSED(camConfig);
// 相机级别的配置主要通过按钮(调平、手眼标定)进行,这里不需要保存
}
void DialogConfigTree::savePackageConfig(WorkPositionConfig* wpConfig,
CameraConfig* camConfig,
PackageTypeConfig* pkgConfig)
{
Q_UNUSED(wpConfig);
// 保存包裹名称
pkgConfig->name = ui->editPkgName->text().toStdString();
// 保存包裹尺寸参数
pkgConfig->bagParam.bagL = ui->spinBagL->value();
pkgConfig->bagParam.bagW = ui->spinBagW->value();
pkgConfig->bagParam.bagH = ui->spinBagH->value();
// 保存垛尺寸参数
pkgConfig->pileParam.pileL = ui->spinPileL->value();
pkgConfig->pileParam.pileW = ui->spinPileW->value();
pkgConfig->pileParam.pileH = ui->spinPileH->value();
// 保存相机参数
VrCameraParams camParams;
camParams.cameraIndex = camConfig->cameraIndex;
camParams.exposure = ui->spinExposure->value();
camParams.gain = ui->spinGain->value();
camParams.frameRate = ui->spinFrameRate->value();
camParams.swingSpeed = ui->spinSwingSpeed->value();
camParams.swingStartAngle = ui->spinSwingStartAngle->value();
camParams.swingStopAngle = ui->spinSwingStopAngle->value();
pkgConfig->SetCameraParam(camParams);
}
void DialogConfigTree::onAddClicked()
{
// 使用 m_currentItem 而不是 currentItem(),因为 m_currentItem 是我们跟踪的有效选择
QTreeWidgetItem* item = m_currentItem;
if (!item) {
// 没有选中项,添加工位
QInputDialog dialog(this);
dialog.setInputMode(QInputDialog::TextInput);
dialog.setWindowTitle("添加工位");
dialog.setLabelText("工位名称:");
// 设置对话框和所有子控件的字体
QFont font("", 16);
dialog.setFont(font);
// 设置样式表确保字体生效
dialog.setStyleSheet("QLabel { font-size: 16pt; } "
"QLineEdit { font-size: 16pt; } "
"QPushButton { font-size: 16pt; }");
if (dialog.exec() == QDialog::Accepted) {
QString name = dialog.textValue();
if (!name.isEmpty()) {
addWorkPosition(name);
}
}
return;
}
int type = item->data(0, Qt::UserRole).toInt();
if (type == TYPE_WORKPOS) {
// 选中工位,添加相机
QString wpId = item->data(0, Qt::UserRole + 1).toString();
// 检查是否有已配置的相机从ConfigResult的cameraList读取
if (!m_configResult || m_configResult->cameraList.empty()) {
StyledMessageBox::warning(this, "提示",
"尚未配置相机IP\n请先在【相机配置】中添加相机IP地址。");
return;
}
// 构建相机名称列表从ConfigResult的cameraList中
QStringList cameraNames;
for (const auto& camera : m_configResult->cameraList) {
// 显示格式:相机名称 (IP地址)
QString displayName = QString("%1 (%2)")
.arg(QString::fromStdString(camera.name))
.arg(QString::fromStdString(camera.ip));
cameraNames << displayName;
}
// 使用下拉框选择相机
QInputDialog dialog(this);
dialog.setWindowTitle("添加相机");
dialog.setLabelText("请选择相机:");
dialog.setComboBoxItems(cameraNames);
dialog.setComboBoxEditable(false);
// 设置对话框和所有子控件的字体
QFont font("", 16);
dialog.setFont(font);
// 设置样式表确保字体生效
dialog.setStyleSheet("QLabel { font-size: 16pt; } "
"QComboBox { font-size: 16pt; padding: 5px; } "
"QPushButton { font-size: 16pt; }");
// 查找ComboBox控件并设置其下拉列表的显示属性
QComboBox* comboBox = dialog.findChild<QComboBox*>();
if (comboBox) {
// 设置下拉列表的样式,增加行高和间距
comboBox->setStyleSheet(
"QComboBox { font-size: 16pt; padding: 5px; } "
"QComboBox QAbstractItemView { font-size: 16pt; } "
"QComboBox QAbstractItemView::item { height: 45px; padding: 10px; }"
);
// 设置下拉列表最大可见项数
comboBox->setMaxVisibleItems(10);
}
if (dialog.exec() == QDialog::Accepted) {
QString selectedCamera = dialog.textValue();
if (!selectedCamera.isEmpty()) {
// 获取用户选择的索引在cameraNames列表中的位置
int selectedIndex = cameraNames.indexOf(selectedCamera);
if (selectedIndex >= 0) {
// 从显示格式中提取相机名称
// 格式: "相机1 (192.168.1.100)"
int bracketIndex = selectedCamera.indexOf(" (");
QString cameraName = (bracketIndex > 0) ? selectedCamera.left(bracketIndex).trimmed() : selectedCamera;
// 传递相机名称和索引(索引+1就是cameraIndex
addCamera(wpId, cameraName, selectedIndex + 1);
}
}
}
} else if (type == TYPE_CAMERA) {
// 选中相机,添加包裹
QString wpId = item->data(0, Qt::UserRole + 1).toString();
QString camId = item->data(0, Qt::UserRole + 2).toString();
QInputDialog dialog(this);
dialog.setInputMode(QInputDialog::TextInput);
dialog.setWindowTitle("添加包裹");
dialog.setLabelText("包裹名称:");
// 设置对话框和所有子控件的字体
QFont font("", 16);
dialog.setFont(font);
// 设置样式表确保字体生效
dialog.setStyleSheet("QLabel { font-size: 16pt; } "
"QLineEdit { font-size: 16pt; } "
"QPushButton { font-size: 16pt; }");
if (dialog.exec() == QDialog::Accepted) {
QString name = dialog.textValue();
if (!name.isEmpty()) {
addPackage(wpId, camId, name);
}
}
} else if (type == TYPE_PACKAGE) {
// 选中包裹,在其父相机下添加包裹
QTreeWidgetItem* camItem = item->parent();
if (camItem) {
QString wpId = camItem->data(0, Qt::UserRole + 1).toString();
QString camId = camItem->data(0, Qt::UserRole + 2).toString();
QInputDialog dialog(this);
dialog.setInputMode(QInputDialog::TextInput);
dialog.setWindowTitle("添加包裹");
dialog.setLabelText("包裹名称:");
// 设置对话框和所有子控件的字体
QFont font("", 16);
dialog.setFont(font);
// 设置样式表确保字体生效
dialog.setStyleSheet("QLabel { font-size: 16pt; } "
"QLineEdit { font-size: 16pt; } "
"QPushButton { font-size: 16pt; }");
if (dialog.exec() == QDialog::Accepted) {
QString name = dialog.textValue();
if (!name.isEmpty()) {
addPackage(wpId, camId, name);
}
}
}
}
}
void DialogConfigTree::addWorkPosition(const QString& name)
{
WorkPositionConfig wp;
wp.id = "wp_" + std::to_string(m_configResult->workPositions.size() + 1);
wp.name = name.toStdString();
m_configResult->workPositions.push_back(wp);
initTree();
}
void DialogConfigTree::addCamera(const QString& wpId, const QString& name, int cameraIndex)
{
for (auto& wp : m_configResult->workPositions) {
if (wp.id == wpId.toStdString()) {
// 检查该工位下是否已存在同名相机
std::string cameraName = name.toStdString();
for (const auto& existingCam : wp.cameras) {
if (existingCam.cameraName == cameraName) {
StyledMessageBox::warning(this, "提示",
QString("该工位下已存在相机【%1】不能重复添加").arg(name));
return;
}
}
CameraConfig cam;
cam.id = "cam_" + std::to_string(wp.cameras.size() + 1);
cam.cameraName = cameraName;
cam.cameraIndex = cameraIndex; // 直接使用传入的cameraIndex选择的下标+1
// 初始化调平参数
cam.planeCalibParam.cameraIndex = cam.cameraIndex;
cam.planeCalibParam.cameraName = cam.cameraName;
cam.planeCalibParam.isCalibrated = false;
cam.planeCalibParam.planeHeight = 0.0;
// 初始化为单位矩阵
for (int i = 0; i < 9; i++) {
cam.planeCalibParam.planeCalib[i] = (i % 4 == 0) ? 1.0 : 0.0;
cam.planeCalibParam.invRMatrix[i] = (i % 4 == 0) ? 1.0 : 0.0;
}
// 初始化手眼标定参数
cam.handEyeCalibParam.cameraIndex = cam.cameraIndex;
cam.handEyeCalibParam.cameraName = cam.cameraName;
cam.handEyeCalibParam.isCalibrated = false;
// 初始化为单位矩阵
for (int i = 0; i < 16; i++) {
cam.handEyeCalibParam.transformMatrix[i] = (i % 5 == 0) ? 1.0 : 0.0;
}
wp.cameras.push_back(cam);
initTree();
break;
}
}
}
void DialogConfigTree::addPackage(const QString& wpId, const QString& camId, const QString& name)
{
// 计算全局包裹ID遍历所有工位、所有相机下的所有包裹
int maxPkgId = 0;
for (const auto& wp : m_configResult->workPositions) {
for (const auto& cam : wp.cameras) {
for (const auto& pkg : cam.packages) {
// 从包裹ID中提取数字部分格式pkg_1, pkg_2, ...
std::string pkgIdStr = pkg.id;
if (pkgIdStr.find("pkg_") == 0) {
int pkgNum = std::stoi(pkgIdStr.substr(4));
if (pkgNum > maxPkgId) {
maxPkgId = pkgNum;
}
}
}
}
}
for (auto& wp : m_configResult->workPositions) {
if (wp.id == wpId.toStdString()) {
CameraConfig* cam = wp.GetCamera(camId.toStdString());
if (cam) {
PackageTypeConfig pkg;
// 使用全局唯一的包裹ID
pkg.id = "pkg_" + std::to_string(maxPkgId + 1);
pkg.name = name.toStdString();
// 使用ConfigResult中的默认值初始化包裹参数
pkg.bagParam = m_configResult->algorithmParams.bagParam;
pkg.pileParam = m_configResult->algorithmParams.pileParam;
// 使用VrCameraParams结构的默认值初始化相机参数
VrCameraParams camParams;
camParams.cameraIndex = cam->cameraIndex;
// 其他参数已通过结构的默认值初始化
pkg.cameraParams.push_back(camParams);
cam->packages.push_back(pkg);
initTree();
}
break;
}
}
}
void DialogConfigTree::onDeleteClicked()
{
QTreeWidgetItem* item = ui->treeWidget->currentItem();
if (!item) return;
int type = item->data(0, Qt::UserRole).toInt();
if (type == TYPE_WORKPOS) {
QString wpId = item->data(0, Qt::UserRole + 1).toString();
auto& wps = m_configResult->workPositions;
wps.erase(std::remove_if(wps.begin(), wps.end(),
[&wpId](const WorkPositionConfig& wp) { return wp.id == wpId.toStdString(); }), wps.end());
initTree();
} else if (type == TYPE_CAMERA) {
QString wpId = item->data(0, Qt::UserRole + 1).toString();
QString camId = item->data(0, Qt::UserRole + 2).toString();
for (auto& wp : m_configResult->workPositions) {
if (wp.id == wpId.toStdString()) {
auto& cams = wp.cameras;
cams.erase(std::remove_if(cams.begin(), cams.end(),
[&camId](const CameraConfig& cam) { return cam.id == camId.toStdString(); }), cams.end());
initTree();
break;
}
}
} else if (type == TYPE_PACKAGE) {
QString wpId = item->data(0, Qt::UserRole + 1).toString();
QString camId = item->data(0, Qt::UserRole + 2).toString();
QString pkgId = item->data(0, Qt::UserRole + 3).toString();
for (auto& wp : m_configResult->workPositions) {
if (wp.id == wpId.toStdString()) {
CameraConfig* cam = wp.GetCamera(camId.toStdString());
if (cam) {
auto& pkgs = cam->packages;
pkgs.erase(std::remove_if(pkgs.begin(), pkgs.end(),
[&pkgId](const PackageTypeConfig& pkg) { return pkg.id == pkgId.toStdString(); }), pkgs.end());
initTree();
}
break;
}
}
}
}
void DialogConfigTree::onSetDefaultClicked()
{
QTreeWidgetItem* item = ui->treeWidget->currentItem();
if (!item) return;
int type = item->data(0, Qt::UserRole).toInt();
if (type == TYPE_WORKPOS) {
QString wpId = item->data(0, Qt::UserRole + 1).toString();
m_configResult->defaultWorkPositionId = wpId.toStdString();
initTree();
saveConfigToFile();
StyledMessageBox::information(this, "成功", "默认工位已设置");
} else if (type == TYPE_CAMERA) {
QString wpId = item->data(0, Qt::UserRole + 1).toString();
QString camId = item->data(0, Qt::UserRole + 2).toString();
m_configResult->defaultWorkPositionId = wpId.toStdString();
m_configResult->defaultCameraId = camId.toStdString();
initTree();
saveConfigToFile();
StyledMessageBox::information(this, "成功", "默认工位和相机已设置");
} else if (type == TYPE_PACKAGE) {
QString wpId = item->data(0, Qt::UserRole + 1).toString();
QString camId = item->data(0, Qt::UserRole + 2).toString();
QString pkgId = item->data(0, Qt::UserRole + 3).toString();
m_configResult->defaultWorkPositionId = wpId.toStdString();
m_configResult->defaultCameraId = camId.toStdString();
m_configResult->defaultPackageId = pkgId.toStdString();
initTree();
saveConfigToFile();
StyledMessageBox::information(this, "成功", "默认工位、相机和包裹已设置");
}
}
void DialogConfigTree::onSaveClicked()
{
saveCurrentConfig();
accept();
}
void DialogConfigTree::onCancelClicked()
{
reject();
}
void DialogConfigTree::onHandEyeCalibSaveClicked()
{
if (!m_currentCamera) {
StyledMessageBox::warning(this, "错误", "请先选择相机");
return;
}
// 从UI读取矩阵
double matrix[16];
matrix[0] = ui->edit_he_m00->text().toDouble();
matrix[1] = ui->edit_he_m01->text().toDouble();
matrix[2] = ui->edit_he_m02->text().toDouble();
matrix[3] = ui->edit_he_m03->text().toDouble();
matrix[4] = ui->edit_he_m10->text().toDouble();
matrix[5] = ui->edit_he_m11->text().toDouble();
matrix[6] = ui->edit_he_m12->text().toDouble();
matrix[7] = ui->edit_he_m13->text().toDouble();
matrix[8] = ui->edit_he_m20->text().toDouble();
matrix[9] = ui->edit_he_m21->text().toDouble();
matrix[10] = ui->edit_he_m22->text().toDouble();
matrix[11] = ui->edit_he_m23->text().toDouble();
matrix[12] = ui->edit_he_m30->text().toDouble();
matrix[13] = ui->edit_he_m31->text().toDouble();
matrix[14] = ui->edit_he_m32->text().toDouble();
matrix[15] = ui->edit_he_m33->text().toDouble();
// 保存到相机配置
for (int i = 0; i < 16; i++) {
m_currentCamera->handEyeCalibParam.transformMatrix[i] = matrix[i];
}
m_currentCamera->handEyeCalibParam.isCalibrated = true;
m_currentCamera->handEyeCalibParam.cameraIndex = m_currentCamera->cameraIndex;
m_currentCamera->handEyeCalibParam.cameraName = m_currentCamera->cameraName;
StyledMessageBox::information(this, "成功", "手眼标定矩阵已保存");
}
void DialogConfigTree::onHandEyeCalibLoadClicked()
{
if (!m_currentCamera) {
StyledMessageBox::warning(this, "错误", "请先选择相机");
return;
}
QString fileName = QFileDialog::getOpenFileName(this,
"选择手眼标定矩阵文件",
"./config",
"INI Files (*.ini);;All Files (*)");
if (fileName.isEmpty()) {
return;
}
// 使用 HandEyeCalibMatrixLoader 加载矩阵
std::string filePath = fileName.toStdString();
HandEyeCalibMatrixInfo matrixInfo;
if (HandEyeCalibMatrixLoader::LoadMatrixByIndex(filePath, 0, matrixInfo)) {
// 显示加载的矩阵
ui->edit_he_m00->setText(QString::number(matrixInfo.calibMatrix[0], 'f', 6));
ui->edit_he_m01->setText(QString::number(matrixInfo.calibMatrix[1], 'f', 6));
ui->edit_he_m02->setText(QString::number(matrixInfo.calibMatrix[2], 'f', 6));
ui->edit_he_m03->setText(QString::number(matrixInfo.calibMatrix[3], 'f', 6));
ui->edit_he_m10->setText(QString::number(matrixInfo.calibMatrix[4], 'f', 6));
ui->edit_he_m11->setText(QString::number(matrixInfo.calibMatrix[5], 'f', 6));
ui->edit_he_m12->setText(QString::number(matrixInfo.calibMatrix[6], 'f', 6));
ui->edit_he_m13->setText(QString::number(matrixInfo.calibMatrix[7], 'f', 6));
ui->edit_he_m20->setText(QString::number(matrixInfo.calibMatrix[8], 'f', 6));
ui->edit_he_m21->setText(QString::number(matrixInfo.calibMatrix[9], 'f', 6));
ui->edit_he_m22->setText(QString::number(matrixInfo.calibMatrix[10], 'f', 6));
ui->edit_he_m23->setText(QString::number(matrixInfo.calibMatrix[11], 'f', 6));
ui->edit_he_m30->setText(QString::number(matrixInfo.calibMatrix[12], 'f', 6));
ui->edit_he_m31->setText(QString::number(matrixInfo.calibMatrix[13], 'f', 6));
ui->edit_he_m32->setText(QString::number(matrixInfo.calibMatrix[14], 'f', 6));
ui->edit_he_m33->setText(QString::number(matrixInfo.calibMatrix[15], 'f', 6));
StyledMessageBox::information(this, "成功", "矩阵加载成功!");
} else {
// 如果按索引加载失败,尝试加载所有矩阵并使用第一个
std::vector<HandEyeCalibMatrixInfo> matrices;
if (HandEyeCalibMatrixLoader::LoadFromIniFile(filePath, matrices) && !matrices.empty()) {
// 显示第一个矩阵
const double* matrix = matrices[0].calibMatrix;
ui->edit_he_m00->setText(QString::number(matrix[0], 'f', 6));
ui->edit_he_m01->setText(QString::number(matrix[1], 'f', 6));
ui->edit_he_m02->setText(QString::number(matrix[2], 'f', 6));
ui->edit_he_m03->setText(QString::number(matrix[3], 'f', 6));
ui->edit_he_m10->setText(QString::number(matrix[4], 'f', 6));
ui->edit_he_m11->setText(QString::number(matrix[5], 'f', 6));
ui->edit_he_m12->setText(QString::number(matrix[6], 'f', 6));
ui->edit_he_m13->setText(QString::number(matrix[7], 'f', 6));
ui->edit_he_m20->setText(QString::number(matrix[8], 'f', 6));
ui->edit_he_m21->setText(QString::number(matrix[9], 'f', 6));
ui->edit_he_m22->setText(QString::number(matrix[10], 'f', 6));
ui->edit_he_m23->setText(QString::number(matrix[11], 'f', 6));
ui->edit_he_m30->setText(QString::number(matrix[12], 'f', 6));
ui->edit_he_m31->setText(QString::number(matrix[13], 'f', 6));
ui->edit_he_m32->setText(QString::number(matrix[14], 'f', 6));
ui->edit_he_m33->setText(QString::number(matrix[15], 'f', 6));
StyledMessageBox::information(this, "成功",
QString("矩阵加载成功!共加载 %1 个标定矩阵,使用第一个。").arg(matrices.size()));
} else {
StyledMessageBox::warning(this, "错误", "矩阵加载失败,请检查文件格式!");
}
}
}
void DialogConfigTree::onCameraLevelClicked()
{
if (!m_currentCamera) {
StyledMessageBox::warning(this, "错误", "请先选择相机节点");
return;
}
if (!m_presenter) {
StyledMessageBox::warning(this, "错误", "Presenter未初始化");
return;
}
// 在对话框中执行调平(模态对话框)
DialogCameraLevel dlg(this);
dlg.setCameraList(m_deviceList, m_presenter);
dlg.setConfig(m_presenter->GetConfig(), m_configResult);
// 将对话框显示在父窗口DialogConfigTree的中间
QRect parentGeometry = this->geometry();
int x = parentGeometry.left() + (parentGeometry.width() - dlg.width()) / 2;
int y = parentGeometry.top() + (parentGeometry.height() - dlg.height()) / 2;
dlg.move(x, y);
dlg.exec();
// 对话框关闭后,重新加载配置文件
IVrConfig* vrConfig = m_presenter->GetConfig();
if (vrConfig) {
ConfigResult newConfig = vrConfig->LoadConfig(PathManager::GetInstance().GetConfigFilePath().toStdString());
*m_configResult = newConfig;
}
// 遍历所有工位的所有相机,刷新配置显示
for (auto& wp : m_configResult->workPositions) {
for (auto& cam : wp.cameras) {
showCameraConfig(&wp, &cam);
}
}
}
void DialogConfigTree::displayHandEyeCalibInline()
{
if (!m_currentCamera) {
return;
}
// 更新标定状态
if (m_currentCamera->handEyeCalibParam.isCalibrated) {
ui->labelHandEyeStatus->setText("标定状态: 已标定");
} else {
ui->labelHandEyeStatus->setText("标定状态: 未标定");
}
// 显示矩阵
const double* matrix = m_currentCamera->handEyeCalibParam.transformMatrix;
ui->edit_he_m00->setText(QString::number(matrix[0], 'f', 6));
ui->edit_he_m01->setText(QString::number(matrix[1], 'f', 6));
ui->edit_he_m02->setText(QString::number(matrix[2], 'f', 6));
ui->edit_he_m03->setText(QString::number(matrix[3], 'f', 6));
ui->edit_he_m10->setText(QString::number(matrix[4], 'f', 6));
ui->edit_he_m11->setText(QString::number(matrix[5], 'f', 6));
ui->edit_he_m12->setText(QString::number(matrix[6], 'f', 6));
ui->edit_he_m13->setText(QString::number(matrix[7], 'f', 6));
ui->edit_he_m20->setText(QString::number(matrix[8], 'f', 6));
ui->edit_he_m21->setText(QString::number(matrix[9], 'f', 6));
ui->edit_he_m22->setText(QString::number(matrix[10], 'f', 6));
ui->edit_he_m23->setText(QString::number(matrix[11], 'f', 6));
ui->edit_he_m30->setText(QString::number(matrix[12], 'f', 6));
ui->edit_he_m31->setText(QString::number(matrix[13], 'f', 6));
ui->edit_he_m32->setText(QString::number(matrix[14], 'f', 6));
ui->edit_he_m33->setText(QString::number(matrix[15], 'f', 6));
// 内容已直接显示在相机页面上,无需切换页面
}
void DialogConfigTree::onUpdateWpNameClicked()
{
if (!m_currentWorkPos) {
StyledMessageBox::warning(this, "错误", "请先选择工位");
return;
}
// 保存工位名称
saveWorkPositionConfig(m_currentWorkPos);
initTree();
// 重新选中当前工位
m_currentItem = nullptr;
updateConfigPanel(nullptr);
StyledMessageBox::information(this, "成功", "工位名称已更新");
}
void DialogConfigTree::onSavePkgParamClicked()
{
if (!m_currentWorkPos || !m_currentCamera || !m_currentPackage) {
StyledMessageBox::warning(this, "错误", "请先选择包裹");
return;
}
// 保存包裹配置
savePackageConfig(m_currentWorkPos, m_currentCamera, m_currentPackage);
initTree();
// 重新选中当前包裹
m_currentItem = nullptr;
updateConfigPanel(nullptr);
StyledMessageBox::information(this, "成功", "包裹参数已保存");
}
void DialogConfigTree::saveConfigToFile()
{
if (!m_presenter || !m_configResult) {
return;
}
// 获取配置接口并保存到文件
IVrConfig* vrConfig = m_presenter->GetConfig();
if (vrConfig) {
vrConfig->SaveConfig(PathManager::GetInstance().GetConfigFilePath().toStdString(), *m_configResult);
}
}