|
|
#include "mainwindow.h"
|
|
|
#include "../models/dronedata.h"
|
|
|
#include "../models/detectiondata.h"
|
|
|
#include "../pages/videopage.h"
|
|
|
#include "../pages/mappage.h"
|
|
|
#include "../pages/datapage.h"
|
|
|
#include "../pages/visionmodelpage.h"
|
|
|
#include "../utils/stylehelper.h"
|
|
|
|
|
|
#include <QApplication>
|
|
|
#include <QStackedWidget>
|
|
|
#include <QHBoxLayout>
|
|
|
#include <QVBoxLayout>
|
|
|
#include <QToolButton>
|
|
|
#include <QPushButton>
|
|
|
#include <QLabel>
|
|
|
#include <QGroupBox>
|
|
|
#include <QFrame>
|
|
|
#include <QLineEdit>
|
|
|
#include <QComboBox>
|
|
|
#include <QSpacerItem>
|
|
|
#include <QProgressBar>
|
|
|
#include <QGraphicsDropShadowEffect>
|
|
|
#include <QDebug> // Added for qDebug
|
|
|
#include <QScrollArea> // Added for QScrollArea
|
|
|
|
|
|
// 添加TaskDecisionPage包含
|
|
|
#include "../pages/taskdecisionpage.h"
|
|
|
#include "../pages/dronemanagementpage.h"
|
|
|
#include "../pages/taskdetailspage.h"
|
|
|
|
|
|
// 在文件顶部添加extern声明
|
|
|
// extern DroneManager* globalDroneManager;
|
|
|
// extern DetectionData* globalDetectionData;
|
|
|
|
|
|
// 改为定义
|
|
|
DroneManager* globalDroneManager = nullptr;
|
|
|
DetectionData* globalDetectionData = nullptr;
|
|
|
|
|
|
static QGroupBox* makeGroupBox(const QString& title, QWidget* inner) {
|
|
|
auto* gb = new QGroupBox(title);
|
|
|
auto* v = new QVBoxLayout(gb);
|
|
|
v->setContentsMargins(10,10,10,10);
|
|
|
v->addWidget(inner);
|
|
|
return gb;
|
|
|
}
|
|
|
|
|
|
MainWindow::MainWindow(QWidget *parent)
|
|
|
: QMainWindow(parent)
|
|
|
, droneManager_(new DroneManager(this))
|
|
|
, droneSelector_(nullptr)
|
|
|
, posXLabel_(nullptr), posYLabel_(nullptr), posZLabel_(nullptr)
|
|
|
, velXLabel_(nullptr), velYLabel_(nullptr), velZLabel_(nullptr)
|
|
|
, attRLabel_(nullptr), attPLabel_(nullptr), attYLabel_(nullptr)
|
|
|
, expXLabel_(nullptr), expYLabel_(nullptr), expZLabel_(nullptr)
|
|
|
, expVxLabel_(nullptr), expVyLabel_(nullptr), expVzLabel_(nullptr)
|
|
|
, videoInfoLabel_(nullptr)
|
|
|
, detectionTargets_(nullptr), detectionTargetObject_(nullptr), detectionAccuracy_(nullptr), detectionTime_(nullptr)
|
|
|
, detectionData_(new DetectionData(this))
|
|
|
// 初始化新成员
|
|
|
, baseFontSize_(10)
|
|
|
, initialWidth_(1200)
|
|
|
{
|
|
|
// 初始化无人机数据
|
|
|
initializeDrones();
|
|
|
|
|
|
// 顶部工具条
|
|
|
auto* header = buildHeader();
|
|
|
|
|
|
// 左侧侧边栏
|
|
|
auto* side = buildSideNav();
|
|
|
|
|
|
// 页面堆栈
|
|
|
pages_ = new QStackedWidget(this);
|
|
|
auto* pageMap = buildMapPage(); // 先地图
|
|
|
auto* pageVideo = buildVideoPage(); // 然后视频
|
|
|
auto* pageData = buildDataPage();
|
|
|
auto* pageDroneMgmt = buildDroneManagementPage();
|
|
|
auto* pageTask = buildTaskDecisionPage(); // 新页面
|
|
|
auto* pageTaskDetails = buildTaskDetailsPage();
|
|
|
auto* pageVision = buildVisionModelPage();
|
|
|
pages_->addWidget(pageMap);
|
|
|
pages_->addWidget(pageVideo);
|
|
|
pages_->addWidget(pageData);
|
|
|
pages_->addWidget(pageDroneMgmt);
|
|
|
pages_->addWidget(pageTask);
|
|
|
pages_->addWidget(pageTaskDetails);
|
|
|
pages_->addWidget(pageVision);
|
|
|
|
|
|
// 右侧状态信息(两页共用静态占位)
|
|
|
rightPanel_ = buildRightStatusPanel();
|
|
|
rightPanel_->setMinimumWidth(260);
|
|
|
|
|
|
// 中间主体
|
|
|
auto* centerWidget = new QWidget(this);
|
|
|
auto* centerV = new QVBoxLayout(centerWidget);
|
|
|
centerV->setContentsMargins(8,8,8,8);
|
|
|
centerV->setSpacing(8);
|
|
|
|
|
|
centerV->addWidget(header);
|
|
|
|
|
|
auto* contentH = new QHBoxLayout();
|
|
|
contentH->setSpacing(8);
|
|
|
contentH->addWidget(side);
|
|
|
contentH->addWidget(pages_, 1);
|
|
|
contentH->addWidget(rightPanel_);
|
|
|
centerV->addLayout(contentH, 1);
|
|
|
|
|
|
setCentralWidget(centerWidget);
|
|
|
setWindowTitle("");
|
|
|
|
|
|
// 设置初始大小(用于缩放计算)
|
|
|
resize(1200, 800); // 假设初始大小,您可以调整
|
|
|
|
|
|
// 连接信号槽
|
|
|
connect(droneManager_, &DroneManager::currentDroneChanged,
|
|
|
this, &MainWindow::onCurrentDroneChanged);
|
|
|
|
|
|
// 连接检测数据信号
|
|
|
connect(detectionData_, &DetectionData::detectionUpdated,
|
|
|
this, &MainWindow::updateDetectionInfo);
|
|
|
|
|
|
// 启动检测模拟
|
|
|
// detectionData_->startSimulation();
|
|
|
|
|
|
globalDroneManager = droneManager_;
|
|
|
globalDetectionData = detectionData_;
|
|
|
|
|
|
// 连接信号到DataPage
|
|
|
DataPage* dataPage = qobject_cast<DataPage*>(pages_->widget(2));
|
|
|
if (dataPage) {
|
|
|
connect(droneManager_, &DroneManager::currentDroneChanged, dataPage, &DataPage::updateDroneInfo);
|
|
|
connect(detectionData_, &DetectionData::detectionUpdated, dataPage, &DataPage::updateTargetInfo);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 增强resizeEvent来调整所有收集的组件
|
|
|
void MainWindow::resizeEvent(QResizeEvent *event) {
|
|
|
QMainWindow::resizeEvent(event);
|
|
|
|
|
|
// 移除所有设置以防止无限resize循环
|
|
|
// 只计算scale用于调试
|
|
|
float scale = static_cast<float>(width()) / initialWidth_;
|
|
|
|
|
|
qDebug() << "MainWindow resized with scale:" << scale;
|
|
|
}
|
|
|
|
|
|
QWidget* MainWindow::buildHeader() {
|
|
|
auto* w = new QWidget(this);
|
|
|
w->setObjectName(QString("headerBar"));
|
|
|
auto* h = new QHBoxLayout(w);
|
|
|
h->setContentsMargins(10,6,10,6);
|
|
|
h->setSpacing(12);
|
|
|
|
|
|
auto* logo = new QLabel("基于无人机的察打一体地面指挥端");
|
|
|
logo->setStyleSheet("font-weight:bold; font-size:16px;");
|
|
|
h->addWidget(logo);
|
|
|
|
|
|
h->addSpacing(20);
|
|
|
h->addStretch(1);
|
|
|
|
|
|
// 在buildHeader中收集头部按钮
|
|
|
for (auto text : {"连接设置"}) {
|
|
|
auto* b = new QPushButton(text);
|
|
|
b->setProperty("primary", true);
|
|
|
h->addWidget(b);
|
|
|
headerButtons_.append(b);
|
|
|
}
|
|
|
auto* rec = new QLabel(" ");
|
|
|
rec->setFixedSize(14,14);
|
|
|
rec->setObjectName(QString("recLed"));
|
|
|
h->addWidget(rec);
|
|
|
|
|
|
// 阴影
|
|
|
auto* shadow = new QGraphicsDropShadowEffect(w);
|
|
|
shadow->setBlurRadius(18);
|
|
|
shadow->setOffset(0, 2);
|
|
|
shadow->setColor(QColor(0,0,0,30));
|
|
|
w->setGraphicsEffect(shadow);
|
|
|
|
|
|
return w;
|
|
|
}
|
|
|
|
|
|
QWidget* MainWindow::buildSideNav() {
|
|
|
auto* side = new QWidget(this);
|
|
|
// side->setFixedWidth(76); // 移除固定宽度,让它可调整
|
|
|
side->setObjectName(QString("sideNav"));
|
|
|
|
|
|
auto* v = new QVBoxLayout(side);
|
|
|
v->setContentsMargins(6,8,6,8);
|
|
|
v->setSpacing(10);
|
|
|
|
|
|
// 在buildSideNav中收集侧边栏按钮
|
|
|
auto makeBtn = [&](const QString& text, auto slot){
|
|
|
auto* b = new QToolButton();
|
|
|
b->setText(text);
|
|
|
b->setToolButtonStyle(Qt::ToolButtonTextOnly);
|
|
|
b->setFixedHeight(48);
|
|
|
connect(b, &QToolButton::clicked, this, slot);
|
|
|
v->addWidget(b);
|
|
|
sideNavButtons_.append(b); // 添加到列表
|
|
|
};
|
|
|
|
|
|
// 在buildSideNav中调整槽连接以交换内容
|
|
|
makeBtn("视频监控", &MainWindow::showMapPage); // 显示原地图内容
|
|
|
makeBtn("地图监控", &MainWindow::showVideoPage); // 显示原视频内容
|
|
|
makeBtn("数据监控", &MainWindow::showDataPage);
|
|
|
makeBtn("无人机管理", &MainWindow::showDroneManagementPage);
|
|
|
makeBtn("任务决策", &MainWindow::showTaskDecisionPage);
|
|
|
makeBtn("任务详情", &MainWindow::showTaskDetailsPage);
|
|
|
makeBtn("视觉模型", &MainWindow::showVisionModelPage);
|
|
|
v->addStretch(1);
|
|
|
|
|
|
auto* shadow = new QGraphicsDropShadowEffect(side);
|
|
|
shadow->setBlurRadius(18);
|
|
|
shadow->setOffset(0, 2);
|
|
|
shadow->setColor(QColor(0,0,0,24));
|
|
|
side->setGraphicsEffect(shadow);
|
|
|
|
|
|
return side;
|
|
|
}
|
|
|
|
|
|
QWidget* MainWindow::buildVideoPage() {
|
|
|
// 左图风格:中间大区域 + 底部控制条
|
|
|
auto* page = new QWidget(this);
|
|
|
auto* v = new QVBoxLayout(page);
|
|
|
v->setSpacing(8);
|
|
|
v->setContentsMargins(0,0,0,0);
|
|
|
|
|
|
// 顶部无人机选择区域
|
|
|
auto* topBar = new QWidget(page);
|
|
|
auto* th = new QHBoxLayout(topBar);
|
|
|
th->setContentsMargins(0,0,0,0);
|
|
|
th->setSpacing(12);
|
|
|
|
|
|
auto* droneLabel = new QLabel("选择无人机:");
|
|
|
droneLabel->setStyleSheet("font-weight:bold;");
|
|
|
th->addWidget(droneLabel);
|
|
|
|
|
|
droneSelector_ = new QComboBox(page);
|
|
|
droneSelector_->setFixedWidth(150);
|
|
|
droneSelector_->setObjectName("droneSelector");
|
|
|
|
|
|
// 从无人机管理器获取无人机列表
|
|
|
QStringList droneIds = droneManager_->getDroneIds();
|
|
|
for (const QString& id : droneIds) {
|
|
|
DroneData* drone = droneManager_->getDrone(id);
|
|
|
if (drone) {
|
|
|
droneSelector_->addItem(drone->getName(), id);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 连接选择变化信号
|
|
|
connect(droneSelector_, QOverload<int>::of(&QComboBox::currentIndexChanged),
|
|
|
this, &MainWindow::onDroneSelectionChanged);
|
|
|
|
|
|
th->addWidget(droneSelector_);
|
|
|
|
|
|
th->addStretch(1);
|
|
|
|
|
|
// 连接状态指示器
|
|
|
auto* statusLabel = new QLabel("● 已连接");
|
|
|
statusLabel->setStyleSheet("color:#4caf50; font-weight:bold;");
|
|
|
th->addWidget(statusLabel);
|
|
|
|
|
|
v->addWidget(topBar);
|
|
|
|
|
|
// 中间黑色占位(视频/地图区域)
|
|
|
auto* center = new QFrame(page);
|
|
|
center->setStyleSheet("QFrame{background:#2b2b2b; border:1px solid #444;}");
|
|
|
center->setMinimumHeight(520);
|
|
|
|
|
|
// 在视频区域添加当前无人机信息显示
|
|
|
videoInfoLabel_ = new QLabel("当前监控: 无人机-001");
|
|
|
videoInfoLabel_->setStyleSheet("color:#ffffff; font-size:16px; font-weight:bold; background:rgba(0,0,0,0.3); padding:8px; border-radius:4px;");
|
|
|
videoInfoLabel_->setAlignment(Qt::AlignCenter);
|
|
|
videoInfoLabel_->setObjectName("videoInfoLabel");
|
|
|
|
|
|
// 将信息标签添加到视频区域中心
|
|
|
auto* centerLayout = new QVBoxLayout(center);
|
|
|
centerLayout->setContentsMargins(0,0,0,0);
|
|
|
centerLayout->addStretch(1);
|
|
|
centerLayout->addWidget(videoInfoLabel_);
|
|
|
centerLayout->addStretch(1);
|
|
|
|
|
|
// 底部条(交换为原地图页的播放控制)
|
|
|
auto* bottom = new QWidget(page);
|
|
|
auto* bh = new QHBoxLayout(bottom);
|
|
|
bh->setContentsMargins(0,0,0,0);
|
|
|
for (auto text : {"播放", "暂停", "拍照(T)", "录像(R)", "停止", "框选", "点击", "手动控制"}) {
|
|
|
bh->addWidget(new QPushButton(text));
|
|
|
}
|
|
|
bh->addStretch(1);
|
|
|
|
|
|
// 检测信息对话框
|
|
|
auto* detectionDialog = new QFrame(page);
|
|
|
detectionDialog->setStyleSheet("QFrame{background:#f8f9fa; border:1px solid #dee2e6; border-radius:8px; margin:4px;}");
|
|
|
detectionDialog->setMaximumHeight(120);
|
|
|
|
|
|
auto* detectionLayout = new QVBoxLayout(detectionDialog);
|
|
|
detectionLayout->setContentsMargins(12,8,12,8);
|
|
|
detectionLayout->setSpacing(6);
|
|
|
|
|
|
// 检测信息标题
|
|
|
auto* detectionTitle = new QLabel("实时检测信息");
|
|
|
detectionTitle->setStyleSheet("font-weight:bold; color:#495057; font-size:14px;");
|
|
|
detectionLayout->addWidget(detectionTitle);
|
|
|
|
|
|
// 检测信息内容区域
|
|
|
auto* detectionContent = new QWidget();
|
|
|
auto* contentLayout = new QHBoxLayout(detectionContent);
|
|
|
contentLayout->setContentsMargins(0,0,0,0);
|
|
|
contentLayout->setSpacing(20);
|
|
|
|
|
|
// 检测目标信息
|
|
|
auto* targetInfo = new QWidget();
|
|
|
auto* targetLayout = new QVBoxLayout(targetInfo);
|
|
|
targetLayout->setContentsMargins(0,0,0,0);
|
|
|
targetLayout->setSpacing(4);
|
|
|
|
|
|
auto* targetLabel = new QLabel("检测目标:");
|
|
|
targetLabel->setStyleSheet("font-weight:bold; color:#6c757d;");
|
|
|
detectionTargets_ = new QLabel("人员 × 3, 车辆 × 2");
|
|
|
detectionTargets_->setStyleSheet("color:#28a745; font-weight:bold;");
|
|
|
detectionTargets_->setObjectName("detectionTargets");
|
|
|
|
|
|
targetLayout->addWidget(targetLabel);
|
|
|
targetLayout->addWidget(detectionTargets_);
|
|
|
|
|
|
// 目标对象信息
|
|
|
auto* targetObjectInfo = new QWidget();
|
|
|
auto* targetObjectLayout = new QVBoxLayout(targetObjectInfo);
|
|
|
targetObjectLayout->setContentsMargins(0,0,0,0);
|
|
|
targetObjectLayout->setSpacing(4);
|
|
|
|
|
|
auto* targetObjectLabel = new QLabel("是否为目标对象:");
|
|
|
targetObjectLabel->setStyleSheet("font-weight:bold; color:#6c757d;");
|
|
|
detectionTargetObject_ = new QLabel("● 是");
|
|
|
detectionTargetObject_->setStyleSheet("color:#28a745; font-weight:bold;");
|
|
|
detectionTargetObject_->setObjectName("detectionTargetObject");
|
|
|
|
|
|
targetObjectLayout->addWidget(targetObjectLabel);
|
|
|
targetObjectLayout->addWidget(detectionTargetObject_);
|
|
|
|
|
|
// 检测精度信息
|
|
|
auto* accuracyInfo = new QWidget();
|
|
|
auto* accuracyLayout = new QVBoxLayout(accuracyInfo);
|
|
|
accuracyLayout->setContentsMargins(0,0,0,0);
|
|
|
accuracyLayout->setSpacing(4);
|
|
|
|
|
|
auto* accuracyLabel = new QLabel("检测精度:");
|
|
|
accuracyLabel->setStyleSheet("font-weight:bold; color:#6c757d;");
|
|
|
detectionAccuracy_ = new QLabel("95.2%");
|
|
|
detectionAccuracy_->setStyleSheet("color:#fd7e14; font-weight:bold;");
|
|
|
detectionAccuracy_->setObjectName("detectionAccuracy");
|
|
|
|
|
|
accuracyLayout->addWidget(accuracyLabel);
|
|
|
accuracyLayout->addWidget(detectionAccuracy_);
|
|
|
|
|
|
// 检测时间信息
|
|
|
auto* timeInfo = new QWidget();
|
|
|
auto* timeLayout = new QVBoxLayout(timeInfo);
|
|
|
timeLayout->setContentsMargins(0,0,0,0);
|
|
|
timeLayout->setSpacing(4);
|
|
|
|
|
|
auto* timeLabel = new QLabel("最后更新:");
|
|
|
timeLabel->setStyleSheet("font-weight:bold; color:#6c757d;");
|
|
|
detectionTime_ = new QLabel("2024-01-15 14:32:15");
|
|
|
detectionTime_->setStyleSheet("color:#6c757d;");
|
|
|
detectionTime_->setObjectName("detectionTime");
|
|
|
|
|
|
timeLayout->addWidget(timeLabel);
|
|
|
timeLayout->addWidget(detectionTime_);
|
|
|
|
|
|
contentLayout->addWidget(targetInfo);
|
|
|
contentLayout->addWidget(targetObjectInfo);
|
|
|
contentLayout->addWidget(accuracyInfo);
|
|
|
contentLayout->addWidget(timeInfo);
|
|
|
contentLayout->addStretch(1);
|
|
|
|
|
|
detectionLayout->addWidget(detectionContent);
|
|
|
|
|
|
v->addWidget(center, 1);
|
|
|
v->addWidget(bottom);
|
|
|
v->addWidget(detectionDialog);
|
|
|
|
|
|
return page;
|
|
|
}
|
|
|
|
|
|
QWidget* MainWindow::buildMapPage() {
|
|
|
return new MapPage(this);
|
|
|
}
|
|
|
|
|
|
QWidget* MainWindow::buildRightStatusPanel() {
|
|
|
auto* panel = new QWidget(this);
|
|
|
panel->setObjectName(QString("rightPanel"));
|
|
|
auto* v = new QVBoxLayout(panel);
|
|
|
v->setSpacing(8);
|
|
|
v->setContentsMargins(0,0,0,0);
|
|
|
|
|
|
// 基本信息 / GPS信息 标签页视觉(简化为两个分组)
|
|
|
auto makeKV = [this](const QString& k, QLabel** labelRef) {
|
|
|
auto* row = new QWidget();
|
|
|
auto* h = new QHBoxLayout(row);
|
|
|
h->setContentsMargins(0,0,0,0);
|
|
|
h->addWidget(new QLabel(k));
|
|
|
auto* val = new QLabel("0.00");
|
|
|
val->setAlignment(Qt::AlignRight);
|
|
|
*labelRef = val; // 保存标签引用
|
|
|
h->addWidget(val);
|
|
|
return row;
|
|
|
};
|
|
|
|
|
|
auto* baseInfo = new QWidget();
|
|
|
auto* bi = new QVBoxLayout(baseInfo);
|
|
|
bi->setContentsMargins(0,0,0,0);
|
|
|
bi->addWidget(new QLabel("位置[x y z] [m]"));
|
|
|
bi->addWidget(makeKV("x:", &posXLabel_));
|
|
|
bi->addWidget(makeKV("y:", &posYLabel_));
|
|
|
bi->addWidget(makeKV("z:", &posZLabel_));
|
|
|
bi->addSpacing(6);
|
|
|
bi->addWidget(new QLabel("速度[x y z] [m/s]"));
|
|
|
bi->addWidget(makeKV("vx:", &velXLabel_));
|
|
|
bi->addWidget(makeKV("vy:", &velYLabel_));
|
|
|
bi->addWidget(makeKV("vz:", &velZLabel_));
|
|
|
bi->addSpacing(6);
|
|
|
bi->addWidget(new QLabel("姿态[r p y] [deg]"));
|
|
|
bi->addWidget(makeKV("r:", &attRLabel_));
|
|
|
bi->addWidget(makeKV("p:", &attPLabel_));
|
|
|
bi->addWidget(makeKV("y:", &attYLabel_));
|
|
|
v->addWidget(makeGroupBox("基本信息", baseInfo));
|
|
|
|
|
|
auto* expectInfo = new QWidget();
|
|
|
auto* ei = new QVBoxLayout(expectInfo);
|
|
|
ei->setContentsMargins(0,0,0,0);
|
|
|
ei->addWidget(new QLabel("期望位置[x y z] [m]"));
|
|
|
ei->addWidget(makeKV("ex:", &expXLabel_));
|
|
|
ei->addWidget(makeKV("ey:", &expYLabel_));
|
|
|
ei->addWidget(makeKV("ez:", &expZLabel_));
|
|
|
ei->addSpacing(6);
|
|
|
ei->addWidget(new QLabel("期望速度[x y z] [m/s]"));
|
|
|
ei->addWidget(makeKV("evx:", &expVxLabel_));
|
|
|
ei->addWidget(makeKV("evy:", &expVyLabel_));
|
|
|
ei->addWidget(makeKV("evz:", &expVzLabel_));
|
|
|
v->addWidget(makeGroupBox("期望信息", expectInfo));
|
|
|
|
|
|
// 右侧按钮区域(当前悬停等)
|
|
|
auto* act = new QWidget();
|
|
|
auto* av = new QVBoxLayout(act);
|
|
|
for (auto text : {"当前点悬停", "初始化悬停", "删除"}) {
|
|
|
auto* b = new QPushButton(text);
|
|
|
b->setProperty("primary", true);
|
|
|
av->addWidget(b);
|
|
|
rightPanelButtons_.append(b); // 添加到列表
|
|
|
}
|
|
|
av->addSpacing(8);
|
|
|
auto* mode = new QComboBox();
|
|
|
mode->addItems({"XYZ_POS", "XYZ_POS_YAW", "VEL", "ACC"});
|
|
|
av->addWidget(mode);
|
|
|
rightPanelModeCombo_ = mode; // 保存引用
|
|
|
|
|
|
for (auto k : {"x [m]", "y [m]", "z [m]", "yaw [deg]"}) {
|
|
|
auto* e = new QLineEdit();
|
|
|
e->setPlaceholderText(k);
|
|
|
av->addWidget(e);
|
|
|
rightPanelInputs_.append(e); // 添加到列表
|
|
|
}
|
|
|
auto* upload = new QPushButton("上传");
|
|
|
upload->setProperty("primary", true);
|
|
|
av->addWidget(upload);
|
|
|
rightPanelButtons_.append(upload); // 添加到列表
|
|
|
av->addStretch(1);
|
|
|
|
|
|
// 阴影
|
|
|
auto* shadow = new QGraphicsDropShadowEffect(panel);
|
|
|
shadow->setBlurRadius(18);
|
|
|
shadow->setOffset(0, 2);
|
|
|
shadow->setColor(QColor(0,0,0,16));
|
|
|
panel->setGraphicsEffect(shadow);
|
|
|
|
|
|
// 使用QScrollArea包裹act以防溢出
|
|
|
auto* scrollArea = new QScrollArea(panel);
|
|
|
scrollArea->setWidgetResizable(true);
|
|
|
scrollArea->setWidget(act);
|
|
|
scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
|
|
|
scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
|
|
|
v->addWidget(scrollArea, 1); // 替换原v->addWidget(act);
|
|
|
|
|
|
return panel;
|
|
|
}
|
|
|
|
|
|
void MainWindow::showVideoPage() { pages_->setCurrentIndex(0); }
|
|
|
void MainWindow::showMapPage() { pages_->setCurrentIndex(1); }
|
|
|
void MainWindow::showDataPage() { pages_->setCurrentIndex(2); }
|
|
|
void MainWindow::showDroneManagementPage() { pages_->setCurrentIndex(3); }
|
|
|
void MainWindow::showTaskDecisionPage() { pages_->setCurrentIndex(4); }
|
|
|
void MainWindow::showTaskDetailsPage() { pages_->setCurrentIndex(5); }
|
|
|
void MainWindow::showVisionModelPage() { pages_->setCurrentIndex(6); }
|
|
|
|
|
|
QWidget* MainWindow::buildDataPage() {
|
|
|
return new DataPage(this); // 使用DataPage类
|
|
|
}
|
|
|
|
|
|
QWidget* MainWindow::buildTaskDecisionPage() {
|
|
|
return new TaskDecisionPage(this); // 使用新类
|
|
|
}
|
|
|
|
|
|
QWidget* MainWindow::buildDroneManagementPage() {
|
|
|
return new DroneManagementPage(this);
|
|
|
}
|
|
|
|
|
|
QWidget* MainWindow::buildTaskDetailsPage() {
|
|
|
return new TaskDetailsPage(this);
|
|
|
}
|
|
|
|
|
|
QWidget* MainWindow::buildVisionModelPage() {
|
|
|
// 使用我们集成了YOLOv5功能的VisionModelPage类
|
|
|
auto* page = new VisionModelPage(this);
|
|
|
return page;
|
|
|
}
|
|
|
|
|
|
// 初始化无人机数据
|
|
|
void MainWindow::initializeDrones() {
|
|
|
// 添加4架无人机
|
|
|
droneManager_->addDrone("drone-001", "无人机-001");
|
|
|
droneManager_->addDrone("drone-002", "无人机-002");
|
|
|
droneManager_->addDrone("drone-003", "无人机-003");
|
|
|
droneManager_->addDrone("drone-004", "无人机-004");
|
|
|
|
|
|
// 设置第一架为当前选中
|
|
|
if (!droneManager_->getDroneIds().isEmpty()) {
|
|
|
droneManager_->setCurrentDrone(droneManager_->getDroneIds().first());
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 无人机选择变化处理
|
|
|
void MainWindow::onDroneSelectionChanged() {
|
|
|
if (!droneSelector_) return;
|
|
|
|
|
|
int index = droneSelector_->currentIndex();
|
|
|
if (index >= 0) {
|
|
|
QString droneId = droneSelector_->itemData(index).toString();
|
|
|
droneManager_->setCurrentDrone(droneId);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 当前无人机变化处理
|
|
|
void MainWindow::onCurrentDroneChanged(const QString& droneId) {
|
|
|
if (!droneSelector_) return;
|
|
|
|
|
|
// 更新下拉框选择
|
|
|
for (int i = 0; i < droneSelector_->count(); ++i) {
|
|
|
if (droneSelector_->itemData(i).toString() == droneId) {
|
|
|
droneSelector_->setCurrentIndex(i);
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 连接当前无人机的数据更新信号
|
|
|
DroneData* currentDrone = droneManager_->getCurrentDrone();
|
|
|
if (currentDrone) {
|
|
|
connect(currentDrone, &DroneData::dataChanged,
|
|
|
this, &MainWindow::updateDroneStatus, Qt::UniqueConnection);
|
|
|
updateDroneStatus(); // 立即更新一次
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 更新无人机状态显示
|
|
|
void MainWindow::updateDroneStatus() {
|
|
|
DroneData* currentDrone = droneManager_->getCurrentDrone();
|
|
|
if (!currentDrone) return;
|
|
|
|
|
|
// 更新视频区域显示信息
|
|
|
if (videoInfoLabel_) {
|
|
|
videoInfoLabel_->setText(QString("当前监控: %1").arg(currentDrone->getName()));
|
|
|
}
|
|
|
|
|
|
// 更新位置信息
|
|
|
QVector3D pos = currentDrone->getPosition();
|
|
|
if (posXLabel_) posXLabel_->setText(QString::number(pos.x(), 'f', 2));
|
|
|
if (posYLabel_) posYLabel_->setText(QString::number(pos.y(), 'f', 2));
|
|
|
if (posZLabel_) posZLabel_->setText(QString::number(pos.z(), 'f', 2));
|
|
|
|
|
|
// 更新速度信息
|
|
|
QVector3D vel = currentDrone->getVelocity();
|
|
|
if (velXLabel_) velXLabel_->setText(QString::number(vel.x(), 'f', 2));
|
|
|
if (velYLabel_) velYLabel_->setText(QString::number(vel.y(), 'f', 2));
|
|
|
if (velZLabel_) velZLabel_->setText(QString::number(vel.z(), 'f', 2));
|
|
|
|
|
|
// 更新姿态信息
|
|
|
QVector3D att = currentDrone->getAttitude();
|
|
|
if (attRLabel_) attRLabel_->setText(QString::number(att.x(), 'f', 2));
|
|
|
if (attPLabel_) attPLabel_->setText(QString::number(att.y(), 'f', 2));
|
|
|
if (attYLabel_) attYLabel_->setText(QString::number(att.z(), 'f', 2));
|
|
|
|
|
|
// 更新期望位置信息
|
|
|
QVector3D expPos = currentDrone->getExpectedPosition();
|
|
|
if (expXLabel_) expXLabel_->setText(QString::number(expPos.x(), 'f', 2));
|
|
|
if (expYLabel_) expYLabel_->setText(QString::number(expPos.y(), 'f', 2));
|
|
|
if (expZLabel_) expZLabel_->setText(QString::number(expPos.z(), 'f', 2));
|
|
|
|
|
|
// 更新期望速度信息
|
|
|
QVector3D expVel = currentDrone->getExpectedVelocity();
|
|
|
if (expVxLabel_) expVxLabel_->setText(QString::number(expVel.x(), 'f', 2));
|
|
|
if (expVyLabel_) expVyLabel_->setText(QString::number(expVel.y(), 'f', 2));
|
|
|
if (expVzLabel_) expVzLabel_->setText(QString::number(expVel.z(), 'f', 2));
|
|
|
}
|
|
|
|
|
|
// 更新检测信息显示
|
|
|
void MainWindow::updateDetectionInfo() {
|
|
|
if (!detectionData_) return;
|
|
|
|
|
|
// 更新检测目标信息
|
|
|
if (detectionTargets_) {
|
|
|
QVector<DetectionTarget> targets = detectionData_->getTargets();
|
|
|
QStringList targetCounts;
|
|
|
|
|
|
// 统计各类型目标数量
|
|
|
QMap<QString, int> typeCount;
|
|
|
for (const auto& target : targets) {
|
|
|
typeCount[target.type]++;
|
|
|
}
|
|
|
|
|
|
// 生成目标描述文本
|
|
|
for (auto it = typeCount.begin(); it != typeCount.end(); ++it) {
|
|
|
targetCounts.append(QString("%1 × %2").arg(it.key()).arg(it.value()));
|
|
|
}
|
|
|
|
|
|
if (targetCounts.isEmpty()) {
|
|
|
detectionTargets_->setText("无检测目标");
|
|
|
detectionTargets_->setStyleSheet("color:#6c757d; font-weight:bold;");
|
|
|
} else {
|
|
|
detectionTargets_->setText(targetCounts.join(", "));
|
|
|
detectionTargets_->setStyleSheet("color:#28a745; font-weight:bold;");
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 更新目标对象信息
|
|
|
if (detectionTargetObject_) {
|
|
|
QVector<DetectionTarget> targets = detectionData_->getTargets();
|
|
|
bool hasTargets = !targets.isEmpty();
|
|
|
|
|
|
if (hasTargets) {
|
|
|
detectionTargetObject_->setText("● 是");
|
|
|
detectionTargetObject_->setStyleSheet("color:#28a745; font-weight:bold;");
|
|
|
} else {
|
|
|
detectionTargetObject_->setText("● 否");
|
|
|
detectionTargetObject_->setStyleSheet("color:#6c757d; font-weight:bold;");
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 更新检测精度
|
|
|
if (detectionAccuracy_) {
|
|
|
float accuracy = detectionData_->getAccuracy();
|
|
|
detectionAccuracy_->setText(QString("%1%").arg(accuracy, 0, 'f', 1));
|
|
|
|
|
|
// 根据精度设置颜色
|
|
|
QString color;
|
|
|
if (accuracy >= 90.0f) {
|
|
|
color = "#28a745";
|
|
|
} else if (accuracy >= 80.0f) {
|
|
|
color = "#fd7e14";
|
|
|
} else {
|
|
|
color = "#dc3545";
|
|
|
}
|
|
|
detectionAccuracy_->setStyleSheet(QString("color:%1; font-weight:bold;").arg(color));
|
|
|
}
|
|
|
|
|
|
// 更新最后更新时间
|
|
|
if (detectionTime_) {
|
|
|
QDateTime lastUpdate = detectionData_->getLastUpdate();
|
|
|
detectionTime_->setText(lastUpdate.toString("yyyy-MM-dd hh:mm:ss"));
|
|
|
}
|
|
|
}
|