#include "mainwindow.h" #include "ui_mainwindow.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "VrLog.h" #include "VrDateUtils.h" #include #include #include #include #include #include #include #include #include #include "WorkpieceSplicePresenter.h" #include #include #include #include #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); // 初始化GraphicsScene QGraphicsScene* scene = new QGraphicsScene(this); ui->detect_image->setScene(scene); // 初始化日志模型 m_logModel = new QStringListModel(this); ui->detect_log->setModel(m_logModel); // 注册自定义类型,使其能够在信号槽中跨线程传递 qRegisterMetaType("WorkpieceSpliceResult"); qRegisterMetaType("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() { // 设置退出标志 m_bExiting = true; // 等待初始化线程完成 if (m_initThread.joinable()) { m_initThread.join(); } // 释放业务逻辑处理类 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(); // 设置状态回调接口(使用BasePresenter的模板方法) 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); // 在线程中执行初始化业务逻辑 m_initThread = std::thread([this]() { if (m_bExiting) return; // 检查退出标志 updateStatusLog(tr("正在初始化系统...")); if (m_bExiting) return; // 检查退出标志 int result = m_presenter->Init(); if (m_bExiting) return; // 检查退出标志 if (result != 0) { updateStatusLog(tr("初始化失败,错误码:%1").arg(result)); } else { updateStatusLog(tr("系统初始化完成")); } }); } 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; } } // 添加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()); }