Compare commits

...

10 Commits

Author SHA1 Message Date
zzh
42c5b7e4e1 新增默认密码 2025-12-09 12:39:47 +08:00
zzh
18ec0257d7 更改布局 2025-10-20 17:10:42 +08:00
zzh
61a27c6882 备份灯条新增自定义备份名称 2025-10-20 11:00:57 +08:00
zzh
15bb1ce07d 新增windows程序ota逻辑 2025-09-24 16:01:16 +08:00
zzh
d049d9d908 修复点击清除列表程序core的问题 2025-09-24 13:43:32 +08:00
zzh
29e3fa2900 升级版本号到1.4.0开发版本 2025-09-24 10:47:58 +08:00
zzh
faae203eb1 新增qt程序ota 2025-09-24 10:45:23 +08:00
zzh
5bf62c410a 新增灯条电量管理和灯条列表备份恢复功能 2025-09-23 09:44:34 +08:00
zzh
3e90c5f980 bug fix 2025-09-19 09:25:36 +08:00
zzh
6c7499efa4 调整菜单栏 2025-09-15 10:16:01 +08:00
7 changed files with 1980 additions and 349 deletions

View File

@ -20,12 +20,14 @@ set(SOURCES
src/mainwindow.cpp
src/mqttclient.cpp
src/lightstripmanager.cpp
src/updatesettingsdialog.cpp
)
set(HEADERS
src/mainwindow.h
src/mqttclient.h
src/lightstripmanager.h
src/updatesettingsdialog.h
)
add_executable(QtDemo ${SOURCES} ${HEADERS}

View File

@ -1,6 +1,14 @@
#include "lightstripmanager.h"
#include <QThread>
#include <QStandardPaths>
#include <QDir>
#include <QFile>
#include <QJsonArray>
#include <QFileDialog>
#include <QInputDialog>
#include <QFileInfo>
#include <QJsonDocument>
#include <QJsonObject>
// 如果不再需要可以删除: #include <QRandomGenerator>
LightStripManager::LightStripManager(QWidget *parent)
@ -11,6 +19,7 @@ LightStripManager::LightStripManager(QWidget *parent)
{
setupUI();
loadSnList();
loadBatteryInfo(); // 加载电量信息
// 设置窗口属性
setWindowTitle("灯条SN管理器");
@ -205,11 +214,42 @@ void LightStripManager::setupControlPanel()
);
connect(refreshBtn, &QPushButton::clicked, this, &LightStripManager::onRefreshClicked);
// 新增:备份按钮
backupBtn = new QPushButton("备份灯条");
backupBtn->setMinimumHeight(35);
backupBtn->setStyleSheet(
"QPushButton { "
" background-color: #673AB7; "
" color: white; "
" font-weight: bold; "
" border-radius: 6px; "
" padding: 8px 16px; "
"} "
"QPushButton:hover { background-color: #5E35B1; }"
);
connect(backupBtn, &QPushButton::clicked, this, &LightStripManager::onBackupClicked);
// 恢复备份按钮
restoreBtn = new QPushButton("恢复备份");
restoreBtn->setStyleSheet(
"QPushButton { "
" background-color: #FF9800; "
" color: white; "
" font-weight: bold; "
" border-radius: 6px; "
" padding: 8px 16px; "
"} "
"QPushButton:hover { background-color: #F57C00; }"
);
connect(restoreBtn, &QPushButton::clicked, this, &LightStripManager::onRestoreClicked);
row1Layout->addWidget(selectAllBtn);
row1Layout->addWidget(deselectAllBtn);
row1Layout->addWidget(deleteSelectedBtn);
row1Layout->addWidget(clearAllBtn);
row1Layout->addWidget(refreshBtn);
row1Layout->addWidget(backupBtn);
row1Layout->addWidget(restoreBtn);
row1Layout->addStretch();
// 统计标签
@ -452,6 +492,7 @@ void LightStripManager::setupLightControlArea()
connect(sendLightSelectedBtn, &QPushButton::clicked, this, &LightStripManager::onSendLightSelectedClicked);
row2Layout->addWidget(sendLightSelectedBtn);
/*
sendLightAllBtn = new QPushButton("点亮全部");
sendLightAllBtn->setMinimumHeight(40);
sendLightAllBtn->setMinimumWidth(120);
@ -468,24 +509,7 @@ void LightStripManager::setupLightControlArea()
);
connect(sendLightAllBtn, &QPushButton::clicked, this, &LightStripManager::onSendLightAllClicked);
row2Layout->addWidget(sendLightAllBtn);
// 新增:分组点亮按钮
groupLightBtn = new QPushButton("分组点亮");
groupLightBtn->setMinimumHeight(40);
groupLightBtn->setMinimumWidth(120);
groupLightBtn->setStyleSheet(
"QPushButton { "
" background-color: #9C27B0; "
" color: white; "
" font-weight: bold; "
" padding: 10px 20px; "
" border-radius: 6px; "
" font-size: 14px; "
"} "
"QPushButton:hover { background-color: #7B1FA2; }"
);
connect(groupLightBtn, &QPushButton::clicked, this, &LightStripManager::onGroupLightClicked);
row2Layout->addWidget(groupLightBtn);
*/
lightControlLayout->addLayout(row2Layout);
@ -543,20 +567,31 @@ QWidget* LightStripManager::createSnWidget(const QString &sn)
"QCheckBox { "
" font-weight: bold; "
" font-size: 12px; "
" spacing: 8px; "
"} "
"QCheckBox::indicator { "
" width: 20px; "
" height: 20px; "
" width: 18px; "
" height: 18px; "
" border-radius: 9px; "
" border: 2px solid #d0d0d0; "
" background-color: #ffffff; "
"} "
"QCheckBox::indicator:unchecked { "
" border: 2px solid #ccc; "
" border-radius: 4px; "
" background-color: white; "
"QCheckBox::indicator:unchecked:hover { "
" border: 2px solid #4CAF50; "
" background-color: #f0f8f0; "
"} "
"QCheckBox::indicator:checked { "
" border: 2px solid #4CAF50; "
" border-radius: 4px; "
" background-color: #4CAF50; "
"} "
"QCheckBox::indicator:checked:hover { "
" background-color: #66BB6A; "
"} "
"QCheckBox::indicator:checked::after { "
" content: '✓'; "
" color: white; "
" font-size: 12px; "
" font-weight: bold; "
"}"
);
connect(checkBox, &QCheckBox::stateChanged, this, &LightStripManager::onCheckBoxStateChanged);
@ -580,9 +615,33 @@ QWidget* LightStripManager::createSnWidget(const QString &sn)
);
snLabel->setWordWrap(true);
// 创建电量标签
QLabel *batteryLabel = new QLabel("电量: --");
batteryLabel->setAlignment(Qt::AlignCenter);
batteryLabel->setStyleSheet(
"QLabel { "
" color: #666; "
" font-size: 10px; "
" background-color: #f5f5f5; "
" border: 1px solid #ddd; "
" border-radius: 3px; "
" padding: 2px 4px; "
" min-height: 8px; "
"}"
);
// 存储电量标签的引用
batteryLabels[sn] = batteryLabel;
// 如果已有电量信息,则显示并设置正确颜色
if (batteryInfoMap.contains(sn)) {
updateBatteryInfo(sn, batteryInfoMap[sn]);
}
// 添加到布局
itemLayout->addWidget(checkBox, 0, Qt::AlignCenter);
itemLayout->addWidget(snLabel);
itemLayout->addWidget(batteryLabel);
itemLayout->addStretch();
return lightStripWidget;
@ -660,12 +719,17 @@ void LightStripManager::onClearSnListClicked()
lightStripCheckBoxes.clear();
uniqueSnSet.clear();
// 清空电量相关数据
batteryInfoMap.clear();
batteryLabels.clear();
// 更新显示
updateSnCount();
updateControlButtons();
// 保存设置
saveSnList();
saveBatteryInfo(); // 保存电量信息
emit clearAllRequested();
}
@ -751,7 +815,12 @@ void LightStripManager::onDeleteSelectedClicked()
// 从集合中移除SN
QLabel *snLabel = lightStripWidgets[i]->findChild<QLabel*>();
if (snLabel) {
uniqueSnSet.remove(snLabel->text());
QString sn = snLabel->text();
uniqueSnSet.remove(sn);
// 清理电量相关数据
batteryInfoMap.remove(sn);
batteryLabels.remove(sn);
}
// 删除widget
@ -861,6 +930,147 @@ void LightStripManager::onAddSnManuallyClicked()
msgBox.exec();
}
void LightStripManager::onRestoreClicked()
{
QString backupDir = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/backups";
// 打开文件选择对话框
QString filePath = QFileDialog::getOpenFileName(
this,
"选择要恢复的备份文件",
backupDir,
"JSON备份文件 (*.json);;所有文件 (*)"
);
if (filePath.isEmpty()) {
return; // 用户取消了选择
}
// 加载备份文件
if (loadBackupFile(filePath)) {
// 恢复成功,刷新显示
applyResponsiveLayout();
updateSnCount();
updateControlButtons();
QMessageBox msgBox;
msgBox.setWindowTitle("恢复完成");
msgBox.setText("备份文件已成功恢复!");
msgBox.setIcon(QMessageBox::Information);
msgBox.setStyleSheet(
"QMessageBox { "
" color: #000000; "
"} "
"QPushButton { "
" color: #000000; "
" background-color: #f0f0f0; "
" border: 1px solid #ccc; "
" border-radius: 4px; "
" padding: 6px 12px; "
" min-width: 60px; "
"} "
"QPushButton:hover { "
" background-color: #e0e0e0; "
"}"
);
msgBox.exec();
} else {
QMessageBox msgBox;
msgBox.setWindowTitle("恢复失败");
msgBox.setText("无法读取或解析备份文件,请检查文件格式是否正确。");
msgBox.setIcon(QMessageBox::Warning);
msgBox.setStyleSheet(
"QMessageBox { "
" color: #000000; "
"} "
"QPushButton { "
" color: #000000; "
" background-color: #f0f0f0; "
" border: 1px solid #ccc; "
" border-radius: 4px; "
" padding: 6px 12px; "
" min-width: 60px; "
"} "
"QPushButton:hover { "
" background-color: #e0e0e0; "
"}"
);
msgBox.exec();
}
}
void LightStripManager::onBackupClicked()
{
if (uniqueSnSet.isEmpty()) {
QMessageBox msgBox;
msgBox.setWindowTitle("提示");
msgBox.setText("当前没有灯条SN数据无需备份。");
msgBox.setIcon(QMessageBox::Information);
msgBox.setStyleSheet(
"QMessageBox { "
" color: #000000; "
"} "
"QPushButton { "
" color: #000000; "
" background-color: #f0f0f0; "
" border: 1px solid #ccc; "
" border-radius: 4px; "
" padding: 6px 12px; "
" min-width: 60px; "
"} "
"QPushButton:hover { "
" background-color: #e0e0e0; "
"}"
);
msgBox.exec();
return;
}
// 弹出输入框,支持自定义备份文件名
bool ok = false;
QString inputName = QInputDialog::getText(
this,
"输入备份文件名",
"备份文件名(可留空自动生成):",
QLineEdit::Normal,
"",
&ok
);
if (!ok) {
return; // 用户取消
}
// 执行备份:使用用户输入或自动生成的文件名
if (inputName.trimmed().isEmpty()) {
backupSnList();
} else {
createBackupFile(inputName.trimmed());
}
// 显示备份成功消息
QMessageBox msgBox;
msgBox.setWindowTitle("备份完成");
msgBox.setText(QString("已成功备份 %1 个灯条SN。\n备份文件保存在应用数据目录的backups文件夹中。").arg(uniqueSnSet.size()));
msgBox.setIcon(QMessageBox::Information);
msgBox.setStyleSheet(
"QMessageBox { "
" color: #000000; "
"} "
"QPushButton { "
" color: #000000; "
" background-color: #f0f0f0; "
" border: 1px solid #ccc; "
" border-radius: 4px; "
" padding: 6px 12px; "
" min-width: 60px; "
"} "
"QPushButton:hover { "
" background-color: #e0e0e0; "
"}"
);
msgBox.exec();
}
void LightStripManager::onSearchTextChanged(const QString &text)
{
filterSnDisplay(text);
@ -1099,7 +1309,7 @@ void LightStripManager::updateControlButtons()
deleteSelectedBtn->setEnabled(selectedCount > 0);
sendLightSelectedBtn->setEnabled(selectedCount > 0);
sendLightAllBtn->setEnabled(totalCount > 0);
//sendLightAllBtn->setEnabled(totalCount > 0);
clearAllBtn->setEnabled(totalCount > 0);
selectAllBtn->setEnabled(totalCount > 0);
deselectAllBtn->setEnabled(selectedCount > 0);
@ -1140,6 +1350,9 @@ void LightStripManager::saveSnList()
QStringList snList = uniqueSnSet.values();
settings->setValue("lightStripSnList", snList);
qDebug() << "已保存" << snList.size() << "个SN到lightStripSnList";
// 注释掉自动备份功能
// backupSnList();
}
// 强制同步到磁盘
@ -1479,6 +1692,9 @@ void LightStripManager::syncSnListFromMainWindow(const QStringList &snList)
lightStripCheckBoxes.clear();
uniqueSnSet.clear();
// 清理电量标签映射(但保留电量信息数据)
batteryLabels.clear();
// 批量添加但不保存
for (const QString &sn : snList) {
if (!uniqueSnSet.contains(sn)) {
@ -1498,7 +1714,7 @@ void LightStripManager::syncSnListFromMainWindow(const QStringList &snList)
void LightStripManager::setupIdentityBindingArea()
{
identityBindingGroup = new QGroupBox("身份信息绑定", this);
identityBindingGroup = new QGroupBox("身份信息绑定未满10个下发绑定后需等1分钟或继续补充满10个立即触发", this);
identityBindingGroup->setStyleSheet(
"QGroupBox { "
" color: #000000; "
@ -1716,6 +1932,24 @@ void LightStripManager::setupIdentityBindingArea()
row2Layout->addWidget(bindIdentityAllBtn);
row2Layout->addStretch();
// 新增:分组点亮按钮
groupLightBtn = new QPushButton("分组绑定点亮");
groupLightBtn->setMinimumHeight(40);
groupLightBtn->setMinimumWidth(120);
groupLightBtn->setStyleSheet(
"QPushButton { "
" background-color: #9C27B0; "
" color: white; "
" font-weight: bold; "
" padding: 10px 20px; "
" border-radius: 6px; "
" font-size: 14px; "
"} "
"QPushButton:hover { background-color: #7B1FA2; }"
);
connect(groupLightBtn, &QPushButton::clicked, this, &LightStripManager::onGroupLightClicked);
row2Layout->addWidget(groupLightBtn);
bindingStatusLabel = new QLabel("状态: 就绪");
bindingStatusLabel->setStyleSheet(
@ -1734,4 +1968,328 @@ void LightStripManager::setupIdentityBindingArea()
// 加载保存的身份信息设置
loadIdentitySettings();
}
// 电量信息管理方法实现
void LightStripManager::updateBatteryInfo(const QString &sn, const QString &batteryLevel)
{
batteryInfoMap[sn] = batteryLevel;
// 添加调试信息
qDebug() << "[DEBUG] 更新电量信息 - SN:" << sn << "电量:" << batteryLevel;
// 更新对应的电量标签
if (batteryLabels.contains(sn)) {
batteryLabels[sn]->setText(QString("电量: %1").arg(batteryLevel));
// 根据电量设置不同颜色
QString color = "#666";
QString bgColor = "#fff3cd";
QString borderColor = "#ffeaa7";
bool ok;
int level = batteryLevel.toInt(&ok);
qDebug() << "[DEBUG] 电量转换 - 原值:" << batteryLevel << "转换后:" << level << "转换成功:" << ok;
if (ok) {
// 判断电量值是毫伏值还是百分比
if (level > 100) {
qDebug() << "[DEBUG] 检测为毫伏值:" << level;
// 毫伏值,一般锂电池范围 2500-4200mV
if (level < 2600) { // 低电量 (<2.8V)
color = "#d32f2f";
bgColor = "#ffebee";
borderColor = "#f8bbd9";
qDebug() << "[DEBUG] 设置为红色 - 低电量";
} else if (level < 2900) { // 中等电量 (2.8V-2.9V)
color = "#f57c00";
bgColor = "#fff3e0";
borderColor = "#ffcc02";
qDebug() << "[DEBUG] 设置为黄色 - 中等电量";
} else { // 健康电量 (≥2.9V)
color = "#388e3c";
bgColor = "#e8f5e8";
borderColor = "#4caf50";
qDebug() << "[DEBUG] 设置为绿色 - 健康电量";
}
} else {
// 百分比值 (0-100)
if (level <= 20) {
color = "#d32f2f";
bgColor = "#ffebee";
borderColor = "#f8bbd9";
} else if (level <= 50) {
color = "#f57c00";
bgColor = "#fff3e0";
borderColor = "#ffcc02";
} else {
color = "#388e3c";
bgColor = "#e8f5e8";
borderColor = "#4caf50";
}
}
}
batteryLabels[sn]->setStyleSheet(
QString("QLabel { "
" color: %1; "
" font-size: 10px; "
" background-color: %2; "
" border: 1px solid %3; "
" border-radius: 3px; "
" padding: 2px 4px; "
" min-height: 8px; "
"}").arg(color, bgColor, borderColor)
);
}
// 保存电量信息
saveBatteryInfo();
}
void LightStripManager::saveBatteryInfo()
{
settings->beginGroup("BatteryInfo");
settings->remove(""); // 清空组内所有键
for (auto it = batteryInfoMap.begin(); it != batteryInfoMap.end(); ++it) {
settings->setValue(it.key(), it.value());
}
settings->endGroup();
settings->sync();
qDebug() << "保存电量信息,共" << batteryInfoMap.size() << "条记录";
}
void LightStripManager::loadBatteryInfo()
{
batteryInfoMap.clear();
settings->beginGroup("BatteryInfo");
QStringList keys = settings->allKeys();
for (const QString &key : keys) {
QString value = settings->value(key).toString();
batteryInfoMap[key] = value;
// 如果对应的电量标签存在,更新显示和颜色
if (batteryLabels.contains(key)) {
updateBatteryInfo(key, value);
}
}
settings->endGroup();
qDebug() << "加载电量信息,共" << batteryInfoMap.size() << "条记录";
}
// 新增SN列表备份功能实现
void LightStripManager::backupSnList()
{
if (uniqueSnSet.isEmpty()) {
qDebug() << "SN列表为空无需备份";
return;
}
createBackupFile();
}
void LightStripManager::createBackupFile()
{
QString backupFileName = generateBackupFileName();
QString backupDir = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/backups";
// 创建备份目录
QDir dir;
if (!dir.exists(backupDir)) {
if (!dir.mkpath(backupDir)) {
qDebug() << "创建备份目录失败:" << backupDir;
return;
}
}
QString backupFilePath = backupDir + "/" + backupFileName;
// 创建备份文件内容
QJsonObject backupData;
backupData["timestamp"] = QDateTime::currentDateTime().toString(Qt::ISODate);
backupData["version"] = "1.0";
backupData["count"] = uniqueSnSet.size();
QJsonArray snArray;
QStringList snList = uniqueSnSet.values();
for (const QString &sn : snList) {
QJsonObject snObject;
snObject["sn"] = sn;
// 如果有电量信息,也一并备份
if (batteryInfoMap.contains(sn)) {
snObject["battery"] = batteryInfoMap[sn];
}
snArray.append(snObject);
}
backupData["lightstrips"] = snArray;
// 写入文件
QJsonDocument doc(backupData);
QFile file(backupFilePath);
if (file.open(QIODevice::WriteOnly)) {
file.write(doc.toJson());
file.close();
qDebug() << "SN列表备份成功:" << backupFilePath;
qDebug() << "备份了" << uniqueSnSet.size() << "个灯条SN";
} else {
qDebug() << "创建备份文件失败:" << backupFilePath;
}
}
QString LightStripManager::generateBackupFileName() const
{
QDateTime now = QDateTime::currentDateTime();
QString timestamp = now.toString("yyyyMMdd_hhmmss");
return QString("lightstrip_sn_backup_%1.json").arg(timestamp);
}
void LightStripManager::createBackupFile(const QString &fileName)
{
QString backupDir = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/backups";
// 创建备份目录
QDir dir;
if (!dir.exists(backupDir)) {
if (!dir.mkpath(backupDir)) {
qDebug() << "创建备份目录失败:" << backupDir;
return;
}
}
QString finalFileName = fileName;
if (finalFileName.trimmed().isEmpty()) {
finalFileName = generateBackupFileName();
}
if (!finalFileName.endsWith(".json", Qt::CaseInsensitive)) {
finalFileName += ".json";
}
QString backupFilePath = backupDir + "/" + finalFileName;
// 若文件已存在,弹出确认是否覆盖
QFileInfo fi(backupFilePath);
if (fi.exists()) {
QMessageBox::StandardButton reply = QMessageBox::question(
this,
"文件已存在",
QString("备份文件 %1 已存在,是否覆盖?").arg(finalFileName),
QMessageBox::Yes | QMessageBox::No,
QMessageBox::No
);
if (reply != QMessageBox::Yes) {
qDebug() << "用户取消覆盖,备份中止";
return;
}
}
// 创建备份文件内容
QJsonObject backupData;
backupData["timestamp"] = QDateTime::currentDateTime().toString(Qt::ISODate);
backupData["version"] = "1.0";
backupData["count"] = uniqueSnSet.size();
QJsonArray snArray;
QStringList snList = uniqueSnSet.values();
for (const QString &sn : snList) {
QJsonObject snObject;
snObject["sn"] = sn;
// 如果有电量信息,也一并备份
if (batteryInfoMap.contains(sn)) {
snObject["battery"] = batteryInfoMap[sn];
}
snArray.append(snObject);
}
backupData["lightstrips"] = snArray;
// 写入文件
QJsonDocument doc(backupData);
QFile file(backupFilePath);
if (file.open(QIODevice::WriteOnly)) {
file.write(doc.toJson());
file.close();
qDebug() << "SN列表备份成功:" << backupFilePath;
qDebug() << "备份了" << uniqueSnSet.size() << "个灯条SN";
} else {
qDebug() << "创建备份文件失败:" << backupFilePath;
}
}
// 新增SN列表恢复功能实现
void LightStripManager::restoreSnList()
{
QString backupDir = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/backups";
// 打开文件选择对话框
QString filePath = QFileDialog::getOpenFileName(
this,
"选择要恢复的备份文件",
backupDir,
"JSON备份文件 (*.json);;所有文件 (*)"
);
if (!filePath.isEmpty()) {
loadBackupFile(filePath);
}
}
bool LightStripManager::loadBackupFile(const QString &filePath)
{
QFile file(filePath);
if (!file.open(QIODevice::ReadOnly)) {
qDebug() << "无法打开备份文件:" << filePath;
return false;
}
QByteArray data = file.readAll();
file.close();
QJsonParseError error;
QJsonDocument doc = QJsonDocument::fromJson(data, &error);
if (error.error != QJsonParseError::NoError) {
qDebug() << "解析JSON文件失败:" << error.errorString();
return false;
}
QJsonObject backupData = doc.object();
// 验证文件格式
if (!backupData.contains("lightstrips") || !backupData["lightstrips"].isArray()) {
qDebug() << "备份文件格式不正确缺少lightstrips数组";
return false;
}
// 清空当前数据
uniqueSnSet.clear();
batteryInfoMap.clear();
// 恢复SN列表和电量信息
QJsonArray snArray = backupData["lightstrips"].toArray();
for (const QJsonValue &value : snArray) {
QJsonObject snObject = value.toObject();
if (snObject.contains("sn")) {
QString sn = snObject["sn"].toString();
uniqueSnSet.insert(sn);
// 如果有电量信息,也一并恢复
if (snObject.contains("battery")) {
batteryInfoMap[sn] = snObject["battery"].toString();
}
}
}
// 保存恢复的数据
saveSnList();
saveBatteryInfo();
qDebug() << "从备份文件恢复了" << uniqueSnSet.size() << "个灯条SN";
qDebug() << "恢复了" << batteryInfoMap.size() << "条电量信息";
return true;
}

View File

@ -47,6 +47,11 @@ public:
// 新增:设置主窗口引用
void setMainWindow(MainWindow *mainWindow);
// 新增:电量信息管理方法
void updateBatteryInfo(const QString &sn, const QString &batteryLevel);
void saveBatteryInfo();
void loadBatteryInfo();
signals:
void snSelectionChanged(const QStringList &selectedSns);
@ -71,6 +76,8 @@ private slots:
void onSendLightAllClicked();
void onCheckBoxStateChanged();
void onRefreshClicked();
void onBackupClicked(); // 新增:备份按钮槽函数
void onRestoreClicked(); // 新增:恢复备份按钮槽函数
// 新增:身份信息绑定相关槽函数
void onBindIdentitySelectedClicked();
@ -97,6 +104,16 @@ private:
QWidget* createSnWidget(const QString &sn);
void applyResponsiveLayout();
// 新增SN列表备份功能
void backupSnList();
void createBackupFile();
void createBackupFile(const QString &fileName);
QString generateBackupFileName() const;
// 新增SN列表恢复功能
void restoreSnList();
bool loadBackupFile(const QString &filePath);
// 新增MQTT发送功能
void sendLightControlMessage(const QStringList &sns);
@ -122,6 +139,8 @@ private:
QPushButton *deleteSelectedBtn;
QPushButton *clearAllBtn;
QPushButton *refreshBtn;
QPushButton *backupBtn; // 新增:备份按钮
QPushButton *restoreBtn; // 新增:恢复备份按钮
QLineEdit *searchEdit;
QLineEdit *manualSnEdit;
QPushButton *addSnBtn;
@ -175,6 +194,10 @@ private:
QString currentLabel1;
QString currentLabel2;
QString currentLabel3;
// 新增:电量信息相关数据
QMap<QString, QString> batteryInfoMap; // SN -> 电量信息的映射
QMap<QString, QLabel*> batteryLabels; // SN -> 电量标签的映射
public:
enum MatchRule {
EQUAL = 0, // = (00)

File diff suppressed because it is too large Load Diff

View File

@ -29,6 +29,12 @@
#include <QGuiApplication>
#include "mqttclient.h"
#include "lightstripmanager.h"
#include <QMenuBar>
#include <QMenu>
#include <QAction>
#include <QMessageBox>
#include <QApplication> // 新增:用于获取系统调色板
#include <QPalette>
class MainWindow : public QMainWindow
{
@ -37,48 +43,59 @@ class MainWindow : public QMainWindow
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
// 新增:公共接口
QString getDeviceSn() const;
bool isMqttConnected() const;
void publishMqttMessage(const QString &topic, const QString &message);
private slots:
void showAbout(); // 简化为关于对话框
void showUseGuide(); // 新增:使用说明对话框
// MQTT相关槽函数
void onConnectClicked();
void onDisconnectClicked();
void onSendLightAllClicked();
void onMessageReceived(const QString &topic, const QString &message);
void onMqttConnected();
void onMqttDisconnected();
void onMqttError(const QString &error);
// OTA相关槽函数
void onMessageReceived(const QString &topic, const QString &message);
void onOtaUpgradeClicked();
void onOtaDowngradeClicked();
void onGetVersionClicked();
// 灯条SN管理相关槽函数
void onClearSnListClicked();
void onSearchLightStripClicked();
void openLightStripManager(); // 新增:打开灯条管理器
void onLightStripManagerClosed(); // 新增:灯条管理器关闭时的处理
void onGetVersionClicked();
void onClearSnListClicked();
void onLightStripManagerClosed();
void onDeviceSnChanged(); // 新增设备SN变化槽函数
// 更新相关槽函数
void checkForUpdates();
void onUpdateCheckFinished();
void onUpdateDownloadProgress(qint64 bytesReceived, qint64 bytesTotal);
void onUpdateDownloadFinished();
void onUpdateSettingsClicked();
private:
void setupUI();
void setupMenuBar();
void setupToolBar();
void createMenus(); // 重命名为更清晰的函数名
void updateConnectionStatus(bool connected);
void resubscribeTopics(); // 添加这个声明
void processOtaMessage(const QJsonObject &otaData);
void sendOtaCommand(const QString &version, bool isUpgrade = true);
// 灯条SN管理相关函数
void processLightReportMessage(const QString &message);
void addSnToList(const QString &sn);
void saveSnList();
void loadSnList();
void sendSearchLightStripCommand();
void openLightStripManager(); // 添加这个声明
// 更新相关函数
void showUpdateDialog(const QString &version, const QString &downloadUrl, const QString &changelog);
void downloadUpdate(const QString &downloadUrl);
void installUpdate();
int compareVersions(const QString &version1, const QString &version2);
// 新增:公共接口
QString getDeviceSn() const;
QString getTextColorForTheme() const;
bool isMqttConnected() const;
void publishMqttMessage(const QString &topic, const QString &message);
// 添加OTA状态跟踪
bool otaOperationPending;
QString pendingOtaVersion;
@ -101,7 +118,7 @@ private:
QGroupBox *deviceGroup;
QLineEdit *deviceSnEdit;
// 点亮参数控制区域
// 灯光控制区域
QGroupBox *lightGroup;
QComboBox *colorCombo;
QComboBox *flashCombo;
@ -110,7 +127,7 @@ private:
QComboBox *soundCombo;
QPushButton *sendLightAllBtn;
// 灯条SN管理区域 - 修改为横向布局
// SN管理区域
QGroupBox *snGroup;
QScrollArea *snScrollArea; // 添加滚动区域
QWidget *snContainerWidget; // 添加容器widget
@ -119,7 +136,7 @@ private:
QPushButton *clearSnBtn;
QPushButton *searchLightStripBtn;
// OTA升级区域 - 添加缺失的成员变量
// OTA升级区域
QGroupBox *otaGroup;
QLineEdit *currentVersionEdit;
QPushButton *getVersionBtn;
@ -128,17 +145,25 @@ private:
QProgressBar *otaProgressBar;
QLabel *otaStatusLabel;
// 消息显示区域 - 添加缺失的成员变量
// 消息显示区域
QGroupBox *messageGroup;
QTextEdit *messageDisplay;
// 状态栏 - 添加缺失的成员变量
// 状态栏
QLabel *statusLabel;
// MQTT客户端 - 添加缺失的成员变量
// MQTT客户端
MqttClient *mqttClient;
// 灯条SN管理相关成员变量
// 更新相关成员变量
QString currentVersion;
QString updateServerUrl;
QNetworkAccessManager *updateNetworkManager;
QNetworkReply *updateCheckReply;
QNetworkReply *updateDownloadReply;
QString downloadedUpdatePath;
// 数据管理
QSet<QString> uniqueSnSet;
QList<QCheckBox*> lightStripCheckBoxes; // 添加复选框列表
QSettings *settings;

View File

@ -0,0 +1,249 @@
#include "updatesettingsdialog.h"
#include <QMessageBox>
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QUrl>
#include <QTimer>
#include <QSpinBox>
UpdateSettingsDialog::UpdateSettingsDialog(QWidget *parent)
: QDialog(parent),
serverUrlEdit(nullptr),
autoCheckBox(nullptr),
checkIntervalEdit(nullptr),
testBtn(nullptr),
resetBtn(nullptr),
okBtn(nullptr),
cancelBtn(nullptr),
settings(nullptr)
{
settings = new QSettings("TuxiApp", "LightStripSN", this);
setupUI();
loadSettings();
}
UpdateSettingsDialog::~UpdateSettingsDialog()
{
}
void UpdateSettingsDialog::setupUI()
{
qDebug() << "开始设置UpdateSettingsDialog UI...";
setWindowTitle("更新设置");
setFixedSize(400, 300);
setModal(true);
QVBoxLayout *mainLayout = new QVBoxLayout(this);
if (!mainLayout) {
qDebug() << "错误: 无法创建主布局";
return;
}
// 服务器设置组
QGroupBox *serverGroup = new QGroupBox("服务器设置", this);
if (!serverGroup) {
qDebug() << "错误: 无法创建服务器设置组";
return;
}
QFormLayout *serverLayout = new QFormLayout(serverGroup);
if (!serverLayout) {
qDebug() << "错误: 无法创建服务器布局";
return;
}
serverUrlEdit = new QLineEdit(this);
if (!serverUrlEdit) {
qDebug() << "错误: 无法创建服务器URL编辑框";
return;
}
serverUrlEdit->setPlaceholderText("http://180.163.74.83:8001/version");
serverLayout->addRow("更新服务器地址:", serverUrlEdit);
QHBoxLayout *testLayout = new QHBoxLayout();
testBtn = new QPushButton("测试连接", this);
resetBtn = new QPushButton("恢复默认", this);
if (!testBtn || !resetBtn) {
qDebug() << "错误: 无法创建测试或重置按钮";
return;
}
testLayout->addWidget(testBtn);
testLayout->addWidget(resetBtn);
testLayout->addStretch();
serverLayout->addRow("", testLayout);
mainLayout->addWidget(serverGroup);
// 自动检查设置组
QGroupBox *autoGroup = new QGroupBox("自动检查设置", this);
QFormLayout *autoLayout = new QFormLayout(autoGroup);
autoCheckBox = new QCheckBox("启用自动检查更新", this);
autoLayout->addRow(autoCheckBox);
checkIntervalEdit = new QLineEdit(this);
checkIntervalEdit->setPlaceholderText("24");
QLabel *intervalLabel = new QLabel("检查间隔(小时):", this);
autoLayout->addRow(intervalLabel, checkIntervalEdit);
// 当自动检查被禁用时,禁用间隔设置
connect(autoCheckBox, &QCheckBox::toggled, [this, intervalLabel](bool checked) {
checkIntervalEdit->setEnabled(checked);
intervalLabel->setEnabled(checked);
});
mainLayout->addWidget(autoGroup);
// 按钮布局
QHBoxLayout *buttonLayout = new QHBoxLayout();
buttonLayout->addStretch();
okBtn = new QPushButton("确定", this);
cancelBtn = new QPushButton("取消", this);
buttonLayout->addWidget(okBtn);
buttonLayout->addWidget(cancelBtn);
mainLayout->addLayout(buttonLayout);
// 连接信号槽
connect(okBtn, &QPushButton::clicked, this, &UpdateSettingsDialog::onOkClicked);
connect(cancelBtn, &QPushButton::clicked, this, &UpdateSettingsDialog::onCancelClicked);
connect(testBtn, &QPushButton::clicked, this, &UpdateSettingsDialog::onTestConnectionClicked);
connect(resetBtn, &QPushButton::clicked, this, &UpdateSettingsDialog::onResetToDefaultClicked);
qDebug() << "UpdateSettingsDialog UI设置完成";
}
void UpdateSettingsDialog::loadSettings()
{
QString defaultUrl = "http://180.163.74.83:8001/version";
serverUrlEdit->setText(settings->value("updateServerUrl", defaultUrl).toString());
autoCheckBox->setChecked(settings->value("autoCheckUpdates", true).toBool());
checkIntervalEdit->setText(settings->value("checkInterval", 24).toString());
// 触发复选框状态变化以更新UI
emit autoCheckBox->toggled(autoCheckBox->isChecked());
}
void UpdateSettingsDialog::saveSettings()
{
settings->setValue("updateServerUrl", serverUrlEdit->text().trimmed());
settings->setValue("autoCheckUpdates", autoCheckBox->isChecked());
settings->setValue("checkInterval", checkIntervalEdit->text().toInt());
settings->sync();
}
QString UpdateSettingsDialog::getUpdateServerUrl() const
{
return serverUrlEdit->text().trimmed();
}
bool UpdateSettingsDialog::isAutoCheckEnabled() const
{
return autoCheckBox->isChecked();
}
int UpdateSettingsDialog::getCheckInterval() const
{
return checkIntervalEdit->text().toInt();
}
void UpdateSettingsDialog::onOkClicked()
{
// 验证输入
QString url = serverUrlEdit->text().trimmed();
if (url.isEmpty()) {
QMessageBox::warning(this, "输入错误", "请输入更新服务器地址!");
return;
}
if (!url.startsWith("http://") && !url.startsWith("https://")) {
QMessageBox::warning(this, "输入错误", "服务器地址必须以 http:// 或 https:// 开头!");
return;
}
int interval = checkIntervalEdit->text().toInt();
if (autoCheckBox->isChecked() && (interval < 1 || interval > 168)) {
QMessageBox::warning(this, "输入错误", "检查间隔必须在1-168小时之间");
return;
}
saveSettings();
accept();
}
void UpdateSettingsDialog::onCancelClicked()
{
reject();
}
void UpdateSettingsDialog::onTestConnectionClicked()
{
QString url = serverUrlEdit->text().trimmed();
if (url.isEmpty()) {
QMessageBox::warning(this, "测试失败", "请先输入服务器地址!");
return;
}
testConnection();
}
void UpdateSettingsDialog::onResetToDefaultClicked()
{
int ret = QMessageBox::question(this, "确认重置",
"确定要恢复默认设置吗?",
QMessageBox::Yes | QMessageBox::No);
if (ret == QMessageBox::Yes) {
serverUrlEdit->setText("http://180.163.74.83:8001/version");
autoCheckBox->setChecked(true);
checkIntervalEdit->setText("24");
}
}
void UpdateSettingsDialog::testConnection()
{
testBtn->setEnabled(false);
testBtn->setText("测试中...");
QNetworkAccessManager *manager = new QNetworkAccessManager(this);
QNetworkRequest request;
request.setUrl(QUrl(serverUrlEdit->text().trimmed()));
request.setRawHeader("User-Agent", "LightStripSN-Updater/1.0");
// 设置超时
QTimer *timer = new QTimer(this);
timer->setSingleShot(true);
QNetworkReply *reply = manager->get(request);
connect(timer, &QTimer::timeout, [this, reply]() {
reply->abort();
testBtn->setEnabled(true);
testBtn->setText("测试连接");
QMessageBox::warning(this, "连接超时", "连接服务器超时,请检查网络或服务器地址!");
});
connect(reply, &QNetworkReply::finished, [this, reply, timer]() {
timer->stop();
testBtn->setEnabled(true);
testBtn->setText("测试连接");
if (reply->error() == QNetworkReply::NoError) {
QMessageBox::information(this, "连接成功", "服务器连接正常!");
} else {
QMessageBox::warning(this, "连接失败",
QString("连接失败: %1").arg(reply->errorString()));
}
reply->deleteLater();
timer->deleteLater();
});
timer->start(10000); // 10秒超时
}

View File

@ -0,0 +1,51 @@
#ifndef UPDATESETTINGSDIALOG_H
#define UPDATESETTINGSDIALOG_H
#include <QDialog>
#include <QLineEdit>
#include <QPushButton>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QFormLayout>
#include <QLabel>
#include <QGroupBox>
#include <QCheckBox>
#include <QSettings>
#include <QSpinBox>
class UpdateSettingsDialog : public QDialog
{
Q_OBJECT
public:
explicit UpdateSettingsDialog(QWidget *parent = nullptr);
~UpdateSettingsDialog();
QString getUpdateServerUrl() const;
bool isAutoCheckEnabled() const;
int getCheckInterval() const;
private slots:
void onOkClicked();
void onCancelClicked();
void onTestConnectionClicked();
void onResetToDefaultClicked();
private:
void setupUI();
void loadSettings();
void saveSettings();
void testConnection();
QLineEdit *serverUrlEdit;
QCheckBox *autoCheckBox;
QLineEdit *checkIntervalEdit;
QPushButton *testBtn;
QPushButton *resetBtn;
QPushButton *okBtn;
QPushButton *cancelBtn;
QSettings *settings;
};
#endif // UPDATESETTINGSDIALOG_H