2025-12-20 16:18:12 +08:00

487 lines
18 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 <QMessageBox>
#include <QDateTime>
#include <QBuffer>
#include <QPainter>
#include <QPushButton>
#include <QTextEdit>
#include <QVBoxLayout>
#include <QGroupBox>
#include <QFileDialog>
#include <QFile>
#include <QTextStream>
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<VrMark3D>& 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));
}
}