570 lines
21 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 "XyzRpyItem.h"
#include "ResultListLayoutHelper.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 <QHBoxLayout>
#include <QMenu>
#include <QAction>
#include <QStandardPaths>
#include <QDir>
#include <QGraphicsView>
#include "RodAndBarPositionPresenter.h"
#include <QEvent>
#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);
#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);
statusBar()->setStyleSheet("QStatusBar { color: rgb(239, 241, 245); padding: 20px; }");
QString versionWithBuildTime = QString("%1_%2%3%4%5%6%7")
.arg(GetRodAndBarPositionFullVersion())
.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;");
buildLabel->setCursor(Qt::PointingHandCursor);
buildLabel->installEventFilter(this);
statusBar()->addPermanentWidget(buildLabel);
m_versionLabel = buildLabel;
setWindowFlags(Qt::FramelessWindowHint);
this->showMaximized();
ui->label_work->setVisible(false);
ui->detect_image_2->setVisible(false);
QGraphicsScene* scene = new QGraphicsScene(this);
ui->detect_image->setScene(scene);
// 为第二个图像视图初始化GraphicsScene
QGraphicsScene* scene2 = new QGraphicsScene(this);
ui->detect_image_2->setScene(scene2);
m_logHelper = new DetectLogHelper(ui->detect_log, this);
qRegisterMetaType<DetectionResult>("DetectionResult");
qRegisterMetaType<WorkStatus>("WorkStatus");
qRegisterMetaType<RodPosition>("RodPosition");
connect(this, &MainWindow::workStatusUpdateRequested, this, &MainWindow::updateWorkStatusLabel);
connect(this, &MainWindow::detectionResultUpdateRequested, this, &MainWindow::updateDetectionResultDisplay);
setupContextMenu();
updateStatusLog(tr("设备开始初始化..."));
Init();
}
MainWindow::~MainWindow()
{
if (m_presenter) {
m_presenter->SetStatusCallback(static_cast<IYRodAndBarPositionStatus*>(nullptr));
m_presenter->DeinitApp();
delete m_presenter;
m_presenter = nullptr;
}
delete ui;
}
void MainWindow::updateStatusLog(const QString& message)
{
if (m_logHelper) {
m_logHelper->appendLog(message);
}
}
void MainWindow::clearDetectionLog()
{
if (m_logHelper) {
m_logHelper->clearLog();
}
}
void MainWindow::Init()
{
m_presenter = new RodAndBarPositionPresenter();
m_presenter->SetStatusCallback<IYRodAndBarPositionStatus>(this);
m_deviceStatusWidget = new DeviceStatusWidget();
connect(m_deviceStatusWidget, SIGNAL(cameraClicked(int)), this, SLOT(onCameraClicked(int)));
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);
}
void MainWindow::addDetectionResult(const DetectionResult& result)
{
ui->detect_result_list->clear();
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();
for (size_t i = 0; i < result.positions.size(); i++) {
XyzRpyItem* resultWidget = new XyzRpyItem();
resultWidget->setAutoFillBackground(true);
resultWidget->setResultData(static_cast<int>(i) + 1, "棒材", result.positions[i]);
QListWidgetItem* item = new QListWidgetItem();
item->setBackground(QBrush(Qt::transparent));
item->setSizeHint(QSize(gridWidth, gridHeight));
item->setFlags(item->flags() & ~Qt::ItemIsDragEnabled & ~Qt::ItemIsDropEnabled);
ui->detect_result_list->addItem(item);
ui->detect_result_list->setItemWidget(item, resultWidget);
}
}
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)
{
emit detectionResultUpdateRequested(result);
}
void MainWindow::OnCamera1StatusChanged(bool isConnected)
{
if (m_deviceStatusWidget) {
m_deviceStatusWidget->updateCamera1Status(isConnected);
}
}
void MainWindow::OnCamera2StatusChanged(bool isConnected)
{
if (m_deviceStatusWidget) {
m_deviceStatusWidget->updateCamera2Status(isConnected);
}
}
void MainWindow::OnCameraCountChanged(int cameraCount)
{
if (m_deviceStatusWidget) {
m_deviceStatusWidget->setCameraCount(cameraCount);
if (m_presenter) {
const auto& cameraList = m_presenter->GetCameraList();
if (cameraList.size() >= 1) {
m_deviceStatusWidget->setCamera1Name(QString::fromStdString(cameraList[0].first));
}
if (cameraList.size() >= 2) {
m_deviceStatusWidget->setCamera2Name(QString::fromStdString(cameraList[1].first));
}
}
}
// 根据相机个数调整页面布局
adjustLayoutForCameraCount(cameraCount);
if (cameraCount < 2) {
updateStatusLog(tr("系统使用单相机模式"));
} else {
updateStatusLog(tr("系统使用双相机模式"));
}
}
void MainWindow::OnRobotConnectionChanged(bool isConnected)
{
if (m_deviceStatusWidget) {
m_deviceStatusWidget->updateRobotStatus(isConnected);
}
}
void MainWindow::OnSerialConnectionChanged(bool isConnected)
{
if (m_deviceStatusWidget) {
m_deviceStatusWidget->updateSerialStatus(isConnected);
}
}
void MainWindow::OnWorkStatusChanged(WorkStatus status)
{
emit workStatusUpdateRequested(status);
}
void MainWindow::updateWorkStatusLabel(WorkStatus status)
{
if (status == WorkStatus::Working) {
clearDetectionLog();
}
QString statusText = QString::fromStdString(WorkStatusToString(status));
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) {
updateStatusLog(tr("相机2检测结果更新"));
displayImageInSecondView(result.image);
} else {
updateStatusLog(tr("相机1检测结果更新"));
displayImage(result.image);
}
addDetectionResult(result);
}
void MainWindow::on_btn_start_clicked()
{
if (!m_presenter) { updateStatusLog(tr("系统未初始化,请等待初始化完成")); return; }
clearDetectionLog();
m_presenter->StartDetection(-1, false);
}
void MainWindow::on_btn_stop_clicked()
{
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; }
if (!m_presenter) { updateStatusLog(tr("系统未初始化,请等待初始化完成")); return; }
setButtonSelectedState(ui->btn_camera, true);
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; }
if (!m_presenter) { updateStatusLog(tr("系统未初始化,请等待初始化完成")); return; }
setButtonSelectedState(ui->btn_algo_config, true);
if(nullptr == ui_dialogConfig){
ui_dialogConfig = new DialogAlgoArg(this);
ui_dialogConfig->SetPresenter(m_presenter);
connect(ui_dialogConfig, &QDialog::finished, this, [this]() { setButtonSelectedState(ui->btn_algo_config, false); });
}
ui_dialogConfig->show();
}
void MainWindow::on_btn_camera_levelling_clicked()
{
if (m_selectedButton != nullptr && m_selectedButton != ui->btn_camera_levelling) { updateStatusLog(tr("请先关闭当前打开的配置窗口")); return; }
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); });
}
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; }
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 { updateStatusLog(tr("调试数据复检失败: %1").arg(result)); }
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);
}
void MainWindow::setButtonImage(QPushButton* button, const QString& imagePath)
{
if (button) {
button->setStyleSheet(QString("image: url(%1);background-color: rgb(38, 40, 47);border: none;").arg(imagePath));
}
}
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::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_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 {
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;
}
}
// 根据相机个数调整页面布局
void MainWindow::adjustLayoutForCameraCount(int cameraCount)
{
if (cameraCount <= 1) {
setupSingleCameraLayout();
} else {
setupMultiCameraLayout();
}
}
// 获取可用高度(屏幕高度 - 任务栏 - 边距)
int MainWindow::getAvailableHeight()
{
QScreen* screen = QApplication::primaryScreen();
if (screen) {
QRect availableGeometry = screen->availableGeometry();
return availableGeometry.height() - 30;
}
return 1050;
}
// 设置单相机模式布局
void MainWindow::setupSingleCameraLayout()
{
int availableHeight = getAvailableHeight();
ResultListLayoutHelper::setupSingleCameraMode(
ui->detect_result_list,
ui->detect_image, ui->detect_image_2,
ui->frame_dev, ui->detect_log,
availableHeight);
m_deviceStatusWidget->setCameraCount(1);
updateStatusLog(tr("页面布局已调整为单相机模式"));
}
// 设置多相机模式布局
void MainWindow::setupMultiCameraLayout()
{
int availableHeight = getAvailableHeight();
ResultListLayoutHelper::setupMultiCameraMode(
ui->detect_result_list,
ui->detect_image, ui->detect_image_2,
ui->frame_dev, ui->detect_log,
availableHeight);
m_deviceStatusWidget->setCameraCount(2);
updateStatusLog(tr("页面布局已调整为多相机模式"));
}
// 事件过滤器:点击版本标签弹出关于对话框
bool MainWindow::eventFilter(QObject* obj, QEvent* event)
{
if (obj == m_versionLabel && event->type() == QEvent::MouseButtonPress) {
QString versionStr = m_versionLabel->text();
QString algoVersion = m_presenter ? m_presenter->GetAlgoVersion() : QString();
AboutDialog dlg(RODANDBARPOSITION_APP_NAME, versionStr, algoVersion, this);
dlg.exec();
return true;
}
return QMainWindow::eventFilter(obj, event);
}