535 lines
17 KiB
C++
535 lines
17 KiB
C++
#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());
|
||
}
|
||
|
||
|