485 lines
16 KiB
C++
485 lines
16 KiB
C++
#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;
|
||
}
|