diff --git a/GrabBagApp/GrabBagApp.pro b/GrabBagApp/GrabBagApp.pro index 6548427..c6581f9 100644 --- a/GrabBagApp/GrabBagApp.pro +++ b/GrabBagApp/GrabBagApp.pro @@ -23,24 +23,30 @@ SOURCES += \ Presenter/Src/GrabBagPresenter.cpp \ Presenter/Src/LaserDataLoader.cpp \ Presenter/Src/RobotProtocol.cpp \ + devstatus.cpp \ dialogcamera.cpp \ dialogconfig.cpp \ main.cpp \ - mainwindow.cpp + mainwindow.cpp \ + resultitem.cpp HEADERS += \ Presenter/Inc/GrabBagPresenter.h \ Presenter/Inc/LaserDataLoader.h \ Presenter/Inc/RobotProtocol.h \ IYGrabBagStatus.h \ + devstatus.h \ dialogcamera.h \ dialogconfig.h \ - mainwindow.h + mainwindow.h \ + resultitem.h FORMS += \ + devstatus.ui \ dialogcamera.ui \ dialogconfig.ui \ - mainwindow.ui + mainwindow.ui \ + resultitem.ui RESOURCES += \ resources.qrc diff --git a/GrabBagApp/Presenter/Src/GrabBagPresenter.cpp b/GrabBagApp/Presenter/Src/GrabBagPresenter.cpp index f294ed9..3345df9 100644 --- a/GrabBagApp/Presenter/Src/GrabBagPresenter.cpp +++ b/GrabBagApp/Presenter/Src/GrabBagPresenter.cpp @@ -53,7 +53,6 @@ int GrabBagPresenter::Init() { // 初始化连接状态 m_bCameraConnected = false; - m_bRobotConnected = false; m_currentWorkStatus = WorkStatus::Error; // 初始化VrConfig模块 @@ -70,12 +69,6 @@ int GrabBagPresenter::Init() // 初始化机械臂协议 int nRet = InitRobotProtocol(); - if (nRet != 0) { - m_pStatus->OnStatusUpdate("机械臂服务初始化失败"); - m_bRobotConnected = false; - } else { - m_pStatus->OnStatusUpdate("机械臂服务初始化成功"); - } // 初始化算法参数 nRet = InitAlgorithmParams(); @@ -94,6 +87,11 @@ int GrabBagPresenter::Init() if(cameraCount > 0){ if (cameraCount >= 1) { // 尝试打开相机1 + LOG_INFO("Attempting to connect to camera 1 with IP: %s\n", configResult.cameraList[0].ip.c_str()); + + // 发送页面提示信息 + m_pStatus->OnStatusUpdate(QString("正在连接相机一,IP: %1").arg(configResult.cameraList[0].ip.c_str()).toStdString()); + // 初始化VrEyeDevice模块 IVrEyeDevice* pDevice = nullptr; IVrEyeDevice::CreateObject(&pDevice); @@ -104,15 +102,21 @@ int GrabBagPresenter::Init() nRet = pDevice->OpenDevice(configResult.cameraList[0].ip.c_str(), &GrabBagPresenter::_StaticCameraNotify, this); // 通过回调更新相机1状态 bool camera1Connected = (SUCCESS == nRet); - if(camera1Connected) - { + if(camera1Connected){ m_vrEyeDeviceList.push_back(pDevice); + LOG_INFO("Camera 1 connection successful, IP: %s\n", configResult.cameraList[0].ip.c_str()); + m_pStatus->OnStatusUpdate(QString("相机一连接成功,IP: %1").arg(configResult.cameraList[0].ip.c_str()).toStdString()); + } else { + LOG_ERROR("Camera 1 connection failed, IP: %s, error code: %d\n", configResult.cameraList[0].ip.c_str(), nRet); + m_pStatus->OnStatusUpdate(QString("相机一连接失败,IP: %1,错误码: %2").arg(configResult.cameraList[0].ip.c_str()).arg(nRet).toStdString()); } m_pStatus->OnCamera1StatusChanged(camera1Connected); m_pStatus->OnStatusUpdate(camera1Connected ? "相机一连接成功" : "相机一连接失败"); m_bCameraConnected = camera1Connected; } else { + LOG_WARNING("Camera count is 0, setting camera 1 as disconnected\n"); + m_pStatus->OnStatusUpdate("配置文件中未找到相机配置,设置相机一为离线状态"); m_pStatus->OnCamera1StatusChanged(false); m_bCameraConnected = false; } @@ -174,12 +178,24 @@ int GrabBagPresenter::Init() int GrabBagPresenter::InitRobotProtocol() { LOG_DEBUG("Start initializing robot protocol\n"); + m_pStatus->OnStatusUpdate("机械臂服务初始化..."); // 创建RobotProtocol实例 if(nullptr == m_pRobotProtocol){ m_pRobotProtocol = new RobotProtocol(); } - + + // 初始化协议服务(使用端口503,避免与原有ModbusTCP冲突) + int nRet = m_pRobotProtocol->Initialize(); + ERR_CODE_RETURN(nRet); + + if (nRet != 0) { + m_pStatus->OnStatusUpdate("机械臂服务初始化失败"); + m_bRobotConnected = false; + } else { + m_pStatus->OnStatusUpdate("机械臂服务初始化成功"); + } + // 设置连接状态回调 m_pRobotProtocol->SetConnectionCallback([this](bool connected) { this->OnRobotConnectionChanged(connected); @@ -190,11 +206,8 @@ int GrabBagPresenter::InitRobotProtocol() this->OnRobotWorkSignal(startWork, cameraIndex); }); - // 初始化协议服务(使用端口503,避免与原有ModbusTCP冲突) - int nRet = m_pRobotProtocol->Initialize(); - ERR_CODE_RETURN(nRet); - LOG_INFO("Robot protocol initialization successful\n"); + LOG_INFO("Robot protocol initialization completed successfully\n"); return SUCCESS; } @@ -281,7 +294,13 @@ void GrabBagPresenter::OnRobotConnectionChanged(bool connected) if (m_pStatus) { m_pStatus->OnRobotConnectionChanged(connected); - m_pStatus->OnStatusUpdate(connected ? "机械臂已连接" : "机械臂连接断开"); + + // 发送详细的页面状态信息 + if (connected) { + m_pStatus->OnStatusUpdate("机械臂连接成功,通信正常"); + } else { + m_pStatus->OnStatusUpdate("机械臂连接断开,请检查网络连接"); + } } // 检查并更新工作状态 @@ -518,6 +537,11 @@ void GrabBagPresenter::_CameraNotify(EVzDeviceWorkStatus eStatus, void *pExtData { LOG_INFO("Received scan finish signal from camera\n"); + // 发送页面提示信息 + if (m_pStatus) { + m_pStatus->OnStatusUpdate("相机扫描完成,开始数据处理..."); + } + // 通知检测线程开始处理 m_algoDetectCondition.notify_one(); break; @@ -567,11 +591,9 @@ void GrabBagPresenter::CheckAndUpdateWorkStatus() if (m_bCameraConnected && m_bRobotConnected) { m_currentWorkStatus = WorkStatus::Ready; m_pStatus->OnWorkStatusChanged(WorkStatus::Ready); - m_pStatus->OnStatusUpdate("设备已准备好"); } else { m_currentWorkStatus = WorkStatus::Error; m_pStatus->OnWorkStatusChanged(WorkStatus::Error); - m_pStatus->OnStatusUpdate("设备未准备好"); } } @@ -890,7 +912,7 @@ QImage GrabBagPresenter::_GeneratePointCloudImage(SVzNL3DLaserLine* scanData, in } } - // 绘制检测目标 + // 绘制检测目标和方向线 for (size_t i = 0; i < objOps.size(); i++) { QColor objColor = (i == 0) ? QColor(255, 0, 0) : QColor(255, 255, 0); int size = (i == 0) ? 12 : 8; @@ -899,9 +921,33 @@ QImage GrabBagPresenter::_GeneratePointCloudImage(SVzNL3DLaserLine* scanData, in int py = (int)((objOps[i].centerPos.y - y_range.min) / y_scale + y_skip); if (px >= 0 && px < imgCols && py >= 0 && py < imgRows) { + // 绘制抓取点圆圈 painter.setPen(QPen(objColor, 2)); painter.setBrush(QBrush(objColor)); painter.drawEllipse(px - size/2, py - size/2, size, size); + + // 绘制方向线 + const double deg2rad = PI / 180.0; + double R = 100; + const double yaw = objOps[i].centerPos.z_yaw * deg2rad; + double cy = cos(yaw); + double sy = sin(yaw); + double x1 = objOps[i].centerPos.x + R * cy; double y1 = objOps[i].centerPos.y - R * sy; + double x2 = objOps[i].centerPos.x - R * cy; double y2 = objOps[i].centerPos.y + R * sy; + int px1 = (int)((x1 - x_range.min) / x_scale + x_skip); + int py1 = (int)((y1 - y_range.min) / y_scale + y_skip); + int px2 = (int)((x2 - x_range.min) / x_scale + x_skip); + int py2 = (int)((y2 - y_range.min) / y_scale + y_skip); + + // 绘制方向线 + painter.setPen(QPen(objColor, 3)); + painter.drawLine(px1, py1, px2, py2); + + // 绘制目标编号 + painter.setPen(QPen(Qt::white, 1)); + painter.setFont(QFont("Arial", 10, QFont::Bold)); + painter.drawText(px + 15, py - 10, QString("%1").arg(i + 1)); + } } diff --git a/GrabBagApp/devstatus.cpp b/GrabBagApp/devstatus.cpp new file mode 100644 index 0000000..6500f06 --- /dev/null +++ b/GrabBagApp/devstatus.cpp @@ -0,0 +1,110 @@ +#include "devstatus.h" +#include "ui_devstatus.h" +#include +#include +#include "VrLog.h" + +devstatus::devstatus(QWidget *parent) + : QWidget(parent) + , ui(new Ui::devstatus) +{ + ui->setupUi(this); + + // 强制应用样式表 - 这是关键! + this->setAttribute(Qt::WA_StyledBackground, true); + + // 初始化时设置样式 + setItemStyle(); + + // 初始化设备状态(离线状态) + updateCamera1Status(false); + updateCamera2Status(false); + updateRobotStatus(false); +} + +devstatus::~devstatus() +{ + delete ui; +} + +// 设置devstatus的样式 +void devstatus::setItemStyle() +{ + // 参考ResultItem的样式设置,添加边框作为格子间分隔线 + this->setStyleSheet( + "devstatus { " + " border: 2px solid #191A1C; " // 添加边框作为格子间分隔线 + " border-radius: 0px; " // 去掉圆角,让分隔线更清晰 + "} " + ); +} + +// 设置相机状态图片的私有成员函数 +void devstatus::setCameraStatusImage(QWidget* widget, bool isOnline) { + if (!widget) return; + + QString imagePath = isOnline ? ":/resource/camera_online.png" : ":/resource/camera_offline.png"; + + widget->setStyleSheet(QString("background-image: url(%1); background-repeat: no-repeat; background-position: center;") + .arg(imagePath)); + widget->setFixedSize(32, 32); // 调整大小以适应图片 +} + +// 设置机械臂状态图片的私有成员函数 +void devstatus::setRobotStatusImage(QWidget* widget, bool isOnline) { + if (!widget) return; + + QString imagePath = isOnline ? ":/resource/robot_online.png" : ":/resource/robot_offline.png"; + + widget->setStyleSheet(QString("background-image: url(%1); background-repeat: no-repeat; background-position: center;") + .arg(imagePath)); + widget->setFixedSize(32, 32); // 调整大小以适应图片 +} + +// 更新相机1状态 +void devstatus::updateCamera1Status(bool isConnected) +{ + setCameraStatusImage(ui->dev_camer_1_img, isConnected); + + QString statusText = isConnected ? "相机1在线" : "相机1离线"; + QString colorStyle = isConnected ? "color: rgb(140, 180, 60);" : "color: rgb(220, 60, 60);"; + + ui->dev_camera_1_txt->setText(statusText); + ui->dev_camera_1_txt->setStyleSheet(colorStyle); +} + +// 更新相机2状态 +void devstatus::updateCamera2Status(bool isConnected) +{ + setCameraStatusImage(ui->dev_camer_2_img, isConnected); + + QString statusText = isConnected ? "相机2在线" : "相机2离线"; + QString colorStyle = isConnected ? "color: rgb(140, 180, 60);" : "color: rgb(220, 60, 60);"; + + ui->dev_camera_2_txt->setText(statusText); + ui->dev_camera_2_txt->setStyleSheet(colorStyle); +} + +// 更新机械臂状态 +void devstatus::updateRobotStatus(bool isConnected) +{ + setRobotStatusImage(ui->dev_robot_img, isConnected); + + QString statusText = isConnected ? "机械臂在线" : "机械臂离线"; + QString colorStyle = isConnected ? "color: rgb(140, 180, 60);" : "color: rgb(220, 60, 60);"; + + ui->dev_robot_txt->setText(statusText); + ui->dev_robot_txt->setStyleSheet(colorStyle); +} + +// 设置相机数量(用于控制相机二的显示) +void devstatus::setCameraCount(int cameraCount) +{ + // 如果相机数量小于2,隐藏相机二相关的UI元素 + bool showCamera2 = (cameraCount >= 2); + + LOG_DEBUG("setCameraCount: %d", cameraCount); + + ui->dev_camer_2_img->setVisible(showCamera2); + ui->dev_camera_2_txt->setVisible(showCamera2); +} diff --git a/GrabBagApp/devstatus.h b/GrabBagApp/devstatus.h new file mode 100644 index 0000000..b287a35 --- /dev/null +++ b/GrabBagApp/devstatus.h @@ -0,0 +1,38 @@ +#ifndef DEVSTATUS_H +#define DEVSTATUS_H + +#include +#include + +namespace Ui { +class devstatus; +} + +class devstatus : public QWidget +{ + Q_OBJECT + +public: + explicit devstatus(QWidget *parent = nullptr); + ~devstatus(); + + // 设备状态更新方法 + void updateCamera1Status(bool isConnected); + void updateCamera2Status(bool isConnected); + void updateRobotStatus(bool isConnected); + + // 设置相机数量(用于控制相机二的显示) + void setCameraCount(int cameraCount); + +private: + Ui::devstatus *ui; + + // 设置状态图片的私有成员函数 + void setCameraStatusImage(QWidget* widget, bool isOnline); + void setRobotStatusImage(QWidget* widget, bool isOnline); + + // 设置样式的私有成员函数 + void setItemStyle(); +}; + +#endif // DEVSTATUS_H diff --git a/GrabBagApp/devstatus.ui b/GrabBagApp/devstatus.ui new file mode 100644 index 0000000..003679a --- /dev/null +++ b/GrabBagApp/devstatus.ui @@ -0,0 +1,85 @@ + + + devstatus + + + + 0 + 0 + 272 + 200 + + + + Form + + + background-color: rgb(37, 38, 42); + + + + + 50 + 20 + 151 + 161 + + + + + + + + + + + + color: rgb(0, 255, 0); + + + 相机在线 + + + + + + + + + + + + + + color: rgb(0, 255, 0); + + + 相机在线 + + + + + + + + + + + + + + color: rgb(0, 255, 0); + + + 机械臂在线 + + + + + + + + + + + diff --git a/GrabBagApp/mainwindow.cpp b/GrabBagApp/mainwindow.cpp index dbc75ba..ae88795 100644 --- a/GrabBagApp/mainwindow.cpp +++ b/GrabBagApp/mainwindow.cpp @@ -1,5 +1,6 @@ #include "mainwindow.h" #include "ui_mainwindow.h" +#include "resultitem.h" #include #include #include @@ -7,6 +8,13 @@ #include #include #include +#include +#include +#include +#include +#include +#include +#include "VrLog.h" MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) @@ -29,14 +37,11 @@ MainWindow::MainWindow(QWidget *parent) QGraphicsScene* scene = new QGraphicsScene(this); ui->detect_image->setScene(scene); - // 初始化ListView的Model - QStandardItemModel* model = new QStandardItemModel(this); - ui->detect_result->setModel(model); + // 初始化日志模型 + m_logModel = new QStringListModel(this); + ui->detect_log->setModel(m_logModel); - // 设置label前圆点(在线/离线) - setStatusDot(ui->label_camera_1_status, false); // 在线绿色 - setStatusDot(ui->label_camera_2_status, false); // 在线绿色 - setStatusDot(ui->label_robot_status, false); // 在线绿色 + // 原有的状态指示器已删除,状态显示统一在devstatus中处理 // 连接菜单信号 // connect(ui->menu_camera, &QMenu::aboutToShow, this, &MainWindow::on_menu_camera_triggered); @@ -49,7 +54,13 @@ MainWindow::MainWindow(QWidget *parent) // 连接检测结果更新信号槽 connect(this, &MainWindow::detectionResultUpdateRequested, this, &MainWindow::updateDetectionResultDisplay); - updateStatusLog(tr("就绪")); + // 连接日志更新信号槽 + connect(this, &MainWindow::logUpdateRequested, this, &MainWindow::updateDetectionLog); + + // 连接清空日志信号槽 + connect(this, &MainWindow::logClearRequested, this, &MainWindow::clearDetectionLogUI); + + updateStatusLog(tr("设备开始初始化...")); // 初始化模块 @@ -69,7 +80,17 @@ MainWindow::~MainWindow() void MainWindow::updateStatusLog(const QString& message) { - statusBar()->showMessage(message); + // 更新状态栏 + // statusBar()->showMessage(message); + + // 通过信号槽机制更新detect_log控件 + emit logUpdateRequested(message); +} + +void MainWindow::clearDetectionLog() +{ + // 通过信号槽机制清空日志 + emit logClearRequested(); } void MainWindow::Init() @@ -80,11 +101,27 @@ void MainWindow::Init() // 设置状态回调接口 m_presenter->SetStatusCallback(this); + m_deviceStatusWidget = new devstatus(); //因为初始化回调的数据要存储,所以要在init前创建好 + // 初始化业务逻辑 int result = m_presenter->Init(); if (result != 0) { updateStatusLog(tr("初始化失败,错误码:%1").arg(result)); } + + // 初始化完成后,在detect_result_list中增加设备状态widget + QListWidgetItem* deviceStatusItem = new QListWidgetItem(); + deviceStatusItem->setBackground(QBrush(Qt::transparent)); + deviceStatusItem->setSizeHint(QSize(272, 200)); + + ui->detect_result_list->addItem(deviceStatusItem); + ui->detect_result_list->setItemWidget(deviceStatusItem, m_deviceStatusWidget); + + // 设置列表视图模式 + ui->detect_result_list->setViewMode(QListView::IconMode); + ui->detect_result_list->setResizeMode(QListView::Adjust); + ui->detect_result_list->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); + ui->detect_result_list->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); } void MainWindow::displayImage(const QImage& image) @@ -105,47 +142,76 @@ void MainWindow::displayImage(const QImage& image) // 添加扩展版本的检测结果函数 void MainWindow::addDetectionResult(const DetectionResult& result) { + // 清空之前的数据(除了第一个设备状态item) + while (ui->detect_result_list->count() > 1) { + QListWidgetItem* item = ui->detect_result_list->takeItem(1); + delete item; + } + + // 更新现有设备状态显示(第一个item就是设备状态) + if (m_deviceStatusWidget) { + // m_deviceStatusWidget会自动从其内部状态获取设备状态信息 + // 不需要手动更新,因为状态变化时会直接更新到widget中 + } + + // 设置为图标模式,实现网格布局(多列显示) + ui->detect_result_list->setViewMode(QListView::IconMode); + ui->detect_result_list->setResizeMode(QListView::Adjust); + + // 设置滚动条策略:始终显示垂直滚动条,隐藏水平滚动条 + ui->detect_result_list->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); + ui->detect_result_list->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + + // 计算gridWidth:考虑12px间距,确保两列布局 + int totalWidth = ui->detect_result_list->width(); + int itemSpacing = 12; + int gridWidth = (totalWidth - itemSpacing - 17) / 2; //20是滚动条的宽度 + int gridHeight = 200; + + ui->detect_result_list->setGridSize(QSize(gridWidth, gridHeight)); // 设置每个格子的大小 + ui->detect_result_list->setSpacing(itemSpacing); // 设置item间距为12px + ui->detect_result_list->setFlow(QListView::LeftToRight); // 从左到右排列 + ui->detect_result_list->setWrapping(true); // 允许换行 + auto layoutIter = result.positionLayout.begin(); + int targetIndex = 1; while(layoutIter != result.positionLayout.end()) { - QString posStr = QString("\n第%1层\n").arg(layoutIter->layerIndex); auto positionIter = layoutIter->position.begin(); while(positionIter != layoutIter->position.end()) { - posStr += QString("坐标: X=%1\n坐标: Y=%2\n坐标: Z=%3\n旋转: RZ=%4\n") - .arg(positionIter->x, 0, 'f', 2) - .arg(positionIter->y, 0, 'f', 2) - .arg(positionIter->z, 0, 'f', 2) - .arg(positionIter->yaw, 0, 'f', 2); + // 创建自定义的ResultItem widget + ResultItem* resultWidget = new ResultItem(); + + // 设置widget的autoFillBackground属性,确保背景色生效 + resultWidget->setAutoFillBackground(true); + + // 设置检测结果数据,直接使用GrabBagPosition + resultWidget->setResultData(targetIndex, *positionIter); + + // 创建QListWidgetItem + QListWidgetItem* item = new QListWidgetItem(); + + // 设置item背景为透明,让自定义widget的背景显示出来 + item->setBackground(QBrush(Qt::transparent)); + + // 设置item的大小提示 - 应该与gridSize一致 + item->setSizeHint(QSize(gridWidth, gridHeight)); + + // 将item添加到列表 + ui->detect_result_list->addItem(item); + + // 将自定义widget设置为item的显示内容 + ui->detect_result_list->setItemWidget(item, resultWidget); + + targetIndex++; positionIter++; } layoutIter++; - QStandardItemModel* model = qobject_cast(ui->detect_result->model()); - if (model) { - QStandardItem* item = new QStandardItem(posStr); - model->appendRow(item); - } - } + } } -// setStatusDot实现移到MainWindow类作用域内 -void MainWindow::setStatusDot(QLabel* label, bool isOnline) { - QPixmap dot(18, 18); - dot.fill(Qt::transparent); - QPainter painter(&dot); - painter.setRenderHint(QPainter::Antialiasing); - QColor color = isOnline ? QColor(140, 180, 60) : QColor(220, 60, 60); // 绿色或红色 - painter.setBrush(QBrush(color)); - painter.setPen(Qt::NoPen); - painter.drawEllipse(0, 0, 18, 18); - painter.end(); - label->setPixmap(dot); - label->setScaledContents(true); - label->setFixedSize(18, 18); -} - - void MainWindow::on_btn_camera_clicked() { // if(nullptr == ui_dialogCamera){ @@ -171,6 +237,9 @@ void MainWindow::on_btn_flow_clicked() void MainWindow::on_btn_start_clicked() { + // 清空检测日志,开始新的检测 + clearDetectionLog(); + // 使用Presenter启动检测 if (m_presenter) { m_presenter->StartDetection(); @@ -184,19 +253,6 @@ void MainWindow::on_btn_stop_clicked() } } -//void MainWindow::on_menu_camera_triggered() -//{ -// qDebug() << __func__ ; -//} - -//void MainWindow::on_menu_config_triggered() -//{ -// qDebug() << __func__ ; -// if(nullptr == ui_dialogConfig){ -// ui_dialogConfig = new DialogConfig(this); -// } -// ui_dialogConfig->show(); -//} void MainWindow::on_menu_camera_adjust_triggered() { @@ -206,6 +262,7 @@ void MainWindow::on_menu_camera_adjust_triggered() // 状态更新槽函数 void MainWindow::OnStatusUpdate(const std::string& statusMessage) { + LOG_DEBUG("[UI Display] Status update: %s\n", statusMessage.c_str()); updateStatusLog(QString::fromStdString(statusMessage)); } @@ -217,43 +274,34 @@ void MainWindow::OnDetectionResult(const DetectionResult& result) void MainWindow::OnCamera1StatusChanged(bool isConnected) { - // 更新相机1状态指示灯 - setStatusDot(ui->label_camera_1_status, isConnected); - - // 更新状态消息 - QString statusMsg = isConnected ? tr("相机一已连接") : tr("相机一已断开"); - ui->label_camera_1_txt->setText(statusMsg); - updateStatusLog(statusMsg); + // 直接更新设备状态widget + if (m_deviceStatusWidget) { + m_deviceStatusWidget->updateCamera1Status(isConnected); + } } // 相机2状态更新槽函数 void MainWindow::OnCamera2StatusChanged(bool isConnected) { - // 更新相机2状态指示灯 - setStatusDot(ui->label_camera_2_status, isConnected); - - // 更新状态消息 - QString statusMsg = isConnected ? tr("相机二已连接") : tr("相机二已断开"); - ui->label_camera_2_txt->setText(statusMsg); - updateStatusLog(statusMsg); + // 直接更新设备状态widget + if (m_deviceStatusWidget) { + m_deviceStatusWidget->updateCamera2Status(isConnected); + } } // 相机个数更新槽函数 void MainWindow::OnCameraCountChanged(int cameraCount) { - // 如果只有一个相机,隐藏相机2相关UI元素 + // 设置设备状态widget中的相机数量 + if (m_deviceStatusWidget) { + m_deviceStatusWidget->setCameraCount(cameraCount); + } + + // 如果只有一个相机,更新状态消息 if (cameraCount < 2) { - // 隐藏相机2状态标签和图标 - ui->label_camera_2_txt->setVisible(false); - ui->label_camera_2_status->setVisible(false); - // 更新状态消息 updateStatusLog(tr("系统使用单相机模式")); } else { - // 显示相机2状态标签和图标 - ui->label_camera_2_txt->setVisible(true); - ui->label_camera_2_status->setVisible(true); - // 更新状态消息 updateStatusLog(tr("系统使用双相机模式")); } @@ -262,12 +310,11 @@ void MainWindow::OnCameraCountChanged(int cameraCount) // 机械臂状态更新槽函数 void MainWindow::OnRobotConnectionChanged(bool isConnected) { - // 更新机械臂状态指示灯 - setStatusDot(ui->label_robot_status, isConnected); - // 更新状态消息 - QString statusMsg = isConnected ? tr("机械臂已连接") : tr("机械臂已断开"); - updateStatusLog(statusMsg); + // 直接更新设备状态widget + if (m_deviceStatusWidget) { + m_deviceStatusWidget->updateRobotStatus(isConnected); + } } // 工作状态更新槽函数 @@ -279,6 +326,11 @@ void MainWindow::OnWorkStatusChanged(WorkStatus status) void MainWindow::updateWorkStatusLabel(WorkStatus status) { + // 如果状态变为Working,清空检测日志(表示开始新的检测) + if (status == WorkStatus::Working) { + clearDetectionLog(); + } + // 获取状态对应的显示文本 QString statusText = QString::fromStdString(WorkStatusToString(status)); @@ -305,9 +357,6 @@ void MainWindow::updateWorkStatusLabel(WorkStatus status) break; } } - - // 同时更新状态栏信息 - updateStatusLog(statusText); } void MainWindow::updateDetectionResultDisplay(const DetectionResult& result) @@ -317,9 +366,6 @@ void MainWindow::updateDetectionResultDisplay(const DetectionResult& result) // 更新检测结果到列表 addDetectionResult(result); - - // 更新目标面板显示 - // updateTargetPanels(result); } @@ -372,6 +418,9 @@ void MainWindow::on_action_tool_debug_data_triggered() return; } + // 清空检测日志,开始新的检测 + clearDetectionLog(); + std::thread t([this, fileName]() { updateStatusLog(tr("正在加载调试数据文件: %1").arg(fileName)); int result = m_presenter->LoadDebugDataAndDetect(fileName.toStdString()); @@ -387,3 +436,34 @@ void MainWindow::on_action_tool_debug_data_triggered() t.detach(); } +void MainWindow::updateDetectionLog(const QString& message) +{ + // 在UI线程中更新detect_log控件(QListView) + if (m_logModel) { + // 添加时间戳(包含年月日) + QString timestamp = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"); + QString logEntry = QString("[%1] %2").arg(timestamp).arg(message); + + // 获取当前数据 + QStringList logList = m_logModel->stringList(); + + // 添加新的日志条目 + logList.append(logEntry); + + // 更新模型 + m_logModel->setStringList(logList); + + // 自动滚动到最底部 + QModelIndex lastIndex = m_logModel->index(logList.size() - 1); + ui->detect_log->scrollTo(lastIndex); + } +} + +void MainWindow::clearDetectionLogUI() +{ + // 在UI线程中清空检测日志 + if (m_logModel) { + m_logModel->setStringList(QStringList()); + } +} + diff --git a/GrabBagApp/mainwindow.h b/GrabBagApp/mainwindow.h index d965001..8670fc9 100644 --- a/GrabBagApp/mainwindow.h +++ b/GrabBagApp/mainwindow.h @@ -8,8 +8,10 @@ #include #include #include +#include #include "dialogcamera.h" #include "dialogconfig.h" +#include "devstatus.h" #include "GrabBagPresenter.h" QT_BEGIN_NAMESPACE @@ -25,6 +27,7 @@ public: ~MainWindow(); void updateStatusLog(const QString& message); + void clearDetectionLog(); void Init(); // 实现IYGrabBagStatus接口 @@ -42,6 +45,12 @@ signals: // 检测结果更新信号 void detectionResultUpdateRequested(const DetectionResult& result); + + // 日志更新信号 + void logUpdateRequested(const QString& message); + + // 清空日志信号 + void logClearRequested(); private slots: // UI操作相关槽 @@ -59,6 +68,12 @@ private slots: // 检测结果更新槽函数 void updateDetectionResultDisplay(const DetectionResult& result); + + // 日志更新槽函数 + void updateDetectionLog(const QString& message); + + // 清空日志槽函数 + void clearDetectionLogUI(); void on_action_camera_1_triggered(); @@ -74,12 +89,15 @@ private: // 业务逻辑处理类 GrabBagPresenter* m_presenter = nullptr; + // 日志模型 + QStringListModel* m_logModel = nullptr; + + // 设备状态显示widget的引用 + devstatus* m_deviceStatusWidget = nullptr; + void displayImage(const QImage& image); // 扩展版本的检测结果添加函数 void addDetectionResult(const DetectionResult& result); - - // 设置状态圆点的私有成员函数 - void setStatusDot(QLabel* label, bool isOnline); }; #endif // MAINWINDOW_H diff --git a/GrabBagApp/mainwindow.ui b/GrabBagApp/mainwindow.ui index b9353fb..a0c8c6b 100644 --- a/GrabBagApp/mainwindow.ui +++ b/GrabBagApp/mainwindow.ui @@ -20,20 +20,6 @@ background-color:rgb(25, 26, 28) - - - - 1344 - 302 - 556 - 411 - - - - background-color: rgb(37, 38, 42); -color: rgb(255, 255, 255); - - @@ -89,109 +75,6 @@ color: rgb(255, 255, 255); - - - - 1370 - 90 - 501 - 91 - - - - - - - - - - - color: rgb(255, 255, 255); - - - C1 - - - - - - - - 12 - - - - color: rgb(255, 255, 255); - - - 相机一离线 - - - - - - - - - - - color: rgb(255, 255, 255); - - - C2 - - - - - - - - 12 - - - - color: rgb(255, 255, 255); - - - 相机二离线 - - - - - - - - - - - - - color: rgb(255, 255, 255); - - - C1 - - - - - - - - 12 - - - - color: rgb(255, 255, 255); - - - 机械臂在线 - - - - - - - @@ -201,8 +84,17 @@ color: rgb(255, 255, 255); 304 + + + 12 + + - background-color: rgb(37, 38, 42); + background-color: rgb(37, 38, 42); +color: rgb(239, 241, 245); + + + QAbstractItemView::EditTrigger::NoEditTriggers @@ -283,7 +175,7 @@ color: rgb(255, 255, 255); 194 21 251 - 29 + 32 @@ -319,6 +211,22 @@ color: rgb(255, 255, 255); 工作状态 + + + + 1344 + 90 + 556 + 621 + + + + background-color: rgb(37, 38, 42); + + + 0 + + diff --git a/GrabBagApp/resource/camera_offline.png b/GrabBagApp/resource/camera_offline.png new file mode 100644 index 0000000..c12f7b8 Binary files /dev/null and b/GrabBagApp/resource/camera_offline.png differ diff --git a/GrabBagApp/resource/camera_online.png b/GrabBagApp/resource/camera_online.png new file mode 100644 index 0000000..57bad65 Binary files /dev/null and b/GrabBagApp/resource/camera_online.png differ diff --git a/GrabBagApp/resource/robot_offline.png b/GrabBagApp/resource/robot_offline.png new file mode 100644 index 0000000..adb8939 Binary files /dev/null and b/GrabBagApp/resource/robot_offline.png differ diff --git a/GrabBagApp/resource/robot_online.png b/GrabBagApp/resource/robot_online.png new file mode 100644 index 0000000..83c4259 Binary files /dev/null and b/GrabBagApp/resource/robot_online.png differ diff --git a/GrabBagApp/resource/结束.png b/GrabBagApp/resource/stop.png similarity index 100% rename from GrabBagApp/resource/结束.png rename to GrabBagApp/resource/stop.png diff --git a/GrabBagApp/resources.qrc b/GrabBagApp/resources.qrc index ba315a2..8e567df 100644 --- a/GrabBagApp/resources.qrc +++ b/GrabBagApp/resources.qrc @@ -6,5 +6,9 @@ resource/camera_config.png resource/camera_level.png resource/model.png + resource/camera_offline.png + resource/camera_online.png + resource/robot_offline.png + resource/robot_online.png diff --git a/GrabBagApp/resultitem.cpp b/GrabBagApp/resultitem.cpp new file mode 100644 index 0000000..8299041 --- /dev/null +++ b/GrabBagApp/resultitem.cpp @@ -0,0 +1,50 @@ +#include "resultitem.h" +#include "ui_resultitem.h" + +ResultItem::ResultItem(QWidget *parent) + : QWidget(parent) + , ui(new Ui::ResultItem) +{ + ui->setupUi(this); + + // 强制应用样式表 - 这是关键! + this->setAttribute(Qt::WA_StyledBackground, true); + + // 初始化时设置样式 + setItemStyle(); + + // 设置输入框为只读 + ui->result_x->setReadOnly(true); + ui->result_y->setReadOnly(true); + ui->result_z->setReadOnly(true); + ui->result_rz->setReadOnly(true); +} + +ResultItem::~ResultItem() +{ + delete ui; +} + +void ResultItem::setResultData(int targetIndex, const GrabBagPosition& position) +{ + // 设置目标编号 + ui->result_id->setText(QString("目标:%1").arg(targetIndex)); + + // 设置数据到对应的LineEdit控件 + ui->result_x->setText(QString("%1").arg(position.x, 0, 'f', 2)); + ui->result_y->setText(QString("%1").arg(position.y, 0, 'f', 2)); + ui->result_z->setText(QString("%1").arg(position.z, 0, 'f', 2)); + ui->result_rz->setText(QString("%1").arg(position.yaw, 0, 'f', 2)); + +} + +void ResultItem::setItemStyle() +{ + // 只设置黄色边框作为格子间分隔线,不修改背景和QLineEdit样式 + this->setStyleSheet( + "ResultItem { " + " border: 2px solid #191A1C; " // 只添加黄色边框作为格子间分隔线 + " border-radius: 0px; " // 去掉圆角,让分隔线更清晰 + "} " + ); +} diff --git a/GrabBagApp/resultitem.h b/GrabBagApp/resultitem.h new file mode 100644 index 0000000..8acb2de --- /dev/null +++ b/GrabBagApp/resultitem.h @@ -0,0 +1,29 @@ +#ifndef RESULTITEM_H +#define RESULTITEM_H + +#include +#include "IYGrabBagStatus.h" + +namespace Ui { +class ResultItem; +} + +class ResultItem : public QWidget +{ + Q_OBJECT + +public: + explicit ResultItem(QWidget *parent = nullptr); + ~ResultItem(); + + // 设置检测结果数据 + void setResultData(int targetIndex, const GrabBagPosition& position); + + // 设置样式 + void setItemStyle(); + +private: + Ui::ResultItem *ui; +}; + +#endif // RESULTITEM_H diff --git a/GrabBagApp/resultitem.ui b/GrabBagApp/resultitem.ui new file mode 100644 index 0000000..1778092 --- /dev/null +++ b/GrabBagApp/resultitem.ui @@ -0,0 +1,178 @@ + + + ResultItem + + + + 0 + 0 + 272 + 200 + + + + Form + + + background-color: rgb(37, 38, 42); + + + + + + 50 + 50 + 181 + 131 + + + + + + + + + Qt::LayoutDirection::RightToLeft + + + color: rgb(239, 241, 245); +background-color: rgb(37, 38, 42); + + + 坐标X: + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + + + + + Qt::LayoutDirection::RightToLeft + + + color: rgb(239, 241, 245); +background-color: rgb(37, 38, 42); + + + 坐标Y: + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + + + + + Qt::LayoutDirection::RightToLeft + + + color: rgb(239, 241, 245); +background-color: rgb(37, 38, 42); + + + 坐标Z: + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + + + + + Qt::LayoutDirection::RightToLeft + + + color: rgb(239, 241, 245); +background-color: rgb(37, 38, 42); + + + 旋转角RZ: + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + + + + + + + + + background-color: rgb(59, 61, 71); +color: rgb(239, 241, 245); +border: 1px solid #3B3D47; +padding: 5px; +font-size: 12px; + + + + + + + background-color: rgb(59, 61, 71); +color: rgb(239, 241, 245); +border: 1px solid #3B3D47; +padding: 5px; +font-size: 12px; + + + + + + + background-color: rgb(59, 61, 71); +color: rgb(239, 241, 245); +border: 1px solid #3B3D47; +padding: 5px; +font-size: 12px; + + + + + + + background-color: rgb(59, 61, 71); +color: rgb(239, 241, 245); +border: 1px solid #3B3D47; +padding: 5px; +font-size: 12px; + + + + + + + + + + + 52 + 17 + 63 + 30 + + + + Qt::LayoutDirection::LeftToRight + + + color: rgb(239, 241, 245); +background-color: rgb(37, 38, 42); + + + 目标 + + + Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignVCenter + + + + + +