2025-12-10 00:01:32 +08:00

535 lines
17 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 "WorkpieceSplicePresenter.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)
{
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<WorkpieceSpliceResult>("WorkpieceSpliceResult");
qRegisterMetaType<WorkStatus>("WorkStatus");
// 连接工作状态更新信号槽
connect(this, &MainWindow::workStatusUpdateRequested, this, &MainWindow::updateWorkStatusLabel);
// 连接Mark结果更新信号槽
connect(this, &MainWindow::markResultUpdateRequested, this, &MainWindow::updateMarkResultDisplay);
// 连接日志更新信号槽
connect(this, &MainWindow::logUpdateRequested, this, &MainWindow::updateDetectionLog);
// 连接清空日志信号槽
connect(this, &MainWindow::logClearRequested, this, &MainWindow::clearDetectionLogUI);
updateStatusLog(tr("设备开始初始化..."));
// 初始化模块
Init();
}
MainWindow::~MainWindow()
{
// 释放业务逻辑处理类
if (m_presenter) {
delete m_presenter;
m_presenter = nullptr;
}
delete ui;
}
void MainWindow::updateStatusLog(const QString& message)
{
// 更新状态栏
// statusBar()->showMessage(message);
// 通过信号槽机制更新detect_log控件
emit logUpdateRequested(message);
}
void MainWindow::clearDetectionLog()
{
// 通过信号槽机制清空日志
emit logClearRequested();
}
void MainWindow::Init()
{
// 创建业务逻辑处理类
m_presenter = new WorkpieceSplicePresenter();
// 设置状态回调接口
m_presenter->SetStatusCallback(this);
m_deviceStatusWidget = new DeviceStatusWidget(); //因为初始化回调的数据要存储所以要在init前创建好
// 将设备状态widget添加到frame_dev中
QVBoxLayout* frameDevLayout = new QVBoxLayout(ui->frame_dev);
frameDevLayout->setContentsMargins(0, 0, 0, 0);
frameDevLayout->addWidget(m_deviceStatusWidget);
// 设置列表视图模式
ui->detect_result_list->setViewMode(QListView::IconMode);
ui->detect_result_list->setResizeMode(QListView::Adjust);
ui->detect_result_list->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
ui->detect_result_list->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
// 初始化期间禁用所有功能按钮
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);
}
// 添加Mark结果显示函数
void MainWindow::addMarkResult(const WorkpieceSpliceResult& result)
{
// 清空之前的所有检测结果数据
ui->detect_result_list->clear();
// 设置item间距和流向
int itemSpacing = 0;
ui->detect_result_list->setSpacing(itemSpacing);
// 获取当前的gridSize
QSize currentGridSize = ui->detect_result_list->gridSize();
int gridWidth = currentGridSize.width();
int gridHeight = currentGridSize.height();
int targetIndex = 1;
for (const auto& mark : result.marks) {
// 创建简单的文本显示widget
QWidget* resultWidget = new QWidget();
resultWidget->setAutoFillBackground(true);
// 创建布局和标签
QVBoxLayout* layout = new QVBoxLayout(resultWidget);
QLabel* idLabel = new QLabel(QString("Mark%1").arg(targetIndex));
QLabel* xLabel = new QLabel(QString("X: %1").arg(mark.x, 0, 'f', 2));
QLabel* yLabel = new QLabel(QString("Y: %1").arg(mark.y, 0, 'f', 2));
QLabel* zLabel = new QLabel(QString("Z: %1").arg(mark.z, 0, 'f', 2));
layout->addWidget(idLabel);
layout->addWidget(xLabel);
layout->addWidget(yLabel);
layout->addWidget(zLabel);
// 设置样式
QString styleSheet = "QLabel { color: rgb(239, 241, 245); background-color: rgb(37, 38, 42); }";
idLabel->setStyleSheet(styleSheet);
xLabel->setStyleSheet(styleSheet);
yLabel->setStyleSheet(styleSheet);
zLabel->setStyleSheet(styleSheet);
resultWidget->setStyleSheet("background-color: rgb(37, 38, 42); border: 1px solid #191A1C;");
// 创建QListWidgetItem
QListWidgetItem* item = new QListWidgetItem();
item->setBackground(QBrush(Qt::transparent));
item->setSizeHint(QSize(gridWidth, gridHeight));
item->setFlags(item->flags() & ~Qt::ItemIsDragEnabled & ~Qt::ItemIsDropEnabled);
// 将item添加到列表
ui->detect_result_list->addItem(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::OnMarkResult(const WorkpieceSpliceResult& result)
{
// 通过信号槽机制更新UI确保在主线程中执行
emit markResultUpdateRequested(result);
}
void MainWindow::OnBinocularMarkConnectionChanged(bool connected)
{
// 直接更新设备状态widget
if (m_deviceStatusWidget) {
// TODO: 根据实际DeviceStatusWidget接口调整
// m_deviceStatusWidget->updateBinocularMarkStatus(connected);
updateStatusLog(tr("BinocularMark连接状态: %1").arg(connected ? "已连接" : "断开"));
}
}
// 工作状态更新槽函数
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::updateMarkResultDisplay(const WorkpieceSpliceResult& result)
{
// 更新日志
updateDetectionLog(tr("收到Mark检测结果Mark数量%1").arg(result.marks.size()));
// 更新Mark结果到列表
addMarkResult(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();
// TODO: WorkpieceSplicePresenter可能没有StartDetection方法
// 如果有触发检测的需求,请添加相应的接口
updateStatusLog(tr("开始按钮点击(功能待实现)"));
}
void MainWindow::on_btn_stop_clicked()
{
// 检查Presenter是否已初始化
if (!m_presenter) {
updateStatusLog(tr("系统未初始化,请等待初始化完成"));
return;
}
// TODO: WorkpieceSplicePresenter可能没有StopDetection方法
updateStatusLog(tr("停止按钮点击(功能待实现)"));
}
void MainWindow::on_btn_hide_clicked()
{
// 最小化窗口
this->showMinimized();
}
void MainWindow::on_btn_close_clicked()
{
// 关闭应用程序
this->close();
}
// 设置按钮启用/禁用状态
void MainWindow::setButtonsEnabled(bool enabled)
{
// 功能按钮
if (ui->btn_start) ui->btn_start->setEnabled(enabled);
if (ui->btn_stop) ui->btn_stop->setEnabled(enabled);
}
// 获取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());
}