#include "mainwindow.h" #include "ui_mainwindow.h" #include #include #include #include #include #include #include #include #include #include #include MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) , m_continuousImageTimer(new QTimer(this)) , m_isContinuousImageRunning(false) , m_continuousDetectionTimer(new QTimer(this)) , m_isContinuousDetectionRunning(false) { ui->setupUi(this); // 创建接收器实例 IBinocularMarkReceiver* pReceiver = nullptr; if (IBinocularMarkReceiver::CreateInstance(&pReceiver) == 0 && pReceiver) { m_receiver.reset(pReceiver); } // 连接信号槽 connect(ui->btn_connect, &QPushButton::clicked, this, &MainWindow::onConnectClicked); connect(ui->btn_startWork, &QPushButton::clicked, this, &MainWindow::onStartWorkClicked); connect(ui->btn_singleImage, &QPushButton::clicked, this, &MainWindow::onSingleImageClicked); connect(ui->btn_singleDetection, &QPushButton::clicked, this, &MainWindow::onSingleDetectionClicked); connect(ui->btn_setExposureTime, &QPushButton::clicked, this, &MainWindow::onSetExposureTimeClicked); connect(ui->btn_setGain, &QPushButton::clicked, this, &MainWindow::onSetGainClicked); connect(ui->btn_continuousImage, &QPushButton::clicked, this, &MainWindow::onContinuousImageClicked); connect(ui->btn_startContinuousImageStream, &QPushButton::clicked, this, &MainWindow::onContinuousImageStreamClicked); connect(ui->btn_loadCalibrationFile, &QPushButton::clicked, this, &MainWindow::onLoadCalibrationFileClicked); connect(ui->btn_getCalibration, &QPushButton::clicked, this, &MainWindow::onGetCalibrationClicked); connect(ui->btn_setCalibration, &QPushButton::clicked, this, &MainWindow::onSetCalibrationClicked); // 连接定时器 connect(m_continuousImageTimer, &QTimer::timeout, this, &MainWindow::onContinuousImageTimeout); connect(m_continuousDetectionTimer, &QTimer::timeout, this, &MainWindow::onContinuousDetectionTimeout); // 设置三个分组框的拉伸因子 ui->horizontalLayout_bottom->setStretch(0, 2); // groupBox_result ui->horizontalLayout_bottom->setStretch(1, 1); // groupBox_control ui->horizontalLayout_bottom->setStretch(2, 1); // groupBox_params // 设置事件回调 if (m_receiver) { m_receiver->SetEventCallback([this](ReceiverEventType eventType, const std::string& errorMsg) { QMetaObject::invokeMethod(this, [this, eventType, errorMsg]() { QString msg; switch (eventType) { case ReceiverEventType::CONNECTED: msg = "已连接"; updateConnectionState(true); break; case ReceiverEventType::DISCONNECTED: msg = "已断开"; updateConnectionState(false); break; case ReceiverEventType::CONNECTION_ERROR: msg = QString("连接错误: %1").arg(QString::fromStdString(errorMsg)); updateConnectionState(false); break; case ReceiverEventType::HEARTBEAT_TIMEOUT: msg = "心跳超时"; updateConnectionState(false); break; default: msg = "未知事件"; break; } appendResult(msg); ui->statusbar->showMessage(msg); }); }); // 设置持续检测结果回调 m_receiver->SetMarkResultCallback([this](const std::vector& marks, int64_t timestamp, int errorCode) { QMetaObject::invokeMethod(this, [this, marks, timestamp, errorCode]() { QString result = QString("[%1] 持续检测结果 (错误码: %2):\n") .arg(QDateTime::fromMSecsSinceEpoch(timestamp).toString("hh:mm:ss.zzz")) .arg(errorCode); if (errorCode == 0 && !marks.empty()) { for (const auto& mark : marks) { result += QString(" Mark ID=%1: (%2, %3, %4)\n") .arg(mark.markID) .arg(mark.x, 0, 'f', 2) .arg(mark.y, 0, 'f', 2) .arg(mark.z, 0, 'f', 2); } } else { result += " 无检测结果\n"; } appendResult(result); }); }); // 设置图像回调 m_receiver->SetImageCallback([this](const std::string& leftImageBase64, const std::string& rightImageBase64, int64_t timestamp) { QMetaObject::invokeMethod(this, [this, leftImageBase64, rightImageBase64]() { displayImage(ui->label_left, leftImageBase64); displayImage(ui->label_right, rightImageBase64); }); }); } } MainWindow::~MainWindow() { if (m_receiver && m_receiver->IsConnected()) { m_receiver->StopWork(); m_receiver->Disconnect(); } delete ui; } void MainWindow::onConnectClicked() { if (!m_receiver) { QMessageBox::warning(this, "错误", "接收器未初始化"); return; } if (m_receiver->IsConnected()) { m_receiver->Disconnect(); ui->btn_connect->setText("连接"); updateConnectionState(false); } else { QString ip = ui->lineEdit_ip->text(); uint16_t port = ui->lineEdit_port->text().toUShort(); int ret = m_receiver->Connect(ip.toStdString(), port); if (ret == 0) { ui->btn_connect->setText("断开"); updateConnectionState(true); appendResult(QString("正在连接 %1:%2...").arg(ip).arg(port)); auto leftInfo = m_receiver->GetCameraInfo(SVrCameraEnum::LEFT); auto rightInfo = m_receiver->GetCameraInfo(SVrCameraEnum::RIGHT); m_leftCameraSN = QString::fromStdString(leftInfo.serialNumber); m_rightCameraSN = QString::fromStdString(rightInfo.serialNumber); if (m_leftCameraSN.isEmpty()) m_leftCameraSN = "0"; if (m_rightCameraSN.isEmpty()) m_rightCameraSN = "1"; } else { QMessageBox::warning(this, "错误", QString("连接失败,错误码: %1").arg(ret)); } } } void MainWindow::onStartWorkClicked() { if (!m_receiver || !m_receiver->IsConnected()) return; if (ui->btn_startWork->text() == "开始持续检测") { int ret = m_receiver->StartWork(); if (ret == 0) { appendResult("开始持续检测"); ui->btn_startWork->setText("停止持续检测"); } else { QMessageBox::warning(this, "错误", QString("启动失败,错误码: %1").arg(ret)); } } else { int ret = m_receiver->StopWork(); if (ret == 0) { appendResult("停止持续检测"); ui->btn_startWork->setText("开始持续检测"); } else { QMessageBox::warning(this, "错误", QString("停止失败,错误码: %1").arg(ret)); } } } void MainWindow::onSingleImageClicked() { if (!m_receiver || !m_receiver->IsConnected()) return; appendResult("请求单次取图..."); auto imageData = m_receiver->RequestSingleImage(5000); if (imageData.timestamp > 0) { displayImage(ui->label_left, imageData.leftImageBase64); displayImage(ui->label_right, imageData.rightImageBase64); appendResult(QString("取图成功 [%1]") .arg(QDateTime::fromMSecsSinceEpoch(imageData.timestamp).toString("hh:mm:ss.zzz"))); } else { appendResult("取图失败或超时"); } } void MainWindow::onSingleDetectionClicked() { if (!m_receiver || !m_receiver->IsConnected()) return; appendResult("请求单次检测..."); auto result = m_receiver->RequestSingleDetection(5000); if (result.timestamp > 0) { displayImage(ui->label_left, result.leftImageBase64); displayImage(ui->label_right, result.rightImageBase64); QString msg = QString("检测完成 [%1] (错误码: %2):\n") .arg(QDateTime::fromMSecsSinceEpoch(result.timestamp).toString("hh:mm:ss.zzz")) .arg(result.errorCode); if (result.errorCode == 0 && !result.marks.empty()) { for (const auto& mark : result.marks) { msg += QString(" Mark ID=%1: (%2, %3, %4)\n") .arg(mark.markID) .arg(mark.x, 0, 'f', 2) .arg(mark.y, 0, 'f', 2) .arg(mark.z, 0, 'f', 2); } } else { msg += " 无检测结果\n"; } appendResult(msg); } else { appendResult("检测失败或超时"); } } void MainWindow::onSetExposureTimeClicked() { if (!m_receiver || !m_receiver->IsConnected()) return; double exposureTime = ui->lineEdit_exposure->text().toDouble(); if (exposureTime <= 0) { appendResult("错误:曝光时间必须大于0"); return; } int ret = m_receiver->SetExposureTime(SVrCameraEnum::BOTH, exposureTime); if (ret == 0) { appendResult(QString("设置曝光时间成功:%.2f").arg(exposureTime)); } else { appendResult(QString("设置曝光时间失败,错误码: %1").arg(ret)); } } void MainWindow::onSetGainClicked() { if (!m_receiver || !m_receiver->IsConnected()) return; double gain = ui->lineEdit_gain->text().toDouble(); if (gain <= 0) { appendResult("错误:增益必须大于0"); return; } int ret = m_receiver->SetGain(SVrCameraEnum::BOTH, gain); if (ret == 0) { appendResult(QString("设置增益成功:%.2f").arg(gain)); } else { appendResult(QString("设置增益失败,错误码: %1").arg(ret)); } } void MainWindow::displayImage(QLabel* label, const std::string& base64Data) { if (base64Data.empty()) return; QByteArray imageData = QByteArray::fromBase64(QByteArray::fromStdString(base64Data)); QImage image; if (image.loadFromData(imageData)) { QPixmap pixmap = QPixmap::fromImage(image); if (label == ui->label_left) { m_leftPixmap = pixmap; } else if (label == ui->label_right) { m_rightPixmap = pixmap; } QPixmap scaledPixmap = pixmap.scaled(label->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation); QPainter painter(&scaledPixmap); painter.setPen(Qt::yellow); painter.setFont(QFont("Arial", 12, QFont::Bold)); QString sn = (label == ui->label_left) ? m_leftCameraSN : m_rightCameraSN; if (!sn.isEmpty()) { painter.drawText(10, 20, "SN: " + sn); } label->setPixmap(scaledPixmap); } } void MainWindow::updateImageDisplay() { if (!m_leftPixmap.isNull()) { QPixmap scaledPixmap = m_leftPixmap.scaled(ui->label_left->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation); QPainter painter(&scaledPixmap); painter.setPen(Qt::yellow); painter.setFont(QFont("Arial", 12, QFont::Bold)); if (!m_leftCameraSN.isEmpty()) { painter.drawText(10, 20, "SN: " + m_leftCameraSN); } ui->label_left->setPixmap(scaledPixmap); } if (!m_rightPixmap.isNull()) { QPixmap scaledPixmap = m_rightPixmap.scaled(ui->label_right->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation); QPainter painter(&scaledPixmap); painter.setPen(Qt::yellow); painter.setFont(QFont("Arial", 12, QFont::Bold)); if (!m_rightCameraSN.isEmpty()) { painter.drawText(10, 20, "SN: " + m_rightCameraSN); } ui->label_right->setPixmap(scaledPixmap); } } void MainWindow::resizeEvent(QResizeEvent* event) { QMainWindow::resizeEvent(event); updateImageDisplay(); } void MainWindow::appendResult(const QString& text) { ui->textEdit_result->append(text); } void MainWindow::updateConnectionState(bool connected) { ui->btn_startWork->setEnabled(connected); ui->btn_singleImage->setEnabled(connected); ui->btn_singleDetection->setEnabled(connected); ui->btn_continuousImage->setEnabled(connected); ui->btn_startContinuousImageStream->setEnabled(connected); ui->btn_setExposureTime->setEnabled(connected); ui->btn_setGain->setEnabled(connected); ui->btn_getCalibration->setEnabled(connected); ui->btn_setCalibration->setEnabled(connected); ui->lineEdit_ip->setEnabled(!connected); ui->lineEdit_port->setEnabled(!connected); if (connected) { ui->btn_startWork->setText("开始持续检测"); ui->btn_startContinuousImageStream->setText("开始持续图像流"); } } void MainWindow::onContinuousImageClicked() { if (!m_receiver || !m_receiver->IsConnected()) return; if (m_isContinuousImageRunning) { m_continuousImageTimer->stop(); m_isContinuousImageRunning = false; ui->btn_continuousImage->setText("连续取图"); appendResult("停止连续取图"); } else { m_continuousImageTimer->start(100); m_isContinuousImageRunning = true; ui->btn_continuousImage->setText("停止连续取图"); appendResult("开始连续取图"); } } void MainWindow::onContinuousImageTimeout() { if (!m_receiver || !m_receiver->IsConnected()) return; auto imageData = m_receiver->RequestSingleImage(5000); if (imageData.timestamp > 0) { displayImage(ui->label_left, imageData.leftImageBase64); displayImage(ui->label_right, imageData.rightImageBase64); } } void MainWindow::onContinuousDetectionClicked() { if (!m_receiver || !m_receiver->IsConnected()) return; if (m_isContinuousDetectionRunning) { m_continuousDetectionTimer->stop(); m_isContinuousDetectionRunning = false; appendResult("停止连续单次检测"); } else { m_continuousDetectionTimer->start(100); m_isContinuousDetectionRunning = true; appendResult("开始连续单次检测"); } } void MainWindow::onContinuousDetectionTimeout() { if (!m_receiver || !m_receiver->IsConnected()) return; auto result = m_receiver->RequestSingleDetection(5000); if (result.timestamp > 0) { displayImage(ui->label_left, result.leftImageBase64); displayImage(ui->label_right, result.rightImageBase64); } } void MainWindow::onContinuousImageStreamClicked() { if (!m_receiver || !m_receiver->IsConnected()) return; if (ui->btn_startContinuousImageStream->text() == "开始持续图像流") { int ret = m_receiver->StartCapture(); if (ret == 0) { appendResult("开始持续图像流"); ui->btn_startContinuousImageStream->setText("停止持续图像流"); } else { QMessageBox::warning(this, "错误", QString("启动失败,错误码: %1").arg(ret)); } } else { int ret = m_receiver->StopCapture(); if (ret == 0) { appendResult("停止持续图像流"); ui->btn_startContinuousImageStream->setText("开始持续图像流"); } else { QMessageBox::warning(this, "错误", QString("停止失败,错误码: %1").arg(ret)); } } } void MainWindow::onLoadCalibrationFileClicked() { QString fileName = QFileDialog::getOpenFileName(this, "选择标定文件", "", "XML文件 (*.xml);;所有文件 (*)"); if (fileName.isEmpty()) { return; } QFile file(fileName); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { QMessageBox::warning(this, "错误", "无法打开文件"); return; } QTextStream in(&file); QString content = in.readAll(); file.close(); ui->textEdit_calibration->setPlainText(content); appendResult(QString("已加载标定文件: %1").arg(fileName)); } void MainWindow::onGetCalibrationClicked() { if (!m_receiver || !m_receiver->IsConnected()) return; appendResult("请求获取标定矩阵..."); std::string calibXml = m_receiver->GetCalibrationMatrix(5000); if (!calibXml.empty()) { ui->textEdit_calibration->setPlainText(QString::fromStdString(calibXml)); appendResult("获取标定矩阵成功"); } else { appendResult("获取标定矩阵失败或超时"); QMessageBox::warning(this, "错误", "获取标定矩阵失败"); } } void MainWindow::onSetCalibrationClicked() { if (!m_receiver || !m_receiver->IsConnected()) return; QString calibXml = ui->textEdit_calibration->toPlainText(); if (calibXml.isEmpty()) { QMessageBox::warning(this, "错误", "标定矩阵内容为空"); return; } appendResult("设置标定矩阵..."); int ret = m_receiver->SetCalibrationMatrix(calibXml.toStdString()); if (ret == 0) { appendResult("设置标定矩阵成功"); QMessageBox::information(this, "成功", "标定矩阵已设置"); } else { appendResult(QString("设置标定矩阵失败,错误码: %1").arg(ret)); QMessageBox::warning(this, "错误", QString("设置标定矩阵失败,错误码: %1").arg(ret)); } }