2025-12-27 09:34:02 +08:00

485 lines
16 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 "dialogcamera.h"
#include "ui_dialogcamera.h"
#include <QRegularExpression>
#include <QPushButton>
#include <QHeaderView>
#include <QHBoxLayout>
#include "VrLog.h"
#include "VrNetUtils.h"
#include "StyledMessageBox.h"
#include "WheelMeasurePresenter.h"
#include "PathManager.h"
DialogCamera::DialogCamera(QWidget *parent) :
QDialog(parent),
ui(new Ui::DialogCamera)
{
ui->setupUi(this);
// 隐藏标题栏
// setWindowFlags(Qt::FramelessWindowHint);
// 初始化表格
InitTable();
// 加载已有的相机配置
LoadExistingCameras();
// 刷新显示
RefreshCameraList();
}
DialogCamera::~DialogCamera()
{
delete ui;
}
void DialogCamera::SetPresenter(WheelMeasurePresenter* presenter)
{
m_presenter = presenter;
// 重新加载已有的相机配置
LoadExistingCameras();
// 刷新显示
RefreshCameraList();
}
void DialogCamera::InitTable()
{
// 设置表格属性
ui->tableWidget_cameras->setColumnCount(4);
ui->tableWidget_cameras->setHorizontalHeaderLabels({"编号", "名称", "IP地址", "操作"});
// 设置表头样式(白色字体)
ui->tableWidget_cameras->horizontalHeader()->setStyleSheet(
"QHeaderView::section {"
" color: rgb(239, 241, 245);"
" background-color: rgb(50, 52, 58);"
" padding: 8px;"
" border: none;"
" font-size: 14px;"
"}"
);
// 设置列宽模式编号和操作固定宽度名称和IP拉伸填充
QHeaderView* header = ui->tableWidget_cameras->horizontalHeader();
header->setSectionResizeMode(0, QHeaderView::Fixed); // 编号列固定
header->setSectionResizeMode(1, QHeaderView::Stretch); // 名称列拉伸
header->setSectionResizeMode(2, QHeaderView::Stretch); // IP地址列拉伸
header->setSectionResizeMode(3, QHeaderView::Fixed); // 操作列固定
// 设置固定列的宽度
ui->tableWidget_cameras->setColumnWidth(0, 60); // 编号列
ui->tableWidget_cameras->setColumnWidth(3, 200); // 操作列(上移、下移、删除)
// 设置表格样式
ui->tableWidget_cameras->horizontalHeader()->setStretchLastSection(false);
ui->tableWidget_cameras->verticalHeader()->setVisible(false);
ui->tableWidget_cameras->setEditTriggers(QAbstractItemView::NoEditTriggers);
ui->tableWidget_cameras->setSelectionBehavior(QAbstractItemView::SelectRows);
ui->tableWidget_cameras->setSelectionMode(QAbstractItemView::SingleSelection);
// 设置字体大小
QFont font = ui->tableWidget_cameras->font();
font.setPointSize(14);
ui->tableWidget_cameras->setFont(font);
}
void DialogCamera::LoadExistingCameras()
{
m_cameraConfigs.clear();
// 从Presenter获取配置
if (m_presenter) {
WheelMeasureConfigResult* configResult = m_presenter->GetConfigResult();
if (configResult) {
// 从cameras加载相机配置
for (const auto& camera : configResult->cameras) {
m_cameraConfigs.push_back(camera);
}
LOG_INFO("Loaded %zu existing cameras from config\n", m_cameraConfigs.size());
} else {
LOG_WARNING("ConfigResult is null\n");
}
} else {
LOG_WARNING("Presenter is not set\n");
}
}
void DialogCamera::RefreshCameraList()
{
// 清空表格
ui->tableWidget_cameras->setRowCount(0);
// 填充数据
for (size_t i = 0; i < m_cameraConfigs.size(); ++i) {
int row = ui->tableWidget_cameras->rowCount();
ui->tableWidget_cameras->insertRow(row);
// 编号列
QTableWidgetItem* indexItem = new QTableWidgetItem(QString::number(m_cameraConfigs[i].cameraIndex));
indexItem->setTextAlignment(Qt::AlignCenter);
ui->tableWidget_cameras->setItem(row, 0, indexItem);
// 名称列
QTableWidgetItem* nameItem = new QTableWidgetItem(QString::fromStdString(m_cameraConfigs[i].name));
nameItem->setTextAlignment(Qt::AlignCenter);
ui->tableWidget_cameras->setItem(row, 1, nameItem);
// IP地址列
QTableWidgetItem* ipItem = new QTableWidgetItem(QString::fromStdString(m_cameraConfigs[i].cameraIP));
ipItem->setTextAlignment(Qt::AlignCenter);
ui->tableWidget_cameras->setItem(row, 2, ipItem);
// 操作列 - 上移、下移、删除按钮
QWidget* btnContainer = new QWidget();
QHBoxLayout* btnLayout = new QHBoxLayout(btnContainer);
btnLayout->setContentsMargins(2, 2, 2, 2);
btnLayout->setSpacing(4);
// 上移按钮
QPushButton* upBtn = new QPushButton("");
upBtn->setFixedSize(40, 35);
upBtn->setStyleSheet(
"QPushButton {"
" color: rgb(221, 225, 233);"
" background-color: rgb(60, 120, 180);"
" border: none;"
" border-radius: 3px;"
" font-size: 16px;"
" font-weight: bold;"
"}"
"QPushButton:hover {"
" background-color: rgb(80, 140, 200);"
"}"
"QPushButton:pressed {"
" background-color: rgb(40, 100, 160);"
"}"
"QPushButton:disabled {"
" background-color: rgb(80, 80, 80);"
" color: rgb(120, 120, 120);"
"}"
);
upBtn->setProperty("row", row);
upBtn->setEnabled(row > 0); // 第一行禁用上移
connect(upBtn, &QPushButton::clicked, this, &DialogCamera::onMoveUpClicked);
// 下移按钮
QPushButton* downBtn = new QPushButton("");
downBtn->setFixedSize(40, 35);
downBtn->setStyleSheet(
"QPushButton {"
" color: rgb(221, 225, 233);"
" background-color: rgb(60, 120, 180);"
" border: none;"
" border-radius: 3px;"
" font-size: 16px;"
" font-weight: bold;"
"}"
"QPushButton:hover {"
" background-color: rgb(80, 140, 200);"
"}"
"QPushButton:pressed {"
" background-color: rgb(40, 100, 160);"
"}"
"QPushButton:disabled {"
" background-color: rgb(80, 80, 80);"
" color: rgb(120, 120, 120);"
"}"
);
downBtn->setProperty("row", row);
downBtn->setEnabled(row < static_cast<int>(m_cameraConfigs.size()) - 1); // 最后一行禁用下移
connect(downBtn, &QPushButton::clicked, this, &DialogCamera::onMoveDownClicked);
// 删除按钮
QPushButton* deleteBtn = new QPushButton("删除");
deleteBtn->setFixedSize(60, 35);
deleteBtn->setStyleSheet(
"QPushButton {"
" color: rgb(221, 225, 233);"
" background-color: rgb(200, 50, 50);"
" border: none;"
" border-radius: 3px;"
" padding: 5px 10px;"
"}"
"QPushButton:hover {"
" background-color: rgb(220, 70, 70);"
"}"
"QPushButton:pressed {"
" background-color: rgb(180, 30, 30);"
"}"
);
deleteBtn->setProperty("row", row);
connect(deleteBtn, &QPushButton::clicked, this, &DialogCamera::onDeleteCameraClicked);
btnLayout->addWidget(upBtn);
btnLayout->addWidget(downBtn);
btnLayout->addWidget(deleteBtn);
ui->tableWidget_cameras->setCellWidget(row, 3, btnContainer);
// 设置行高
ui->tableWidget_cameras->setRowHeight(row, 45);
}
// 更新添加按钮状态
UpdateAddButtonState();
LOG_DEBUG("Refreshed camera list, total: %zu cameras\n", m_cameraConfigs.size());
}
void DialogCamera::on_btn_add_camera_clicked()
{
QString name = ui->lineEdit_name->text().trimmed();
QString ip = ui->lineEdit_ip->text().trimmed();
LOG_DEBUG("ADD Camera: name=%s, ip=%s\n", name.toStdString().c_str(), ip.toStdString().c_str());
if (AddCamera(name, ip)) {
// 清空输入框
ui->lineEdit_name->clear();
ui->lineEdit_ip->clear();
// 刷新显示
RefreshCameraList();
LOG_INFO("Camera added successfully: %s (%s)\n", name.toStdString().c_str(), ip.toStdString().c_str());
}
}
bool DialogCamera::AddCamera(const QString& name, const QString& ip)
{
// 检查是否已达到最大数量
if (static_cast<int>(m_cameraConfigs.size()) >= MAX_CAMERAS) {
StyledMessageBox::warning(this, "限制", QString("最多只能添加%1个相机").arg(MAX_CAMERAS));
return false;
}
// 检查名称是否为空
if (name.isEmpty()) {
StyledMessageBox::warning(this, "错误", "请输入相机名称!");
return false;
}
// 验证IP格式
bool bValid = ValidateIPAddress(ip);
LOG_DEBUG("ValidateIPAddress ret: %d\n", bValid);
if (!bValid) {
StyledMessageBox::warning(this, "错误", "IP地址格式不正确\n格式应为: xxx.xxx.xxx.xxx");
return false;
}
// 检查IP是否已存在
for (const auto& config : m_cameraConfigs) {
if (config.cameraIP == ip.toStdString()) {
StyledMessageBox::warning(this, "错误", "该IP地址已存在");
return false;
}
}
// 添加到列表
WheelCameraParam camera;
camera.cameraIndex = static_cast<int>(m_cameraConfigs.size()) + 1;
camera.name = name.toStdString();
camera.cameraIP = ip.toStdString();
camera.enabled = true;
m_cameraConfigs.push_back(camera);
return true;
}
void DialogCamera::onDeleteCameraClicked()
{
QPushButton* btn = qobject_cast<QPushButton*>(sender());
if (!btn) return;
int row = btn->property("row").toInt();
// 确认删除
QMessageBox::StandardButton reply = StyledMessageBox::question(
this,
"确认删除",
QString("确定要删除相机 %1 吗?").arg(row + 1),
QMessageBox::Yes | QMessageBox::No
);
if (reply == QMessageBox::Yes) {
RemoveCamera(row);
RefreshCameraList();
LOG_INFO("Camera %d deleted\n", row + 1);
}
}
void DialogCamera::onMoveUpClicked()
{
QPushButton* btn = qobject_cast<QPushButton*>(sender());
if (!btn) return;
int row = btn->property("row").toInt();
if (row <= 0 || row >= static_cast<int>(m_cameraConfigs.size())) {
return;
}
// 交换当前行和上一行
std::swap(m_cameraConfigs[row], m_cameraConfigs[row - 1]);
// 重新分配编号
for (size_t i = 0; i < m_cameraConfigs.size(); ++i) {
m_cameraConfigs[i].cameraIndex = static_cast<int>(i) + 1;
}
// 刷新显示
RefreshCameraList();
LOG_INFO("Camera moved up: row %d -> %d\n", row + 1, row);
}
void DialogCamera::onMoveDownClicked()
{
QPushButton* btn = qobject_cast<QPushButton*>(sender());
if (!btn) return;
int row = btn->property("row").toInt();
if (row < 0 || row >= static_cast<int>(m_cameraConfigs.size()) - 1) {
return;
}
// 交换当前行和下一行
std::swap(m_cameraConfigs[row], m_cameraConfigs[row + 1]);
// 重新分配编号
for (size_t i = 0; i < m_cameraConfigs.size(); ++i) {
m_cameraConfigs[i].cameraIndex = static_cast<int>(i) + 1;
}
// 刷新显示
RefreshCameraList();
LOG_INFO("Camera moved down: row %d -> %d\n", row + 1, row + 2);
}
void DialogCamera::RemoveCamera(int row)
{
if (row >= 0 && row < static_cast<int>(m_cameraConfigs.size())) {
m_cameraConfigs.erase(m_cameraConfigs.begin() + row);
// 重新分配编号
for (size_t i = 0; i < m_cameraConfigs.size(); ++i) {
m_cameraConfigs[i].cameraIndex = static_cast<int>(i) + 1;
}
}
}
void DialogCamera::on_btn_save_clicked()
{
if (m_cameraConfigs.empty()) {
StyledMessageBox::warning(this, "提示", "请至少添加一个相机!");
return;
}
// 保存配置
if (SaveConfigToFile()) {
StyledMessageBox::information(this, "成功", "相机配置已保存!\n需要重启应用才能生效。");
emit configSaved();
accept();
} else {
StyledMessageBox::warning(this, "失败", "保存配置失败!");
}
}
void DialogCamera::on_btn_cancel_clicked()
{
reject();
}
bool DialogCamera::ValidateIPAddress(const QString& ip)
{
// 清理输入:去除首尾空白字符
QString cleanIp = ip.trimmed();
if (cleanIp.isEmpty()) {
return false;
}
// 转换为C字符串
QByteArray ipBytes = cleanIp.toLatin1();
const char* ipStr = ipBytes.constData();
// 使用 VrNetUtils 提供的 IP 验证函数
bool isValid = CVrNetUtils::IsValidIP(ipStr);
if (isValid) {
LOG_INFO("IP validation successful: %s\n", ipStr);
} else {
LOG_WARNING("IP validation failed: %s\n", ipStr);
}
return isValid;
}
void DialogCamera::UpdateAddButtonState()
{
// 如果已达到最大数量,禁用添加按钮
bool canAdd = static_cast<int>(m_cameraConfigs.size()) < MAX_CAMERAS;
ui->btn_add_camera->setEnabled(canAdd);
if (!canAdd) {
ui->lineEdit_name->setEnabled(false);
ui->lineEdit_ip->setEnabled(false);
ui->lineEdit_name->setPlaceholderText("已达到最大数量");
ui->lineEdit_ip->setPlaceholderText("已达到最大数量");
} else {
ui->lineEdit_name->setEnabled(true);
ui->lineEdit_ip->setEnabled(true);
ui->lineEdit_name->setPlaceholderText("例如: 相机1");
ui->lineEdit_ip->setPlaceholderText("例如: 192.168.1.100");
}
}
bool DialogCamera::SaveConfigToFile()
{
if (!m_presenter) {
LOG_ERROR("Presenter is not set, cannot save configuration\n");
return false;
}
try {
WheelMeasureConfigResult* configResult = m_presenter->GetConfigResult();
IVrWheelMeasureConfig* vrConfig = m_presenter->GetConfig();
if (!configResult || !vrConfig) {
LOG_ERROR("ConfigResult or IVrWheelMeasureConfig is null\n");
return false;
}
// 更新ConfigResult中的cameras
configResult->cameras.clear();
for (const auto& camera : m_cameraConfigs) {
configResult->cameras.push_back(camera);
LOG_INFO(" Camera %d: %s (IP: %s)\n",
camera.cameraIndex,
camera.name.c_str(),
camera.cameraIP.c_str());
}
// 保存配置到文件
std::string configFilePath = PathManager::GetInstance().GetConfigFilePath().toStdString();
bool saveResult = vrConfig->SaveConfig(configFilePath, *configResult);
if (saveResult) {
LOG_INFO("Camera configuration saved successfully to: %s\n", configFilePath.c_str());
} else {
LOG_ERROR("Failed to save camera configuration to file\n");
}
return saveResult;
} catch (const std::exception& e) {
LOG_ERROR("Exception while saving camera configuration: %s\n", e.what());
return false;
} catch (...) {
LOG_ERROR("Unknown exception while saving camera configuration\n");
return false;
}
}
std::vector<WheelCameraParam> DialogCamera::GetCameraConfigs() const
{
return m_cameraConfigs;
}