972 lines
31 KiB
C++
Raw Normal View History

#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);
}