487 lines
18 KiB
C++
487 lines
18 KiB
C++
#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));
|
||
}
|
||
}
|