487 lines
18 KiB
C++
Raw Normal View History

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QMessageBox>
#include <QDateTime>
#include <QBuffer>
2025-12-20 16:18:12 +08:00
#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)
2025-12-20 16:18:12 +08:00
, 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);
2025-12-20 16:18:12 +08:00
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) {
2025-12-20 16:18:12 +08:00
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);
});
});
2025-12-20 16:18:12 +08:00
// 设置图像回调
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));
2025-12-20 16:18:12 +08:00
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;
2025-12-20 16:18:12 +08:00
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 {
2025-12-20 16:18:12 +08:00
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) {
2025-12-20 16:18:12 +08:00
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;
}
2025-12-20 16:18:12 +08:00
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;
}
2025-12-20 16:18:12 +08:00
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)) {
2025-12-20 16:18:12 +08:00
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);
}
2025-12-20 16:18:12 +08:00
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);
2025-12-20 16:18:12 +08:00
ui->btn_continuousImage->setEnabled(connected);
ui->btn_startContinuousImageStream->setEnabled(connected);
ui->btn_setExposureTime->setEnabled(connected);
ui->btn_setGain->setEnabled(connected);
2025-12-20 16:18:12 +08:00
ui->btn_getCalibration->setEnabled(connected);
ui->btn_setCalibration->setEnabled(connected);
ui->lineEdit_ip->setEnabled(!connected);
ui->lineEdit_port->setEnabled(!connected);
2025-12-20 16:18:12 +08:00
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));
}
}