#include "mainwindow.h" #include "ui_mainwindow.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "IVrUtils.h" #include "PathManager.h" #include "SingleTargetResultWidget.h" #include "StyledMessageBox.h" #include "Version.h" #include "VrDateUtils.h" #include "VrLog.h" namespace { QString GuideStateName(ParkingGuideState state) { switch (state) { case ParkingGuideState::DockingStarted: return QStringLiteral("泊位引导启动"); case ParkingGuideState::Capturing: return QStringLiteral("捕获"); case ParkingGuideState::Tracking: return QStringLiteral("跟踪"); case ParkingGuideState::ApproachRate: return QStringLiteral("接近率"); case ParkingGuideState::CenterLineAligned: return QStringLiteral("对准中心线"); case ParkingGuideState::Slow: return QStringLiteral("慢速"); case ParkingGuideState::AzimuthGuidance: return QStringLiteral("方位引导"); case ParkingGuideState::StopPositionReached: return QStringLiteral("到达停止位置"); case ParkingGuideState::DockingCompleted: return QStringLiteral("泊位引导完成"); case ParkingGuideState::Overshot: return QStringLiteral("越位"); case ParkingGuideState::StoppedShort: return QStringLiteral("提前停止"); case ParkingGuideState::Waiting: return QStringLiteral("等待"); case ParkingGuideState::SlowBadWeather: return QStringLiteral("异常慢速-恶劣天气"); case ParkingGuideState::SlowAircraftLost: return QStringLiteral("异常慢速-飞机丢失"); case ParkingGuideState::AircraftVerificationFailed: return QStringLiteral("飞机验证失败"); case ParkingGuideState::GateBlocked: return QStringLiteral("机位受阻"); case ParkingGuideState::ViewBlocked: return QStringLiteral("视野受阻"); case ParkingGuideState::SbuStop: return QStringLiteral("SBU停止"); case ParkingGuideState::TooFast: return QStringLiteral("速度过快"); case ParkingGuideState::EmergencyStop: return QStringLiteral("紧急停止"); case ParkingGuideState::ChocksOn: return QStringLiteral("挡轮挡"); case ParkingGuideState::SystemError: return QStringLiteral("系统错误"); case ParkingGuideState::SystemFailure: return QStringLiteral("系统故障"); case ParkingGuideState::PowerFailure: return QStringLiteral("电源故障"); case ParkingGuideState::Unknown: default: return QStringLiteral("未知"); } } QString FormatGuideState(const ParkingSpaceGuideInfo& info) { const ParkingGuideState state = ParkingGuideStateFromCode(info.guideStateCode); const int displayCode = state == ParkingGuideState::Unknown ? info.guideStateCode : static_cast(state); QString text = QStringLiteral("%1 %2").arg(displayCode).arg(GuideStateName(state)); const QString guideText = info.guideText.trimmed(); if (!guideText.isEmpty()) { text += QStringLiteral(" / %1").arg(guideText); } return text; } QString FormatNumber(double value, const QString& unit, int precision = 2) { return QStringLiteral("%1%2").arg(value, 0, 'f', precision).arg(unit); } bool IsKnownAircraftModel(const QString& modelType) { const QString normalized = modelType.trimmed().toUpper(); return normalized == QStringLiteral("A320") || normalized == QStringLiteral("B737"); } QString FormatModelVerification(const ParkingSpaceGuideInfo& info) { const QString expected = info.expectedModelType.trimmed().toUpper(); const QString recognized = info.modelType.trimmed().toUpper(); if (!IsKnownAircraftModel(recognized)) { return QStringLiteral("未知"); } if (!IsKnownAircraftModel(expected)) { return QStringLiteral("实测(%1)").arg(recognized); } if (info.modelMatched) { return QStringLiteral("匹配(%1)").arg(recognized); } if (recognized.compare(expected, Qt::CaseInsensitive) != 0) { return QStringLiteral("不匹配(%1)").arg(recognized); } return QStringLiteral("未知"); } void ConfigureResultWidget(SingleTargetResultWidget* resultWidget) { if (!resultWidget) { return; } // Keep this presentation local to ParkingSpaceGuide; UICommon defaults are shared by other apps. auto* mainLayout = qobject_cast(resultWidget->layout()); if (mainLayout && mainLayout->count() > 0) { auto* headerLayout = qobject_cast(mainLayout->itemAt(0)->layout()); if (headerLayout && headerLayout->count() == 2) { headerLayout->setSpacing(12); headerLayout->setStretch(0, 0); headerLayout->setStretch(1, 0); headerLayout->addStretch(1); } } resultWidget->setStyleSheet( resultWidget->styleSheet() + QStringLiteral( "#singleTargetTitle { font-size: 28px; }" "#singleTargetStatus { font-size: 34px; font-weight: 700; padding: 0; }" "#singleTargetEmpty { font-size: 28px; }" "#singleTargetFieldName { font-size: 24px; }" "#singleTargetFieldValue { font-size: 26px; }")); auto* statusLabel = resultWidget->findChild(QStringLiteral("singleTargetStatus")); if (statusLabel) { statusLabel->setStyleSheet(QStringLiteral("background-color: transparent;")); } } } // namespace MainWindow::MainWindow(QWidget* parent) : MainWindow(ParkingSpaceGuideSimulationOptions(), parent) { } MainWindow::MainWindow(const ParkingSpaceGuideSimulationOptions& simulationOptions, QWidget* parent) : QMainWindow(parent) , ui(new Ui::MainWindow) , m_simulationOptions(simulationOptions) { ui->setupUi(this); setWindowTitle(m_simulationOptions.enabled ? QStringLiteral("停机引导(仿真)") : QStringLiteral("停机引导")); if (ui->label) { ui->label->setText(QStringLiteral("停机引导")); } #ifdef _WIN32 setWindowIcon(QIcon(":/common/resource/logo.ico")); #else setWindowIcon(QIcon(":/common/resource/logo.png")); #endif QFont statusFont = statusBar()->font(); statusFont.setPointSize(12); statusBar()->setFont(statusFont); statusBar()->setStyleSheet("QStatusBar { color: rgb(239, 241, 245); padding: 20px; }"); const QString versionWithBuildTime = QString("%1_%2%3%4%5%6%7") .arg(GetParkingSpaceGuideFullVersion()) .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')); m_versionLabel = new QLabel(versionWithBuildTime); m_versionLabel->setStyleSheet("color: rgb(239, 241, 245); font-size: 20px; margin-right: 16px;"); m_versionLabel->setCursor(Qt::PointingHandCursor); m_versionLabel->installEventFilter(this); statusBar()->addPermanentWidget(m_versionLabel); setWindowFlags(Qt::FramelessWindowHint); showMaximized(); ui->label_work->setVisible(false); ui->detect_image->setScene(new QGraphicsScene(this)); m_logHelper = new DetectLogHelper(ui->detect_log, this); qRegisterMetaType("DetectionResult"); qRegisterMetaType("WorkStatus"); qRegisterMetaType("ParkingSpaceGuidePosition"); connect(this, &MainWindow::workStatusUpdateRequested, this, &MainWindow::updateWorkStatusLabel, Qt::QueuedConnection); setupContextMenu(); updateStatusLog(tr("设备初始化开始")); Init(); } MainWindow::~MainWindow() { if (m_presenter) { m_presenter->RequestShutdown(); } if (m_initThread.joinable()) { m_initThread.join(); } if (m_testThread.joinable()) { m_testThread.join(); } if (m_presenter) { m_presenter->DeinitApp(); m_presenter->SetStatusCallback(static_cast(nullptr)); delete m_presenter; m_presenter = nullptr; } delete ui; } void MainWindow::closeEvent(QCloseEvent* event) { if (m_shutdownComplete) { event->accept(); return; } event->ignore(); if (m_closing.exchange(true)) { return; } QApplication::setQuitOnLastWindowClosed(false); setEnabled(false); hide(); if (m_presenter) { m_presenter->RequestShutdown(); } QTimer::singleShot(0, this, &MainWindow::continueShutdown); } void MainWindow::continueShutdown() { if (m_initRunning.load() || m_testRunning.load()) { QTimer::singleShot(20, this, &MainWindow::continueShutdown); return; } if (m_initThread.joinable()) { m_initThread.join(); } if (m_testThread.joinable()) { m_testThread.join(); } if (m_presenter) { m_presenter->DeinitApp(); m_presenter->SetStatusCallback(static_cast(nullptr)); } m_shutdownComplete = true; QApplication::quit(); } bool MainWindow::eventFilter(QObject* obj, QEvent* event) { if (obj == m_versionLabel && event->type() == QEvent::MouseButtonPress) { const QString algoVersion = m_presenter ? m_presenter->GetAlgoVersion() : QString(); AboutDialog dlg(PARKINGSPACEGUIDE_APP_NAME, m_versionLabel->text(), algoVersion, this); dlg.exec(); return true; } return QMainWindow::eventFilter(obj, event); } void MainWindow::updateStatusLog(const QString& message) { if (QThread::currentThread() != thread()) { QMetaObject::invokeMethod(this, [this, message]() { updateStatusLog(message); }, Qt::QueuedConnection); return; } if (m_logHelper) { m_logHelper->appendLog(message); } } void MainWindow::clearDetectionLog() { if (m_logHelper) { m_logHelper->clearLog(); } } void MainWindow::Init() { m_presenter = new ParkingSpaceGuidePresenter(m_simulationOptions); m_presenter->SetStatusCallback(this); connect(m_presenter, &ParkingSpaceGuidePresenter::SelectedModelChanged, this, [this](const QString& modelType) { const int index = ui->combo_aircraft_model->findText( modelType, Qt::MatchFixedString); if (index >= 0 && ui->combo_aircraft_model->currentIndex() != index) { ui->combo_aircraft_model->setCurrentIndex(index); } }, Qt::QueuedConnection); m_deviceStatusWidget = new DeviceStatusWidget(ui->frame_dev); m_deviceStatusWidget->setCameraCount(2, true); m_deviceStatusWidget->setCamera1Name(tr("平面相机")); m_deviceStatusWidget->setCamera2Name(tr("雷达")); m_deviceStatusWidget->setRobotVisible(false); connect(m_deviceStatusWidget, SIGNAL(cameraClicked(int)), this, SLOT(onCameraClicked(int))); auto* frameDevLayout = new QVBoxLayout(ui->frame_dev); frameDevLayout->setContentsMargins(0, 0, 0, 0); frameDevLayout->addWidget(m_deviceStatusWidget); SingleTargetResultWidget::setupListWidget(ui->detect_result_list, QSize(520, 420)); ensureResultWidget(); setButtonsEnabled(false); m_initRunning.store(true); m_initThread = std::thread([this]() { updateStatusLog(tr("系统初始化中...")); const int result = m_presenter->Init(); if (m_closing.load()) { m_initRunning.store(false); return; } if (result != 0) { updateStatusLog(tr("初始化失败,错误码:%1").arg(result)); } else { updateStatusLog(tr("初始化完成")); if (m_simulationOptions.enabled) { updateStatusLog(tr("仿真数据已全部加载到内存,请选择机型并点击开始进行回放")); } else if (!m_presenter->StartAutomaticDetection(-1)) { updateStatusLog(tr("自动实时检测启动失败,请检查雷达和安全状态")); } } m_initRunning.store(false); }); } void MainWindow::displayImage(const QImage& image) { if (image.isNull()) { updateStatusLog(tr("无预览图像")); return; } if (m_imagePixmapItem && m_lastImageCacheKey == image.cacheKey()) { return; } QGraphicsScene* scene = ui->detect_image->scene(); const QPixmap pixmap = QPixmap::fromImage(image); if (!m_imagePixmapItem) { m_imagePixmapItem = scene->addPixmap(pixmap); m_imagePixmapItem->setTransformationMode(Qt::SmoothTransformation); } else { m_imagePixmapItem->setPixmap(pixmap); } const bool sizeChanged = m_lastImageSize != image.size(); m_lastImageSize = image.size(); m_lastImageCacheKey = image.cacheKey(); if (sizeChanged) { scene->setSceneRect(m_imagePixmapItem->boundingRect()); ui->detect_image->fitInView(m_imagePixmapItem, Qt::KeepAspectRatio); } } void MainWindow::ensureResultWidget() { if (m_resultWidget && m_resultItem) { return; } m_resultWidget = new SingleTargetResultWidget(); m_resultWidget->setAutoFillBackground(true); m_resultItem = new QListWidgetItem(); m_resultItem->setBackground(QBrush(Qt::transparent)); m_resultItem->setFlags(m_resultItem->flags() & ~Qt::ItemIsDragEnabled & ~Qt::ItemIsDropEnabled); ui->detect_result_list->addItem(m_resultItem); ui->detect_result_list->setItemWidget(m_resultItem, m_resultWidget); ConfigureResultWidget(m_resultWidget); updateResultItemSize(); addDetectionResult(DetectionResult()); } void MainWindow::updateResultItemSize() { if (!m_resultItem || !ui || !ui->detect_result_list) { return; } const int itemWidth = (std::max)( 360, ui->detect_result_list->viewport()->width() - 8); const int itemHeight = (std::max)( 360, ui->detect_result_list->viewport()->height()); const QSize itemSize(itemWidth, itemHeight); if (m_resultItem->sizeHint() != itemSize) { m_resultItem->setSizeHint(itemSize); SingleTargetResultWidget::setupListWidget( ui->detect_result_list, itemSize); } } void MainWindow::addDetectionResult(const DetectionResult& result) { ensureResultWidget(); ParkingSpaceGuideInfo displayInfo; if (result.parkingSpaceInfoList.empty()) { displayInfo.targetId = QStringLiteral("T001"); displayInfo.parkId = QStringLiteral("P01"); displayInfo.expectedModelType.clear(); displayInfo.modelType = QStringLiteral("未知"); displayInfo.guideStateCode = static_cast(ParkingGuideState::Waiting); displayInfo.guideText = QStringLiteral("WAIT"); } else { displayInfo = result.parkingSpaceInfoList.front(); } const ParkingSpaceGuideInfo& info = displayInfo; const ParkingGuideState guideState = ParkingGuideStateFromCode(info.guideStateCode); m_resultWidget->setTitle(tr("当前状态")); m_resultWidget->setStatus( GuideStateName(guideState), info.hasException || guideState == ParkingGuideState::EmergencyStop); QLabel* statusLabel = m_resultWidget->findChild( QStringLiteral("singleTargetStatus")); if (statusLabel) { statusLabel->setStyleSheet( QStringLiteral("background-color: transparent;")); } m_resultWidget->setFields({ {tr("目标编号"), info.targetId}, {tr("停机位"), info.parkId}, {tr("机型校验"), FormatModelVerification(info)}, {tr("轮挡区域人员"), info.personInChockRegion ? tr("已检测") : tr("未检测")}, {tr("区域人数"), QString::number(info.personCount)}, {tr("引导状态"), FormatGuideState(info)}, {tr("到停止线"), FormatNumber(info.distance, tr("毫米"))}, {tr("横向偏移"), FormatNumber(info.lateralOffset, tr("毫米"))}, {tr("偏转角"), FormatNumber(info.angle, tr("度"))}, {tr("速度"), FormatNumber(info.aircraftSpeed, tr("米/秒"), 2)} }); } void MainWindow::OnStatusUpdate(const std::string& statusMessage) { updateStatusLog(QString::fromStdString(statusMessage)); } void MainWindow::OnDetectionResult(const DetectionResult& result) { const ParkingGuideState guideState = result.parkingSpaceInfoList.empty() ? ParkingGuideState::Unknown : ParkingGuideStateFromCode( result.parkingSpaceInfoList.front().guideStateCode); const bool preserveStateTransition = result.imageIsRoi || guideState == ParkingGuideState::StopPositionReached || guideState == ParkingGuideState::DockingCompleted || guideState == ParkingGuideState::ChocksOn; if (preserveStateTransition) { if (QThread::currentThread() == thread()) { updateDetectionResultDisplay(result); } else { QMetaObject::invokeMethod(this, [this, result]() { updateDetectionResultDisplay(result); }, Qt::QueuedConnection); } return; } bool queueUpdate = false; { std::lock_guard lock(m_pendingResultMutex); m_pendingDetectionResult = result; if (!m_detectionResultUpdateQueued) { m_detectionResultUpdateQueued = true; queueUpdate = true; } } if (!queueUpdate) { return; } QMetaObject::invokeMethod(this, [this]() { DetectionResult latestResult; { std::lock_guard lock(m_pendingResultMutex); latestResult = m_pendingDetectionResult; m_detectionResultUpdateQueued = false; } updateDetectionResultDisplay(latestResult); }, Qt::QueuedConnection); } void MainWindow::resizeEvent(QResizeEvent* event) { QMainWindow::resizeEvent(event); updateResultItemSize(); if (m_imagePixmapItem && ui && ui->detect_image) { ui->detect_image->fitInView(m_imagePixmapItem, Qt::KeepAspectRatio); } } void MainWindow::OnCamera1StatusChanged(bool isConnected) { if (QThread::currentThread() != thread()) { QMetaObject::invokeMethod(this, [this, isConnected]() { OnCamera1StatusChanged(isConnected); }, Qt::QueuedConnection); return; } if (m_deviceStatusWidget) { m_deviceStatusWidget->updateCamera1Status(isConnected); } } void MainWindow::OnCamera2StatusChanged(bool isConnected) { if (QThread::currentThread() != thread()) { QMetaObject::invokeMethod(this, [this, isConnected]() { OnCamera2StatusChanged(isConnected); }, Qt::QueuedConnection); return; } if (m_deviceStatusWidget) { m_deviceStatusWidget->updateCamera2Status(isConnected); } } void MainWindow::OnRobotConnectionChanged(bool isConnected) { Q_UNUSED(isConnected); } void MainWindow::OnSerialConnectionChanged(bool isConnected) { if (QThread::currentThread() != thread()) { QMetaObject::invokeMethod(this, [this, isConnected]() { OnSerialConnectionChanged(isConnected); }, Qt::QueuedConnection); return; } if (m_deviceStatusWidget) { m_deviceStatusWidget->updateSerialStatus(isConnected); } } void MainWindow::OnCameraCountChanged(int cameraCount) { Q_UNUSED(cameraCount); if (QThread::currentThread() != thread()) { QMetaObject::invokeMethod(this, [this]() { OnCameraCountChanged(2); }, Qt::QueuedConnection); return; } if (m_deviceStatusWidget) { m_deviceStatusWidget->setCameraCount(2, true); m_deviceStatusWidget->setCamera1Name(tr("平面相机")); m_deviceStatusWidget->setCamera2Name(tr("雷达")); } } void MainWindow::OnWorkStatusChanged(WorkStatus status) { emit workStatusUpdateRequested(status); } void MainWindow::updateWorkStatusLabel(WorkStatus status) { m_emergencyStopControlsLocked = status == WorkStatus::EmergencyStop; if (ui_dialogAlgoArg) { const bool detectionRunning = m_presenter && m_presenter->IsDetectionRunning(); ui_dialogAlgoArg->setEnabled( !safetyControlsLocked() && !detectionRunning); } if (status == WorkStatus::Working || status == WorkStatus::Detecting) { clearDetectionLog(); } if (!ui->label_work) { return; } const QString statusText = QString::fromStdString(WorkStatusToString(status)); ui->label_work->setText(statusText); updateStatusLog(tr("工作状态:%1").arg(statusText)); switch (status) { case WorkStatus::Ready: ui->label_work->setStyleSheet("color: green;"); resetAircraftModelSelection(); setButtonsEnabled(true); if (ui->btn_stop) ui->btn_stop->setEnabled(false); break; case WorkStatus::InitIng: ui->label_work->setStyleSheet("color: blue;"); setButtonsEnabled(false); break; case WorkStatus::Working: ui->label_work->setStyleSheet("color: blue;"); setButtonsEnabled(false); if (ui->btn_stop) ui->btn_stop->setEnabled(true); break; case WorkStatus::Detecting: ui->label_work->setStyleSheet("color: blue;"); setButtonsEnabled(false); break; case WorkStatus::Completed: ui->label_work->setStyleSheet("color: green; font-weight: bold;"); resetAircraftModelSelection(); setButtonsEnabled(true); if (ui->btn_stop) ui->btn_stop->setEnabled(false); break; case WorkStatus::Error: ui->label_work->setStyleSheet("color: red; font-weight: bold;"); resetAircraftModelSelection(); setButtonsEnabled(true); if (ui->btn_stop) ui->btn_stop->setEnabled(false); break; case WorkStatus::EmergencyStop: ui->label_work->setStyleSheet("color: red; font-weight: bold;"); setButtonsEnabled(false); if (ui->btn_stop) ui->btn_stop->setEnabled(false); break; default: ui->label_work->setStyleSheet(""); setButtonsEnabled(false); break; } } void MainWindow::updateDetectionResultDisplay(const DetectionResult& result) { if (!result.parkingSpaceInfoList.empty()) { const ParkingGuideState state = ParkingGuideStateFromCode( result.parkingSpaceInfoList.front().guideStateCode); if (state == ParkingGuideState::EmergencyStop || state == ParkingGuideState::SystemFailure) { m_safetyResultControlsLocked = true; } else if (state == ParkingGuideState::Waiting) { m_safetyResultControlsLocked = false; } if (ui_dialogAlgoArg) { const bool detectionRunning = m_presenter && m_presenter->IsDetectionRunning(); ui_dialogAlgoArg->setEnabled( !safetyControlsLocked() && !detectionRunning); } if (safetyControlsLocked()) { setButtonsEnabled(false); } } if (!result.image.isNull()) { displayImage(result.image); } addDetectionResult(result); } void MainWindow::on_btn_start_clicked() { if (!m_presenter) { updateStatusLog(tr("系统未初始化")); return; } const QString selectedModel = ui->combo_aircraft_model->currentIndex() > 0 ? ui->combo_aircraft_model->currentText().trimmed() : QString(); setButtonsEnabled(false); if (ui_dialogAlgoArg) { ui_dialogAlgoArg->setEnabled(false); } clearDetectionLog(); updateStatusLog(selectedModel.isEmpty() ? tr("开始停机引导检测,机型以实际检测结果为准") : tr("开始%1停机引导检测").arg(selectedModel)); if (!m_presenter->StartDetection(selectedModel, -1)) { updateStatusLog(tr("启动检测失败")); setButtonsEnabled(true); if (ui_dialogAlgoArg) { ui_dialogAlgoArg->setEnabled(!safetyControlsLocked()); } } } void MainWindow::on_btn_stop_clicked() { if (m_presenter) { m_presenter->StopDetection(); } } void MainWindow::showParameterDialog(DialogAlgoArg::ConfigPage page, QPushButton* button) { if (m_selectedButton && m_selectedButton != button) { updateStatusLog(tr("请先关闭当前参数窗口")); return; } if (!m_presenter) { updateStatusLog(tr("系统未初始化")); return; } if (m_presenter->IsDetectionRunning()) { updateStatusLog(tr("停机引导进行中,暂不能修改参数")); return; } setButtonSelectedState(button, true); if (!ui_dialogAlgoArg) { ui_dialogAlgoArg = new DialogAlgoArg(this); connect(ui_dialogAlgoArg, &QDialog::finished, this, [this]() { if (m_selectedButton) { setButtonSelectedState(m_selectedButton, false); } }); } ui_dialogAlgoArg->SetPresenter(m_presenter); ui_dialogAlgoArg->SetCurrentPage(page); ui_dialogAlgoArg->show(); ui_dialogAlgoArg->raise(); ui_dialogAlgoArg->activateWindow(); } void MainWindow::on_btn_camera_clicked() { updateStatusLog(tr("打开相机参数设置")); showParameterDialog(DialogAlgoArg::ConfigPage::Camera, ui->btn_camera); } void MainWindow::on_btn_algo_config_clicked() { updateStatusLog(tr("打开检测参数设置")); showParameterDialog(DialogAlgoArg::ConfigPage::Detection, ui->btn_algo_config); } void MainWindow::on_btn_camera_levelling_clicked() { updateStatusLog(tr("打开雷达地面调平参数")); showParameterDialog(DialogAlgoArg::ConfigPage::Calibration, ui->btn_camera_levelling); } void MainWindow::on_btn_hide_clicked() { showMinimized(); } void MainWindow::on_btn_close_clicked() { close(); } void MainWindow::on_btn_test_clicked() { if (!m_presenter) { StyledMessageBox::warning(this, tr("错误"), tr("系统未初始化")); return; } if (m_selectedButton && m_selectedButton != ui->btn_test) { updateStatusLog(tr("请先关闭当前参数窗口")); return; } setButtonSelectedState(ui->btn_test, true); setButtonsEnabled(false); clearDetectionLog(); updateStatusLog(tr("运行预留算法接口测试")); if (m_testThread.joinable()) { m_testThread.join(); } m_testRunning.store(true); m_testThread = std::thread([this]() { const bool ok = m_presenter->TriggerDetection(-1); if (!m_closing.load()) { updateStatusLog(ok ? tr("测试检测完成") : tr("测试检测失败")); } QMetaObject::invokeMethod(this, [this]() { if (m_closing.load()) { return; } setButtonSelectedState(ui->btn_test, false); const bool running = m_presenter && m_presenter->IsDetectionRunning(); setButtonsEnabled(!running); if (ui->btn_stop) { ui->btn_stop->setEnabled( running && !safetyControlsLocked()); } }, Qt::QueuedConnection); m_testRunning.store(false); }); } void MainWindow::setButtonsEnabled(bool enabled) { const bool controlsEnabled = enabled && !safetyControlsLocked() && !(m_presenter && m_presenter->IsDetectionRunning()); if (ui->combo_aircraft_model) { ui->combo_aircraft_model->setEnabled(controlsEnabled); } if (ui->btn_start) { ui->btn_start->setEnabled(controlsEnabled); } if (ui->btn_stop) ui->btn_stop->setEnabled(controlsEnabled); if (ui->btn_camera) ui->btn_camera->setEnabled(controlsEnabled); if (ui->btn_algo_config) ui->btn_algo_config->setEnabled(controlsEnabled); if (ui->btn_camera_levelling) ui->btn_camera_levelling->setEnabled(controlsEnabled); if (ui->btn_test) ui->btn_test->setEnabled(controlsEnabled); } void MainWindow::resetAircraftModelSelection() { if (ui->combo_aircraft_model) { ui->combo_aircraft_model->setCurrentIndex(0); } } bool MainWindow::safetyControlsLocked() const { return m_emergencyStopControlsLocked || m_safetyResultControlsLocked; } void MainWindow::setButtonImage(QPushButton* button, const QString& imagePath) { if (button) { button->setStyleSheet(QString("image: url(%1);background-color: rgb(38, 40, 47);border: none;") .arg(imagePath)); } } void MainWindow::setButtonSelectedState(QPushButton* button, bool selected) { if (!button) { return; } QString normalImage; QString selectedImage; if (button == ui->btn_camera) { normalImage = ":/common/resource/config_camera.png"; selectedImage = ":/common/resource/config_camera_s.png"; } else if (button == ui->btn_algo_config) { normalImage = ":/common/resource/config_algo.png"; selectedImage = ":/common/resource/config_algo_s.png"; } else if (button == ui->btn_camera_levelling) { normalImage = ":/common/resource/config_camera_level.png"; selectedImage = ":/common/resource/config_camera_level_s.png"; } else if (button == ui->btn_test) { normalImage = ":/common/resource/config_data_test.png"; selectedImage = ":/common/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) { const QList configButtons = { ui->btn_camera, ui->btn_algo_config, ui->btn_camera_levelling, ui->btn_test }; const bool controlsEnabled = enabled && !safetyControlsLocked() && !(m_presenter && m_presenter->IsDetectionRunning()); for (QPushButton* button : configButtons) { if (button && button != exceptButton) { button->setEnabled(controlsEnabled); } } } void MainWindow::onCameraClicked(int cameraIndex) { if (m_presenter) { m_presenter->SetDefaultCameraIndex(cameraIndex); } } void MainWindow::setupContextMenu() { m_contextMenu = new QMenu(this); m_saveDataAction = new QAction(tr("保存检测数据"), this); m_contextMenu->addAction(m_saveDataAction); connect(m_saveDataAction, &QAction::triggered, this, &MainWindow::onSaveDetectionData); ui->detect_image->setContextMenuPolicy(Qt::CustomContextMenu); connect(ui->detect_image, &QGraphicsView::customContextMenuRequested, this, &MainWindow::showContextMenu); } void MainWindow::showContextMenu(const QPoint& pos) { if (!m_presenter) { updateStatusLog(tr("系统未初始化,无法保存数据")); return; } if (m_presenter->GetDetectionDataCacheSize() == 0) { updateStatusLog(tr("没有可保存的检测数据")); return; } m_contextMenu->exec(ui->detect_image->mapToGlobal(pos)); } void MainWindow::onSaveDetectionData() { if (!m_presenter) { updateStatusLog(tr("系统未初始化,无法保存数据")); return; } const QString dirPath = QFileDialog::getExistingDirectory( this, tr("选择保存目录"), QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation), QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks); if (dirPath.isEmpty()) { updateStatusLog(tr("保存已取消")); return; } const std::string timeStamp = CVrDateUtils::GetNowTime(); const QString fileName = QString("停机引导_%1_%2.txt") .arg(m_presenter->GetDetectIndex()) .arg(QString::fromStdString(timeStamp)); const QString fullPath = QDir(dirPath).filePath(fileName); if (saveDetectionDataToFile(fullPath)) { updateStatusLog(tr("检测数据已保存:%1").arg(fileName)); } else { updateStatusLog(tr("保存检测数据失败")); } } bool MainWindow::saveDetectionDataToFile(const QString& filePath) { if (!m_presenter) { return false; } try { const int result = m_presenter->SaveDetectionDataToFile(filePath.toStdString()); if (result != 0) { updateStatusLog(tr("保存数据失败,错误码:%1").arg(result)); return false; } return true; } catch (const std::exception&) { updateStatusLog(tr("保存数据异常")); return false; } }