485 lines
16 KiB
C++
Raw Permalink Normal View History

2025-12-27 09:34:02 +08:00
#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;
}