Compare commits
No commits in common. 'main' and 'qinxinqi' have entirely different histories.
@ -1,18 +0,0 @@
|
||||
<role>
|
||||
<personality>
|
||||
@!thought://remember
|
||||
@!thought://recall
|
||||
@!thought://ui-design-thinking
|
||||
@!thought://documentation-expression
|
||||
</personality>
|
||||
|
||||
<principle>
|
||||
@!execution://qt-ui-design-workflow
|
||||
@!execution://design-documentation-process
|
||||
</principle>
|
||||
|
||||
<knowledge>
|
||||
@!knowledge://qt-ui-expertise
|
||||
@!knowledge://design-documentation-methods
|
||||
</knowledge>
|
||||
</role>
|
Binary file not shown.
@ -1,682 +0,0 @@
|
||||
// RightFunctionPanel.h
|
||||
#ifndef RIGHTFUNCTIONPANEL_H
|
||||
#define RIGHTFUNCTIONPANEL_H
|
||||
|
||||
#include <QWidget>
|
||||
#include <QVBoxLayout>
|
||||
#include <QHBoxLayout>
|
||||
#include <QGridLayout>
|
||||
#include <QGroupBox>
|
||||
#include <QLabel>
|
||||
#include <QPushButton>
|
||||
#include <QFrame>
|
||||
#include <QProgressBar>
|
||||
#include <QSlider>
|
||||
|
||||
class ModuleCard : public QFrame
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ModuleCard(const QString &title, const QString &icon, QWidget *parent = nullptr);
|
||||
void addContent(QWidget *content);
|
||||
|
||||
private:
|
||||
QVBoxLayout *m_contentLayout;
|
||||
QLabel *m_titleLabel;
|
||||
};
|
||||
|
||||
class DeviceCard : public QFrame
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit DeviceCard(const QString &name, const QString &iconPath, QWidget *parent = nullptr);
|
||||
void setStatus(const QString &status, const QColor &color);
|
||||
void setActive(bool active);
|
||||
|
||||
signals:
|
||||
void deviceSelected(const QString &deviceName);
|
||||
|
||||
protected:
|
||||
void mousePressEvent(QMouseEvent *event) override;
|
||||
void paintEvent(QPaintEvent *event) override;
|
||||
|
||||
private:
|
||||
QString m_deviceName;
|
||||
QLabel *m_iconLabel;
|
||||
QLabel *m_nameLabel;
|
||||
QLabel *m_statusLabel;
|
||||
bool m_isActive = false;
|
||||
};
|
||||
|
||||
class RightFunctionPanel : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit RightFunctionPanel(QWidget *parent = nullptr);
|
||||
|
||||
signals:
|
||||
// 战场探索信号
|
||||
void startMapping();
|
||||
void stopMapping();
|
||||
void startNavigation();
|
||||
void stopNavigation();
|
||||
void startPhotoTransmission();
|
||||
void stopPhotoTransmission();
|
||||
void startPersonRecognition();
|
||||
void stopPersonRecognition();
|
||||
|
||||
// 情报传输信号
|
||||
void startVoiceCall();
|
||||
void endVoiceCall();
|
||||
void muteCall(bool muted);
|
||||
void setCallVolume(int volume);
|
||||
|
||||
// 敌情统计信号
|
||||
void refreshEnemyStats();
|
||||
void exportReport();
|
||||
void requestAIAnalysis();
|
||||
|
||||
public slots:
|
||||
void updateEnemyStats(int totalEnemies, const QString &threatLevel);
|
||||
void updateDeviceStatus(const QString &deviceName, bool online, int battery);
|
||||
|
||||
private slots:
|
||||
void onDeviceSelected(const QString &deviceName);
|
||||
void onMappingToggle();
|
||||
void onNavigationToggle();
|
||||
void onPhotoTransmissionToggle();
|
||||
void onPersonRecognitionToggle();
|
||||
void onVoiceCallToggle();
|
||||
void onRefreshStats();
|
||||
void onAIAnalysis();
|
||||
|
||||
private:
|
||||
void setupUI();
|
||||
void setupBattlefieldExplorationModule();
|
||||
void setupIntelligenceModule();
|
||||
void setupEnemyStatsModule();
|
||||
void applyStyles();
|
||||
|
||||
// UI组件
|
||||
QVBoxLayout *m_mainLayout;
|
||||
|
||||
// 战场探索模块
|
||||
ModuleCard *m_explorationCard;
|
||||
DeviceCard *m_robotDogCard;
|
||||
DeviceCard *m_droneCard;
|
||||
QPushButton *m_mappingBtn;
|
||||
QPushButton *m_navigationBtn;
|
||||
QPushButton *m_photoBtn;
|
||||
QPushButton *m_recognitionBtn;
|
||||
QString m_selectedDevice;
|
||||
|
||||
// 情报传输模块
|
||||
ModuleCard *m_intelligenceCard;
|
||||
QPushButton *m_voiceCallBtn;
|
||||
QPushButton *m_muteBtn;
|
||||
QSlider *m_volumeSlider;
|
||||
QLabel *m_callStatusLabel;
|
||||
bool m_isInCall = false;
|
||||
|
||||
// 敌情统计模块
|
||||
ModuleCard *m_statsCard;
|
||||
QLabel *m_totalEnemiesLabel;
|
||||
QLabel *m_threatLevelLabel;
|
||||
QPushButton *m_refreshBtn;
|
||||
QPushButton *m_aiAnalysisBtn;
|
||||
QPushButton *m_exportBtn;
|
||||
};
|
||||
|
||||
#endif // RIGHTFUNCTIONPANEL_H
|
||||
|
||||
// RightFunctionPanel.cpp
|
||||
#include "RightFunctionPanel.h"
|
||||
#include <QMouseEvent>
|
||||
#include <QPainter>
|
||||
#include <QTimer>
|
||||
|
||||
// ModuleCard实现
|
||||
ModuleCard::ModuleCard(const QString &title, const QString &icon, QWidget *parent)
|
||||
: QFrame(parent)
|
||||
{
|
||||
setObjectName("ModuleCard");
|
||||
setFrameStyle(QFrame::StyledPanel);
|
||||
|
||||
QVBoxLayout *layout = new QVBoxLayout(this);
|
||||
layout->setSpacing(12);
|
||||
layout->setContentsMargins(12, 12, 12, 12);
|
||||
|
||||
// 标题栏
|
||||
QHBoxLayout *headerLayout = new QHBoxLayout();
|
||||
QLabel *iconLabel = new QLabel();
|
||||
iconLabel->setObjectName("ModuleIcon");
|
||||
iconLabel->setText(icon); // 使用Unicode图标或设置图片
|
||||
iconLabel->setFixedSize(20, 20);
|
||||
|
||||
m_titleLabel = new QLabel(title);
|
||||
m_titleLabel->setObjectName("ModuleTitle");
|
||||
|
||||
headerLayout->addWidget(iconLabel);
|
||||
headerLayout->addWidget(m_titleLabel);
|
||||
headerLayout->addStretch();
|
||||
|
||||
layout->addLayout(headerLayout);
|
||||
|
||||
// 内容区域
|
||||
m_contentLayout = new QVBoxLayout();
|
||||
m_contentLayout->setSpacing(8);
|
||||
layout->addLayout(m_contentLayout);
|
||||
}
|
||||
|
||||
void ModuleCard::addContent(QWidget *content)
|
||||
{
|
||||
m_contentLayout->addWidget(content);
|
||||
}
|
||||
|
||||
// DeviceCard实现
|
||||
DeviceCard::DeviceCard(const QString &name, const QString &iconPath, QWidget *parent)
|
||||
: QFrame(parent), m_deviceName(name)
|
||||
{
|
||||
setObjectName("DeviceCard");
|
||||
setFrameStyle(QFrame::StyledPanel);
|
||||
setCursor(Qt::PointingHandCursor);
|
||||
setFixedHeight(80);
|
||||
|
||||
QVBoxLayout *layout = new QVBoxLayout(this);
|
||||
layout->setAlignment(Qt::AlignCenter);
|
||||
layout->setSpacing(4);
|
||||
|
||||
m_iconLabel = new QLabel();
|
||||
m_iconLabel->setObjectName("DeviceIcon");
|
||||
m_iconLabel->setFixedSize(32, 32);
|
||||
m_iconLabel->setAlignment(Qt::AlignCenter);
|
||||
// 设置图标,这里用文字代替
|
||||
m_iconLabel->setText(name.contains("机器狗") ? "🐕" : "🚁");
|
||||
|
||||
m_nameLabel = new QLabel(name);
|
||||
m_nameLabel->setObjectName("DeviceName");
|
||||
m_nameLabel->setAlignment(Qt::AlignCenter);
|
||||
|
||||
m_statusLabel = new QLabel("离线");
|
||||
m_statusLabel->setObjectName("DeviceStatus");
|
||||
m_statusLabel->setAlignment(Qt::AlignCenter);
|
||||
|
||||
layout->addWidget(m_iconLabel);
|
||||
layout->addWidget(m_nameLabel);
|
||||
layout->addWidget(m_statusLabel);
|
||||
}
|
||||
|
||||
void DeviceCard::setStatus(const QString &status, const QColor &color)
|
||||
{
|
||||
m_statusLabel->setText(status);
|
||||
m_statusLabel->setStyleSheet(QString("color: %1;").arg(color.name()));
|
||||
}
|
||||
|
||||
void DeviceCard::setActive(bool active)
|
||||
{
|
||||
m_isActive = active;
|
||||
update();
|
||||
}
|
||||
|
||||
void DeviceCard::mousePressEvent(QMouseEvent *event)
|
||||
{
|
||||
if (event->button() == Qt::LeftButton) {
|
||||
emit deviceSelected(m_deviceName);
|
||||
}
|
||||
QFrame::mousePressEvent(event);
|
||||
}
|
||||
|
||||
void DeviceCard::paintEvent(QPaintEvent *event)
|
||||
{
|
||||
QFrame::paintEvent(event);
|
||||
|
||||
if (m_isActive) {
|
||||
QPainter painter(this);
|
||||
painter.setPen(QPen(QColor("#2E5D31"), 2));
|
||||
painter.drawRect(rect().adjusted(1, 1, -1, -1));
|
||||
}
|
||||
}
|
||||
|
||||
// RightFunctionPanel实现
|
||||
RightFunctionPanel::RightFunctionPanel(QWidget *parent)
|
||||
: QWidget(parent)
|
||||
{
|
||||
setupUI();
|
||||
applyStyles();
|
||||
}
|
||||
|
||||
void RightFunctionPanel::setupUI()
|
||||
{
|
||||
m_mainLayout = new QVBoxLayout(this);
|
||||
m_mainLayout->setSpacing(12);
|
||||
m_mainLayout->setContentsMargins(16, 16, 16, 16);
|
||||
|
||||
// 面板标题
|
||||
QLabel *titleLabel = new QLabel("功能面板");
|
||||
titleLabel->setObjectName("PanelTitle");
|
||||
titleLabel->setAlignment(Qt::AlignCenter);
|
||||
m_mainLayout->addWidget(titleLabel);
|
||||
|
||||
setupBattlefieldExplorationModule();
|
||||
setupIntelligenceModule();
|
||||
setupEnemyStatsModule();
|
||||
|
||||
m_mainLayout->addStretch();
|
||||
}
|
||||
|
||||
void RightFunctionPanel::setupBattlefieldExplorationModule()
|
||||
{
|
||||
m_explorationCard = new ModuleCard("战场探索", "🔍", this);
|
||||
|
||||
// 设备选择
|
||||
QHBoxLayout *deviceLayout = new QHBoxLayout();
|
||||
m_robotDogCard = new DeviceCard("机器狗", "robot_dog.png", this);
|
||||
m_droneCard = new DeviceCard("无人机", "drone.png", this);
|
||||
|
||||
connect(m_robotDogCard, &DeviceCard::deviceSelected, this, &RightFunctionPanel::onDeviceSelected);
|
||||
connect(m_droneCard, &DeviceCard::deviceSelected, this, &RightFunctionPanel::onDeviceSelected);
|
||||
|
||||
deviceLayout->addWidget(m_robotDogCard);
|
||||
deviceLayout->addWidget(m_droneCard);
|
||||
|
||||
QWidget *deviceWidget = new QWidget();
|
||||
deviceWidget->setLayout(deviceLayout);
|
||||
m_explorationCard->addContent(deviceWidget);
|
||||
|
||||
// 功能按钮
|
||||
QGridLayout *buttonLayout = new QGridLayout();
|
||||
m_mappingBtn = new QPushButton("自主建图");
|
||||
m_navigationBtn = new QPushButton("导航避障");
|
||||
m_photoBtn = new QPushButton("照片传输");
|
||||
m_recognitionBtn = new QPushButton("人物识别");
|
||||
|
||||
// 设置按钮样式类名
|
||||
m_mappingBtn->setObjectName("FunctionBtn");
|
||||
m_navigationBtn->setObjectName("FunctionBtn");
|
||||
m_photoBtn->setObjectName("FunctionBtn");
|
||||
m_recognitionBtn->setObjectName("FunctionBtn");
|
||||
|
||||
buttonLayout->addWidget(m_mappingBtn, 0, 0);
|
||||
buttonLayout->addWidget(m_navigationBtn, 0, 1);
|
||||
buttonLayout->addWidget(m_photoBtn, 1, 0);
|
||||
buttonLayout->addWidget(m_recognitionBtn, 1, 1);
|
||||
|
||||
connect(m_mappingBtn, &QPushButton::clicked, this, &RightFunctionPanel::onMappingToggle);
|
||||
connect(m_navigationBtn, &QPushButton::clicked, this, &RightFunctionPanel::onNavigationToggle);
|
||||
connect(m_photoBtn, &QPushButton::clicked, this, &RightFunctionPanel::onPhotoTransmissionToggle);
|
||||
connect(m_recognitionBtn, &QPushButton::clicked, this, &RightFunctionPanel::onPersonRecognitionToggle);
|
||||
|
||||
QWidget *buttonWidget = new QWidget();
|
||||
buttonWidget->setLayout(buttonLayout);
|
||||
m_explorationCard->addContent(buttonWidget);
|
||||
|
||||
m_mainLayout->addWidget(m_explorationCard);
|
||||
}
|
||||
|
||||
void RightFunctionPanel::setupIntelligenceModule()
|
||||
{
|
||||
m_intelligenceCard = new ModuleCard("情报传输", "📡", this);
|
||||
|
||||
// 通话控制
|
||||
QHBoxLayout *callLayout = new QHBoxLayout();
|
||||
m_voiceCallBtn = new QPushButton("开始通话");
|
||||
m_muteBtn = new QPushButton("静音");
|
||||
m_voiceCallBtn->setObjectName("FunctionBtn");
|
||||
m_muteBtn->setObjectName("FunctionBtn");
|
||||
m_muteBtn->setEnabled(false);
|
||||
|
||||
callLayout->addWidget(m_voiceCallBtn);
|
||||
callLayout->addWidget(m_muteBtn);
|
||||
|
||||
connect(m_voiceCallBtn, &QPushButton::clicked, this, &RightFunctionPanel::onVoiceCallToggle);
|
||||
|
||||
QWidget *callWidget = new QWidget();
|
||||
callWidget->setLayout(callLayout);
|
||||
m_intelligenceCard->addContent(callWidget);
|
||||
|
||||
// 音量控制
|
||||
QHBoxLayout *volumeLayout = new QHBoxLayout();
|
||||
QLabel *volumeLabel = new QLabel("音量:");
|
||||
m_volumeSlider = new QSlider(Qt::Horizontal);
|
||||
m_volumeSlider->setRange(0, 100);
|
||||
m_volumeSlider->setValue(70);
|
||||
|
||||
volumeLayout->addWidget(volumeLabel);
|
||||
volumeLayout->addWidget(m_volumeSlider);
|
||||
|
||||
QWidget *volumeWidget = new QWidget();
|
||||
volumeWidget->setLayout(volumeLayout);
|
||||
m_intelligenceCard->addContent(volumeWidget);
|
||||
|
||||
// 通话状态
|
||||
m_callStatusLabel = new QLabel("未连接");
|
||||
m_callStatusLabel->setObjectName("CallStatus");
|
||||
m_intelligenceCard->addContent(m_callStatusLabel);
|
||||
|
||||
m_mainLayout->addWidget(m_intelligenceCard);
|
||||
}
|
||||
|
||||
void RightFunctionPanel::setupEnemyStatsModule()
|
||||
{
|
||||
m_statsCard = new ModuleCard("敌情统计", "📊", this);
|
||||
|
||||
// 统计信息
|
||||
QVBoxLayout *statsLayout = new QVBoxLayout();
|
||||
|
||||
m_totalEnemiesLabel = new QLabel("已发现目标: 0");
|
||||
m_threatLevelLabel = new QLabel("威胁等级: 无");
|
||||
m_totalEnemiesLabel->setObjectName("StatLabel");
|
||||
m_threatLevelLabel->setObjectName("StatLabel");
|
||||
|
||||
statsLayout->addWidget(m_totalEnemiesLabel);
|
||||
statsLayout->addWidget(m_threatLevelLabel);
|
||||
|
||||
QWidget *statsWidget = new QWidget();
|
||||
statsWidget->setLayout(statsLayout);
|
||||
m_statsCard->addContent(statsWidget);
|
||||
|
||||
// 操作按钮
|
||||
QHBoxLayout *statsButtonLayout = new QHBoxLayout();
|
||||
m_refreshBtn = new QPushButton("刷新");
|
||||
m_aiAnalysisBtn = new QPushButton("AI分析");
|
||||
m_exportBtn = new QPushButton("导出报告");
|
||||
|
||||
m_refreshBtn->setObjectName("FunctionBtn");
|
||||
m_aiAnalysisBtn->setObjectName("FunctionBtn");
|
||||
m_exportBtn->setObjectName("FunctionBtn");
|
||||
|
||||
statsButtonLayout->addWidget(m_refreshBtn);
|
||||
statsButtonLayout->addWidget(m_aiAnalysisBtn);
|
||||
|
||||
connect(m_refreshBtn, &QPushButton::clicked, this, &RightFunctionPanel::onRefreshStats);
|
||||
connect(m_aiAnalysisBtn, &QPushButton::clicked, this, &RightFunctionPanel::onAIAnalysis);
|
||||
|
||||
QWidget *statsButtonWidget = new QWidget();
|
||||
statsButtonWidget->setLayout(statsButtonLayout);
|
||||
m_statsCard->addContent(statsButtonWidget);
|
||||
|
||||
// 导出按钮单独一行
|
||||
m_statsCard->addContent(m_exportBtn);
|
||||
|
||||
m_mainLayout->addWidget(m_statsCard);
|
||||
}
|
||||
|
||||
void RightFunctionPanel::applyStyles()
|
||||
{
|
||||
QString styles = R"(
|
||||
QWidget {
|
||||
font-family: "Microsoft YaHei", "SimHei", sans-serif;
|
||||
}
|
||||
|
||||
RightFunctionPanel {
|
||||
background-color: #2A3F47;
|
||||
border-left: 2px solid #546E7A;
|
||||
}
|
||||
|
||||
#PanelTitle {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
color: #FFFFFF;
|
||||
border-bottom: 1px solid #546E7A;
|
||||
padding-bottom: 8px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
#ModuleCard {
|
||||
background-color: #354A54;
|
||||
border: 1px solid #546E7A;
|
||||
border-radius: 8px;
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
#ModuleCard:hover {
|
||||
border-color: #2E5D31;
|
||||
background-color: #4A6572;
|
||||
}
|
||||
|
||||
#ModuleTitle {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
#ModuleIcon {
|
||||
font-size: 16px;
|
||||
color: #2E5D31;
|
||||
}
|
||||
|
||||
#DeviceCard {
|
||||
background-color: #3D525E;
|
||||
border: 1px solid #546E7A;
|
||||
border-radius: 6px;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
#DeviceCard:hover {
|
||||
background-color: #4A6572;
|
||||
border-color: #2E5D31;
|
||||
}
|
||||
|
||||
#DeviceName {
|
||||
font-size: 12px;
|
||||
color: #FFFFFF;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
#DeviceStatus {
|
||||
font-size: 10px;
|
||||
color: #78909C;
|
||||
}
|
||||
|
||||
#FunctionBtn {
|
||||
background-color: #2E5D31;
|
||||
color: #FFFFFF;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
padding: 8px 12px;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
min-height: 32px;
|
||||
}
|
||||
|
||||
#FunctionBtn:hover {
|
||||
background-color: #245429;
|
||||
}
|
||||
|
||||
#FunctionBtn:pressed {
|
||||
background-color: #1a3d1d;
|
||||
}
|
||||
|
||||
#FunctionBtn:disabled {
|
||||
background-color: #78909C;
|
||||
}
|
||||
|
||||
#StatLabel {
|
||||
font-size: 12px;
|
||||
color: #B0BEC5;
|
||||
padding: 4px 0;
|
||||
}
|
||||
|
||||
#CallStatus {
|
||||
font-size: 11px;
|
||||
color: #78909C;
|
||||
font-style: italic;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
QSlider::groove:horizontal {
|
||||
border: 1px solid #546E7A;
|
||||
height: 4px;
|
||||
background: #3D525E;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
QSlider::handle:horizontal {
|
||||
background: #2E5D31;
|
||||
border: 1px solid #546E7A;
|
||||
width: 12px;
|
||||
margin: -4px 0;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
QSlider::handle:horizontal:hover {
|
||||
background: #245429;
|
||||
}
|
||||
)";
|
||||
|
||||
setStyleSheet(styles);
|
||||
}
|
||||
|
||||
// 槽函数实现
|
||||
void RightFunctionPanel::onDeviceSelected(const QString &deviceName)
|
||||
{
|
||||
m_selectedDevice = deviceName;
|
||||
|
||||
// 更新设备选择状态
|
||||
m_robotDogCard->setActive(deviceName.contains("机器狗"));
|
||||
m_droneCard->setActive(deviceName.contains("无人机"));
|
||||
|
||||
// 根据设备类型启用/禁用相应按钮
|
||||
bool isRobotDog = deviceName.contains("机器狗");
|
||||
m_mappingBtn->setEnabled(isRobotDog);
|
||||
m_navigationBtn->setEnabled(isRobotDog);
|
||||
m_photoBtn->setEnabled(!isRobotDog);
|
||||
m_recognitionBtn->setEnabled(!isRobotDog);
|
||||
}
|
||||
|
||||
void RightFunctionPanel::onMappingToggle()
|
||||
{
|
||||
static bool isMappingActive = false;
|
||||
isMappingActive = !isMappingActive;
|
||||
|
||||
m_mappingBtn->setText(isMappingActive ? "停止建图" : "自主建图");
|
||||
|
||||
if (isMappingActive) {
|
||||
emit startMapping();
|
||||
} else {
|
||||
emit stopMapping();
|
||||
}
|
||||
}
|
||||
|
||||
void RightFunctionPanel::onNavigationToggle()
|
||||
{
|
||||
static bool isNavigationActive = false;
|
||||
isNavigationActive = !isNavigationActive;
|
||||
|
||||
m_navigationBtn->setText(isNavigationActive ? "停止导航" : "导航避障");
|
||||
|
||||
if (isNavigationActive) {
|
||||
emit startNavigation();
|
||||
} else {
|
||||
emit stopNavigation();
|
||||
}
|
||||
}
|
||||
|
||||
void RightFunctionPanel::onPhotoTransmissionToggle()
|
||||
{
|
||||
static bool isTransmissionActive = false;
|
||||
isTransmissionActive = !isTransmissionActive;
|
||||
|
||||
m_photoBtn->setText(isTransmissionActive ? "停止传输" : "照片传输");
|
||||
|
||||
if (isTransmissionActive) {
|
||||
emit startPhotoTransmission();
|
||||
} else {
|
||||
emit stopPhotoTransmission();
|
||||
}
|
||||
}
|
||||
|
||||
void RightFunctionPanel::onPersonRecognitionToggle()
|
||||
{
|
||||
static bool isRecognitionActive = false;
|
||||
isRecognitionActive = !isRecognitionActive;
|
||||
|
||||
m_recognitionBtn->setText(isRecognitionActive ? "停止识别" : "人物识别");
|
||||
|
||||
if (isRecognitionActive) {
|
||||
emit startPersonRecognition();
|
||||
} else {
|
||||
emit stopPersonRecognition();
|
||||
}
|
||||
}
|
||||
|
||||
void RightFunctionPanel::onVoiceCallToggle()
|
||||
{
|
||||
m_isInCall = !m_isInCall;
|
||||
|
||||
m_voiceCallBtn->setText(m_isInCall ? "结束通话" : "开始通话");
|
||||
m_muteBtn->setEnabled(m_isInCall);
|
||||
m_callStatusLabel->setText(m_isInCall ? "通话中..." : "未连接");
|
||||
|
||||
if (m_isInCall) {
|
||||
emit startVoiceCall();
|
||||
} else {
|
||||
emit endVoiceCall();
|
||||
}
|
||||
}
|
||||
|
||||
void RightFunctionPanel::onRefreshStats()
|
||||
{
|
||||
emit refreshEnemyStats();
|
||||
|
||||
// 模拟刷新效果
|
||||
m_refreshBtn->setText("刷新中...");
|
||||
m_refreshBtn->setEnabled(false);
|
||||
|
||||
QTimer::singleShot(2000, [this]() {
|
||||
m_refreshBtn->setText("刷新");
|
||||
m_refreshBtn->setEnabled(true);
|
||||
});
|
||||
}
|
||||
|
||||
void RightFunctionPanel::onAIAnalysis()
|
||||
{
|
||||
emit requestAIAnalysis();
|
||||
|
||||
// 显示分析状态
|
||||
m_aiAnalysisBtn->setText("分析中...");
|
||||
m_aiAnalysisBtn->setEnabled(false);
|
||||
|
||||
QTimer::singleShot(3000, [this]() {
|
||||
m_aiAnalysisBtn->setText("AI分析");
|
||||
m_aiAnalysisBtn->setEnabled(true);
|
||||
});
|
||||
}
|
||||
|
||||
void RightFunctionPanel::updateEnemyStats(int totalEnemies, const QString &threatLevel)
|
||||
{
|
||||
m_totalEnemiesLabel->setText(QString("已发现目标: %1").arg(totalEnemies));
|
||||
m_threatLevelLabel->setText(QString("威胁等级: %1").arg(threatLevel));
|
||||
|
||||
// 根据威胁等级设置颜色
|
||||
if (threatLevel == "高") {
|
||||
m_threatLevelLabel->setStyleSheet("color: #DC143C;");
|
||||
} else if (threatLevel == "中") {
|
||||
m_threatLevelLabel->setStyleSheet("color: #FF8C00;");
|
||||
} else {
|
||||
m_threatLevelLabel->setStyleSheet("color: #4CAF50;");
|
||||
}
|
||||
}
|
||||
|
||||
void RightFunctionPanel::updateDeviceStatus(const QString &deviceName, bool online, int battery)
|
||||
{
|
||||
DeviceCard *deviceCard = nullptr;
|
||||
if (deviceName.contains("机器狗")) {
|
||||
deviceCard = m_robotDogCard;
|
||||
} else if (deviceName.contains("无人机")) {
|
||||
deviceCard = m_droneCard;
|
||||
}
|
||||
|
||||
if (deviceCard) {
|
||||
if (online) {
|
||||
deviceCard->setStatus(QString("在线 %1%").arg(battery), QColor("#4CAF50"));
|
||||
} else {
|
||||
deviceCard->setStatus("离线", QColor("#78909C"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#include "RightFunctionPanel.moc"
|
@ -1,949 +0,0 @@
|
||||
/**
|
||||
* @file RightFunctionPanel.cpp
|
||||
* @brief 右侧功能面板组件实现
|
||||
* @author BattlefieldExplorationSystem Team
|
||||
* @date 2024-01-15
|
||||
* @version 2.0 - UI优化版本
|
||||
*/
|
||||
|
||||
#include "ui/components/RightFunctionPanel.h"
|
||||
#include <QMouseEvent>
|
||||
#include <QPainter>
|
||||
#include <QTimer>
|
||||
#include <QStyle>
|
||||
#include <QPropertyAnimation>
|
||||
#include <QGraphicsDropShadowEffect>
|
||||
#include <QFile>
|
||||
#include <QDebug>
|
||||
|
||||
// ModuleCard实现
|
||||
ModuleCard::ModuleCard(const QString &title, const QString &icon, QWidget *parent)
|
||||
: QFrame(parent)
|
||||
{
|
||||
setObjectName("ModuleCard");
|
||||
setFrameStyle(QFrame::NoFrame);
|
||||
|
||||
QVBoxLayout *layout = new QVBoxLayout(this);
|
||||
layout->setSpacing(16);
|
||||
layout->setContentsMargins(16, 16, 16, 16);
|
||||
|
||||
// 标题栏 - 改进设计
|
||||
QHBoxLayout *headerLayout = new QHBoxLayout();
|
||||
headerLayout->setSpacing(12);
|
||||
|
||||
QLabel *iconLabel = new QLabel();
|
||||
iconLabel->setObjectName("ModuleIcon");
|
||||
iconLabel->setText(icon.isEmpty() ? "📋" : icon);
|
||||
iconLabel->setFixedSize(24, 24);
|
||||
iconLabel->setAlignment(Qt::AlignCenter);
|
||||
|
||||
m_titleLabel = new QLabel(title);
|
||||
m_titleLabel->setObjectName("ModuleTitle");
|
||||
|
||||
headerLayout->addWidget(iconLabel);
|
||||
headerLayout->addWidget(m_titleLabel);
|
||||
headerLayout->addStretch();
|
||||
|
||||
layout->addLayout(headerLayout);
|
||||
|
||||
// 分隔线
|
||||
QFrame *separator = new QFrame();
|
||||
separator->setFrameShape(QFrame::HLine);
|
||||
separator->setObjectName("ModuleSeparator");
|
||||
layout->addWidget(separator);
|
||||
|
||||
// 内容区域
|
||||
m_contentLayout = new QVBoxLayout();
|
||||
m_contentLayout->setSpacing(12);
|
||||
layout->addLayout(m_contentLayout);
|
||||
}
|
||||
|
||||
void ModuleCard::addContent(QWidget *content)
|
||||
{
|
||||
m_contentLayout->addWidget(content);
|
||||
}
|
||||
|
||||
// RightDeviceCard实现 - 改进版本
|
||||
RightDeviceCard::RightDeviceCard(const QString &name, const QString &iconPath, QWidget *parent)
|
||||
: QFrame(parent), m_deviceName(name)
|
||||
{
|
||||
setObjectName("RightDeviceCard");
|
||||
setFrameStyle(QFrame::NoFrame);
|
||||
setCursor(Qt::PointingHandCursor);
|
||||
setFixedHeight(90);
|
||||
|
||||
// 添加阴影效果
|
||||
QGraphicsDropShadowEffect *shadowEffect = new QGraphicsDropShadowEffect(this);
|
||||
shadowEffect->setBlurRadius(8);
|
||||
shadowEffect->setColor(QColor(0, 0, 0, 80));
|
||||
shadowEffect->setOffset(0, 2);
|
||||
setGraphicsEffect(shadowEffect);
|
||||
|
||||
QVBoxLayout *layout = new QVBoxLayout(this);
|
||||
layout->setAlignment(Qt::AlignCenter);
|
||||
layout->setSpacing(6);
|
||||
layout->setContentsMargins(12, 12, 12, 12);
|
||||
|
||||
m_iconLabel = new QLabel();
|
||||
m_iconLabel->setObjectName("DeviceIcon");
|
||||
m_iconLabel->setFixedSize(40, 40);
|
||||
m_iconLabel->setAlignment(Qt::AlignCenter);
|
||||
|
||||
// 设置图标,使用更大更清晰的图标
|
||||
if (name.contains("机器狗") || name.contains("robot") || name.contains("dog")) {
|
||||
m_iconLabel->setText("🐕");
|
||||
m_iconLabel->setStyleSheet("font-size: 32px;");
|
||||
} else if (name.contains("无人机") || name.contains("drone") || name.contains("uav")) {
|
||||
m_iconLabel->setText("🚁");
|
||||
m_iconLabel->setStyleSheet("font-size: 32px;");
|
||||
} else {
|
||||
m_iconLabel->setText("📡");
|
||||
m_iconLabel->setStyleSheet("font-size: 32px;");
|
||||
}
|
||||
|
||||
m_nameLabel = new QLabel(name);
|
||||
m_nameLabel->setObjectName("DeviceName");
|
||||
m_nameLabel->setAlignment(Qt::AlignCenter);
|
||||
|
||||
m_statusLabel = new QLabel("离线");
|
||||
m_statusLabel->setObjectName("DeviceStatus");
|
||||
m_statusLabel->setAlignment(Qt::AlignCenter);
|
||||
|
||||
layout->addWidget(m_iconLabel);
|
||||
layout->addWidget(m_nameLabel);
|
||||
layout->addWidget(m_statusLabel);
|
||||
}
|
||||
|
||||
void RightDeviceCard::setStatus(const QString &status, const QColor &color)
|
||||
{
|
||||
m_statusLabel->setText(status);
|
||||
m_statusLabel->setStyleSheet(QString("color: %1; font-weight: 600;").arg(color.name()));
|
||||
}
|
||||
|
||||
void RightDeviceCard::setActive(bool active)
|
||||
{
|
||||
m_isActive = active;
|
||||
setProperty("active", active);
|
||||
style()->unpolish(this);
|
||||
style()->polish(this);
|
||||
update();
|
||||
}
|
||||
|
||||
void RightDeviceCard::mousePressEvent(QMouseEvent *event)
|
||||
{
|
||||
if (event->button() == Qt::LeftButton) {
|
||||
// 添加点击动画效果
|
||||
QPropertyAnimation *animation = new QPropertyAnimation(this, "geometry");
|
||||
animation->setDuration(100);
|
||||
animation->setStartValue(geometry());
|
||||
QRect targetGeometry = geometry().adjusted(2, 2, -2, -2);
|
||||
animation->setEndValue(targetGeometry);
|
||||
animation->start(QAbstractAnimation::DeleteWhenStopped);
|
||||
|
||||
QTimer::singleShot(100, [this]() {
|
||||
QPropertyAnimation *backAnimation = new QPropertyAnimation(this, "geometry");
|
||||
backAnimation->setDuration(100);
|
||||
backAnimation->setStartValue(geometry());
|
||||
QRect originalGeometry = geometry().adjusted(-2, -2, 2, 2);
|
||||
backAnimation->setEndValue(originalGeometry);
|
||||
backAnimation->start(QAbstractAnimation::DeleteWhenStopped);
|
||||
});
|
||||
|
||||
emit deviceSelected(m_deviceName);
|
||||
}
|
||||
QFrame::mousePressEvent(event);
|
||||
}
|
||||
|
||||
void RightDeviceCard::paintEvent(QPaintEvent *event)
|
||||
{
|
||||
QFrame::paintEvent(event);
|
||||
|
||||
if (m_isActive) {
|
||||
QPainter painter(this);
|
||||
painter.setRenderHint(QPainter::Antialiasing);
|
||||
QPen pen(QColor("#00a8ff"), 3);
|
||||
painter.setPen(pen);
|
||||
painter.drawRoundedRect(rect().adjusted(2, 2, -2, -2), 8, 8);
|
||||
|
||||
// 添加发光效果
|
||||
QPen glowPen(QColor("#00a8ff"));
|
||||
glowPen.setWidth(1);
|
||||
painter.setPen(glowPen);
|
||||
painter.drawRoundedRect(rect().adjusted(4, 4, -4, -4), 6, 6);
|
||||
}
|
||||
}
|
||||
|
||||
// RightFunctionPanel实现 - 全面优化版本
|
||||
RightFunctionPanel::RightFunctionPanel(QWidget *parent)
|
||||
: QWidget(parent)
|
||||
{
|
||||
setupUI();
|
||||
applyStyles();
|
||||
}
|
||||
|
||||
void RightFunctionPanel::setupUI()
|
||||
{
|
||||
setFixedWidth(360); // 进一步增加宽度
|
||||
setObjectName("rightFunctionPanel");
|
||||
|
||||
m_mainLayout = new QVBoxLayout(this);
|
||||
m_mainLayout->setSpacing(28); // 增加模块间距
|
||||
m_mainLayout->setContentsMargins(24, 24, 24, 24); // 增加内边距
|
||||
|
||||
// 面板标题 - 军事风格
|
||||
QLabel *titleLabel = new QLabel("⚔️ 作战控制面板");
|
||||
titleLabel->setObjectName("PanelTitle");
|
||||
titleLabel->setAlignment(Qt::AlignCenter);
|
||||
m_mainLayout->addWidget(titleLabel);
|
||||
|
||||
setupBattlefieldExplorationModule();
|
||||
setupIntelligenceModule();
|
||||
setupEnemyStatsModule();
|
||||
|
||||
m_mainLayout->addStretch();
|
||||
}
|
||||
|
||||
void RightFunctionPanel::setupBattlefieldExplorationModule()
|
||||
{
|
||||
m_explorationCard = new ModuleCard("🎯 战场探索", "🎯", this);
|
||||
m_explorationCard->setObjectName("ModuleCard");
|
||||
m_explorationCard->setProperty("data-module", "battlefield");
|
||||
|
||||
// 设备选择器 - 全新设计
|
||||
QWidget *deviceSelectorWidget = new QWidget();
|
||||
deviceSelectorWidget->setObjectName("device-selector");
|
||||
QHBoxLayout *deviceLayout = new QHBoxLayout(deviceSelectorWidget);
|
||||
deviceLayout->setSpacing(12);
|
||||
deviceLayout->setContentsMargins(8, 8, 8, 8);
|
||||
|
||||
m_robotDogCard = new RightDeviceCard("🐕 机器狗-01", "", this);
|
||||
m_droneCard = new RightDeviceCard("🚁 侦察机-01", "", this);
|
||||
|
||||
connect(m_robotDogCard, &RightDeviceCard::deviceSelected, this, &RightFunctionPanel::onDeviceSelected);
|
||||
connect(m_droneCard, &RightDeviceCard::deviceSelected, this, &RightFunctionPanel::onDeviceSelected);
|
||||
|
||||
deviceLayout->addWidget(m_robotDogCard);
|
||||
deviceLayout->addWidget(m_droneCard);
|
||||
m_explorationCard->addContent(deviceSelectorWidget);
|
||||
|
||||
// 主要功能按钮 - 突出显示
|
||||
m_mappingBtn = new QPushButton("🗺️ 开始建图");
|
||||
m_mappingBtn->setObjectName("FunctionBtn");
|
||||
m_mappingBtn->setProperty("class", "primary-large");
|
||||
m_mappingBtn->setMinimumHeight(52); // 增加主要按钮高度
|
||||
m_mappingBtn->setEnabled(false);
|
||||
connect(m_mappingBtn, &QPushButton::clicked, this, &RightFunctionPanel::onMappingToggle);
|
||||
m_explorationCard->addContent(m_mappingBtn);
|
||||
|
||||
// 次要功能按钮 - 三列布局
|
||||
QWidget *secondaryWidget = new QWidget();
|
||||
QHBoxLayout *secondaryLayout = new QHBoxLayout(secondaryWidget);
|
||||
secondaryLayout->setSpacing(8);
|
||||
secondaryLayout->setContentsMargins(0, 8, 0, 0);
|
||||
|
||||
m_navigationBtn = new QPushButton("🧭 导航");
|
||||
m_photoBtn = new QPushButton("📸 传输");
|
||||
m_recognitionBtn = new QPushButton("👁️ 识别");
|
||||
|
||||
// 设置次要按钮样式
|
||||
QList<QPushButton*> secondaryBtns = {m_navigationBtn, m_photoBtn, m_recognitionBtn};
|
||||
for(auto btn : secondaryBtns) {
|
||||
btn->setObjectName("FunctionBtn");
|
||||
btn->setProperty("class", "secondary-small");
|
||||
btn->setMinimumHeight(38);
|
||||
btn->setEnabled(false);
|
||||
}
|
||||
|
||||
connect(m_navigationBtn, &QPushButton::clicked, this, &RightFunctionPanel::onNavigationToggle);
|
||||
connect(m_photoBtn, &QPushButton::clicked, this, &RightFunctionPanel::onPhotoTransmissionToggle);
|
||||
connect(m_recognitionBtn, &QPushButton::clicked, this, &RightFunctionPanel::onPersonRecognitionToggle);
|
||||
|
||||
secondaryLayout->addWidget(m_navigationBtn);
|
||||
secondaryLayout->addWidget(m_photoBtn);
|
||||
secondaryLayout->addWidget(m_recognitionBtn);
|
||||
m_explorationCard->addContent(secondaryWidget);
|
||||
|
||||
m_mainLayout->addWidget(m_explorationCard);
|
||||
}
|
||||
|
||||
void RightFunctionPanel::setupIntelligenceModule()
|
||||
{
|
||||
m_intelligenceCard = new ModuleCard("📡 情报传输", "📡", this);
|
||||
m_intelligenceCard->setObjectName("ModuleCard");
|
||||
m_intelligenceCard->setProperty("data-module", "intelligence");
|
||||
|
||||
// 通话控制按钮 - 改进布局
|
||||
QWidget *callWidget = new QWidget();
|
||||
QHBoxLayout *callLayout = new QHBoxLayout(callWidget);
|
||||
callLayout->setSpacing(12);
|
||||
callLayout->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
m_voiceCallBtn = new QPushButton("📞 开始通话");
|
||||
m_muteBtn = new QPushButton("🔇 静音");
|
||||
|
||||
m_voiceCallBtn->setObjectName("FunctionBtn");
|
||||
m_muteBtn->setObjectName("FunctionBtn");
|
||||
m_voiceCallBtn->setProperty("class", "primary-medium");
|
||||
m_muteBtn->setProperty("class", "secondary-medium");
|
||||
m_voiceCallBtn->setMinimumHeight(48);
|
||||
m_muteBtn->setMinimumHeight(48);
|
||||
m_muteBtn->setEnabled(false);
|
||||
|
||||
callLayout->addWidget(m_voiceCallBtn, 2); // 通话按钮占更多空间
|
||||
callLayout->addWidget(m_muteBtn, 1);
|
||||
|
||||
connect(m_voiceCallBtn, &QPushButton::clicked, this, &RightFunctionPanel::onVoiceCallToggle);
|
||||
m_intelligenceCard->addContent(callWidget);
|
||||
|
||||
// 音量控制 - 全新设计
|
||||
QWidget *volumeWidget = new QWidget();
|
||||
QVBoxLayout *volumeLayout = new QVBoxLayout(volumeWidget);
|
||||
volumeLayout->setSpacing(12);
|
||||
volumeLayout->setContentsMargins(0, 16, 0, 0);
|
||||
|
||||
QHBoxLayout *volumeLabelLayout = new QHBoxLayout();
|
||||
QLabel *volumeLabel = new QLabel("🔊 音量控制");
|
||||
volumeLabel->setObjectName("volume-label");
|
||||
|
||||
QLabel *volumePercent = new QLabel("70%");
|
||||
volumePercent->setObjectName("volume-percent");
|
||||
volumePercent->setAlignment(Qt::AlignRight);
|
||||
|
||||
volumeLabelLayout->addWidget(volumeLabel);
|
||||
volumeLabelLayout->addWidget(volumePercent);
|
||||
|
||||
m_volumeSlider = new QSlider(Qt::Horizontal);
|
||||
m_volumeSlider->setRange(0, 100);
|
||||
m_volumeSlider->setValue(70);
|
||||
m_volumeSlider->setObjectName("volume-slider");
|
||||
|
||||
// 连接音量滑块信号
|
||||
connect(m_volumeSlider, &QSlider::valueChanged, [volumePercent](int value) {
|
||||
volumePercent->setText(QString("%1%").arg(value));
|
||||
});
|
||||
|
||||
volumeLayout->addLayout(volumeLabelLayout);
|
||||
volumeLayout->addWidget(m_volumeSlider);
|
||||
m_intelligenceCard->addContent(volumeWidget);
|
||||
|
||||
// 连接状态指示器 - 改进设计
|
||||
m_callStatusLabel = new QLabel("📋 未连接");
|
||||
m_callStatusLabel->setObjectName("call-status");
|
||||
m_callStatusLabel->setAlignment(Qt::AlignCenter);
|
||||
m_intelligenceCard->addContent(m_callStatusLabel);
|
||||
|
||||
m_mainLayout->addWidget(m_intelligenceCard);
|
||||
}
|
||||
|
||||
void RightFunctionPanel::setupEnemyStatsModule()
|
||||
{
|
||||
m_statsCard = new ModuleCard("📊 敌情统计", "📊", this);
|
||||
m_statsCard->setObjectName("ModuleCard");
|
||||
m_statsCard->setProperty("data-module", "statistics");
|
||||
|
||||
// 统计信息显示区域 - 全新设计
|
||||
QWidget *statsDisplayWidget = new QWidget();
|
||||
statsDisplayWidget->setObjectName("stats-display");
|
||||
|
||||
QVBoxLayout *statsLayout = new QVBoxLayout(statsDisplayWidget);
|
||||
statsLayout->setContentsMargins(20, 16, 20, 16);
|
||||
statsLayout->setSpacing(12);
|
||||
|
||||
// 已发现目标 - 突出显示
|
||||
QHBoxLayout *targetLayout = new QHBoxLayout();
|
||||
QLabel *targetLabel = new QLabel("已发现目标:");
|
||||
targetLabel->setObjectName("stat-label");
|
||||
|
||||
m_totalEnemiesLabel = new QLabel("3");
|
||||
m_totalEnemiesLabel->setObjectName("stat-value");
|
||||
m_totalEnemiesLabel->setAlignment(Qt::AlignRight);
|
||||
|
||||
targetLayout->addWidget(targetLabel);
|
||||
targetLayout->addWidget(m_totalEnemiesLabel);
|
||||
|
||||
// 威胁等级
|
||||
QHBoxLayout *threatLayout = new QHBoxLayout();
|
||||
QLabel *threatLabel = new QLabel("威胁等级:");
|
||||
threatLabel->setObjectName("stat-label");
|
||||
|
||||
m_threatLevelLabel = new QLabel("中等");
|
||||
m_threatLevelLabel->setObjectName("threat-level");
|
||||
m_threatLevelLabel->setAlignment(Qt::AlignRight);
|
||||
|
||||
threatLayout->addWidget(threatLabel);
|
||||
threatLayout->addWidget(m_threatLevelLabel);
|
||||
|
||||
statsLayout->addLayout(targetLayout);
|
||||
statsLayout->addLayout(threatLayout);
|
||||
m_statsCard->addContent(statsDisplayWidget);
|
||||
|
||||
// 操作按钮 - 改进布局
|
||||
QWidget *analysisWidget = new QWidget();
|
||||
QHBoxLayout *analysisLayout = new QHBoxLayout(analysisWidget);
|
||||
analysisLayout->setSpacing(12);
|
||||
analysisLayout->setContentsMargins(0, 8, 0, 0);
|
||||
|
||||
m_refreshBtn = new QPushButton("🔍 刷新");
|
||||
m_aiAnalysisBtn = new QPushButton("🤖 AI分析");
|
||||
|
||||
m_refreshBtn->setObjectName("FunctionBtn");
|
||||
m_aiAnalysisBtn->setObjectName("FunctionBtn");
|
||||
m_refreshBtn->setProperty("class", "secondary-medium");
|
||||
m_aiAnalysisBtn->setProperty("class", "secondary-medium");
|
||||
m_refreshBtn->setMinimumHeight(40);
|
||||
m_aiAnalysisBtn->setMinimumHeight(40);
|
||||
|
||||
analysisLayout->addWidget(m_refreshBtn);
|
||||
analysisLayout->addWidget(m_aiAnalysisBtn);
|
||||
|
||||
connect(m_refreshBtn, &QPushButton::clicked, this, &RightFunctionPanel::onRefreshStats);
|
||||
connect(m_aiAnalysisBtn, &QPushButton::clicked, this, &RightFunctionPanel::onAIAnalysis);
|
||||
m_statsCard->addContent(analysisWidget);
|
||||
|
||||
// 导出报告按钮 - 主要操作
|
||||
m_exportBtn = new QPushButton("📄 导出报告");
|
||||
m_exportBtn->setObjectName("FunctionBtn");
|
||||
m_exportBtn->setProperty("class", "primary-large");
|
||||
m_exportBtn->setMinimumHeight(52); // 突出重要性
|
||||
connect(m_exportBtn, &QPushButton::clicked, this, &RightFunctionPanel::exportReport);
|
||||
m_statsCard->addContent(m_exportBtn);
|
||||
|
||||
m_mainLayout->addWidget(m_statsCard);
|
||||
}
|
||||
|
||||
void RightFunctionPanel::applyStyles()
|
||||
{
|
||||
// 直接使用蓝色配色的完整样式
|
||||
QString blueStyles = R"(
|
||||
/* 全局字体和基础样式 */
|
||||
QWidget {
|
||||
font-family: "Microsoft YaHei", "SimHei", sans-serif;
|
||||
color: #ffffff;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* 主面板样式 */
|
||||
#rightFunctionPanel {
|
||||
background: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:1,
|
||||
stop:0 #0f1419, stop:1 #1a252f);
|
||||
border-left: 3px solid #00a8ff;
|
||||
border-radius: 0px;
|
||||
}
|
||||
|
||||
/* 面板标题 */
|
||||
#PanelTitle {
|
||||
background: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:0,
|
||||
stop:0 #00a8ff, stop:1 #0078d4);
|
||||
color: #ffffff;
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
padding: 16px 20px;
|
||||
border-radius: 10px;
|
||||
margin-bottom: 20px;
|
||||
text-align: center;
|
||||
border: 2px solid #00a8ff;
|
||||
text-shadow: none;
|
||||
}
|
||||
|
||||
/* 模块卡片 */
|
||||
#ModuleCard {
|
||||
background: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:1,
|
||||
stop:0 #1e2832, stop:1 #2a3441);
|
||||
border-radius: 12px;
|
||||
border: 2px solid #3c4a59;
|
||||
border-left: 4px solid #00a8ff;
|
||||
padding: 0px;
|
||||
margin-bottom: 28px;
|
||||
}
|
||||
|
||||
#ModuleCard:hover {
|
||||
border-color: #00a8ff;
|
||||
background: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:1,
|
||||
stop:0 #243340, stop:1 #304050);
|
||||
}
|
||||
|
||||
/* 模块标题 */
|
||||
#ModuleTitle {
|
||||
color: #00a8ff;
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
text-shadow: 0 0 5px rgba(0, 168, 255, 0.3);
|
||||
}
|
||||
|
||||
#ModuleIcon {
|
||||
color: #00a8ff;
|
||||
font-size: 20px;
|
||||
text-shadow: 0 0 8px rgba(0, 168, 255, 0.5);
|
||||
}
|
||||
|
||||
/* 模块分隔线 */
|
||||
#ModuleSeparator {
|
||||
border: none;
|
||||
background: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:0,
|
||||
stop:0 transparent, stop:0.5 #3c4a59, stop:1 transparent);
|
||||
height: 1px;
|
||||
margin: 8px 0px;
|
||||
}
|
||||
|
||||
/* 设备选择器 */
|
||||
#device-selector {
|
||||
background: #2a3441;
|
||||
border: 1px solid #3c4a59;
|
||||
border-radius: 8px;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
/* 设备卡片 */
|
||||
#RightDeviceCard {
|
||||
background: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:1,
|
||||
stop:0 #2a3441, stop:1 #34404f);
|
||||
border-radius: 10px;
|
||||
border: 2px solid #3c4a59;
|
||||
padding: 12px;
|
||||
margin: 4px;
|
||||
min-height: 80px;
|
||||
}
|
||||
|
||||
#RightDeviceCard:hover {
|
||||
border-color: #66d6ff;
|
||||
background: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:1,
|
||||
stop:0 #34404f, stop:1 #3e4a5f);
|
||||
}
|
||||
|
||||
#RightDeviceCard[active="true"] {
|
||||
border-color: #00a8ff;
|
||||
background: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:1,
|
||||
stop:0 rgba(0, 168, 255, 0.1), stop:1 rgba(0, 168, 255, 0.05));
|
||||
box-shadow: 0 0 15px rgba(0, 168, 255, 0.3);
|
||||
}
|
||||
|
||||
#DeviceName {
|
||||
color: #ffffff;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
#DeviceStatus {
|
||||
color: #a4b0be;
|
||||
font-size: 11px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* 功能按钮基础样式 */
|
||||
#FunctionBtn {
|
||||
background: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:1,
|
||||
stop:0 #2a3441, stop:1 #34404f);
|
||||
color: #ffffff;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
padding: 12px 16px;
|
||||
border-radius: 8px;
|
||||
border: 2px solid #3c4a59;
|
||||
margin: 4px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#FunctionBtn:hover {
|
||||
background: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:1,
|
||||
stop:0 #34404f, stop:1 #3e4a5f);
|
||||
border-color: #66d6ff;
|
||||
}
|
||||
|
||||
/* 主要按钮样式 */
|
||||
#FunctionBtn[class="primary-large"] {
|
||||
background: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:1,
|
||||
stop:0 #00a8ff, stop:1 #0078d4);
|
||||
color: #ffffff;
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
border: 2px solid #00a8ff;
|
||||
}
|
||||
|
||||
#FunctionBtn[class="primary-large"]:hover {
|
||||
background: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:1,
|
||||
stop:0 #0078d4, stop:1 #005a9e);
|
||||
box-shadow: 0 4px 15px rgba(0, 168, 255, 0.4);
|
||||
}
|
||||
|
||||
#FunctionBtn[class="primary-medium"] {
|
||||
background: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:1,
|
||||
stop:0 #00a8ff, stop:1 #0078d4);
|
||||
color: #ffffff;
|
||||
font-weight: 700;
|
||||
border: 2px solid #00a8ff;
|
||||
}
|
||||
|
||||
#FunctionBtn[class="primary-medium"]:hover {
|
||||
background: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:1,
|
||||
stop:0 #0078d4, stop:1 #005a9e);
|
||||
box-shadow: 0 3px 12px rgba(0, 168, 255, 0.3);
|
||||
}
|
||||
|
||||
/* 次要按钮样式 */
|
||||
#FunctionBtn[class="secondary-medium"] {
|
||||
background: #2a3441;
|
||||
border: 2px solid #3c4a59;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
#FunctionBtn[class="secondary-medium"]:hover {
|
||||
border-color: #66d6ff;
|
||||
background: #34404f;
|
||||
}
|
||||
|
||||
#FunctionBtn[class="secondary-small"] {
|
||||
background: #2a3441;
|
||||
border: 2px solid #3c4a59;
|
||||
color: #ffffff;
|
||||
font-size: 12px;
|
||||
padding: 8px 12px;
|
||||
}
|
||||
|
||||
#FunctionBtn[class="secondary-small"]:hover {
|
||||
border-color: #66d6ff;
|
||||
background: #34404f;
|
||||
}
|
||||
|
||||
/* 危险按钮样式 */
|
||||
#FunctionBtn[class="danger"] {
|
||||
background: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:1,
|
||||
stop:0 #ff3838, stop:1 #c44569);
|
||||
border: 2px solid #ff3838;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
#FunctionBtn[class="danger"]:hover {
|
||||
background: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:1,
|
||||
stop:0 #e53e3e, stop:1 #b83b5e);
|
||||
box-shadow: 0 4px 15px rgba(255, 56, 56, 0.4);
|
||||
}
|
||||
|
||||
#FunctionBtn:disabled {
|
||||
background: #1e2832;
|
||||
color: #556983;
|
||||
border-color: #2a3441;
|
||||
}
|
||||
|
||||
/* 加载状态按钮 */
|
||||
#FunctionBtn[class="loading"] {
|
||||
background: #34404f;
|
||||
border-color: #3c4a59;
|
||||
color: #a4b0be;
|
||||
}
|
||||
|
||||
/* 统计显示区域 */
|
||||
#stats-display {
|
||||
background: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:1,
|
||||
stop:0 #2a3441, stop:1 #34404f);
|
||||
border-radius: 8px;
|
||||
border: 2px solid #3c4a59;
|
||||
border-left: 4px solid #00a8ff;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
#stat-label {
|
||||
color: #a4b0be;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
#stat-value {
|
||||
color: #00a8ff;
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
text-shadow: 0 0 8px rgba(0, 168, 255, 0.5);
|
||||
}
|
||||
|
||||
#threat-level {
|
||||
color: #ffa502;
|
||||
font-size: 15px;
|
||||
font-weight: 700;
|
||||
text-shadow: 0 0 5px rgba(255, 165, 2, 0.3);
|
||||
}
|
||||
|
||||
/* 通话状态 */
|
||||
#call-status {
|
||||
background: #2a3441;
|
||||
border: 2px solid #3c4a59;
|
||||
border-radius: 6px;
|
||||
padding: 12px 16px;
|
||||
color: #a4b0be;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
/* 通话状态样式类 */
|
||||
QLabel[class="call-status"] {
|
||||
background: #2a3441;
|
||||
border: 2px solid #3c4a59;
|
||||
border-radius: 6px;
|
||||
padding: 12px 16px;
|
||||
color: #a4b0be;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
QLabel[class="call-status-active"] {
|
||||
background: #2a3441;
|
||||
border: 2px solid #00a8ff;
|
||||
border-radius: 6px;
|
||||
padding: 12px 16px;
|
||||
color: #00a8ff;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
/* 音量控制 */
|
||||
#volume-label {
|
||||
color: #a4b0be;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
#volume-percent {
|
||||
color: #00a8ff;
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
/* 音量滑块样式 */
|
||||
#volume-slider::groove:horizontal {
|
||||
border: 2px solid #3c4a59;
|
||||
height: 8px;
|
||||
background: #2a3441;
|
||||
border-radius: 4px;
|
||||
margin: 2px 0;
|
||||
}
|
||||
|
||||
#volume-slider::handle:horizontal {
|
||||
background: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:1,
|
||||
stop:0 #00a8ff, stop:1 #0078d4);
|
||||
border: 2px solid #00a8ff;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
margin: -8px 0;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
#volume-slider::handle:horizontal:hover {
|
||||
background: #0078d4;
|
||||
box-shadow: 0 0 8px rgba(0, 168, 255, 0.5);
|
||||
}
|
||||
|
||||
#volume-slider::sub-page:horizontal {
|
||||
background: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:0,
|
||||
stop:0 #00a8ff, stop:1 #66d6ff);
|
||||
border-radius: 4px;
|
||||
}
|
||||
)";
|
||||
|
||||
setStyleSheet(blueStyles);
|
||||
qDebug() << "已应用蓝色配色样式";
|
||||
}
|
||||
|
||||
// 槽函数实现
|
||||
void RightFunctionPanel::onDeviceSelected(const QString &deviceName)
|
||||
{
|
||||
m_selectedDevice = deviceName;
|
||||
|
||||
// 更新设备选择状态
|
||||
m_robotDogCard->setActive(deviceName.contains("机器狗") || deviceName.contains("robot") || deviceName.contains("dog"));
|
||||
m_droneCard->setActive(deviceName.contains("无人机") || deviceName.contains("drone") || deviceName.contains("uav"));
|
||||
|
||||
// 根据设备类型启用/禁用相应按钮
|
||||
bool isRobotDog = deviceName.contains("机器狗") || deviceName.contains("robot") || deviceName.contains("dog");
|
||||
m_mappingBtn->setEnabled(isRobotDog);
|
||||
m_navigationBtn->setEnabled(isRobotDog);
|
||||
m_photoBtn->setEnabled(!isRobotDog);
|
||||
m_recognitionBtn->setEnabled(!isRobotDog);
|
||||
}
|
||||
|
||||
void RightFunctionPanel::onMappingToggle()
|
||||
{
|
||||
static bool isMappingActive = false;
|
||||
isMappingActive = !isMappingActive;
|
||||
|
||||
m_mappingBtn->setText(isMappingActive ? "⏹️ 停止建图" : "🗺️ 开始建图");
|
||||
m_mappingBtn->setProperty("class", isMappingActive ? "danger" : "");
|
||||
m_mappingBtn->style()->unpolish(m_mappingBtn);
|
||||
m_mappingBtn->style()->polish(m_mappingBtn);
|
||||
|
||||
if (isMappingActive) {
|
||||
emit startMapping();
|
||||
} else {
|
||||
emit stopMapping();
|
||||
}
|
||||
}
|
||||
|
||||
void RightFunctionPanel::onNavigationToggle()
|
||||
{
|
||||
static bool isNavigationActive = false;
|
||||
isNavigationActive = !isNavigationActive;
|
||||
|
||||
m_navigationBtn->setText(isNavigationActive ? "⏹️ 停止导航" : "🧭 导航避障");
|
||||
m_navigationBtn->setProperty("class", isNavigationActive ? "danger" : "secondary-medium");
|
||||
m_navigationBtn->style()->unpolish(m_navigationBtn);
|
||||
m_navigationBtn->style()->polish(m_navigationBtn);
|
||||
|
||||
if (isNavigationActive) {
|
||||
emit startNavigation();
|
||||
} else {
|
||||
emit stopNavigation();
|
||||
}
|
||||
}
|
||||
|
||||
void RightFunctionPanel::onPhotoTransmissionToggle()
|
||||
{
|
||||
static bool isTransmissionActive = false;
|
||||
isTransmissionActive = !isTransmissionActive;
|
||||
|
||||
m_photoBtn->setText(isTransmissionActive ? "⏹️ 停止传输" : "📷 照片传输");
|
||||
m_photoBtn->setProperty("class", isTransmissionActive ? "danger" : "secondary-medium");
|
||||
m_photoBtn->style()->unpolish(m_photoBtn);
|
||||
m_photoBtn->style()->polish(m_photoBtn);
|
||||
|
||||
if (isTransmissionActive) {
|
||||
emit startPhotoTransmission();
|
||||
} else {
|
||||
emit stopPhotoTransmission();
|
||||
}
|
||||
}
|
||||
|
||||
void RightFunctionPanel::onPersonRecognitionToggle()
|
||||
{
|
||||
static bool isRecognitionActive = false;
|
||||
isRecognitionActive = !isRecognitionActive;
|
||||
|
||||
m_recognitionBtn->setText(isRecognitionActive ? "⏹️ 停止识别" : "👤 人物识别");
|
||||
m_recognitionBtn->setProperty("class", isRecognitionActive ? "danger" : "secondary-medium");
|
||||
m_recognitionBtn->style()->unpolish(m_recognitionBtn);
|
||||
m_recognitionBtn->style()->polish(m_recognitionBtn);
|
||||
|
||||
if (isRecognitionActive) {
|
||||
emit startPersonRecognition();
|
||||
} else {
|
||||
emit stopPersonRecognition();
|
||||
}
|
||||
}
|
||||
|
||||
void RightFunctionPanel::onVoiceCallToggle()
|
||||
{
|
||||
m_isInCall = !m_isInCall;
|
||||
|
||||
m_voiceCallBtn->setText(m_isInCall ? "📞 结束通话" : "📞 开始通话");
|
||||
m_voiceCallBtn->setProperty("class", m_isInCall ? "danger" : "primary-medium");
|
||||
m_voiceCallBtn->style()->unpolish(m_voiceCallBtn);
|
||||
m_voiceCallBtn->style()->polish(m_voiceCallBtn);
|
||||
|
||||
m_muteBtn->setEnabled(m_isInCall);
|
||||
m_callStatusLabel->setText(m_isInCall ? "📞 通话中..." : "📋 未连接");
|
||||
|
||||
// 更新通话状态的样式 - 使用CSS类
|
||||
m_callStatusLabel->setProperty("class", m_isInCall ? "call-status-active" : "call-status");
|
||||
m_callStatusLabel->style()->unpolish(m_callStatusLabel);
|
||||
m_callStatusLabel->style()->polish(m_callStatusLabel);
|
||||
|
||||
if (m_isInCall) {
|
||||
emit startVoiceCall();
|
||||
} else {
|
||||
emit endVoiceCall();
|
||||
}
|
||||
}
|
||||
|
||||
void RightFunctionPanel::onRefreshStats()
|
||||
{
|
||||
emit refreshEnemyStats();
|
||||
|
||||
// 模拟刷新效果
|
||||
m_refreshBtn->setText("⏳ 刷新中...");
|
||||
m_refreshBtn->setProperty("class", "loading");
|
||||
m_refreshBtn->setEnabled(false);
|
||||
m_refreshBtn->style()->unpolish(m_refreshBtn);
|
||||
m_refreshBtn->style()->polish(m_refreshBtn);
|
||||
|
||||
QTimer::singleShot(2000, [this]() {
|
||||
m_refreshBtn->setText("🔍 刷新");
|
||||
m_refreshBtn->setProperty("class", "secondary-medium");
|
||||
m_refreshBtn->setEnabled(true);
|
||||
m_refreshBtn->style()->unpolish(m_refreshBtn);
|
||||
m_refreshBtn->style()->polish(m_refreshBtn);
|
||||
});
|
||||
}
|
||||
|
||||
void RightFunctionPanel::onAIAnalysis()
|
||||
{
|
||||
emit requestAIAnalysis();
|
||||
|
||||
// 显示分析状态
|
||||
m_aiAnalysisBtn->setText("🧠 分析中...");
|
||||
m_aiAnalysisBtn->setProperty("class", "loading");
|
||||
m_aiAnalysisBtn->setEnabled(false);
|
||||
m_aiAnalysisBtn->style()->unpolish(m_aiAnalysisBtn);
|
||||
m_aiAnalysisBtn->style()->polish(m_aiAnalysisBtn);
|
||||
|
||||
QTimer::singleShot(3000, [this]() {
|
||||
m_aiAnalysisBtn->setText("🤖 AI分析");
|
||||
m_aiAnalysisBtn->setProperty("class", "secondary-medium");
|
||||
m_aiAnalysisBtn->setEnabled(true);
|
||||
m_aiAnalysisBtn->style()->unpolish(m_aiAnalysisBtn);
|
||||
m_aiAnalysisBtn->style()->polish(m_aiAnalysisBtn);
|
||||
});
|
||||
}
|
||||
|
||||
void RightFunctionPanel::updateEnemyStats(int totalEnemies, const QString &threatLevel)
|
||||
{
|
||||
m_totalEnemiesLabel->setText(QString::number(totalEnemies));
|
||||
m_threatLevelLabel->setText(threatLevel);
|
||||
|
||||
// 根据威胁等级设置颜色和样式
|
||||
if (threatLevel == "高" || threatLevel == "高等") {
|
||||
m_threatLevelLabel->setStyleSheet(
|
||||
"color: #ff3838; "
|
||||
"font-size: 15px; "
|
||||
"font-weight: 700; "
|
||||
"text-shadow: 0 0 8px rgba(255, 56, 56, 0.5);"
|
||||
);
|
||||
} else if (threatLevel == "中" || threatLevel == "中等") {
|
||||
m_threatLevelLabel->setStyleSheet(
|
||||
"color: #ffa502; "
|
||||
"font-size: 15px; "
|
||||
"font-weight: 700; "
|
||||
"text-shadow: 0 0 5px rgba(255, 165, 2, 0.3);"
|
||||
);
|
||||
} else {
|
||||
m_threatLevelLabel->setStyleSheet(
|
||||
"color: #00a8ff; "
|
||||
"font-size: 15px; "
|
||||
"font-weight: 700; "
|
||||
"text-shadow: 0 0 5px rgba(0, 168, 255, 0.3);"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void RightFunctionPanel::updateDeviceStatus(const QString &deviceName, bool online, int battery)
|
||||
{
|
||||
RightDeviceCard *deviceCard = nullptr;
|
||||
if (deviceName.contains("机器狗") || deviceName.contains("robot") || deviceName.contains("dog")) {
|
||||
deviceCard = m_robotDogCard;
|
||||
} else if (deviceName.contains("无人机") || deviceName.contains("drone") || deviceName.contains("uav")) {
|
||||
deviceCard = m_droneCard;
|
||||
}
|
||||
|
||||
if (deviceCard) {
|
||||
if (online) {
|
||||
if (battery > 50) {
|
||||
deviceCard->setStatus(QString("在线 %1%").arg(battery), QColor("#4CAF50"));
|
||||
} else if (battery > 20) {
|
||||
deviceCard->setStatus(QString("在线 %1%").arg(battery), QColor("#FF8C00"));
|
||||
} else {
|
||||
deviceCard->setStatus(QString("在线 %1%").arg(battery), QColor("#DC143C"));
|
||||
}
|
||||
} else {
|
||||
deviceCard->setStatus("离线", QColor("#78909C"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,476 +0,0 @@
|
||||
/**
|
||||
* @file SystemLogPanel.cpp
|
||||
* @brief 系统日志面板界面组件实现
|
||||
* @author Qt UI Developer Expert
|
||||
* @date 2024-12-21
|
||||
* @version 1.0
|
||||
*/
|
||||
|
||||
#include "ui/components/SystemLogPanel.h"
|
||||
#include "utils/SystemLogger.h"
|
||||
|
||||
#include <QVBoxLayout>
|
||||
#include <QHBoxLayout>
|
||||
#include <QTextEdit>
|
||||
#include <QPushButton>
|
||||
#include <QComboBox>
|
||||
#include <QLabel>
|
||||
#include <QTimer>
|
||||
#include <QDateTime>
|
||||
#include <QScrollBar>
|
||||
#include <QTextCursor>
|
||||
#include <QDebug>
|
||||
|
||||
// 静态常量定义
|
||||
const int SystemLogPanel::MAX_LOG_LINES = 500;
|
||||
const int SystemLogPanel::STATUS_UPDATE_INTERVAL = 2000; // 2秒
|
||||
|
||||
SystemLogPanel::SystemLogPanel(QWidget *parent)
|
||||
: QWidget(parent)
|
||||
, m_logTextEdit(nullptr)
|
||||
, m_clearButton(nullptr)
|
||||
, m_pauseButton(nullptr)
|
||||
, m_levelFilter(nullptr)
|
||||
, m_statusLabel(nullptr)
|
||||
, m_titleLabel(nullptr)
|
||||
, m_mainLayout(nullptr)
|
||||
, m_controlLayout(nullptr)
|
||||
, m_statusLayout(nullptr)
|
||||
, m_isPaused(false)
|
||||
, m_minLevel(Debug)
|
||||
, m_totalLogCount(0)
|
||||
, m_maxLogLines(MAX_LOG_LINES)
|
||||
, m_statusUpdateTimer(nullptr)
|
||||
{
|
||||
// 初始化日志计数器
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
m_logCounts[i] = 0;
|
||||
}
|
||||
|
||||
setupUI();
|
||||
setupStyle();
|
||||
connectSignals();
|
||||
|
||||
// 连接到SystemLogger的信号
|
||||
connect(SystemLogger::getInstance(), &SystemLogger::logAdded,
|
||||
this, [this](SystemLogger::LogLevel level, const QString &message) {
|
||||
addLog(static_cast<LogLevel>(level), message);
|
||||
});
|
||||
|
||||
qDebug() << "SystemLogPanel initialized successfully";
|
||||
}
|
||||
|
||||
SystemLogPanel::~SystemLogPanel()
|
||||
{
|
||||
if (m_statusUpdateTimer) {
|
||||
m_statusUpdateTimer->stop();
|
||||
}
|
||||
qDebug() << "SystemLogPanel destroyed";
|
||||
}
|
||||
|
||||
void SystemLogPanel::setupUI()
|
||||
{
|
||||
// 创建主布局
|
||||
m_mainLayout = new QVBoxLayout(this);
|
||||
m_mainLayout->setSpacing(4);
|
||||
m_mainLayout->setContentsMargins(8, 8, 8, 10);
|
||||
|
||||
// 创建标题标签
|
||||
m_titleLabel = new QLabel("🖥️ 系统日志", this);
|
||||
m_titleLabel->setMinimumHeight(25);
|
||||
m_titleLabel->setAlignment(Qt::AlignCenter);
|
||||
|
||||
// 创建控制按钮布局
|
||||
m_controlLayout = new QHBoxLayout();
|
||||
m_controlLayout->setSpacing(4);
|
||||
|
||||
// 创建控制按钮
|
||||
m_clearButton = new QPushButton("清空", this);
|
||||
m_clearButton->setMinimumSize(68, 30);
|
||||
m_clearButton->setMaximumSize(68, 30);
|
||||
|
||||
m_pauseButton = new QPushButton("暂停", this);
|
||||
m_pauseButton->setMinimumSize(68, 30);
|
||||
m_pauseButton->setMaximumSize(68, 30);
|
||||
|
||||
// 创建级别过滤器
|
||||
m_levelFilter = new QComboBox(this);
|
||||
m_levelFilter->addItem("全部", static_cast<int>(Debug));
|
||||
m_levelFilter->addItem("信息+", static_cast<int>(Info));
|
||||
m_levelFilter->addItem("警告+", static_cast<int>(Warning));
|
||||
m_levelFilter->addItem("错误+", static_cast<int>(Error));
|
||||
m_levelFilter->addItem("成功", static_cast<int>(Success));
|
||||
m_levelFilter->setMinimumSize(88, 30);
|
||||
m_levelFilter->setMaximumSize(88, 30);
|
||||
|
||||
// 添加控制组件到布局
|
||||
m_controlLayout->addWidget(m_clearButton);
|
||||
m_controlLayout->addWidget(m_pauseButton);
|
||||
m_controlLayout->addStretch();
|
||||
m_controlLayout->addWidget(m_levelFilter);
|
||||
|
||||
// 创建日志显示文本框
|
||||
m_logTextEdit = new QTextEdit(this);
|
||||
m_logTextEdit->setReadOnly(true);
|
||||
m_logTextEdit->setMinimumHeight(250);
|
||||
m_logTextEdit->setMaximumHeight(350);
|
||||
m_logTextEdit->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
|
||||
m_logTextEdit->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
|
||||
|
||||
// 创建状态信息标签
|
||||
m_statusLabel = new QLabel("就绪", this);
|
||||
m_statusLabel->setMinimumHeight(26);
|
||||
m_statusLabel->setMaximumHeight(28);
|
||||
m_statusLabel->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
|
||||
|
||||
// 创建状态布局
|
||||
m_statusLayout = new QHBoxLayout();
|
||||
m_statusLayout->setContentsMargins(0, 0, 0, 0);
|
||||
m_statusLayout->addWidget(m_statusLabel);
|
||||
m_statusLayout->addStretch();
|
||||
|
||||
// 添加所有组件到主布局
|
||||
m_mainLayout->addWidget(m_titleLabel);
|
||||
m_mainLayout->addLayout(m_controlLayout);
|
||||
m_mainLayout->addWidget(m_logTextEdit);
|
||||
m_mainLayout->addLayout(m_statusLayout);
|
||||
|
||||
// 创建状态更新定时器
|
||||
m_statusUpdateTimer = new QTimer(this);
|
||||
m_statusUpdateTimer->setInterval(STATUS_UPDATE_INTERVAL);
|
||||
m_statusUpdateTimer->start();
|
||||
|
||||
qDebug() << "SystemLogPanel UI setup completed";
|
||||
}
|
||||
|
||||
void SystemLogPanel::setupStyle()
|
||||
{
|
||||
// 主面板样式
|
||||
setStyleSheet(
|
||||
"SystemLogPanel {"
|
||||
" background-color: rgba(25, 35, 45, 0.95);"
|
||||
" border: 2px solid rgba(82, 194, 242, 0.4);"
|
||||
" border-radius: 8px;"
|
||||
" padding: 4px;"
|
||||
"}"
|
||||
);
|
||||
|
||||
// 标题样式
|
||||
m_titleLabel->setStyleSheet(
|
||||
"QLabel {"
|
||||
" color: rgb(82, 194, 242);"
|
||||
" font-size: 14px;"
|
||||
" font-weight: bold;"
|
||||
" padding: 4px 8px;"
|
||||
" background: qlineargradient(x1:0, y1:0, x2:1, y2:1,"
|
||||
" stop:0 rgba(82, 194, 242, 0.2),"
|
||||
" stop:1 rgba(45, 120, 180, 0.2));"
|
||||
" border: 1px solid rgba(82, 194, 242, 0.5);"
|
||||
" border-radius: 4px;"
|
||||
"}"
|
||||
);
|
||||
|
||||
// 按钮通用样式
|
||||
QString buttonStyle =
|
||||
"QPushButton {"
|
||||
" background: qlineargradient(x1:0, y1:0, x2:0, y2:1,"
|
||||
" stop:0 rgba(45, 65, 95, 0.8),"
|
||||
" stop:1 rgba(25, 40, 65, 0.8));"
|
||||
" color: rgb(220, 230, 242);"
|
||||
" border: 1px solid rgba(82, 194, 242, 0.5);"
|
||||
" border-radius: 4px;"
|
||||
" font-size: 12px;"
|
||||
" font-weight: bold;"
|
||||
" padding: 3px 8px;"
|
||||
"}"
|
||||
"QPushButton:hover {"
|
||||
" background: qlineargradient(x1:0, y1:0, x2:0, y2:1,"
|
||||
" stop:0 rgba(82, 194, 242, 0.6),"
|
||||
" stop:1 rgba(45, 120, 180, 0.6));"
|
||||
" border: 1px solid rgba(82, 194, 242, 0.8);"
|
||||
"}"
|
||||
"QPushButton:pressed {"
|
||||
" background: qlineargradient(x1:0, y1:0, x2:0, y2:1,"
|
||||
" stop:0 rgba(82, 194, 242, 0.8),"
|
||||
" stop:1 rgba(45, 120, 180, 0.8));"
|
||||
"}";
|
||||
|
||||
m_clearButton->setStyleSheet(buttonStyle);
|
||||
m_pauseButton->setStyleSheet(buttonStyle);
|
||||
|
||||
// 下拉框样式
|
||||
m_levelFilter->setStyleSheet(
|
||||
"QComboBox {"
|
||||
" background: rgba(25, 40, 65, 0.8);"
|
||||
" color: rgb(220, 230, 242);"
|
||||
" border: 1px solid rgba(82, 194, 242, 0.5);"
|
||||
" border-radius: 4px;"
|
||||
" padding: 3px 8px;"
|
||||
" font-size: 12px;"
|
||||
" font-weight: bold;"
|
||||
"}"
|
||||
"QComboBox::drop-down {"
|
||||
" subcontrol-origin: padding;"
|
||||
" subcontrol-position: top right;"
|
||||
" width: 15px;"
|
||||
" border-left: 1px solid rgba(82, 194, 242, 0.5);"
|
||||
"}"
|
||||
"QComboBox::down-arrow {"
|
||||
" width: 8px;"
|
||||
" height: 8px;"
|
||||
"}"
|
||||
"QComboBox QAbstractItemView {"
|
||||
" background-color: rgba(25, 40, 65, 0.95);"
|
||||
" color: rgb(220, 230, 242);"
|
||||
" border: 1px solid rgba(82, 194, 242, 0.5);"
|
||||
" border-radius: 4px;"
|
||||
" selection-background-color: rgba(82, 194, 242, 0.3);"
|
||||
"}"
|
||||
);
|
||||
|
||||
// 文本框样式
|
||||
m_logTextEdit->setStyleSheet(
|
||||
"QTextEdit {"
|
||||
" background-color: rgba(15, 22, 32, 0.9);"
|
||||
" color: rgb(220, 230, 242);"
|
||||
" border: 1px solid rgba(82, 194, 242, 0.4);"
|
||||
" border-radius: 6px;"
|
||||
" padding: 6px;"
|
||||
" font-family: 'Consolas', 'Monaco', monospace;"
|
||||
" font-size: 13px;"
|
||||
" line-height: 1.4;"
|
||||
" selection-background-color: rgba(82, 194, 242, 0.3);"
|
||||
"}"
|
||||
"QScrollBar:vertical {"
|
||||
" background-color: rgba(30, 44, 62, 0.8);"
|
||||
" width: 10px;"
|
||||
" border-radius: 5px;"
|
||||
"}"
|
||||
"QScrollBar::handle:vertical {"
|
||||
" background-color: rgba(82, 194, 242, 0.6);"
|
||||
" border-radius: 5px;"
|
||||
" min-height: 20px;"
|
||||
"}"
|
||||
"QScrollBar::handle:vertical:hover {"
|
||||
" background-color: rgba(82, 194, 242, 0.8);"
|
||||
"}"
|
||||
);
|
||||
|
||||
// 状态标签样式
|
||||
m_statusLabel->setStyleSheet(
|
||||
"QLabel {"
|
||||
" color: rgb(150, 180, 210);"
|
||||
" font-size: 12px;"
|
||||
" font-weight: normal;"
|
||||
" padding: 3px 6px;"
|
||||
" background: transparent;"
|
||||
"}"
|
||||
);
|
||||
|
||||
qDebug() << "SystemLogPanel styles applied";
|
||||
}
|
||||
|
||||
void SystemLogPanel::connectSignals()
|
||||
{
|
||||
// 连接按钮信号
|
||||
connect(m_clearButton, &QPushButton::clicked,
|
||||
this, &SystemLogPanel::onClearButtonClicked);
|
||||
connect(m_pauseButton, &QPushButton::clicked,
|
||||
this, &SystemLogPanel::onPauseButtonClicked);
|
||||
|
||||
// 连接下拉框信号
|
||||
connect(m_levelFilter, QOverload<int>::of(&QComboBox::currentIndexChanged),
|
||||
this, &SystemLogPanel::onLevelFilterChanged);
|
||||
|
||||
// 连接定时器信号
|
||||
connect(m_statusUpdateTimer, &QTimer::timeout,
|
||||
this, &SystemLogPanel::updateStatusInfo);
|
||||
|
||||
qDebug() << "SystemLogPanel signals connected";
|
||||
}
|
||||
|
||||
void SystemLogPanel::addLog(LogLevel level, const QString &message)
|
||||
{
|
||||
// 如果暂停则不添加日志
|
||||
if (m_isPaused) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查级别过滤
|
||||
if (level < m_minLevel) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 更新计数器
|
||||
if (level >= 0 && level < 5) {
|
||||
m_logCounts[level]++;
|
||||
}
|
||||
m_totalLogCount++;
|
||||
|
||||
// 格式化并添加日志条目
|
||||
QString formattedEntry = formatLogEntry(level, message);
|
||||
m_logTextEdit->append(formattedEntry);
|
||||
|
||||
// 限制日志行数
|
||||
limitLogLines();
|
||||
|
||||
// 自动滚动到底部
|
||||
scrollToBottom();
|
||||
|
||||
qDebug() << QString("Log added: [%1] %2").arg(getLevelName(level)).arg(message);
|
||||
}
|
||||
|
||||
void SystemLogPanel::clearLogs()
|
||||
{
|
||||
m_logTextEdit->clear();
|
||||
|
||||
// 重置计数器
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
m_logCounts[i] = 0;
|
||||
}
|
||||
m_totalLogCount = 0;
|
||||
|
||||
// 更新状态信息
|
||||
updateStatusInfo();
|
||||
|
||||
qDebug() << "System logs cleared";
|
||||
}
|
||||
|
||||
void SystemLogPanel::pauseLogging()
|
||||
{
|
||||
m_isPaused = true;
|
||||
m_pauseButton->setText("恢复");
|
||||
updateStatusInfo();
|
||||
qDebug() << "Logging paused";
|
||||
}
|
||||
|
||||
void SystemLogPanel::resumeLogging()
|
||||
{
|
||||
m_isPaused = false;
|
||||
m_pauseButton->setText("暂停");
|
||||
updateStatusInfo();
|
||||
qDebug() << "Logging resumed";
|
||||
}
|
||||
|
||||
void SystemLogPanel::setLogLevelFilter(LogLevel minLevel)
|
||||
{
|
||||
m_minLevel = minLevel;
|
||||
// 更新下拉框选择
|
||||
for (int i = 0; i < m_levelFilter->count(); ++i) {
|
||||
if (m_levelFilter->itemData(i).toInt() == static_cast<int>(minLevel)) {
|
||||
m_levelFilter->setCurrentIndex(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
qDebug() << "Log level filter set to:" << getLevelName(minLevel);
|
||||
}
|
||||
|
||||
void SystemLogPanel::onClearButtonClicked()
|
||||
{
|
||||
clearLogs();
|
||||
}
|
||||
|
||||
void SystemLogPanel::onPauseButtonClicked()
|
||||
{
|
||||
if (m_isPaused) {
|
||||
resumeLogging();
|
||||
} else {
|
||||
pauseLogging();
|
||||
}
|
||||
}
|
||||
|
||||
void SystemLogPanel::onLevelFilterChanged(int index)
|
||||
{
|
||||
if (index >= 0 && index < m_levelFilter->count()) {
|
||||
LogLevel newLevel = static_cast<LogLevel>(m_levelFilter->itemData(index).toInt());
|
||||
m_minLevel = newLevel;
|
||||
qDebug() << "Log level filter changed to:" << getLevelName(newLevel);
|
||||
}
|
||||
}
|
||||
|
||||
void SystemLogPanel::updateStatusInfo()
|
||||
{
|
||||
QString statusText = QString("总计: %1 | 错误: %2 | 警告: %3")
|
||||
.arg(m_totalLogCount)
|
||||
.arg(m_logCounts[Error])
|
||||
.arg(m_logCounts[Warning]);
|
||||
|
||||
if (m_isPaused) {
|
||||
statusText += " | 已暂停";
|
||||
} else {
|
||||
statusText += " | 运行中";
|
||||
}
|
||||
|
||||
m_statusLabel->setText(statusText);
|
||||
}
|
||||
|
||||
QString SystemLogPanel::formatLogEntry(LogLevel level, const QString &message)
|
||||
{
|
||||
QString timestamp = QDateTime::currentDateTime().toString("hh:mm:ss.zzz");
|
||||
QString levelIcon = getLevelIcon(level);
|
||||
QString levelColor = getLevelColor(level);
|
||||
|
||||
return QString("<span style='color: %1'>[%2] %3 %4</span>")
|
||||
.arg(levelColor)
|
||||
.arg(timestamp)
|
||||
.arg(levelIcon)
|
||||
.arg(message);
|
||||
}
|
||||
|
||||
QString SystemLogPanel::getLevelIcon(LogLevel level)
|
||||
{
|
||||
switch (level) {
|
||||
case Debug: return "🔍";
|
||||
case Info: return "🔵";
|
||||
case Warning: return "🟡";
|
||||
case Error: return "🔴";
|
||||
case Success: return "🟢";
|
||||
default: return "⚪";
|
||||
}
|
||||
}
|
||||
|
||||
QString SystemLogPanel::getLevelColor(LogLevel level)
|
||||
{
|
||||
switch (level) {
|
||||
case Debug: return "#9E9E9E";
|
||||
case Info: return "#52C2F2";
|
||||
case Warning: return "#FFD700";
|
||||
case Error: return "#FF4444";
|
||||
case Success: return "#00FF7F";
|
||||
default: return "#FFFFFF";
|
||||
}
|
||||
}
|
||||
|
||||
QString SystemLogPanel::getLevelName(LogLevel level)
|
||||
{
|
||||
switch (level) {
|
||||
case Debug: return "Debug";
|
||||
case Info: return "Info";
|
||||
case Warning: return "Warning";
|
||||
case Error: return "Error";
|
||||
case Success: return "Success";
|
||||
default: return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
void SystemLogPanel::limitLogLines()
|
||||
{
|
||||
// 获取文档和光标
|
||||
QTextDocument *document = m_logTextEdit->document();
|
||||
int blockCount = document->blockCount();
|
||||
|
||||
// 如果超过最大行数,删除最旧的行
|
||||
if (blockCount > m_maxLogLines) {
|
||||
QTextCursor cursor(document);
|
||||
cursor.movePosition(QTextCursor::Start);
|
||||
cursor.movePosition(QTextCursor::Down, QTextCursor::KeepAnchor,
|
||||
blockCount - m_maxLogLines);
|
||||
cursor.removeSelectedText();
|
||||
}
|
||||
}
|
||||
|
||||
void SystemLogPanel::scrollToBottom()
|
||||
{
|
||||
QScrollBar *scrollBar = m_logTextEdit->verticalScrollBar();
|
||||
scrollBar->setValue(scrollBar->maximum());
|
||||
}
|
@ -1,158 +0,0 @@
|
||||
/**
|
||||
* @file SystemLogger.cpp
|
||||
* @brief 系统日志管理器单例类实现
|
||||
* @author Qt UI Developer Expert
|
||||
* @date 2024-12-21
|
||||
* @version 1.0
|
||||
*/
|
||||
|
||||
#include "utils/SystemLogger.h"
|
||||
#include <QDateTime>
|
||||
#include <QDebug>
|
||||
#include <QMutexLocker>
|
||||
#include <QCoreApplication>
|
||||
#include <iostream>
|
||||
|
||||
// 静态成员初始化
|
||||
SystemLogger* SystemLogger::s_instance = nullptr;
|
||||
QMutex SystemLogger::s_mutex;
|
||||
|
||||
SystemLogger::SystemLogger(QObject *parent)
|
||||
: QObject(parent)
|
||||
, m_consoleOutputEnabled(true)
|
||||
{
|
||||
qDebug() << "SystemLogger instance created";
|
||||
}
|
||||
|
||||
SystemLogger::~SystemLogger()
|
||||
{
|
||||
qDebug() << "SystemLogger instance destroyed";
|
||||
}
|
||||
|
||||
SystemLogger* SystemLogger::getInstance()
|
||||
{
|
||||
// 双重检查锁定模式确保线程安全
|
||||
if (s_instance == nullptr) {
|
||||
QMutexLocker locker(&s_mutex);
|
||||
if (s_instance == nullptr) {
|
||||
s_instance = new SystemLogger();
|
||||
}
|
||||
}
|
||||
return s_instance;
|
||||
}
|
||||
|
||||
void SystemLogger::destroyInstance()
|
||||
{
|
||||
QMutexLocker locker(&s_mutex);
|
||||
if (s_instance != nullptr) {
|
||||
delete s_instance;
|
||||
s_instance = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void SystemLogger::logDebug(const QString &message)
|
||||
{
|
||||
log(Debug, message);
|
||||
}
|
||||
|
||||
void SystemLogger::logInfo(const QString &message)
|
||||
{
|
||||
log(Info, message);
|
||||
}
|
||||
|
||||
void SystemLogger::logWarning(const QString &message)
|
||||
{
|
||||
log(Warning, message);
|
||||
}
|
||||
|
||||
void SystemLogger::logError(const QString &message)
|
||||
{
|
||||
log(Error, message);
|
||||
}
|
||||
|
||||
void SystemLogger::logSuccess(const QString &message)
|
||||
{
|
||||
log(Success, message);
|
||||
}
|
||||
|
||||
void SystemLogger::log(LogLevel level, const QString &message)
|
||||
{
|
||||
logInternal(level, message);
|
||||
}
|
||||
|
||||
void SystemLogger::setConsoleOutputEnabled(bool enabled)
|
||||
{
|
||||
QMutexLocker locker(&m_logMutex);
|
||||
m_consoleOutputEnabled = enabled;
|
||||
|
||||
QString statusMsg = enabled ? "Console output enabled" : "Console output disabled";
|
||||
qDebug() << "SystemLogger:" << statusMsg;
|
||||
}
|
||||
|
||||
QString SystemLogger::getLevelString(LogLevel level)
|
||||
{
|
||||
switch (level) {
|
||||
case Debug: return "DEBUG";
|
||||
case Info: return "INFO";
|
||||
case Warning: return "WARNING";
|
||||
case Error: return "ERROR";
|
||||
case Success: return "SUCCESS";
|
||||
default: return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
void SystemLogger::logInternal(LogLevel level, const QString &message)
|
||||
{
|
||||
// 线程安全保护
|
||||
QMutexLocker locker(&m_logMutex);
|
||||
|
||||
// 输出到控制台(如果启用)
|
||||
if (m_consoleOutputEnabled) {
|
||||
outputToConsole(level, message);
|
||||
}
|
||||
|
||||
// 发出信号通知UI组件
|
||||
emit logAdded(level, message);
|
||||
}
|
||||
|
||||
void SystemLogger::outputToConsole(LogLevel level, const QString &message)
|
||||
{
|
||||
QString timestamp = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz");
|
||||
QString levelStr = getLevelString(level);
|
||||
QString logLine = QString("[%1] [%2] %3").arg(timestamp, levelStr, message);
|
||||
|
||||
// 根据日志级别选择输出流
|
||||
switch (level) {
|
||||
case Error:
|
||||
// 错误输出到标准错误流
|
||||
std::cerr << logLine.toStdString() << std::endl;
|
||||
break;
|
||||
case Warning:
|
||||
// 警告也输出到标准错误流
|
||||
std::cerr << logLine.toStdString() << std::endl;
|
||||
break;
|
||||
default:
|
||||
// 其他级别输出到标准输出流
|
||||
std::cout << logLine.toStdString() << std::endl;
|
||||
break;
|
||||
}
|
||||
|
||||
// 同时也输出到Qt的调试系统
|
||||
switch (level) {
|
||||
case Debug:
|
||||
qDebug().noquote() << logLine;
|
||||
break;
|
||||
case Info:
|
||||
qInfo().noquote() << logLine;
|
||||
break;
|
||||
case Warning:
|
||||
qWarning().noquote() << logLine;
|
||||
break;
|
||||
case Error:
|
||||
qCritical().noquote() << logLine;
|
||||
break;
|
||||
case Success:
|
||||
qInfo().noquote() << logLine; // 成功信息使用qInfo输出
|
||||
break;
|
||||
}
|
||||
}
|
@ -1,625 +0,0 @@
|
||||
/*
|
||||
* BattlefieldExplorationSystem - 军事专业主题样式表
|
||||
* 版本: v3.0 - 军事专业配色增强版
|
||||
* 日期: 2024-06-23
|
||||
* 描述: 基于军事专业标准的深色配色主题,突出军事风格和操作效率
|
||||
*/
|
||||
|
||||
/* ================================
|
||||
军事专业配色变量定义 - v3.0
|
||||
================================ */
|
||||
|
||||
QWidget {
|
||||
/* 军事基础背景色系 */
|
||||
font-family: "Microsoft YaHei", "SimHei", sans-serif;
|
||||
color: rgba(255, 255, 255, 0.95);
|
||||
|
||||
/* 主背景 - 深黑蓝军事色 */
|
||||
--bg-primary: #0f1419;
|
||||
--bg-secondary: #1e2832;
|
||||
--bg-tertiary: #2a3441;
|
||||
|
||||
/* 军事绿强调色系 - 战术绿 */
|
||||
--accent-primary: #00ff88; /* 军绿强调色 */
|
||||
--accent-secondary: #00a8ff; /* 蓝色辅助 */
|
||||
--accent-hover: #00c46a; /* 军绿悬停 */
|
||||
--accent-light: rgba(0, 255, 136, 0.1); /* 军绿浅色背景 */
|
||||
|
||||
/* 军事状态色系 */
|
||||
--status-online: #00ff88; /* 在线 - 明亮军绿 */
|
||||
--status-warning: #ffa502; /* 警告 - 战术橙 */
|
||||
--status-danger: #ff3838; /* 危险 - 警报红 */
|
||||
--status-offline: #747d8c; /* 离线 - 战术灰 */
|
||||
--status-info: #00a8ff; /* 信息 - 战术蓝 */
|
||||
|
||||
/* 文字色系 */
|
||||
--text-primary: #ffffff; /* 主要文字 - 纯白 */
|
||||
--text-secondary: #a4b0be; /* 次要文字 - 战术灰 */
|
||||
--text-accent: #00ff88; /* 强调文字 - 军绿 */
|
||||
--text-muted: rgba(255, 255, 255, 0.5); /* 辅助文字 */
|
||||
|
||||
/* 边框色系 */
|
||||
--border-primary: #3c4a59; /* 主要边框 */
|
||||
--border-accent: #00ff88; /* 强调边框 - 军绿 */
|
||||
--border-subtle: #2a3441; /* 细微边框 */
|
||||
--border-danger: #ff3838; /* 危险边框 */
|
||||
}
|
||||
|
||||
/* ================================
|
||||
功能面板主容器
|
||||
================================ */
|
||||
|
||||
#rightFunctionPanel {
|
||||
background: #0f1419;
|
||||
border-left: 2px solid #00ff88;
|
||||
padding: 20px;
|
||||
width: 340px;
|
||||
}
|
||||
|
||||
/* ================================
|
||||
模块标题样式
|
||||
================================ */
|
||||
|
||||
#PanelTitle {
|
||||
background: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:0,
|
||||
stop:0 #00ff88, stop:1 rgba(0, 255, 136, 0.6));
|
||||
color: #0f1419;
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
padding: 12px 16px;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 20px;
|
||||
text-align: center;
|
||||
border: 1px solid #00ff88;
|
||||
}
|
||||
|
||||
/* ================================
|
||||
模块卡片样式 - 三层视觉层次
|
||||
================================ */
|
||||
|
||||
/* 通用模块卡片样式 - 军事专业版 */
|
||||
#ModuleCard {
|
||||
background: #1e2832;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #3c4a59;
|
||||
border-left: 4px solid #00ff88;
|
||||
padding: 16px;
|
||||
margin-bottom: 24px;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
#ModuleCard:hover {
|
||||
border-color: #00ff88;
|
||||
background: #2a3441;
|
||||
}
|
||||
|
||||
/* 战场探索模块 - Level 1 */
|
||||
#ModuleCard[data-module="battlefield"] {
|
||||
min-height: 220px;
|
||||
}
|
||||
|
||||
/* 情报传输模块 - Level 2 */
|
||||
#ModuleCard[data-module="intelligence"] {
|
||||
min-height: 180px;
|
||||
}
|
||||
|
||||
/* 敌情统计模块 - Level 3 */
|
||||
#ModuleCard[data-module="statistics"] {
|
||||
min-height: 200px;
|
||||
}
|
||||
|
||||
/* 模块标题内部样式 */
|
||||
#ModuleTitle {
|
||||
color: #00ff88;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 16px;
|
||||
text-align: left;
|
||||
padding-bottom: 8px;
|
||||
border-bottom: 1px solid #3c4a59;
|
||||
}
|
||||
|
||||
#ModuleIcon {
|
||||
color: #00ff88;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
/* ================================
|
||||
设备选择卡片优化
|
||||
================================ */
|
||||
|
||||
#RightDeviceCard {
|
||||
background: #2a3441;
|
||||
border-radius: 6px;
|
||||
border: 2px solid #2a3441;
|
||||
padding: 12px;
|
||||
margin: 8px;
|
||||
min-height: 80px;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
#RightDeviceCard:hover {
|
||||
border-color: #00a8ff;
|
||||
background: #2a3441;
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
#RightDeviceCard[selected="true"] {
|
||||
border-color: #00ff88;
|
||||
background: rgba(0, 255, 136, 0.1);
|
||||
}
|
||||
|
||||
/* 设备图标样式 */
|
||||
#DeviceIcon {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
margin-bottom: 8px;
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
/* 设备名称样式 */
|
||||
#DeviceName {
|
||||
color: #ffffff;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* 设备状态样式 */
|
||||
#DeviceStatus {
|
||||
color: #a4b0be;
|
||||
font-size: 10px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* ================================
|
||||
功能按钮统一样式
|
||||
================================ */
|
||||
|
||||
/* 主要功能按钮 - 军绿配色 */
|
||||
#FunctionBtn {
|
||||
background: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:1,
|
||||
stop:0 #00ff88, stop:1 #00c46a);
|
||||
color: #0f1419;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
padding: 12px 20px;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #00ff88;
|
||||
margin: 6px;
|
||||
min-height: 44px;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
#FunctionBtn:hover {
|
||||
background: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:1,
|
||||
stop:0 #00c46a, stop:1 #009951);
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 4px 12px rgba(0, 255, 136, 0.3);
|
||||
}
|
||||
|
||||
#FunctionBtn:pressed {
|
||||
background: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:1,
|
||||
stop:0 #009951, stop:1 #007a3d);
|
||||
transform: translateY(1px);
|
||||
box-shadow: 0 2px 4px rgba(0, 255, 136, 0.2);
|
||||
}
|
||||
|
||||
#FunctionBtn:disabled {
|
||||
background: #2a3441;
|
||||
color: #747d8c;
|
||||
border-color: #3c4a59;
|
||||
}
|
||||
|
||||
/* ================================
|
||||
威胁等级特殊强化样式
|
||||
================================ */
|
||||
|
||||
#threat-level-display {
|
||||
background: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:0,
|
||||
stop:0 #ffa502, stop:1 rgba(255, 165, 2, 0.6));
|
||||
border-radius: 10px;
|
||||
padding: 16px;
|
||||
margin: 16px 0;
|
||||
border: 2px solid #ffa502;
|
||||
text-align: center;
|
||||
color: #0f1419;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
/* ================================
|
||||
目标计数器样式
|
||||
================================ */
|
||||
|
||||
#target-counter {
|
||||
background: #2a3441;
|
||||
border-radius: 8px;
|
||||
padding: 12px;
|
||||
margin: 8px 0;
|
||||
border: 1px solid #3c4a59;
|
||||
border-left: 4px solid #00a8ff;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#target-count-number {
|
||||
color: #00ff88;
|
||||
font-size: 28px;
|
||||
font-weight: bold;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
#target-count-label {
|
||||
color: #a4b0be;
|
||||
font-size: 12px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
/* ================================
|
||||
滑块控件优化
|
||||
================================ */
|
||||
|
||||
QSlider::groove:horizontal {
|
||||
border: 1px solid #3c4a59;
|
||||
height: 6px;
|
||||
background: #2a3441;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
QSlider::sub-page:horizontal {
|
||||
background: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:0,
|
||||
stop:0 #00ff88, stop:1 #00a8ff);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
QSlider::handle:horizontal {
|
||||
background: #00ff88;
|
||||
border: 2px solid #00ff88;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
margin: -8px 0;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
QSlider::handle:horizontal:hover {
|
||||
background: #00c46a;
|
||||
border-color: #00c46a;
|
||||
}
|
||||
|
||||
QSlider::handle:horizontal:pressed {
|
||||
background: #009951;
|
||||
border-color: #009951;
|
||||
}
|
||||
|
||||
/* ================================
|
||||
状态指示器
|
||||
================================ */
|
||||
|
||||
.status-indicator {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-radius: 6px;
|
||||
margin: 0 8px;
|
||||
}
|
||||
|
||||
.status-safe { background: var(--status-safe); }
|
||||
.status-warning { background: var(--status-warning); }
|
||||
.status-danger { background: var(--status-danger); }
|
||||
.status-info { background: var(--status-info); }
|
||||
|
||||
/* ================================
|
||||
响应式适配和字体优化
|
||||
================================ */
|
||||
|
||||
/* 全局字体系统 */
|
||||
QWidget {
|
||||
font-family: "Consolas", "Monaco", "Courier New", "Microsoft YaHei", monospace;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
/* 标题字体 */
|
||||
#PanelTitle, #ModuleTitle {
|
||||
font-family: "Microsoft YaHei", "SimHei", sans-serif;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
/* 数据显示字体 - 等宽字体便于对齐 */
|
||||
#target-count-number, #volume-percent {
|
||||
font-family: "Consolas", "Monaco", "Courier New", monospace;
|
||||
letter-spacing: 0;
|
||||
}
|
||||
|
||||
/* 小屏幕适配 */
|
||||
@media (max-width: 400px) {
|
||||
#rightFunctionPanel {
|
||||
width: 300px;
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
#ModuleCard {
|
||||
padding: 12px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
#FunctionBtn {
|
||||
padding: 10px 16px;
|
||||
font-size: 12px;
|
||||
min-height: 40px;
|
||||
}
|
||||
|
||||
#PanelTitle {
|
||||
font-size: 16px;
|
||||
padding: 10px 14px;
|
||||
}
|
||||
|
||||
#ModuleTitle {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 中等屏幕适配 */
|
||||
@media (max-width: 1200px) {
|
||||
#rightFunctionPanel {
|
||||
width: 320px;
|
||||
padding: 18px;
|
||||
}
|
||||
|
||||
#FunctionBtn {
|
||||
font-size: 13px;
|
||||
min-height: 42px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 高分辨率屏幕优化 */
|
||||
@media (min-width: 1600px) {
|
||||
#rightFunctionPanel {
|
||||
width: 360px;
|
||||
padding: 22px;
|
||||
}
|
||||
|
||||
#PanelTitle {
|
||||
font-size: 20px;
|
||||
padding: 14px 18px;
|
||||
}
|
||||
|
||||
#ModuleTitle {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
#FunctionBtn {
|
||||
font-size: 15px;
|
||||
min-height: 48px;
|
||||
padding: 14px 22px;
|
||||
}
|
||||
|
||||
#ModuleCard {
|
||||
padding: 18px;
|
||||
margin-bottom: 28px;
|
||||
}
|
||||
}
|
||||
|
||||
/* ================================
|
||||
次要按钮样式
|
||||
================================ */
|
||||
|
||||
/* 次要操作按钮 - 蓝色配色 */
|
||||
QPushButton.secondary {
|
||||
background: #2a3441;
|
||||
border: 1px solid #3c4a59;
|
||||
color: #ffffff;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
padding: 10px 16px;
|
||||
border-radius: 6px;
|
||||
margin: 4px;
|
||||
min-height: 36px;
|
||||
}
|
||||
|
||||
QPushButton.secondary:hover {
|
||||
background: #2a3441;
|
||||
border-color: #00a8ff;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
/* 危险操作按钮 - 红色配色 */
|
||||
QPushButton.danger {
|
||||
background: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:1,
|
||||
stop:0 #ff3838, stop:1 #c44569);
|
||||
border: 1px solid #ff3838;
|
||||
color: #ffffff;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
padding: 12px 20px;
|
||||
border-radius: 8px;
|
||||
margin: 6px;
|
||||
min-height: 44px;
|
||||
}
|
||||
|
||||
QPushButton.danger:hover {
|
||||
background: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:1,
|
||||
stop:0 #c44569, stop:1 #a23651);
|
||||
box-shadow: 0 4px 12px rgba(255, 56, 56, 0.3);
|
||||
}
|
||||
|
||||
/* ================================
|
||||
加载和动画效果
|
||||
================================ */
|
||||
|
||||
/* 按钮加载状态 */
|
||||
QPushButton.loading {
|
||||
background: #747d8c;
|
||||
color: #a4b0be;
|
||||
border-color: #3c4a59;
|
||||
}
|
||||
|
||||
/* 呼吸效果 - 用于在线状态指示 */
|
||||
@keyframes breathe {
|
||||
0%, 100% { opacity: 1; }
|
||||
50% { opacity: 0.7; }
|
||||
}
|
||||
|
||||
.breathing-effect {
|
||||
animation: breathe 2s ease-in-out infinite;
|
||||
}
|
||||
|
||||
/* 滑动扫描效果 */
|
||||
@keyframes scan-line {
|
||||
0% { left: -100%; }
|
||||
100% { left: 100%; }
|
||||
}
|
||||
|
||||
.scan-effect::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: -100%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg, transparent, rgba(0, 255, 136, 0.2), transparent);
|
||||
animation: scan-line 2s ease-in-out infinite;
|
||||
}
|
||||
|
||||
/* ================================
|
||||
高级交互效果
|
||||
================================ */
|
||||
|
||||
/* 按钮光亮扫描效果 */
|
||||
#FunctionBtn::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: -100%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
|
||||
transition: left 0.5s;
|
||||
}
|
||||
|
||||
#FunctionBtn:hover::before {
|
||||
left: 100%;
|
||||
}
|
||||
|
||||
/* 模块卡片悬停发光效果 */
|
||||
#ModuleCard {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#ModuleCard::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -50%;
|
||||
left: -50%;
|
||||
width: 200%;
|
||||
height: 200%;
|
||||
background: radial-gradient(circle, rgba(0, 255, 136, 0.1) 0%, transparent 70%);
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
#ModuleCard:hover::after {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* 设备卡片脉搏效果 - 在线状态 */
|
||||
#RightDeviceCard.online {
|
||||
border-color: #00ff88;
|
||||
box-shadow: 0 0 20px rgba(0, 255, 136, 0.3);
|
||||
animation: pulse 2s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0%, 100% {
|
||||
box-shadow: 0 0 20px rgba(0, 255, 136, 0.3);
|
||||
}
|
||||
50% {
|
||||
box-shadow: 0 0 30px rgba(0, 255, 136, 0.6);
|
||||
}
|
||||
}
|
||||
|
||||
/* 威胁等级警告闪烁 */
|
||||
#threat-level-display.high-threat {
|
||||
animation: threat-warning 1.5s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes threat-warning {
|
||||
0%, 100% {
|
||||
background: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:0,
|
||||
stop:0 #ff3838, stop:1 rgba(255, 56, 56, 0.6));
|
||||
border-color: #ff3838;
|
||||
}
|
||||
50% {
|
||||
background: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:0,
|
||||
stop:0 #ff6b6b, stop:1 rgba(255, 107, 107, 0.8));
|
||||
border-color: #ff6b6b;
|
||||
}
|
||||
}
|
||||
|
||||
/* 数据更新动画 */
|
||||
#target-count-number.updating {
|
||||
animation: data-update 0.5s ease-out;
|
||||
}
|
||||
|
||||
@keyframes data-update {
|
||||
0% {
|
||||
transform: scale(1);
|
||||
color: #00ff88;
|
||||
}
|
||||
50% {
|
||||
transform: scale(1.2);
|
||||
color: #00a8ff;
|
||||
}
|
||||
100% {
|
||||
transform: scale(1);
|
||||
color: #00ff88;
|
||||
}
|
||||
}
|
||||
|
||||
/* 按钮点击波纹效果 */
|
||||
#FunctionBtn {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#FunctionBtn::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-radius: 50%;
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
transform: translate(-50%, -50%);
|
||||
transition: width 0.3s, height 0.3s;
|
||||
}
|
||||
|
||||
#FunctionBtn:active::after {
|
||||
width: 300px;
|
||||
height: 300px;
|
||||
}
|
||||
|
||||
/* 加载状态旋转动画 */
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
QPushButton.loading::after {
|
||||
content: '';
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border: 2px solid transparent;
|
||||
border-top: 2px solid currentColor;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
display: inline-block;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
/* 状态变化过渡 */
|
||||
* {
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
Loading…
Reference in new issue