972 lines
31 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 "mainwindow.h"
#include "ui_mainwindow.h"
#include <QGraphicsScene>
#include <QStandardItemModel>
#include <QDebug>
#include <QPainter>
#include <QBrush>
#include <QMessageBox>
#include <QFileDialog>
#include <QListWidgetItem>
#include <QtMath>
#include <QThread>
#include <QDateTime>
#include <QTextCursor>
#include <QStringListModel>
#include <QScreen>
#include <QApplication>
#include "VrLog.h"
#include "VrDateUtils.h"
#include <QIcon>
#include <QMetaType>
#include <QLabel>
#include <QVBoxLayout>
#include <QMenu>
#include <QAction>
#include <QStandardPaths>
#include <QDir>
#include <QGraphicsView>
#include <QEvent>
#include "TunnelChannelPresenter.h"
#include "Version.h"
#include "IVrUtils.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
, m_selectedButton(nullptr)
, m_contextMenu(nullptr)
, m_saveDataAction(nullptr)
{
ui->setupUi(this);
// 设置窗口图标Windows使用.ico格式
#ifdef _WIN32
this->setWindowIcon(QIcon(":/common/resource/logo.ico"));
#else
this->setWindowIcon(QIcon(":/common/resource/logo.png"));
#endif
// 设置状态栏字体
QFont statusFont = statusBar()->font();
statusFont.setPointSize(12);
statusBar()->setFont(statusFont);
// 设置状态栏颜色和padding
statusBar()->setStyleSheet("QStatusBar { color: rgb(239, 241, 245); padding: 20px; }");
// 在状态栏右侧添加版本信息(包含编译时间)
QString versionWithBuildTime = QString("%1_%2%3%4%5%6%7")
.arg(GetTunnelChannelFullVersion())
.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);
// 初始化3D图像视图的GraphicsScene
QGraphicsScene* scene3d = new QGraphicsScene(this);
ui->detect_image_3d->setScene(scene3d);
// 初始化2D图像视图的GraphicsScene
QGraphicsScene* scene2d = new QGraphicsScene(this);
ui->detect_image_2d->setScene(scene2d);
// 初始化日志模型
m_logModel = new QStringListModel(this);
ui->detect_log->setModel(m_logModel);
// 注册自定义类型,使其能够在信号槽中跨线程传递
qRegisterMetaType<TunnelDetectionResult>("TunnelDetectionResult");
qRegisterMetaType<WorkStatus>("WorkStatus");
qRegisterMetaType<TunnelPosition>("TunnelPosition");
// 连接工作状态更新信号槽
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);
// 连接2D图像更新信号槽
connect(this, &MainWindow::hikImageUpdateRequested, this, &MainWindow::updateHikImageDisplay);
// 初始化右键菜单
setupContextMenu();
// 初始化补光灯按钮
setupSupplementLightButton();
updateStatusLog(tr("设备开始初始化..."));
// 初始化模块
Init();
}
MainWindow::~MainWindow()
{
// 释放业务逻辑处理类
if (m_presenter) {
delete m_presenter;
m_presenter = nullptr;
}
delete ui;
}
void MainWindow::updateStatusLog(const QString& message)
{
// 通过信号槽机制更新detect_log控件
emit logUpdateRequested(message);
}
void MainWindow::clearDetectionLog()
{
// 通过信号槽机制清空日志
emit logClearRequested();
}
void MainWindow::Init()
{
// 创建业务逻辑处理类
m_presenter = new TunnelChannelPresenter();
// 设置状态回调接口使用BasePresenter的模板方法
m_presenter->SetStatusCallback<IYTunnelChannelStatus>(this);
// 设置海康相机显示窗口句柄(使用 QGraphicsView 的 viewport 进行硬件渲染)
// viewport() 返回内部用于显示的 QWidget适合作为海康SDK的渲染目标
m_presenter->SetHikDisplayWindow((void*)ui->detect_image_2d->viewport()->winId());
m_deviceStatusWidget = new DeviceStatusWidget();
// 连接DeviceStatusWidget的相机点击信号到MainWindow的槽函数
connect(m_deviceStatusWidget, SIGNAL(cameraClicked(int)), this, SLOT(onCameraClicked(int)));
// 将设备状态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);
// 设置网格大小(双相机布局)
int gridWidth = 530;
int gridHeight = 200;
ui->detect_result_list->setGridSize(QSize(gridWidth, gridHeight));
// 设置默认相机数量为23D相机 + 海康2D相机
m_deviceStatusWidget->setCameraCount(2);
m_deviceStatusWidget->setCamera1Name(tr("3D相机"));
m_deviceStatusWidget->setCamera2Name(tr("2D相机"));
// 隐藏机械臂状态显示TunnelChannel项目不需要显示机械臂
m_deviceStatusWidget->setRobotVisible(false);
// 禁用相机点击功能TunnelChannel项目不需要切换相机
m_deviceStatusWidget->setCameraClickable(false);
// 初始化期间禁用所有功能按钮
setButtonsEnabled(false);
#if 1
// 在线程中执行初始化业务逻辑
std::thread initThread([this]() {
updateStatusLog(tr("正在初始化系统..."));
int result = m_presenter->Init();
if (result != 0) {
updateStatusLog(tr("初始化失败,错误码:%1").arg(result));
} else {
updateStatusLog(tr("系统初始化完成"));
}
});
// 分离线程,让其在后台运行
initThread.detach();
#endif
}
void MainWindow::display3DImage(const QImage& image)
{
if (image.isNull()) {
updateStatusLog(tr("3D图片无效"));
return;
}
QGraphicsScene* scene = ui->detect_image_3d->scene();
scene->clear();
QPixmap pixmap = QPixmap::fromImage(image);
scene->addPixmap(pixmap);
ui->detect_image_3d->fitInView(scene->sceneRect(), Qt::KeepAspectRatio);
}
void MainWindow::display2DImage(const QImage& image)
{
if (image.isNull()) {
updateStatusLog(tr("2D图片无效"));
return;
}
QGraphicsScene* scene = ui->detect_image_2d->scene();
scene->clear();
QPixmap pixmap = QPixmap::fromImage(image);
scene->addPixmap(pixmap);
ui->detect_image_2d->fitInView(scene->sceneRect(), Qt::KeepAspectRatio);
}
void MainWindow::addDetectionResult(const TunnelDetectionResult& result)
{
// 清空之前的所有检测结果数据
ui->detect_result_list->clear();
// 添加检测结果信息
QWidget* resultWidget = new QWidget();
resultWidget->setAutoFillBackground(true);
QVBoxLayout* layout = new QVBoxLayout(resultWidget);
layout->setContentsMargins(20, 20, 20, 20);
layout->setSpacing(12);
// 状态信息
QLabel* statusLabel = new QLabel(QString("状态: %1").arg(0 == result.errorCode ? "成功" : "失败"));
statusLabel->setStyleSheet("color: rgb(239, 241, 245); font-size: 16pt; font-weight: bold;");
layout->addWidget(statusLabel);
// 通道间距
QLabel* spaceLabel = new QLabel(QString("通道间距: %1 mm").arg(result.channelSpace, 0, 'f', 2));
spaceLabel->setStyleSheet("color: rgb(100, 200, 255); font-size: 18pt; font-weight: bold;");
layout->addWidget(spaceLabel);
// 通道1宽度和深度
QLabel* channel1Label = new QLabel(QString("通道1 - 宽度: %1 mm, 深度: %2 mm")
.arg(result.channelWidth[0], 0, 'f', 2)
.arg(result.channelDepth[0], 0, 'f', 2));
channel1Label->setStyleSheet("color: rgb(239, 241, 245); font-size: 15pt;");
layout->addWidget(channel1Label);
// 通道2宽度和深度
QLabel* channel2Label = new QLabel(QString("通道2 - 宽度: %1 mm, 深度: %2 mm")
.arg(result.channelWidth[1], 0, 'f', 2)
.arg(result.channelDepth[1], 0, 'f', 2));
channel2Label->setStyleSheet("color: rgb(239, 241, 245); font-size: 15pt;");
layout->addWidget(channel2Label);
// 报警信息
if (!result.alarmMessage.empty()) {
QLabel* alarmLabel = new QLabel(QString("报警: %1").arg(QString::fromStdString(result.alarmMessage)));
alarmLabel->setStyleSheet("color: rgb(255, 100, 100); font-size: 15pt;");
alarmLabel->setWordWrap(true);
layout->addWidget(alarmLabel);
}
resultWidget->setStyleSheet("background-color: rgb(37, 38, 42); border: 1px solid #191A1C;");
QListWidgetItem* item = new QListWidgetItem();
item->setBackground(QBrush(Qt::transparent));
item->setSizeHint(QSize(480, 220));
item->setFlags(item->flags() & ~Qt::ItemIsDragEnabled & ~Qt::ItemIsDropEnabled);
ui->detect_result_list->addItem(item);
ui->detect_result_list->setItemWidget(item, resultWidget);
}
// 状态更新槽函数
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 TunnelDetectionResult& result)
{
// 通过信号槽机制更新UI确保在主线程中执行
emit detectionResultUpdateRequested(result);
}
void MainWindow::OnCamera1StatusChanged(bool isConnected)
{
// 直接更新设备状态widget
if (m_deviceStatusWidget) {
m_deviceStatusWidget->updateCamera1Status(isConnected);
}
}
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);
// 设置相机名称
if (m_presenter) {
const auto& cameraList = m_presenter->GetCameraList();
if (cameraList.size() >= 1) {
QString camera1Name = QString::fromStdString(cameraList[0].first);
m_deviceStatusWidget->setCamera1Name(camera1Name);
}
if (cameraList.size() >= 2) {
QString camera2Name = QString::fromStdString(cameraList[1].first);
m_deviceStatusWidget->setCamera2Name(camera2Name);
}
}
}
// 更新状态消息
updateStatusLog(tr("3D相机数量: %1").arg(cameraCount));
}
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::OnHikImageUpdated(const QImage& image)
{
// 通过信号槽机制更新UI确保在主线程中执行
emit hikImageUpdateRequested(image);
}
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 TunnelDetectionResult& result)
{
// 显示3D点云图像
if (!result.image.isNull()) {
updateDetectionLog(tr("3D检测结果更新"));
display3DImage(result.image);
}
// 显示2D图像海康相机
if (!result.hikImage.isNull()) {
updateDetectionLog(tr("2D图像更新"));
display2DImage(result.hikImage);
}
// 更新检测结果到列表
addDetectionResult(result);
}
void MainWindow::updateHikImageDisplay(const QImage& image)
{
display2DImage(image);
}
void MainWindow::updateDetectionLog(const QString& message)
{
// 在UI线程中更新detect_log控件
if (!m_logModel) return;
// 获取当前数据
QStringList logList = m_logModel->stringList();
// 检查是否与最后一条消息相同
if (message == m_lastLogMessage && !logList.isEmpty()) {
// 相同消息,增加计数并替换最后一条
m_lastLogCount++;
// 添加时间戳
QString timestamp = QDateTime::currentDateTime().toString("hh:mm:ss");
QString logEntry = QString("[%1] %2 (x%3)").arg(timestamp).arg(message).arg(m_lastLogCount);
// 替换最后一条
logList[logList.size() - 1] = logEntry;
} else {
// 新消息,重置计数
m_lastLogMessage = message;
m_lastLogCount = 1;
// 添加时间戳
QString timestamp = QDateTime::currentDateTime().toString("hh:mm:ss");
QString logEntry = QString("[%1] %2").arg(timestamp).arg(message);
// 添加新的日志条目
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());
}
// 重置日志计数器
m_lastLogMessage.clear();
m_lastLogCount = 0;
}
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);
// 如果对话框不存在创建新的CommonDialogCamera
if (nullptr == ui_dialogCamera) {
ui_dialogCamera = new CommonDialogCamera(m_presenter->GetCameraList(), 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);
// 如果对话框不存在创建新的DialogAlgoConfig
if (nullptr == ui_dialogAlgoConfig) {
ui_dialogAlgoConfig = new DialogAlgoConfig(m_presenter, this);
// 连接对话框关闭信号
connect(ui_dialogAlgoConfig, &QDialog::finished, this, [this]() {
setButtonSelectedState(ui->btn_algo_config, false);
});
}
ui_dialogAlgoConfig->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 CommonDialogCameraLevel(this);
// 连接对话框关闭信号
connect(ui_dialogCameraLevel, &QDialog::finished, this, [this]() {
setButtonSelectedState(ui->btn_camera_levelling, false);
});
}
// 设置相机列表、presenter、计算器和结果保存器到对话框
ui_dialogCameraLevel->SetCameraList(m_presenter->GetCameraList(), m_presenter, m_presenter, 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);
}
// 设置按钮图像
void MainWindow::setButtonImage(QPushButton* button, const QString& imagePath)
{
if (button) {
QString styleSheet = QString("image: url(%1);background-color: rgb(38, 40, 47);border: none;").arg(imagePath);
button->setStyleSheet(styleSheet);
}
}
// 设置按钮选中状态
void MainWindow::setButtonSelectedState(QPushButton* button, bool selected)
{
if (!button) return;
QString normalImage, 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)
{
QList<QPushButton*> 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::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->setStyleSheet(
"QMenu::item { color: gray; }"
"QMenu::item:enabled { color: gray; }"
"QMenu::item:disabled { color: gray; }"
);
// 添加动作到菜单
m_contextMenu->addAction(m_saveDataAction);
// 连接信号槽
connect(m_saveDataAction, &QAction::triggered, this, &MainWindow::onSaveDetectionData);
// 为图像视图设置右键菜单事件
ui->detect_image_3d->setContextMenuPolicy(Qt::CustomContextMenu);
ui->detect_image_2d->setContextMenuPolicy(Qt::CustomContextMenu);
// 连接右键菜单信号
connect(ui->detect_image_3d, &QGraphicsView::customContextMenuRequested,
this, [this](const QPoint& pos) { showContextMenu(pos, ui->detect_image_3d); });
connect(ui->detect_image_2d, &QGraphicsView::customContextMenuRequested,
this, [this](const QPoint& pos) { showContextMenu(pos, ui->detect_image_2d); });
}
// 显示右键菜单
void MainWindow::showContextMenu(const QPoint& pos, QGraphicsView* view)
{
// 检查是否有检测数据可以保存
if (!m_presenter) {
updateStatusLog(tr("系统未初始化,无法保存数据"));
return;
}
if (m_presenter->GetDetectionDataCacheSize() == 0) {
updateStatusLog(tr("没有检测数据可以保存"));
return;
}
// 显示右键菜单
m_contextMenu->exec(view->mapToGlobal(pos));
}
// 保存检测数据槽函数
void MainWindow::onSaveDetectionData()
{
if (!m_presenter) {
updateStatusLog(tr("系统未初始化,无法保存数据"));
return;
}
// 让用户选择保存目录
QString dirPath = QFileDialog::getExistingDirectory(this,
tr("选择保存目录"),
QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation),
QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
if (dirPath.isEmpty()) {
updateStatusLog(tr("用户取消了保存操作"));
return;
}
// 生成文件名(包含时间戳和相机信息)
std::string timeStamp = CVrDateUtils::GetNowTime();
std::string fileName = "Laserline_" + std::to_string(m_presenter->GetDetectIndex()) + "_" + timeStamp + ".txt";
QString fullPath = QDir(dirPath).filePath(QString::fromStdString(fileName));
// 保存数据
if (saveDetectionDataToFile(fullPath, m_presenter->GetDetectIndex())) {
updateStatusLog(tr("检测数据已保存到:%1").arg(fileName.c_str()));
} else {
updateStatusLog(tr("保存检测数据失败"));
}
}
// 保存检测数据到文件
bool MainWindow::saveDetectionDataToFile(const QString& filePath, int cameraIndex)
{
try {
// 直接调用Presenter的保存方法
int result = m_presenter->SaveDetectionDataToFile(filePath.toStdString());
if (result == 0) {
updateStatusLog(tr("检测数据保存成功"));
return true;
} else {
updateStatusLog(tr("保存数据失败,错误码:%1").arg(result));
return false;
}
} catch (const std::exception& e) {
updateStatusLog(tr("保存数据时发生异常:%1").arg(e.what()));
return false;
}
}
// ============ 补光灯控制相关 ============
void MainWindow::setupSupplementLightButton()
{
return;
// 创建补光灯按钮悬浮在2D图像视图上
m_btnSupplementLight = new QPushButton(ui->detect_image_2d);
m_btnSupplementLight->setFixedSize(32, 32);
m_btnSupplementLight->setToolTip(tr("补光灯开关"));
m_btnSupplementLight->setCursor(Qt::PointingHandCursor);
m_btnSupplementLight->setFlat(true);
m_btnSupplementLight->setAttribute(Qt::WA_TranslucentBackground);
m_btnSupplementLight->setAutoFillBackground(false);
// 设置按钮样式
updateSupplementLightButtonState(false);
// 设置初始位置到右上角
int viewWidth = ui->detect_image_2d->width();
if (viewWidth < 100) viewWidth = 400;
m_btnSupplementLight->move(viewWidth - 42, 10);
m_btnSupplementLight->raise();
m_btnSupplementLight->show();
connect(m_btnSupplementLight, &QPushButton::clicked, this, &MainWindow::onSupplementLightToggle);
ui->detect_image_2d->installEventFilter(this);
}
void MainWindow::updateSupplementLightButtonState(bool enabled)
{
if (!m_btnSupplementLight) return;
m_bSupplementLightOn = enabled;
// 简单的圆形指示灯样式
if (enabled) {
m_btnSupplementLight->setStyleSheet(
"QPushButton { background: qradialgradient(cx:0.5, cy:0.5, radius:0.5, fx:0.3, fy:0.3, stop:0 #FFFF00, stop:1 #FF8C00); "
"border: 2px solid #FFD700; border-radius: 16px; }"
"QPushButton:hover { border: 2px solid #FFFFFF; }"
);
} else {
m_btnSupplementLight->setStyleSheet(
"QPushButton { background: qradialgradient(cx:0.5, cy:0.5, radius:0.5, fx:0.3, fy:0.3, stop:0 #666666, stop:1 #333333); "
"border: 2px solid #555555; border-radius: 16px; }"
"QPushButton:hover { border: 2px solid #888888; }"
);
}
m_btnSupplementLight->setText("");
}
void MainWindow::onSupplementLightToggle()
{
if (!m_presenter) {
updateStatusLog(tr("系统未初始化"));
return;
}
// 切换状态
bool newState = !m_bSupplementLightOn;
// TODO: 在此处添加控制其他补光设备的代码
// 例如通过串口、Modbus、GPIO等方式控制外部补光灯
// int result = m_presenter->SetExternalSupplementLight(newState);
// 暂时直接更新UI状态
updateSupplementLightButtonState(newState);
updateStatusLog(tr("补光灯已%1").arg(newState ? tr("开启") : tr("关闭")));
}
bool MainWindow::eventFilter(QObject* watched, QEvent* event)
{
// 处理2D图像视图的resize和show事件重新定位补光灯按钮到右上角
if (watched == ui->detect_image_2d) {
if (event->type() == QEvent::Resize || event->type() == QEvent::Show) {
if (m_btnSupplementLight) {
int viewWidth = ui->detect_image_2d->width();
m_btnSupplementLight->move(viewWidth - 42, 10);
m_btnSupplementLight->raise();
}
}
}
return QMainWindow::eventFilter(watched, event);
}