1134 lines
37 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 "resultitem.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 "WorkpiecePresenter.h"
#include <QProcess>
#include <QSettings>
#include <QFile>
#include <QTextStream>
#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; }");
// 设置CPU序列号显示左下角
setupCPUSerialDisplay();
// 在状态栏右侧添加版本信息(包含编译时间)
// 使用VrSimpleLog.h中的宏定义构建编译时间
QString versionWithBuildTime = QString("%1_%2%3%4%5%6%7")
.arg(GetWorkpieceFullVersion())
.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);
// 注册自定义类型,使其能够在信号槽中跨线程传递
qRegisterMetaType<DetectionResult>("DetectionResult");
qRegisterMetaType<WorkStatus>("WorkStatus");
qRegisterMetaType<WorkpiecePosition>("WorkpiecePosition");
// 连接工作状态更新信号槽
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);
// 初始化右键菜单
setupContextMenu();
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 WorkpiecePresenter();
// 设置状态回调接口使用BasePresenter的模板方法
m_presenter->SetStatusCallback<IYWorkpieceStatus>(this);
m_deviceStatusWidget = new DeviceStatusWidget(); //因为初始化回调的数据要存储所以要在init前创建好
// 连接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);
#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);
// 设置检测结果数据直接使用WorkpiecePosition
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);
// 设置相机名称(从配置文件中读取)
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);
}
}
}
// 根据相机个数调整页面布局
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;
// 获取当前数据
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);
// 获取配置对象
ConfigManager* config = m_presenter->GetConfigManager();
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){
try {
ui_dialogConfig = new DialogAlgoarg(config, this);
// 连接对话框关闭信号
connect(ui_dialogConfig, &QDialog::finished, this, [this]() {
setButtonSelectedState(ui->btn_algo_config, false);
});
} catch (const std::exception& e) {
// 恢复按钮状态
setButtonSelectedState(ui->btn_algo_config, false);
// 删除可能已创建的对话框
if (ui_dialogConfig) {
delete ui_dialogConfig;
ui_dialogConfig = nullptr;
}
QMessageBox msgBox;
msgBox.setWindowTitle("错误");
msgBox.setText(QString("创建配置对话框失败: %1").arg(e.what()));
msgBox.setIcon(QMessageBox::Critical);
msgBox.setStyleSheet("QMessageBox { color: white; }");
msgBox.exec();
return;
} catch (...) {
// 恢复按钮状态
setButtonSelectedState(ui->btn_algo_config, false);
// 删除可能已创建的对话框
if (ui_dialogConfig) {
delete ui_dialogConfig;
ui_dialogConfig = nullptr;
}
QMessageBox msgBox;
msgBox.setWindowTitle("错误");
msgBox.setText("创建配置对话框失败: 未知错误");
msgBox.setIcon(QMessageBox::Critical);
msgBox.setStyleSheet("QMessageBox { color: white; }");
msgBox.exec();
return;
}
}
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 CommonDialogCameraLevel(this);
// 连接对话框关闭信号
connect(ui_dialogCameraLevel, &QDialog::finished, this, [this]() {
setButtonSelectedState(ui->btn_camera_levelling, false);
});
}
// 设置相机列表、presenter、计算器和结果保存器到对话框
// m_presenter 同时实现了 ICameraLevelCalculator 和 ICameraLevelResultSaver 接口
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);
// 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);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::adjustLayoutForCameraCount(int cameraCount)
{
if (cameraCount <= 1) {
// 单相机模式布局
setupSingleCameraLayout();
} else {
// 多相机模式布局
setupMultiCameraLayout();
}
}
// 获取可用高度(屏幕高度 - 任务栏 - 边距)
int MainWindow::getAvailableHeight()
{
// 获取主屏幕的可用几何区域(自动排除任务栏)
QScreen* screen = QApplication::primaryScreen();
if (screen) {
QRect availableGeometry = screen->availableGeometry();
// 额外预留20px底部边距
return availableGeometry.height() - 30;
}
// 如果无法获取屏幕信息,返回默认值
return 1050; // 默认值1080 - 任务栏约30px - 20px边距
}
// 设置单相机模式布局
void MainWindow::setupSingleCameraLayout()
{
// 获取可用高度已排除任务栏和20px边距
int availableHeight = getAvailableHeight();
// 单相机模式下的原始布局
// detect_image: 占据左侧大部分空间,动态计算高度
int detectImageTop = 140;
int detectImageHeight = availableHeight - detectImageTop;
ui->detect_image->setGeometry(20, detectImageTop, 1311, detectImageHeight);
// 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); // 允许换行
// 单相机模式设置gridSizegridHeight = 207
int gridWidth = 183;
int gridHeight = 154;
ui->detect_result_list->setGridSize(QSize(gridWidth, gridHeight));
// 恢复设备状态widget的原始布局
m_deviceStatusWidget->setCameraCount(1);
// 日志: 位于右下方,动态计算高度
int detectLogTop = 850;
int detectLogHeight = availableHeight - detectLogTop;
ui->detect_log->setGeometry(1344, detectLogTop, 556, detectLogHeight);
updateStatusLog(tr("页面布局已调整为单相机模式"));
}
// 设置多相机模式布局
void MainWindow::setupMultiCameraLayout()
{
// 获取可用高度已排除任务栏和20px边距
int availableHeight = getAvailableHeight();
// 多相机模式下的布局调整
// detect_image: 缩小到左侧一半空间,动态计算高度
int detectImageTop = 140;
int detectImageHeight = availableHeight - detectImageTop;
ui->detect_image->setGeometry(10, detectImageTop, 940, detectImageHeight);
// detect_image_2: 显示在右侧一半空间,动态计算高度
ui->detect_image_2->setVisible(true);
ui->detect_image_2->setGeometry(970, detectImageTop, 940, detectImageHeight);
// 设备状态框架: 移动到下方左侧,调整为更高的尺寸以适应纵向排布
ui->frame_dev->setGeometry(10, 806, 180, 200);
// 检测结果列表: 移动到下方中间
ui->detect_result_list->setGeometry(196, 806, 1380, 200);
// 多相机模式设置gridSizegridHeight = 200
int gridWidth = 230;
int gridHeight = 190;
ui->detect_result_list->setGridSize(QSize(gridWidth, gridHeight));
m_deviceStatusWidget->setCameraCount(2);
// 日志: 移动到下方右侧,动态计算高度
int detectLogTop = 806;
int detectLogHeight = availableHeight - detectLogTop;
ui->detect_log->setGeometry(1582, detectLogTop, 328, detectLogHeight);
updateStatusLog(tr("页面布局已调整为多相机模式"));
}
// 处理相机点击事件
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_saveDataAction->setIcon(QIcon(":/common/resource/save.png")); // 如果有保存图标的话
// 设置右键菜单的样式表,使菜单项默认显示为灰色
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->setContextMenuPolicy(Qt::CustomContextMenu);
ui->detect_image_2->setContextMenuPolicy(Qt::CustomContextMenu);
// 连接右键菜单信号
connect(ui->detect_image, &QGraphicsView::customContextMenuRequested,
this, [this](const QPoint& pos) { showContextMenu(pos, ui->detect_image); });
connect(ui->detect_image_2, &QGraphicsView::customContextMenuRequested,
this, [this](const QPoint& pos) { showContextMenu(pos, ui->detect_image_2); });
}
// 显示右键菜单
void MainWindow::showContextMenu(const QPoint& pos, QGraphicsView* view)
{
// 检查是否有检测数据可以保存
if (!m_presenter) {
updateStatusLog(tr("系统未初始化,无法保存数据"));
return;
}
// 根据视图确定相机索引
int cameraIndex = (view == ui->detect_image_2) ? 2 : 1;
// 检查是否是上次检测的相机
if (cameraIndex != m_presenter->GetDetectIndex()) {
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;
}
}
// 获取CPU序列号跨平台实现
QString MainWindow::getCPUSerialNumber()
{
QString serialNumber = "UNKNOWN";
#ifdef _WIN32
// Windows平台使用WMI获取CPU序列号
QProcess process;
process.start("wmic", QStringList() << "cpu" << "get" << "ProcessorId");
process.waitForFinished();
QString output = QString::fromLocal8Bit(process.readAllStandardOutput());
QStringList lines = output.split('\n', Qt::SkipEmptyParts);
// 第一行是标题"ProcessorId",第二行是实际的序列号
if (lines.size() >= 2) {
serialNumber = lines[1].trimmed();
}
// 如果WMIC失败尝试读取注册表备用方案
if (serialNumber.isEmpty() || serialNumber == "UNKNOWN") {
QSettings settings("HKEY_LOCAL_MACHINE\\HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", QSettings::NativeFormat);
serialNumber = settings.value("ProcessorNameString", "UNKNOWN").toString();
// 从处理器名称中提取唯一标识
if (serialNumber != "UNKNOWN") {
// 使用处理器名称的哈希作为替代
serialNumber = QString("WIN_%1").arg(QString::number(qHash(serialNumber), 16).toUpper());
}
}
#else
// Linux/ARM Ubuntu平台从/proc/cpuinfo读取
QFile file("/proc/cpuinfo");
if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
QTextStream in(&file);
QString line;
// 首先尝试查找Serial字段
while (!in.atEnd()) {
line = in.readLine();
if (line.contains("Serial", Qt::CaseInsensitive)) {
QStringList parts = line.split(':');
if (parts.size() >= 2) {
serialNumber = parts[1].trimmed();
break;
}
}
}
file.close();
}
#endif
// 转换为大写
return serialNumber.toUpper();
}
// 设置CPU序列号显示
void MainWindow::setupCPUSerialDisplay()
{
return;
QString cpuSerial = getCPUSerialNumber();
// 创建CPU序列号标签
m_cpuSerialLabel = new QLabel(QString("编号: %1").arg(cpuSerial));
m_cpuSerialLabel->setStyleSheet("color: rgb(239, 241, 245); font-size: 16px; margin-left: 16px;");
// 添加到状态栏左侧
statusBar()->addWidget(m_cpuSerialLabel);
LOG_INFO("CPU Serial Number: %s\n", cpuSerial.toStdString().c_str());
}