#include "mainwindow.h" #include "ui_mainwindow.h" #include "resultitem.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "VrLog.h" #include #include #include #include #include "Version.h" #include "VrSimpleLog.h" MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) , m_selectedButton(nullptr) { ui->setupUi(this); // 设置窗口图标 this->setWindowIcon(QIcon(":/resource/logo.png")); // 设置状态栏字体 QFont statusFont = statusBar()->font(); statusFont.setPointSize(12); statusBar()->setFont(statusFont); // 设置状态栏颜色和padding statusBar()->setStyleSheet("QStatusBar { color: rgb(239, 241, 245); padding: 20px; }"); // 在状态栏右侧添加版本信息(包含编译时间) // 使用VrSimpleLog.h中的宏定义构建编译时间 QString versionWithBuildTime = QString("%1_%2%3%4%5%6%7") .arg(GetGrabBagFullVersion()) .arg(YEAR) .arg(MONTH, 2, 10, QChar('0')) .arg(DAY, 2, 10, QChar('0')) .arg(HOUR, 2, 10, QChar('0')) .arg(MINUTE, 2, 10, QChar('0')) .arg(SECOND, 2, 10, QChar('0')); QLabel* buildLabel = new QLabel(versionWithBuildTime); buildLabel->setStyleSheet("color: rgb(239, 241, 245); font-size: 20px; margin-right: 16px;"); statusBar()->addPermanentWidget(buildLabel); // 隐藏标题栏 setWindowFlags(Qt::FramelessWindowHint); // 启动后自动最大化显示 this->showMaximized(); // 初始化时隐藏label_work ui->label_work->setVisible(false); ui->detect_image_2->setVisible(false); // 初始化GraphicsScene QGraphicsScene* scene = new QGraphicsScene(this); ui->detect_image->setScene(scene); // 为第二个图像视图初始化GraphicsScene QGraphicsScene* scene2 = new QGraphicsScene(this); ui->detect_image_2->setScene(scene2); // 初始化日志模型 m_logModel = new QStringListModel(this); ui->detect_log->setModel(m_logModel); // 原有的状态指示器已删除,状态显示统一在devstatus中处理 // 注册自定义类型,使其能够在信号槽中跨线程传递 qRegisterMetaType("DetectionResult"); qRegisterMetaType("WorkStatus"); qRegisterMetaType("GrabBagPosition"); // 连接工作状态更新信号槽 connect(this, &MainWindow::workStatusUpdateRequested, this, &MainWindow::updateWorkStatusLabel); // 连接检测结果更新信号槽 connect(this, &MainWindow::detectionResultUpdateRequested, this, &MainWindow::updateDetectionResultDisplay); // 连接日志更新信号槽 connect(this, &MainWindow::logUpdateRequested, this, &MainWindow::updateDetectionLog); // 连接清空日志信号槽 connect(this, &MainWindow::logClearRequested, this, &MainWindow::clearDetectionLogUI); updateStatusLog(tr("设备开始初始化...")); // 初始化模块 Init(); } MainWindow::~MainWindow() { // 释放业务逻辑处理类 if (m_presenter) { delete m_presenter; m_presenter = nullptr; } delete ui; } void MainWindow::updateStatusLog(const QString& message) { // 更新状态栏 // statusBar()->showMessage(message); // 通过信号槽机制更新detect_log控件 emit logUpdateRequested(message); } void MainWindow::clearDetectionLog() { // 通过信号槽机制清空日志 emit logClearRequested(); } void MainWindow::Init() { // 创建业务逻辑处理类 m_presenter = new GrabBagPresenter(); // 设置状态回调接口 m_presenter->SetStatusCallback(this); m_deviceStatusWidget = new devstatus(); //因为初始化回调的数据要存储,所以要在init前创建好 // 将设备状态widget添加到frame_dev中 QVBoxLayout* frameDevLayout = new QVBoxLayout(ui->frame_dev); frameDevLayout->setContentsMargins(0, 0, 0, 0); frameDevLayout->addWidget(m_deviceStatusWidget); // 设置列表视图模式 ui->detect_result_list->setViewMode(QListView::IconMode); ui->detect_result_list->setResizeMode(QListView::Adjust); ui->detect_result_list->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); ui->detect_result_list->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); #if 0 // 完全禁用拖拽和移动功能 ui->detect_result_list->setDragDropMode(QAbstractItemView::NoDragDrop); ui->detect_result_list->setDragEnabled(false); ui->detect_result_list->setAcceptDrops(false); ui->detect_result_list->setDefaultDropAction(Qt::IgnoreAction); ui->detect_result_list->setMovement(QListView::Static); // 禁止项目移动 // 设置选择模式但禁用多选 ui->detect_result_list->setSelectionMode(QAbstractItemView::SingleSelection); ui->detect_result_list->setSelectionBehavior(QAbstractItemView::SelectItems); // 确保滚动功能正常工作 ui->detect_result_list->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); ui->detect_result_list->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); #endif // 初始化期间禁用所有功能按钮 setButtonsEnabled(false); // 在线程中执行初始化业务逻辑 std::thread initThread([this]() { updateStatusLog(tr("正在初始化系统...")); int result = m_presenter->Init(); if (result != 0) { updateStatusLog(tr("初始化失败,错误码:%1").arg(result)); } else { updateStatusLog(tr("系统初始化完成")); } }); // 分离线程,让其在后台运行 initThread.detach(); } void MainWindow::displayImage(const QImage& image) { if (image.isNull()) { updateStatusLog(tr("图片无效")); return; } QGraphicsScene* scene = ui->detect_image->scene(); scene->clear(); QPixmap pixmap = QPixmap::fromImage(image); scene->addPixmap(pixmap); ui->detect_image->fitInView(scene->sceneRect(), Qt::KeepAspectRatio); } // 在第二个图像视图中显示图像 void MainWindow::displayImageInSecondView(const QImage& image) { if (image.isNull()) { updateStatusLog(tr("第二相机图片无效")); return; } QGraphicsScene* scene2 = ui->detect_image_2->scene(); scene2->clear(); QPixmap pixmap = QPixmap::fromImage(image); scene2->addPixmap(pixmap); ui->detect_image_2->fitInView(scene2->sceneRect(), Qt::KeepAspectRatio); } // 添加扩展版本的检测结果函数 void MainWindow::addDetectionResult(const DetectionResult& result) { // 清空之前的所有检测结果数据 ui->detect_result_list->clear(); // 设置item间距和流向(gridSize现在在布局调整函数中动态设置) int itemSpacing = 0; ui->detect_result_list->setSpacing(itemSpacing); // 设置item间距为0px // 获取当前的gridSize(由布局调整函数设置) QSize currentGridSize = ui->detect_result_list->gridSize(); int gridWidth = currentGridSize.width(); int gridHeight = currentGridSize.height(); int targetIndex = 1; for (const auto& position : result.positions) { // 创建自定义的ResultItem widget ResultItem* resultWidget = new ResultItem(); // 设置widget的autoFillBackground属性,确保背景色生效 resultWidget->setAutoFillBackground(true); // 设置检测结果数据,直接使用GrabBagPosition resultWidget->setResultData(targetIndex, position); // 创建QListWidgetItem QListWidgetItem* item = new QListWidgetItem(); // 设置item背景为透明,让自定义widget的背景显示出来 item->setBackground(QBrush(Qt::transparent)); // 设置item的大小提示 - 应该与gridSize一致 item->setSizeHint(QSize(gridWidth, gridHeight)); // 确保每个item都不能被拖动 item->setFlags(item->flags() & ~Qt::ItemIsDragEnabled & ~Qt::ItemIsDropEnabled); // 将item添加到列表 ui->detect_result_list->addItem(item); // 将自定义widget设置为item的显示内容 ui->detect_result_list->setItemWidget(item, resultWidget); targetIndex++; } } // 状态更新槽函数 void MainWindow::OnStatusUpdate(const std::string& statusMessage) { LOG_DEBUG("[UI Display] Status update: %s\n", statusMessage.c_str()); updateStatusLog(QString::fromStdString(statusMessage)); } void MainWindow::OnDetectionResult(const DetectionResult& result) { // 通过信号槽机制更新UI(确保在主线程中执行) emit detectionResultUpdateRequested(result); } void MainWindow::OnCamera1StatusChanged(bool isConnected) { // 直接更新设备状态widget if (m_deviceStatusWidget) { m_deviceStatusWidget->updateCamera1Status(isConnected); } } // 相机2状态更新槽函数 void MainWindow::OnCamera2StatusChanged(bool isConnected) { // 直接更新设备状态widget if (m_deviceStatusWidget) { m_deviceStatusWidget->updateCamera2Status(isConnected); } } // 相机个数更新槽函数 void MainWindow::OnCameraCountChanged(int cameraCount) { // 设置设备状态widget中的相机数量 if (m_deviceStatusWidget) { m_deviceStatusWidget->setCameraCount(cameraCount); } // 根据相机个数调整页面布局 adjustLayoutForCameraCount(cameraCount); // 如果只有一个相机,更新状态消息 if (cameraCount < 2) { // 更新状态消息 updateStatusLog(tr("系统使用单相机模式")); } else { // 更新状态消息 updateStatusLog(tr("系统使用双相机模式")); } } // 机械臂状态更新槽函数 void MainWindow::OnRobotConnectionChanged(bool isConnected) { // 直接更新设备状态widget if (m_deviceStatusWidget) { m_deviceStatusWidget->updateRobotStatus(isConnected); } } // 串口连接状态更新槽函数 void MainWindow::OnSerialConnectionChanged(bool isConnected) { // 直接更新设备状态widget if (m_deviceStatusWidget) { m_deviceStatusWidget->updateSerialStatus(isConnected); } } // 工作状态更新槽函数 void MainWindow::OnWorkStatusChanged(WorkStatus status) { // 通过信号槽机制更新UI(确保在主线程中执行) emit workStatusUpdateRequested(status); } void MainWindow::updateWorkStatusLabel(WorkStatus status) { // 如果状态变为Working,清空检测日志(表示开始新的检测) if (status == WorkStatus::Working) { clearDetectionLog(); } // 获取状态对应的显示文本 QString statusText = QString::fromStdString(WorkStatusToString(status)); // 在label_work中显示状态 if (!ui->label_work) return; ui->label_work->setText(statusText); statusText = "【工作状态】" + statusText; updateStatusLog(statusText); // 根据不同状态设置不同的样式和按钮启用状态 switch (status) { case WorkStatus::Ready: ui->label_work->setStyleSheet("color: green;"); setButtonsEnabled(true); // 就绪状态下启用所有按钮 break; case WorkStatus::InitIng: ui->label_work->setStyleSheet("color: blue;"); setButtonsEnabled(false); // 初始化期间禁用按钮 break; case WorkStatus::Working: ui->label_work->setStyleSheet("color: blue;"); setButtonsEnabled(false); // 工作期间禁用按钮 break; case WorkStatus::Completed: ui->label_work->setStyleSheet("color: green; font-weight: bold;"); setButtonsEnabled(true); // 完成后启用按钮 break; case WorkStatus::Error: ui->label_work->setStyleSheet("color: red; font-weight: bold;"); setButtonsEnabled(true); // 错误状态下仍可操作 break; default: ui->label_work->setStyleSheet(""); setButtonsEnabled(false); // 未知状态禁用按钮 break; } } void MainWindow::updateDetectionResultDisplay(const DetectionResult& result) { // 根据相机索引决定显示在哪个图像视图上 if (result.cameraIndex == 2) { // 第二个相机的结果显示在detect_image_2上 updateDetectionLog(tr("相机2检测结果更新")); displayImageInSecondView(result.image); } else { // 第一个相机或默认显示在detect_image上 updateDetectionLog(tr("相机1检测结果更新")); displayImage(result.image); } // 更新检测结果到列表 addDetectionResult(result); } void MainWindow::updateDetectionLog(const QString& message) { // 在UI线程中更新detect_log控件(QListView) if (!m_logModel) return ; // 添加时间戳(包含年月日) 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()); } } void MainWindow::on_btn_start_clicked() { // 检查Presenter是否已初始化 if (!m_presenter) { updateStatusLog(tr("系统未初始化,请等待初始化完成")); return; } // 清空检测日志,开始新的检测 clearDetectionLog(); // 使用Presenter启动检测 m_presenter->StartDetection(1, false); } void MainWindow::on_btn_stop_clicked() { // 检查Presenter是否已初始化 if (!m_presenter) { updateStatusLog(tr("系统未初始化,请等待初始化完成")); return; } m_presenter->StopDetection(); } void MainWindow::on_btn_camera_clicked() { // 检查是否有其他按钮已被选中 if (m_selectedButton != nullptr && m_selectedButton != ui->btn_camera) { updateStatusLog(tr("请先关闭当前打开的配置窗口")); return; } // 检查Presenter是否已初始化 if (!m_presenter) { updateStatusLog(tr("系统未初始化,请等待初始化完成")); return; } // 设置当前按钮为选中状态 setButtonSelectedState(ui->btn_camera, true); // 使用新的相机列表获取功能 std::vector availableCameras; std::vector cameraNames; // 获取相机列表和名称 m_presenter->GetCameraListWithNames(availableCameras, cameraNames); // 如果对话框不存在,创建新的DialogCamera if (nullptr == ui_dialogCamera) { ui_dialogCamera = new DialogCamera(availableCameras, cameraNames, this); // 连接对话框关闭信号 connect(ui_dialogCamera, &QDialog::finished, this, [this]() { setButtonSelectedState(ui->btn_camera, false); }); } ui_dialogCamera->show(); } void MainWindow::on_btn_algo_config_clicked() { // 检查是否有其他按钮已被选中 if (m_selectedButton != nullptr && m_selectedButton != ui->btn_algo_config) { updateStatusLog(tr("请先关闭当前打开的配置窗口")); return; } // 检查Presenter是否已初始化 if (!m_presenter) { updateStatusLog(tr("系统未初始化,请等待初始化完成")); return; } // 设置当前按钮为选中状态 setButtonSelectedState(ui->btn_algo_config, true); // 获取配置对象 IVrConfig* config = m_presenter->GetConfig(); if (!config) { // 恢复按钮状态 setButtonSelectedState(ui->btn_algo_config, false); // 设置白色字体的警告消息框 QMessageBox msgBox; msgBox.setWindowTitle("错误"); msgBox.setText("配置模块未正确初始化!"); msgBox.setIcon(QMessageBox::Warning); msgBox.setStyleSheet("QMessageBox { color: white; }"); msgBox.exec(); return; } if(nullptr == ui_dialogConfig){ ui_dialogConfig = new DialogConfig(config, this); // 连接对话框关闭信号 connect(ui_dialogConfig, &QDialog::finished, this, [this]() { setButtonSelectedState(ui->btn_algo_config, false); }); } ui_dialogConfig->show(); } void MainWindow::on_btn_camera_levelling_clicked() { // 检查是否有其他按钮已被选中 if (m_selectedButton != nullptr && m_selectedButton != ui->btn_camera_levelling) { updateStatusLog(tr("请先关闭当前打开的配置窗口")); return; } // 检查Presenter是否已初始化 if (!m_presenter) { updateStatusLog(tr("系统未初始化,请等待初始化完成")); return; } // 设置当前按钮为选中状态 setButtonSelectedState(ui->btn_camera_levelling, true); if(nullptr == ui_dialogCameraLevel){ ui_dialogCameraLevel = new DialogCameraLevel(this); // 连接对话框关闭信号 connect(ui_dialogCameraLevel, &QDialog::finished, this, [this]() { setButtonSelectedState(ui->btn_camera_levelling, false); }); } // 获取相机列表和名称 std::vector availableCameras; std::vector cameraNames; m_presenter->GetCameraListWithNames(availableCameras, cameraNames); // 设置相机列表和presenter到对话框 ui_dialogCameraLevel->setCameraList(availableCameras, cameraNames, m_presenter); ui_dialogCameraLevel->show(); } void MainWindow::on_btn_hide_clicked() { // 最小化窗口 this->showMinimized(); } void MainWindow::on_btn_close_clicked() { // 关闭应用程序 this->close(); } void MainWindow::on_btn_test_clicked() { // 检查是否有其他按钮已被选中 if (m_selectedButton != nullptr && m_selectedButton != ui->btn_test) { updateStatusLog(tr("请先关闭当前打开的配置窗口")); return; } // 设置当前按钮为选中状态 setButtonSelectedState(ui->btn_test, true); // 打开文件选择对话框 QString fileName = QFileDialog::getOpenFileName( this, tr("选择调试数据文件"), QString(), tr("激光数据文件 (*.txt);;所有文件 (*.*)") ); if (fileName.isEmpty()) { // 用户取消了文件选择,恢复按钮状态 setButtonSelectedState(ui->btn_test, false); return; } // 检查Presenter是否已初始化 if (!m_presenter) { // 恢复按钮状态 setButtonSelectedState(ui->btn_test, false); QMessageBox::warning(this, tr("错误"), tr("系统未正确初始化!")); return; } // 清空检测日志,开始新的检测 clearDetectionLog(); std::thread t([this, fileName]() { int result = m_presenter->LoadDebugDataAndDetect(fileName.toStdString()); if (result == 0) { updateStatusLog(tr("调试数据加载和检测成功")); } else { QString errorMsg = tr("调试数据加载失败,错误码: %1").arg(result); updateStatusLog(errorMsg); } // 测试完成后恢复按钮状态 QMetaObject::invokeMethod(this, [this]() { setButtonSelectedState(ui->btn_test, false); }, Qt::QueuedConnection); }); t.detach(); } // 设置按钮启用/禁用状态 void MainWindow::setButtonsEnabled(bool enabled) { // 功能按钮 if (ui->btn_start) ui->btn_start->setEnabled(enabled); if (ui->btn_stop) ui->btn_stop->setEnabled(enabled); if (ui->btn_camera) ui->btn_camera->setEnabled(enabled); if (ui->btn_algo_config) ui->btn_algo_config->setEnabled(enabled); if (ui->btn_camera_levelling) ui->btn_camera_levelling->setEnabled(enabled); // if (ui->btn_test) ui->btn_test->setEnabled(enabled); // 窗口控制按钮(最小化和关闭)始终可用 // if (ui->btn_hide) ui->btn_hide->setEnabled(true); // if (ui->btn_close) ui->btn_close->setEnabled(true); } // 设置按钮图像 void MainWindow::setButtonImage(QPushButton* button, const QString& imagePath) { if (button) { QString styleSheet = QString("image: url(%1);").arg(imagePath); button->setStyleSheet(styleSheet); } } // 设置按钮选中状态 void MainWindow::setButtonSelectedState(QPushButton* button, bool selected) { if (!button) return; QString normalImage, selectedImage; // 根据按钮确定对应的图像路径 if (button == ui->btn_camera) { normalImage = ":/resource/config_camera.png"; selectedImage = ":/resource/config_camera_s.png"; } else if (button == ui->btn_algo_config) { normalImage = ":/resource/config_algo.png"; selectedImage = ":/resource/config_algo_s.png"; } else if (button == ui->btn_camera_levelling) { normalImage = ":/resource/config_camera_level.png"; selectedImage = ":/resource/config_camera_level_s.png"; } else if (button == ui->btn_test) { normalImage = ":/resource/config_data_test.png"; selectedImage = ":/resource/config_data_test_s.png"; } // 设置对应的图像 if (selected) { setButtonImage(button, selectedImage); m_selectedButton = button; // 禁用其他按钮 setOtherButtonsEnabled(button, false); } else { setButtonImage(button, normalImage); if (m_selectedButton == button) { m_selectedButton = nullptr; // 启用所有按钮 setOtherButtonsEnabled(nullptr, true); } } } // 恢复所有按钮状态 void MainWindow::restoreAllButtonStates() { setButtonSelectedState(ui->btn_camera, false); setButtonSelectedState(ui->btn_algo_config, false); setButtonSelectedState(ui->btn_camera_levelling, false); setButtonSelectedState(ui->btn_test, false); } // 设置其他按钮的启用状态(用于互斥控制) void MainWindow::setOtherButtonsEnabled(QPushButton* exceptButton, bool enabled) { QList configButtons = { ui->btn_camera, ui->btn_algo_config, ui->btn_camera_levelling, ui->btn_test }; for (QPushButton* button : configButtons) { if (button && button != exceptButton) { button->setEnabled(enabled); } } } // 根据相机个数调整页面布局 void MainWindow::adjustLayoutForCameraCount(int cameraCount) { if (cameraCount <= 1) { // 单相机模式布局 setupSingleCameraLayout(); } else { // 多相机模式布局 setupMultiCameraLayout(); } } // 设置单相机模式布局 void MainWindow::setupSingleCameraLayout() { // 单相机模式下的原始布局 // detect_image: 占据左侧大部分空间 ui->detect_image->setGeometry(20, 140, 1311, 890); // detect_image_2: 隐藏 ui->detect_image_2->setVisible(false); // 设备状态框架: 位于右上方,恢复原始尺寸 ui->frame_dev->setGeometry(1344, 140, 556, 64); // 检测结果列表: 位于右侧中间 ui->detect_result_list->setGeometry(1344, 216, 556, 622); // 单相机模式下恢复原有的垂直滚动设置 ui->detect_result_list->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); ui->detect_result_list->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); ui->detect_result_list->setFlow(QListView::LeftToRight); // 从左到右排列 ui->detect_result_list->setWrapping(true); // 允许换行 // 单相机模式:设置gridSize,gridHeight = 207 int gridWidth = 275; int gridHeight = 206; ui->detect_result_list->setGridSize(QSize(gridWidth, gridHeight)); // 恢复设备状态widget的原始布局 m_deviceStatusWidget->setCameraCount(1); // 日志: 位于右下方 ui->detect_log->setGeometry(1344, 850, 556, 176); updateStatusLog(tr("页面布局已调整为单相机模式")); } // 设置多相机模式布局 void MainWindow::setupMultiCameraLayout() { // 多相机模式下的布局调整 // detect_image: 缩小到左侧一半空间 ui->detect_image->setGeometry(20, 140, 934, 660); // detect_image_2: 显示在右侧一半空间 ui->detect_image_2->setVisible(true); ui->detect_image_2->setGeometry(964, 140, 934, 660); // 设备状态框架: 移动到下方左侧,调整为更高的尺寸以适应纵向排布 ui->frame_dev->setGeometry(20, 806, 180, 200); // 检测结果列表: 移动到下方中间 ui->detect_result_list->setGeometry(206, 806, 1380, 200); // 多相机模式:设置gridSize,gridHeight = 200 int gridWidth = 230; int gridHeight = 190; ui->detect_result_list->setGridSize(QSize(gridWidth, gridHeight)); m_deviceStatusWidget->setCameraCount(2); // 日志: 移动到下方右侧 ui->detect_log->setGeometry(1592, 806, 308, 200); updateStatusLog(tr("页面布局已调整为多相机模式")); }