2025-12-27 09:34:02 +08:00

420 lines
13 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 <QDebug>
#include <QMessageBox>
#include <QThread>
#include <QDateTime>
#include <QTextCursor>
#include <QStringListModel>
#include "VrLog.h"
#include <QMetaType>
#include <QLabel>
#include <QVBoxLayout>
#include "WorkpiecePositionPresenter.h"
#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; }");
// 在状态栏右侧添加版本信息(包含编译时间)
QString versionWithBuildTime = QString("%1_%2%3%4%5%6%7")
.arg(GetWorkpiecePositionFullVersion())
.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);
// 初始化日志模型
m_logModel = new QStringListModel(this);
ui->detect_log->setModel(m_logModel);
// 注册自定义类型,使其能够在信号槽中跨线程传递
qRegisterMetaType<WorkpiecePositionDetectionResult>("WorkpiecePositionDetectionResult");
qRegisterMetaType<WorkStatus>("WorkStatus");
qRegisterMetaType<WorkpieceCenterPosition>("WorkpieceCenterPosition");
// 连接工作状态更新信号槽
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);
updateStatusLog(tr("设备开始初始化..."));
// 初始化模块
Init();
}
MainWindow::~MainWindow()
{
// 设置退出标志
m_bExiting = true;
LOG_DEBUG("release mainwindow \n");
// 等待初始化线程完成
if (m_initThread.joinable()) {
m_initThread.join();
}
// 释放业务逻辑处理类
if (m_presenter) {
delete m_presenter;
m_presenter = nullptr;
}
LOG_DEBUG("release persenter success \n");
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 WorkpiecePositionPresenter();
// 设置状态回调接口使用BasePresenter的模板方法
m_presenter->SetStatusCallback<IYWorkpiecePositionStatus>(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);
// 初始化期间禁用所有功能按钮
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::addDetectionResult(const WorkpiecePositionDetectionResult& result)
{
// 清空之前的所有检测结果数据
ui->detect_result_list->clear();
QString resultText;
// 添加每个工件位置信息
for (size_t i = 0; i < result.positions.size(); i++) {
const auto& pos = result.positions[i];
resultText += QString("工件%1: X=%2, Y=%3, Z=%4\n")
.arg(i + 1)
.arg(pos.x, 0, 'f', 2)
.arg(pos.y, 0, 'f', 2)
.arg(pos.z, 0, 'f', 2);
resultText += QString(" Roll=%1, Pitch=%2, Yaw=%3\n")
.arg(pos.roll, 0, 'f', 2)
.arg(pos.pitch, 0, 'f', 2)
.arg(pos.yaw, 0, 'f', 2);
}
// 添加Mark点信息
if (!result.marks.empty()) {
resultText += QString("\nMark点数量: %1\n").arg(result.marks.size());
}
// 创建一个QLabel来显示文本
QLabel* resultLabel = new QLabel(resultText);
resultLabel->setStyleSheet("color: rgb(239, 241, 245); font-size: 12pt; padding: 10px;");
resultLabel->setWordWrap(true);
QListWidgetItem* item = new QListWidgetItem();
item->setSizeHint(resultLabel->sizeHint());
ui->detect_result_list->addItem(item);
ui->detect_result_list->setItemWidget(item, resultLabel);
}
// 状态更新槽函数
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 WorkpiecePositionDetectionResult& result)
{
// 通过信号槽机制更新UI确保在主线程中执行
emit detectionResultUpdateRequested(result);
}
// BinocularMark连接状态更新
void MainWindow::OnBinocularMarkConnectionChanged(bool isConnected)
{
// 更新设备状态widget
if (m_deviceStatusWidget) {
m_deviceStatusWidget->updateCamera1Status(isConnected);
}
}
// EpicEye连接状态更新
void MainWindow::OnEpicEyeConnectionChanged(bool isConnected)
{
// 更新设备状态widget
if (m_deviceStatusWidget) {
m_deviceStatusWidget->updateCamera2Status(isConnected);
}
}
// 实现基类的纯虚函数(映射到具体设备)
void MainWindow::OnCamera1StatusChanged(bool isConnected)
{
OnBinocularMarkConnectionChanged(isConnected);
}
void MainWindow::OnCamera2StatusChanged(bool isConnected)
{
OnEpicEyeConnectionChanged(isConnected);
}
void MainWindow::OnRobotConnectionChanged(bool isConnected)
{
// WorkpiecePositionApp 不使用机械臂,忽略
}
void MainWindow::OnSerialConnectionChanged(bool isConnected)
{
// WorkpiecePositionApp 不使用串口,忽略
}
void MainWindow::OnCameraCountChanged(int cameraCount)
{
// WorkpiecePositionApp 使用固定的两个传感器设备,忽略
}
// 工作状态更新槽函数
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 WorkpiecePositionDetectionResult& result)
{
// 更新日志
updateDetectionLog(tr("检测结果更新"));
// 更新检测结果到列表
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();
}
void MainWindow::on_btn_stop_clicked()
{
// 检查Presenter是否已初始化
if (!m_presenter) {
updateStatusLog(tr("系统未初始化,请等待初始化完成"));
return;
}
m_presenter->StopDetection();
}
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);
}