#include "mainwindow.h" #include "lightstripmanager.h" #include "updatesettingsdialog.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), otaOperationPending(false), isUpgradeOperation(true) { // 初始化设置对象 settings = new QSettings("TuxiApp", "LightStripSN", this); // 初始化更新相关变量 currentVersion = "1.5.2"; // 当前程序版本 updateServerUrl = settings->value("updateServerUrl", "http://180.163.74.83:8001/version").toString(); updateNetworkManager = new QNetworkAccessManager(this); updateCheckReply = nullptr; updateDownloadReply = nullptr; downloadedUpdatePath = ""; setupUI(); // 创建MQTT客户端 mqttClient = new MqttClient(this); // 连接信号和槽 connect(connectBtn, &QPushButton::clicked, this, &MainWindow::onConnectClicked); connect(disconnectBtn, &QPushButton::clicked, this, &MainWindow::onDisconnectClicked); connect(sendLightAllBtn, &QPushButton::clicked, this, &MainWindow::onSendLightAllClicked); connect(clearSnBtn, &QPushButton::clicked, this, &MainWindow::onClearSnListClicked); connect(searchLightStripBtn, &QPushButton::clicked, this, &MainWindow::onSearchLightStripClicked); connect(mqttClient, &MqttClient::connected, this, &MainWindow::onMqttConnected); connect(mqttClient, &MqttClient::disconnected, this, &MainWindow::onMqttDisconnected); connect(mqttClient, &MqttClient::messageReceived, this, &MainWindow::onMessageReceived); connect(mqttClient, &MqttClient::errorOccurred, this, &MainWindow::onMqttError); connect(otaUpgradeBtn, &QPushButton::clicked, this, &MainWindow::onOtaUpgradeClicked); connect(otaDowngradeBtn, &QPushButton::clicked, this, &MainWindow::onOtaDowngradeClicked); connect(getVersionBtn, &QPushButton::clicked, this, &MainWindow::onGetVersionClicked); // 添加设备SN输入框变化监听,自动重新订阅主题 connect(deviceSnEdit, &QLineEdit::textChanged, this, &MainWindow::onDeviceSnChanged); // 初始化灯条管理器 lightStripManager = nullptr; // 加载已保存的灯条SN列表 loadSnList(); // 初始化状态 updateConnectionStatus(false); currentVersionEdit->setText("请点击获取版本"); } MainWindow::~MainWindow() { } /* void MainWindow::setupMenuBar() { // 获取主窗口的菜单栏 QMenuBar *mainMenuBar = this->menuBar(); // 创建帮助菜单 QMenu *helpMenu = mainMenuBar->addMenu("帮助(&H)"); // 创建版本更新说明菜单项 versionUpdateAction = new QAction("版本更新说明(&V)", this); helpMenu->addAction(versionUpdateAction); // 连接信号槽 connect(versionUpdateAction, &QAction::triggered, this, &MainWindow::showVersionUpdateInfo); } void MainWindow::showVersionUpdateInfo() { QString versionInfo = "版本更新说明\n\n" "版本 1.3.0 (2024-01-15)\n" "• 新增版本更新说明菜单\n" "• 优化MQTT连接稳定性\n" "• 修复已知问题\n\n" "版本 1.2.0 (2023-12-20)\n" "• 改进用户界面\n" "• 增强数据处理能力\n\n" "版本 1.1.0 (2023-11-10)\n" "• 初始版本发布\n" "• 基础功能实现"; QMessageBox::information(this, "版本更新说明", versionInfo); } */ void MainWindow::setupUI() { setWindowTitle("兔喜Test1.5.2 Author:Zhangzhenghao Email:zzh9953477@gmail.com"); // 参考qt_bak的合理尺寸设置,增加竖向高度 setMinimumSize(850, 720); // 增加最小高度 resize(900, 770); // 增加初始高度,从750增加到850 // 获取屏幕尺寸并设置合适的窗口大小 QScreen *screen = QGuiApplication::primaryScreen(); if (screen) { QRect screenGeometry = screen->geometry(); int screenWidth = screenGeometry.width(); int screenHeight = screenGeometry.height(); // 设置合理的窗口大小,增加竖向比例 int windowWidth = qMin(1000, static_cast(screenWidth * 0.6)); int windowHeight = qMin(900, static_cast(screenHeight * 0.7)); // 增加到75% // 确保高度明显大于宽度 if (windowHeight < windowWidth * 0.95) { windowHeight = static_cast(windowWidth * 0.9); // 高度等于宽度 } resize(windowWidth, windowHeight); // 居中显示 move((screenWidth - windowWidth) / 2, (screenHeight - windowHeight) / 2 - 50); } setWindowIcon(QIcon(":/image/src/tuxi.ico")); centralWidget = new QWidget(this); setCentralWidget(centralWidget); mainLayout = new QVBoxLayout(centralWidget); mainLayout->setSpacing(10); // 减少布局间距 mainLayout->setContentsMargins(15, 10, 15, 10); // 减少边距 // 获取适合当前主题的文字颜色 QString textColor = getTextColorForTheme(); // MQTT连接区域 connectionGroup = new QGroupBox("MQTT连接设置", this); connectionGroup->setStyleSheet(QString("QGroupBox { font-weight: bold; font-size: 12px; padding-top: 10px; color: %1; }").arg(textColor)); QVBoxLayout *connectionLayout = new QVBoxLayout(connectionGroup); connectionLayout->setSpacing(10); // 服务器和端口 - 使用水平布局 QHBoxLayout *serverLayout = new QHBoxLayout(); QLabel *serverLabel = new QLabel("服务器:"); serverLabel->setStyleSheet(QString("QLabel { color: %1; }").arg(textColor)); serverLayout->addWidget(serverLabel); brokerEdit = new QLineEdit("tx-mqtt.zt-express.com"); brokerEdit->setMinimumHeight(30); serverLayout->addWidget(brokerEdit); QLabel *portLabel = new QLabel("端口:"); portLabel->setStyleSheet(QString("QLabel { color: %1; }").arg(textColor)); serverLayout->addWidget(portLabel); portEdit = new QLineEdit("1883"); portEdit->setMinimumHeight(30); portEdit->setMaximumWidth(100); serverLayout->addWidget(portEdit); connectionLayout->addLayout(serverLayout); // 用户名和密码 QHBoxLayout *authLayout = new QHBoxLayout(); QLabel *usernameLabel = new QLabel("用户名:"); usernameLabel->setStyleSheet(QString("QLabel { color: %1; }").arg(textColor)); authLayout->addWidget(usernameLabel); usernameEdit = new QLineEdit("TJ251679787196"); usernameEdit->setMinimumHeight(30); authLayout->addWidget(usernameEdit); QLabel *passwordLabel = new QLabel("密码:"); passwordLabel->setStyleSheet(QString("QLabel { color: %1; }").arg(textColor)); authLayout->addWidget(passwordLabel); passwordEdit = new QLineEdit(); passwordEdit->setEchoMode(QLineEdit::Password); passwordEdit->setMinimumHeight(30); authLayout->addWidget(passwordEdit); connectionLayout->addLayout(authLayout); // 连接按钮 QHBoxLayout *btnLayout = new QHBoxLayout(); connectBtn = new QPushButton("连接"); connectBtn->setMinimumHeight(35); connectBtn->setStyleSheet("QPushButton { background-color: #4CAF50; color: white; font-weight: bold; padding: 8px 16px; border-radius: 4px; }"); disconnectBtn = new QPushButton("断开"); disconnectBtn->setMinimumHeight(35); disconnectBtn->setStyleSheet("QPushButton { background-color: #f44336; color: white; font-weight: bold; padding: 8px 16px; border-radius: 4px; }"); btnLayout->addWidget(connectBtn); btnLayout->addWidget(disconnectBtn); btnLayout->addStretch(); connectionLayout->addLayout(btnLayout); mainLayout->addWidget(connectionGroup); // 设备控制区域 deviceGroup = new QGroupBox("设备控制", this); deviceGroup->setStyleSheet("QGroupBox { font-weight: bold; font-size: 12px; padding-top: 10px; }"); QHBoxLayout *deviceLayout = new QHBoxLayout(deviceGroup); deviceLayout->setSpacing(10); QLabel *deviceSnLabel = new QLabel("需要测试的设备SN:"); deviceSnLabel->setStyleSheet(QString("QLabel { color: %1; }").arg(textColor)); deviceLayout->addWidget(deviceSnLabel); deviceSnEdit = new QLineEdit("TJ251617198122"); deviceSnEdit->setMinimumHeight(30); deviceLayout->addWidget(deviceSnEdit); mainLayout->addWidget(deviceGroup); // 点亮参数控制区域 lightGroup = new QGroupBox("点亮参数设置", this); lightGroup->setStyleSheet("QGroupBox { font-weight: bold; font-size: 12px; padding-top: 10px; }"); QVBoxLayout *lightLayout = new QVBoxLayout(lightGroup); lightLayout->setSpacing(10); // 第一行:颜色和闪烁 QHBoxLayout *row1Layout = new QHBoxLayout(); QLabel *colorLabel = new QLabel("颜色:"); colorLabel->setStyleSheet(QString("QLabel { color: %1; }").arg(textColor)); row1Layout->addWidget(colorLabel); colorCombo = new QComboBox(); colorCombo->addItems({"8-灭", "1-红", "2-黄", "3-蓝", "4-绿", "5-青", "6-白", "7-紫"}); colorCombo->setCurrentIndex(6); colorCombo->setMinimumHeight(30); row1Layout->addWidget(colorCombo); QLabel *flashLabel = new QLabel("闪烁:"); flashLabel->setStyleSheet(QString("QLabel { color: %1; }").arg(textColor)); row1Layout->addWidget(flashLabel); flashCombo = new QComboBox(); flashCombo->addItems({"0-关闭", "1-开启"}); flashCombo->setCurrentIndex(0); flashCombo->setMinimumHeight(30); row1Layout->addWidget(flashCombo); row1Layout->addStretch(); lightLayout->addLayout(row1Layout); // 第二行:闪烁间隔和点亮时长 QHBoxLayout *row2Layout = new QHBoxLayout(); QLabel *flashIntervalLabel = new QLabel("闪烁间隔(秒):"); flashIntervalLabel->setStyleSheet(QString("QLabel { color: %1; }").arg(textColor)); row2Layout->addWidget(flashIntervalLabel); flashIntervalSpin = new QSpinBox(); flashIntervalSpin->setRange(1, 60); flashIntervalSpin->setValue(4); flashIntervalSpin->setMinimumHeight(30); row2Layout->addWidget(flashIntervalSpin); QLabel *lightDurationLabel = new QLabel("点亮时长(秒):"); lightDurationLabel->setStyleSheet(QString("QLabel { color: %1; }").arg(textColor)); row2Layout->addWidget(lightDurationLabel); lightDurationSpin = new QSpinBox(); lightDurationSpin->setRange(1, 300); lightDurationSpin->setValue(30); lightDurationSpin->setMinimumHeight(30); row2Layout->addWidget(lightDurationSpin); row2Layout->addStretch(); lightLayout->addLayout(row2Layout); // 第三行:声音和发送按钮 QHBoxLayout *row3Layout = new QHBoxLayout(); QLabel *soundLabel = new QLabel("声音:"); soundLabel->setStyleSheet(QString("QLabel { color: %1; }").arg(textColor)); row3Layout->addWidget(soundLabel); soundCombo = new QComboBox(); soundCombo->addItems({"0-关闭", "1-开启"}); soundCombo->setCurrentIndex(0); soundCombo->setMinimumHeight(30); row3Layout->addWidget(soundCombo); row3Layout->addStretch(); sendLightAllBtn = new QPushButton("发送全部点亮"); sendLightAllBtn->setMinimumHeight(35); sendLightAllBtn->setMinimumWidth(120); sendLightAllBtn->setStyleSheet("QPushButton { background-color: #4CAF50; color: white; font-weight: bold; padding: 10px 20px; border-radius: 6px; font-size: 14px; }"); row3Layout->addWidget(sendLightAllBtn); lightLayout->addLayout(row3Layout); mainLayout->addWidget(lightGroup); // 灯条SN管理区域 - 简化为一个按钮 snGroup = new QGroupBox("灯条SN管理", this); snGroup->setStyleSheet( "QGroupBox { " " font-weight: bold; " " font-size: 14px; " " padding-top: 15px; " " margin: 5px; " " border: 2px solid #ddd; " " border-radius: 8px; " "}" ); snGroup->setMinimumHeight(120); // 减少最小高度 snGroup->setMaximumHeight(140); // 设置最大高度限制 QVBoxLayout *snLayout = new QVBoxLayout(snGroup); snLayout->setSpacing(8); // 减少组件间距 snLayout->setContentsMargins(10, 20, 10, 10); // 减少内边距 // 初始化snHorizontalLayout snHorizontalLayout = new QHBoxLayout(); // 统计信息和搜索按钮布局 QHBoxLayout *snHeaderLayout = new QHBoxLayout(); snHeaderLayout->setSpacing(8); snCountLabel = new QLabel("已发现灯条: 0 个"); snCountLabel->setStyleSheet( "QLabel { " " font-weight: bold; " " font-size: 13px; " " color: #2196F3; " " padding: 3px 8px; " " background-color: #f0f8ff; " " border: 1px solid #ddd; " " border-radius: 6px; " " min-height: 12px; " "}" ); snHeaderLayout->addWidget(snCountLabel); snHeaderLayout->addStretch(); searchLightStripBtn = new QPushButton("搜索灯条", this); searchLightStripBtn->setMinimumHeight(26); // 减少按钮高度 searchLightStripBtn->setMinimumWidth(90); searchLightStripBtn->setStyleSheet( "QPushButton { " " background-color: #2196F3; " " color: white; " " padding: 4px 10px; " " font-weight: bold; " " border-radius: 6px; " " font-size: 12px; " " border: none; " "} " "QPushButton:hover { " " background-color: #1976D2; " "}" ); snHeaderLayout->addWidget(searchLightStripBtn); // 添加清除按钮 clearSnBtn = new QPushButton("清除列表", this); clearSnBtn->setMinimumHeight(26); clearSnBtn->setMinimumWidth(80); clearSnBtn->setStyleSheet( "QPushButton { " " background-color: #f44336; " " color: white; " " padding: 4px 10px; " " font-weight: bold; " " border-radius: 6px; " " font-size: 12px; " " border: none; " "} " "QPushButton:hover { " " background-color: #d32f2f; " "}" ); snHeaderLayout->addWidget(clearSnBtn); snLayout->addLayout(snHeaderLayout); // 添加snHorizontalLayout到snLayout中 snLayout->addLayout(snHorizontalLayout); // 打开管理器按钮 openManagerBtn = new QPushButton("打开灯条SN管理器"); openManagerBtn->setMinimumHeight(32); // 减少按钮高度 openManagerBtn->setStyleSheet( "QPushButton { " " background-color: #2196F3; " " color: white; " " font-weight: bold; " " font-size: 14px; " " padding: 6px 16px; " " border-radius: 8px; " " border: none; " " margin: 1px; " "} " "QPushButton:hover { " " background-color: #1976D2; " "} " "QPushButton:pressed { " " background-color: #1565C0; " "}" ); connect(openManagerBtn, &QPushButton::clicked, this, &MainWindow::openLightStripManager); snLayout->addWidget(openManagerBtn); mainLayout->addWidget(snGroup); // 消息显示区域 messageGroup = new QGroupBox("消息日志", this); QVBoxLayout *msgLayout = new QVBoxLayout(messageGroup); messageDisplay = new QTextEdit(); messageDisplay->setReadOnly(true); msgLayout->addWidget(messageDisplay); mainLayout->addWidget(messageGroup); // 状态栏 statusLabel = new QLabel("未连接"); statusBar()->addWidget(statusLabel); // OTA升级区域 otaGroup = new QGroupBox("OTA升降级", this); QVBoxLayout *otaLayout = new QVBoxLayout(otaGroup); // 当前版本显示 QHBoxLayout *versionLayout = new QHBoxLayout(); QLabel *versionLabel = new QLabel("当前版本:"); versionLabel->setStyleSheet(QString("QLabel { color: %1; }").arg(textColor)); versionLayout->addWidget(versionLabel); currentVersionEdit = new QLineEdit(this); currentVersionEdit->setReadOnly(true); versionLayout->addWidget(currentVersionEdit); // 创建获取版本按钮 getVersionBtn = new QPushButton("获取版本", this); versionLayout->addWidget(getVersionBtn); otaLayout->addLayout(versionLayout); // OTA按钮 QHBoxLayout *otaButtonLayout = new QHBoxLayout(); otaUpgradeBtn = new QPushButton("升级", this); otaDowngradeBtn = new QPushButton("降级", this); otaButtonLayout->addWidget(otaUpgradeBtn); otaButtonLayout->addWidget(otaDowngradeBtn); otaLayout->addLayout(otaButtonLayout); // 下载进度条 otaProgressBar = new QProgressBar(this); // 修正变量名 otaProgressBar->setVisible(false); otaLayout->addWidget(otaProgressBar); // OTA状态标签 otaStatusLabel = new QLabel("就绪", this); otaLayout->addWidget(otaStatusLabel); mainLayout->addWidget(otaGroup); // 在setupUI的最后调用菜单创建 createMenus(); } void MainWindow::onConnectClicked() { QString broker = brokerEdit->text().trimmed(); int portNum = portEdit->text().toInt(); QString username = usernameEdit->text().trimmed(); QString password = passwordEdit->text(); QString deviceSn = deviceSnEdit->text().trimmed(); if (broker.isEmpty()) { QMessageBox::warning(this, "警告", "请输入MQTT服务器地址"); return; } if (portNum <= 0 || portNum > 65535) { QMessageBox::warning(this, "警告", "请输入有效的端口号 (1-65535)"); return; } // 设置用户名和密码(如果有的话) if (!username.isEmpty()) { mqttClient->setUsername(username); } if (!password.isEmpty()) { mqttClient->setPassword(password); } // 连接到MQTT服务器 mqttClient->connectToHost(broker, static_cast(portNum)); // 更新状态 statusLabel->setText("正在连接..."); connectBtn->setEnabled(false); // 注意:订阅操作应该在连接成功后进行,在onMqttConnected()函数中处理 } void MainWindow::onDisconnectClicked() { mqttClient->disconnectFromHost(); } void MainWindow::onSendLightAllClicked() { QString deviceSn = deviceSnEdit->text().trimmed(); if (deviceSn.isEmpty()) { messageDisplay->append(QString("[%1] 错误: 请输入设备SN") .arg(QDateTime::currentDateTime().toString("hh:mm:ss"))); return; } if (!mqttClient->isConnected()) { messageDisplay->append(QString("[%1] 错误: MQTT未连接") .arg(QDateTime::currentDateTime().toString("hh:mm:ss"))); return; } // 获取参数值 QString color = QString::number(colorCombo->currentIndex()); QString flash = QString::number(flashCombo->currentIndex()); QString flashInterval = QString::number(flashIntervalSpin->value()); QString lightDuration = QString::number(lightDurationSpin->value()); QString sound = QString::number(soundCombo->currentIndex()); // 构造消息内容 QJsonObject dataObj; dataObj["color"] = color; dataObj["flash"] = flash; dataObj["flashInterval"] = flashInterval; dataObj["lightDuration"] = lightDuration; dataObj["sound"] = sound; QJsonObject msgObj; msgObj["data"] = dataObj; msgObj["msgType"] = "3027"; QJsonObject rootObj; rootObj["deviceId"] = deviceSn; rootObj["messageId"] = "1933039995430551552"; // 修复:将QByteArray转换为QString rootObj["msg"] = QString::fromUtf8(QJsonDocument(msgObj).toJson(QJsonDocument::Compact)); rootObj["timestamp"] = 2147483647; QString message = QJsonDocument(rootObj).toJson(QJsonDocument::Compact); // 发送到指定主题 QString topic = QString("iot/10045/%1/message/adviceDevice").arg(deviceSn); if (mqttClient->publish(topic, message)) { messageDisplay->append(QString("[%1] 发送全部点亮指令到设备 %2") .arg(QDateTime::currentDateTime().toString("hh:mm:ss")) .arg(deviceSn)); messageDisplay->append(QString("主题: %1").arg(topic)); messageDisplay->append(QString("参数: 颜色=%1, 闪烁=%2, 间隔=%3s, 时长=%4s, 声音=%5") .arg(colorCombo->currentText()) .arg(flashCombo->currentText()) .arg(flashInterval) .arg(lightDuration) .arg(soundCombo->currentText())); } else { messageDisplay->append(QString("[%1] 发送全部点亮指令失败") .arg(QDateTime::currentDateTime().toString("hh:mm:ss"))); } } void MainWindow::onMqttConnected() { qDebug() << "MQTT连接成功"; updateConnectionStatus(true); // 连接成功后,如果设备SN不为空,则订阅主题 QString deviceSn = deviceSnEdit->text().trimmed(); if (!deviceSn.isEmpty()) { qDebug() << "重新订阅"; resubscribeTopics(); } } void MainWindow::onMqttDisconnected() { messageDisplay->append(QString("[%1] MQTT连接断开") .arg(QDateTime::currentDateTime().toString("hh:mm:ss"))); updateConnectionStatus(false); } void MainWindow::onMqttError(const QString &error) { messageDisplay->append(QString("[%1] MQTT错误: %2") .arg(QDateTime::currentDateTime().toString("hh:mm:ss")) .arg(error)); updateConnectionStatus(false); } void MainWindow::onMessageReceived(const QString &topic, const QString &message) { // 显示接收到的消息 messageDisplay->append(QString("[%1] 收到消息").arg(QDateTime::currentDateTime().toString("hh:mm:ss"))); messageDisplay->append(QString("Topic: %1").arg(topic)); messageDisplay->append(QString("Message: %1").arg(message)); messageDisplay->append("---"); // 处理消息格式问题:提取纯JSON部分 QString jsonMessage = message; QString realTopic = topic; // 如果topic是默认的incoming/topic,尝试从消息中提取真实主题 if (topic == "incoming/topic" && message.contains("{")) { // 查找消息中的主题信息(格式:%topic{json}) if (message.startsWith("%")) { int jsonStart = message.indexOf("{"); if (jsonStart > 1) { realTopic = message.mid(1, jsonStart - 1); // 去掉开头的% messageDisplay->append(QString("[DEBUG] 从消息中提取到真实主题: %1").arg(realTopic)); } } } // 去除开头的单引号 if (jsonMessage.startsWith("'")) { jsonMessage = jsonMessage.mid(1); } // 去除结尾的单引号 if (jsonMessage.endsWith("'")) { jsonMessage = jsonMessage.left(jsonMessage.length() - 1); } // 检查消息是否包含主题信息(格式:%topic{json}或topic{json}) if (jsonMessage.contains("{")) { int jsonStart = jsonMessage.indexOf("{"); if (jsonStart != -1) { jsonMessage = jsonMessage.mid(jsonStart); } } // 解析JSON消息 QJsonParseError error; QJsonDocument doc = QJsonDocument::fromJson(jsonMessage.toUtf8(), &error); if (error.error != QJsonParseError::NoError) { messageDisplay->append(QString("[%1] JSON解析错误: %2") .arg(QDateTime::currentDateTime().toString("hh:mm:ss")) .arg(error.errorString())); return; } QJsonObject root = doc.object(); messageDisplay->append(QString("[%1] JSON解析成功").arg(QDateTime::currentDateTime().toString("hh:mm:ss"))); // 检查是否包含msg字段(优先处理) if (root.contains("msg")) { messageDisplay->append(QString("[%1] 找到msg字段").arg(QDateTime::currentDateTime().toString("hh:mm:ss"))); QJsonValue msgValue = root["msg"]; QJsonObject msgObj; // msg可能是字符串或对象 if (msgValue.isString()) { QJsonDocument msgDoc = QJsonDocument::fromJson(msgValue.toString().toUtf8()); if (!msgDoc.isNull()) { msgObj = msgDoc.object(); } } else if (msgValue.isObject()) { msgObj = msgValue.toObject(); messageDisplay->append(QString("[%1] msg是对象类型").arg(QDateTime::currentDateTime().toString("hh:mm:ss"))); } // 解析data中的baseVersion if (msgObj.contains("data")) { messageDisplay->append(QString("[%1] 找到data字段").arg(QDateTime::currentDateTime().toString("hh:mm:ss"))); QJsonObject dataObj = msgObj["data"].toObject(); if (dataObj.contains("baseVersion")) { QString version = dataObj["baseVersion"].toString(); messageDisplay->append(QString("[%1] 找到baseVersion: %2") .arg(QDateTime::currentDateTime().toString("hh:mm:ss")) .arg(version)); // 确保currentVersionEdit存在 if (currentVersionEdit) { currentVersionEdit->setText(version); messageDisplay->append(QString("[%1] 版本已设置到输入框: %2") .arg(QDateTime::currentDateTime().toString("hh:mm:ss")) .arg(version)); } else { messageDisplay->append(QString("[%1] 错误: currentVersionEdit为空") .arg(QDateTime::currentDateTime().toString("hh:mm:ss"))); } statusLabel->setText(QString("获取到当前版本: %1").arg(version)); return; } } } // 检查是否包含resource字段(直接在根对象中) if (root.contains("resource")) { QString resource = root["resource"].toString(); QString version = resource.trimmed(); messageDisplay->append(QString("[%1] 找到resource字段: %2") .arg(QDateTime::currentDateTime().toString("hh:mm:ss")) .arg(version)); if (currentVersionEdit) { currentVersionEdit->setText(version); messageDisplay->append(QString("[%1] 版本已设置到输入框: %2") .arg(QDateTime::currentDateTime().toString("hh:mm:ss")) .arg(version)); } statusLabel->setText(QString("获取到当前版本: %1").arg(version)); return; } // 检查是否是light/report主题(使用提取的真实主题) if (realTopic.contains("/light/report")) { messageDisplay->append("[DEBUG] 检测到light/report消息,开始解析SN"); processLightReportMessage(jsonMessage); } // 检查是否是station/report主题且有待处理的OTA操作 if (otaOperationPending && (realTopic.contains("/station/report") || realTopic.contains("/resource/report"))) { QString operationType = isUpgradeOperation ? "升级" : "降级"; QString successMessage = QString("OTA%1成功 版本: %2") .arg(operationType) .arg(pendingOtaVersion); messageDisplay->append(QString("[%1] %2") .arg(QDateTime::currentDateTime().toString("hh:mm:ss")) .arg(successMessage)); // 重置OTA状态 otaOperationPending = false; pendingOtaVersion.clear(); isUpgradeOperation = false; statusLabel->setText(successMessage); } } void MainWindow::processOtaMessage(const QJsonObject &otaData) { QString version = otaData["version"].toString(); QString zipPath = otaData["zipPath"].toString(); int installType = otaData["installType"].toInt(); int immediately = otaData["immediately"].toInt(); otaStatusLabel->setText(QString("收到OTA任务: 版本 %1").arg(version)); } void MainWindow::onOtaUpgradeClicked() { // 获取设备SN QString deviceSn = deviceSnEdit->text().trimmed(); if (deviceSn.isEmpty()) { QMessageBox::warning(this, "警告", "请先输入设备SN"); return; } // 设置OTA状态跟踪 otaOperationPending = true; pendingOtaVersion = "1.1.41"; isUpgradeOperation = true; // 构造符合您格式的OTA升级消息 QJsonObject data; data["msgType"] = "1014"; QJsonObject otaData; otaData["needWifi"] = 2; otaData["zipPath"] = "http://180.163.74.83:8000/tx_ota_1.1.41.zip"; otaData["installType"] = 2; otaData["immediately"] = 0; otaData["installTime"] = QDateTime::currentMSecsSinceEpoch() + 300000; // 5分钟后安装 otaData["version"] = "1.1.41"; data["data"] = otaData; QJsonObject root; root["deviceId"] = deviceSn; // 使用输入的设备SN root["header"] = QJsonValue::Null; root["messageId"] = QString::number(QDateTime::currentMSecsSinceEpoch()); root["msg"] = QString::fromUtf8(QJsonDocument(data).toJson(QJsonDocument::Compact)); root["productId"] = ""; root["timestamp"] = QDateTime::currentMSecsSinceEpoch(); QJsonDocument doc(root); QString message = QString::fromUtf8(doc.toJson(QJsonDocument::Compact)); // 构建正确的MQTT主题 QString topic = QString("iot/10045/%1/message/adviceDevice").arg(deviceSn); if (mqttClient && mqttClient->isConnected()) { mqttClient->publish(topic, message); statusLabel->setText(QString("已发送OTA升级命令到设备 %1 (版本 1.1.41)").arg(deviceSn)); // 显示发送的消息内容 QMessageBox::information(this, "OTA升级命令", QString("已发送升级命令:\n设备SN: %1\n版本: 1.1.41\n主题: %2\n消息: %3") .arg(deviceSn, topic, message)); } else { statusLabel->setText("MQTT未连接,无法发送OTA命令"); // 如果发送失败,重置状态 otaOperationPending = false; } } void MainWindow::onOtaDowngradeClicked() { // 获取设备SN QString deviceSn = deviceSnEdit->text().trimmed(); if (deviceSn.isEmpty()) { QMessageBox::warning(this, "警告", "请先输入设备SN"); return; } // 设置OTA状态跟踪 otaOperationPending = true; pendingOtaVersion = "1.1.40"; isUpgradeOperation = false; // 构造符合您格式的OTA降级消息 QJsonObject data; data["msgType"] = "1014"; QJsonObject otaData; otaData["needWifi"] = 2; otaData["zipPath"] = "http://180.163.74.83:8000/tx_ota_1.1.40.zip"; otaData["installType"] = 2; otaData["immediately"] = 0; otaData["installTime"] = QDateTime::currentMSecsSinceEpoch() + 300000; // 5分钟后安装 otaData["version"] = "1.1.40"; data["data"] = otaData; QJsonObject root; root["deviceId"] = deviceSn; // 使用输入的设备SN root["header"] = QJsonValue::Null; root["messageId"] = QString::number(QDateTime::currentMSecsSinceEpoch()); root["msg"] = QString::fromUtf8(QJsonDocument(data).toJson(QJsonDocument::Compact)); root["productId"] = ""; root["timestamp"] = QDateTime::currentMSecsSinceEpoch(); QJsonDocument doc(root); QString message = QString::fromUtf8(doc.toJson(QJsonDocument::Compact)); // 构建正确的MQTT主题 QString topic = QString("iot/10045/%1/message/adviceDevice").arg(deviceSn); if (mqttClient && mqttClient->isConnected()) { mqttClient->publish(topic, message); statusLabel->setText(QString("已发送OTA降级命令到设备 %1 (版本 1.1.40)").arg(deviceSn)); // 显示发送的消息内容 QMessageBox::information(this, "OTA降级命令", QString("已发送降级命令:\n设备SN: %1\n版本: 1.1.40\n主题: %2\n消息: %3") .arg(deviceSn, topic, message)); } else { statusLabel->setText("MQTT未连接,无法发送OTA命令"); // 如果发送失败,重置状态 otaOperationPending = false; } } void MainWindow::onSearchLightStripClicked() { sendSearchLightStripCommand(); } void MainWindow::sendSearchLightStripCommand() { QString deviceSn = deviceSnEdit->text().trimmed(); if (deviceSn.isEmpty()) { messageDisplay->append(QString("[%1] 错误: 设备SN不能为空") .arg(QDateTime::currentDateTime().toString("hh:mm:ss"))); return; } QString topic = QString("iot/10045/%1/message/adviceDevice").arg(deviceSn); // 构造消息 QJsonObject message; message["deviceId"] = ""; message["header"] = QJsonValue::Null; message["messageId"] = "1958474134031921152"; message["productId"] = ""; message["timestamp"] = QDateTime::currentMSecsSinceEpoch(); // 构造msg字段 QJsonObject msgData; msgData["scene"] = "1"; msgData["timeout"] = "120"; QJsonObject msgObj; msgObj["data"] = msgData; msgObj["msgType"] = "5005"; QJsonDocument msgDoc(msgObj); message["msg"] = QString::fromUtf8(msgDoc.toJson(QJsonDocument::Compact)); QJsonDocument doc(message); QString jsonString = doc.toJson(QJsonDocument::Compact); // 发送消息 if (mqttClient->publish(topic, jsonString)) { messageDisplay->append(QString("[%1] 发送搜索灯条命令") .arg(QDateTime::currentDateTime().toString("hh:mm:ss"))); messageDisplay->append(QString("Topic: %1").arg(topic)); messageDisplay->append(QString("Message: %1").arg(jsonString)); messageDisplay->append("---"); // 弹出提示框 QMessageBox::information(this, "搜索灯条", "不要做其他指令,正在搜索灯条,请等待2分钟返回结果..."); } else { messageDisplay->append(QString("[%1] 发送搜索灯条命令失败") .arg(QDateTime::currentDateTime().toString("hh:mm:ss"))); messageDisplay->append("---"); // 弹出错误提示框 QMessageBox::warning(this, "错误", "发送搜索灯条命令失败,请检查网络连接"); } } void MainWindow::sendOtaCommand(const QString& version, bool isUpgrade) { QJsonObject msg; msg["action"] = isUpgrade ? "upgrade" : "downgrade"; msg["version"] = version; QJsonObject root; root["type"] = "ota_command"; root["msg"] = QString::fromUtf8(QJsonDocument(msg).toJson(QJsonDocument::Compact)); QJsonDocument doc(root); QString message = QString::fromUtf8(doc.toJson(QJsonDocument::Compact)); if (mqttClient && mqttClient->isConnected()) { mqttClient->publish("device/ota/command", message); statusLabel->setText(QString("已发送OTA%1命令 (版本 %2)").arg(isUpgrade ? "升级" : "降级", version)); } else { statusLabel->setText("MQTT未连接,无法发送OTA命令"); } } void MainWindow::onGetVersionClicked() { // 获取设备SN QString deviceSn = deviceSnEdit->text().trimmed(); if (deviceSn.isEmpty()) { QMessageBox::warning(this, "警告", "请先输入设备SN"); return; } // 构造获取版本的消息 QJsonObject data; data["msgType"] = "2335"; QJsonObject cmdData; cmdData["cmd"] = "cat /userdata/tx_version"; data["data"] = cmdData; QJsonObject root; root["deviceId"] = deviceSn; root["header"] = QJsonValue::Null; root["messageId"] = QString::number(QDateTime::currentMSecsSinceEpoch()); root["msg"] = QString::fromUtf8(QJsonDocument(data).toJson(QJsonDocument::Compact)); root["productId"] = ""; root["timestamp"] = QDateTime::currentMSecsSinceEpoch(); QJsonDocument doc(root); QString message = QString::fromUtf8(doc.toJson(QJsonDocument::Compact)); // 使用正确的发送主题 QString topic = QString("iot/10045/%1/message/adviceDevice").arg(deviceSn); if (mqttClient && mqttClient->isConnected()) { mqttClient->publish(topic, message); statusLabel->setText(QString("已发送获取版本命令到设备 %1").arg(deviceSn)); } else { statusLabel->setText("MQTT未连接,无法发送命令"); } } void MainWindow::onDeviceSnChanged() { // 只有在MQTT连接状态下才重新订阅主题 if (mqttClient && mqttClient->isConnected()) { resubscribeTopics(); } } void MainWindow::resubscribeTopics() { if (!mqttClient || !mqttClient->isConnected()) { return; } QString deviceSn = deviceSnEdit->text().trimmed(); if (deviceSn.isEmpty()) { qDebug() << "设备SN为空,不进行订阅"; return; } qDebug() << "重新订阅主题,设备SN:" << deviceSn; // 订阅相关主题 mqttClient->subscribe(QString("iot/10045/%1/light/report").arg(deviceSn)); mqttClient->subscribe(QString("iot/10045/%1/station/report").arg(deviceSn)); mqttClient->subscribe(QString("iot/10045/%1/version/report").arg(deviceSn)); mqttClient->subscribe(QString("iot/10045/%1/response").arg(deviceSn)); mqttClient->subscribe(QString("iot/10045/%1/resource/report").arg(deviceSn)); } void MainWindow::updateConnectionStatus(bool connected) { connectBtn->setEnabled(!connected); disconnectBtn->setEnabled(connected); sendLightAllBtn->setEnabled(connected); // 控制设备SN输入框:只有连接成功后才能输入 deviceSnEdit->setEnabled(connected); // 新增:连接成功后禁用连接参数输入框,断开后重新启用 usernameEdit->setEnabled(!connected); portEdit->setEnabled(!connected); passwordEdit->setEnabled(!connected); brokerEdit->setEnabled(!connected); // 同时也禁用服务器地址输入框 // 修改连接按钮样式:连接成功后设置灰色背景,断开后恢复绿色 if (connected) { connectBtn->setStyleSheet("QPushButton { background-color: #cccccc; color: #666666; font-weight: bold; padding: 8px 16px; border-radius: 4px; }"); statusLabel->setText("已连接"); statusLabel->setStyleSheet("color: green;"); } else { connectBtn->setStyleSheet("QPushButton { background-color: #4CAF50; color: white; font-weight: bold; padding: 8px 16px; border-radius: 4px; }"); statusLabel->setText("未连接"); statusLabel->setStyleSheet("color: red;"); } } void MainWindow::processLightReportMessage(const QString &message) { messageDisplay->append(QString("[DEBUG] 开始处理light/report消息: %1").arg(message)); QJsonParseError error; QJsonDocument doc = QJsonDocument::fromJson(message.toUtf8(), &error); if (error.error != QJsonParseError::NoError) { messageDisplay->append(QString("[ERROR] JSON解析失败: %1").arg(error.errorString())); return; } QJsonObject rootObj = doc.object(); messageDisplay->append(QString("[DEBUG] 根对象键: %1").arg(QStringList(rootObj.keys()).join(", "))); QJsonObject msgObj = rootObj["msg"].toObject(); messageDisplay->append(QString("[DEBUG] msg对象键: %1").arg(QStringList(msgObj.keys()).join(", "))); QJsonObject dataObj = msgObj["data"].toObject(); messageDisplay->append(QString("[DEBUG] data对象键: %1").arg(QStringList(dataObj.keys()).join(", "))); QJsonArray lightsArray = dataObj["lights"].toArray(); messageDisplay->append(QString("[DEBUG] lights数组长度: %1").arg(lightsArray.size())); int newSnCount = 0; for (const QJsonValue &lightValue : lightsArray) { QJsonObject lightObj = lightValue.toObject(); QString sn = lightObj["sn"].toString(); QString battery = lightObj["battery"].toString(); messageDisplay->append(QString("[DEBUG] 处理灯条SN: %1, 电量: %2").arg(sn, battery)); if (!sn.isEmpty() && !uniqueSnSet.contains(sn)) { messageDisplay->append(QString("[DEBUG] 添加新SN到列表: %1").arg(sn)); addSnToList(sn); newSnCount++; } else if (sn.isEmpty()) { messageDisplay->append("[DEBUG] SN为空,跳过"); } else if (uniqueSnSet.contains(sn)) { messageDisplay->append(QString("[DEBUG] SN已存在,跳过: %1").arg(sn)); } // 更新电量信息到LightStripManager if (!sn.isEmpty() && !battery.isEmpty() && lightStripManager) { lightStripManager->updateBatteryInfo(sn, battery); messageDisplay->append(QString("[DEBUG] 更新电量信息: SN=%1, 电量=%2").arg(sn, battery)); } } if (newSnCount > 0) { messageDisplay->append(QString("[INFO] 新发现 %1 个灯条SN").arg(newSnCount)); saveSnList(); } else { messageDisplay->append("[DEBUG] 没有发现新的灯条SN"); } } void MainWindow::addSnToList(const QString &sn) { if (uniqueSnSet.contains(sn)) { return; } uniqueSnSet.insert(sn); // 更新主窗口的计数显示 snCountLabel->setText(QString("已发现灯条: %1 个").arg(uniqueSnSet.size())); // 如果灯条管理器已打开,同步添加(但不重复更新MainWindow的计数) if (lightStripManager) { // 临时断开信号连接,避免重复更新 disconnect(lightStripManager, &LightStripManager::snCountChanged, this, nullptr); lightStripManager->addSnToList(sn); // 重新连接信号 connect(lightStripManager, &LightStripManager::snCountChanged, this, [this](int count) { snCountLabel->setText(QString("已发现灯条: %1 个").arg(count)); }); } // 移除这里的saveSnList()调用,让调用方决定何时保存 // saveSnList(); } void MainWindow::saveSnList() { QStringList snList = uniqueSnSet.values(); settings->setValue("lightStripSnList", snList); settings->sync(); } void MainWindow::loadSnList() { QStringList snList = settings->value("lightStripSnList").toStringList(); for (const QString &sn : snList) { addSnToList(sn); } } void MainWindow::onClearSnListClicked() { // 检查snHorizontalLayout是否存在,避免段错误 if (snHorizontalLayout) { // 清空所有灯条widget QLayoutItem *item; while ((item = snHorizontalLayout->takeAt(0)) != nullptr) { if (item->widget()) { delete item->widget(); } delete item; } } // 清空复选框列表和SN集合 lightStripCheckBoxes.clear(); uniqueSnSet.clear(); // 更新计数显示 if (snCountLabel) { snCountLabel->setText("已发现灯条: 0 个"); } // 不再调用saveSnList(),让LightStripManager管理数据 // saveSnList(); // 删除这行 // 如果LightStripManager存在,调用公有的清空方法 if (lightStripManager) { lightStripManager->clearAllData(); } } void MainWindow::openLightStripManager() { if (!lightStripManager) { lightStripManager = new LightStripManager(this); // 连接信号 connect(lightStripManager, &LightStripManager::lightControlRequested, this, [this](const QStringList &sns, const QString &color, bool flash, int interval, int duration, bool sound) { // 处理点亮控制请求 // 实现MQTT发送逻辑 // 检查MQTT连接状态 if (!mqttClient->isConnected()) { qDebug() << "MQTT未连接,无法发送灯条控制命令"; QMessageBox::warning(this, "警告", "MQTT未连接,请先连接MQTT服务器"); return; } // 获取设备SN(从界面获取) QString deviceSn = deviceSnEdit->text().trimmed(); if (deviceSn.isEmpty()) { QMessageBox::warning(this, "警告", "请先输入需要测试的设备SN"); return; } // 构建内层msg数据 QJsonObject msgData; QRegularExpression re("\\d+"); QRegularExpressionMatch match = re.match(color); if (match.hasMatch()) { msgData["color"] = match.captured(0); // 提取到的数字字符串 } else { msgData["color"] = "0"; // 默认值 } msgData["flash"] = flash ? "1" : "0"; msgData["flashInterval"] = QString::number(interval); msgData["lightDuration"] = QString::number(duration); msgData["scene"] = "3"; // 根据业务需求设置 msgData["sound"] = sound ? "1" : "0"; // 构建lights数组 QJsonArray lightsArray; for (const QString &sn : sns) { QJsonObject lightObj; lightObj["sn"] = sn; lightsArray.append(lightObj); } msgData["lights"] = lightsArray; // 构建完整的msg对象 QJsonObject fullMsg; fullMsg["data"] = msgData; fullMsg["msgType"] = "3015"; // 根据业务协议设置 // 转换msg为JSON字符串 QJsonDocument msgDoc(fullMsg); QString msgString = msgDoc.toJson(QJsonDocument::Compact); // 构建最外层消息 QJsonObject outerMessage; outerMessage["deviceId"] = ""; // 根据需要设置 outerMessage["messageId"] = QString::number(QDateTime::currentMSecsSinceEpoch()); // 生成唯一消息ID outerMessage["msg"] = msgString; outerMessage["timestamp"] = QDateTime::currentMSecsSinceEpoch(); // 转换为最终JSON字符串 QJsonDocument finalDoc(outerMessage); QString finalMessage = finalDoc.toJson(QJsonDocument::Compact); // 构建MQTT主题 QString topic = QString("iot/10045/%1/message/adviceDevice").arg(deviceSn); // 发送MQTT消息 bool success = mqttClient->publish(topic, finalMessage); if (success) { qDebug() << "成功发送灯条控制命令"; qDebug() << "主题:" << topic; qDebug() << "消息:" << finalMessage; // 显示成功提示 QString resultMessage = QString("已向设备 %1 发送控制命令,控制 %2 个灯条") .arg(deviceSn) .arg(sns.size()); QMessageBox::information(this, "成功", resultMessage); } else { qDebug() << "发送灯条控制命令失败"; QMessageBox::warning(this, "错误", "发送MQTT消息失败,请检查网络连接"); } }); connect(lightStripManager, &LightStripManager::snSelectionChanged, this, [this](const QStringList &selectedSns) { // 处理选择变化 qDebug() << "Selected SNs changed:" << selectedSns; }); // 新增:连接数量变化信号 connect(lightStripManager, &LightStripManager::snCountChanged, this, [this](int count) { // 同步更新MainWindow中的灯条数量显示 snCountLabel->setText(QString("已发现灯条: %1 个").arg(count)); }); // 新增:连接身份信息绑定信号 connect(lightStripManager, &LightStripManager::identityBindingRequested, this, [this](const QString &sn, const QString &label1, const QString &label2, const QString &label3) { // 处理身份信息绑定请求 // 检查MQTT连接状态 if (!mqttClient->isConnected()) { qDebug() << "MQTT未连接,无法发送身份信息绑定命令"; QMessageBox::warning(this, "警告", "MQTT未连接,请先连接MQTT服务器"); return; } // 获取设备SN(从界面获取) QString deviceSn = deviceSnEdit->text().trimmed(); if (deviceSn.isEmpty()) { QMessageBox::warning(this, "警告", "请先输入需要测试的设备SN"); return; } // 构建身份信息绑定消息 QJsonObject msgData; msgData["sn"] = sn; msgData["label1"] = label1; msgData["label2"] = label2; msgData["label3"] = label3; // 构建完整的msg对象 QJsonObject fullMsg; fullMsg["data"] = msgData; fullMsg["msgType"] = "3022"; // 身份信息绑定消息类型 // 转换msg为JSON字符串 QJsonDocument msgDoc(fullMsg); QString msgString = msgDoc.toJson(QJsonDocument::Compact); // 构建最外层消息 QJsonObject outerMessage; outerMessage["deviceId"] = ""; outerMessage["messageId"] = QString::number(QDateTime::currentMSecsSinceEpoch()); outerMessage["msg"] = msgString; outerMessage["timestamp"] = QDateTime::currentMSecsSinceEpoch(); // 转换为最终JSON字符串 QJsonDocument finalDoc(outerMessage); QString finalMessage = finalDoc.toJson(QJsonDocument::Compact); // 构建MQTT主题 QString topic = QString("iot/10045/%1/message/adviceDevice").arg(deviceSn); // 发送MQTT消息 bool success = mqttClient->publish(topic, finalMessage); if (success) { qDebug() << "成功发送身份信息绑定命令"; qDebug() << "主题:" << topic; qDebug() << "消息:" << finalMessage; // 在消息显示区域显示发送的消息 messageDisplay->append(QString("[%1] 发送身份信息绑定到设备 %2") .arg(QDateTime::currentDateTime().toString("hh:mm:ss")) .arg(deviceSn)); messageDisplay->append(QString("主题: %1").arg(topic)); messageDisplay->append(QString("灯条SN: %1, Label1: %2, Label2: %3, Label3: %4") .arg(sn).arg(label1).arg(label2).arg(label3)); } else { qDebug() << "发送身份信息绑定命令失败"; QMessageBox::warning(this, "错误", "发送MQTT消息失败,请检查网络连接"); } }); // 新增:连接分组点亮信号 connect(lightStripManager, &LightStripManager::groupLightRequested, this, [this](const QString &label1, const QString &label2, const QString &label3, int rule1, int rule2, int rule3, const QString &color, int flash, int duration, bool sound, int flashInterval) { // 检查MQTT连接状态 if (!mqttClient->isConnected()) { qDebug() << "MQTT未连接,无法发送分组点亮命令"; QMessageBox::warning(this, "警告", "MQTT未连接,请先连接MQTT服务器"); return; } // 获取设备SN QString deviceSn = deviceSnEdit->text().trimmed(); if (deviceSn.isEmpty()) { QMessageBox::warning(this, "警告", "请先输入需要测试的设备SN"); return; } // 构建labelConfig对象 QJsonObject labelConfig; labelConfig["label1"] = label1; labelConfig["label2"] = label2; labelConfig["label3"] = label3; labelConfig["label1Rule"] = QString("%1").arg(rule1, 2, 10, QChar('0')); labelConfig["label2Rule"] = QString("%1").arg(rule2, 2, 10, QChar('0')); labelConfig["label3Rule"] = QString("%1").arg(rule3, 2, 10, QChar('0')); // 构建分组点亮消息数据 QJsonObject msgData; msgData["labelConfig"] = labelConfig; msgData["color"] = color; msgData["sound"] = sound ? "1" : "0"; msgData["flash"] = QString::number(flash); msgData["flashInterval"] = QString::number(flashInterval); msgData["lightDuration"] = QString::number(duration); // 构建完整的msg对象 QJsonObject fullMsg; fullMsg["msgType"] = "3023"; fullMsg["data"] = msgData; // 转换msg为JSON字符串 QJsonDocument msgDoc(fullMsg); QString msgString = msgDoc.toJson(QJsonDocument::Compact); // 构建最外层消息 QJsonObject outerMessage; outerMessage["deviceId"] = ""; outerMessage["messageId"] = QString::number(QDateTime::currentMSecsSinceEpoch()); outerMessage["msg"] = msgString; outerMessage["timestamp"] = QDateTime::currentMSecsSinceEpoch(); // 转换为最终JSON字符串 QJsonDocument finalDoc(outerMessage); QString finalMessage = finalDoc.toJson(QJsonDocument::Compact); // 构建MQTT主题 QString topic = QString("iot/10045/%1/message/adviceDevice").arg(deviceSn); // 发送MQTT消息 bool success = mqttClient->publish(topic, finalMessage); if (success) { qDebug() << "成功发送分组点亮命令"; qDebug() << "主题:" << topic; qDebug() << "消息:" << finalMessage; // 在消息显示区域显示发送的消息 messageDisplay->append(QString("[%1] 发送分组点亮命令到设备 %2") .arg(QDateTime::currentDateTime().toString("hh:mm:ss")) .arg(deviceSn)); messageDisplay->append(QString("主题: %1").arg(topic)); messageDisplay->append(QString("匹配规则: Label1=%1(rule:%2), Label2=%3(rule:%4), Label3=%5(rule:%6)") .arg(label1).arg(rule1, 2, 10, QChar('0')) .arg(label2).arg(rule2, 2, 10, QChar('0')) .arg(label3).arg(rule3, 2, 10, QChar('0'))); } else { qDebug() << "发送分组点亮命令失败"; QMessageBox::warning(this, "错误", "发送MQTT消息失败,请检查网络连接"); } }); // 连接关闭信号 connect(lightStripManager, &QWidget::destroyed, this, &MainWindow::onLightStripManagerClosed); // 同步当前的SN列表到管理器 QStringList snList = uniqueSnSet.values(); lightStripManager->syncSnListFromMainWindow(snList); } lightStripManager->show(); lightStripManager->raise(); lightStripManager->activateWindow(); } void MainWindow::onLightStripManagerClosed() { // 灯条管理器关闭时的处理 lightStripManager = nullptr; qDebug() << "Light strip manager closed"; } QString MainWindow::getDeviceSn() const { return deviceSnEdit->text().trimmed(); } bool MainWindow::isMqttConnected() const { return mqttClient && mqttClient->isConnected(); } void MainWindow::publishMqttMessage(const QString &topic, const QString &message) { if (mqttClient && mqttClient->isConnected()) { mqttClient->publish(topic, message); messageDisplay->append(QString("[%1] 发送消息到主题: %2") .arg(QDateTime::currentDateTime().toString("hh:mm:ss")) .arg(topic)); } else { QMessageBox::warning(this, "错误", "MQTT未连接,无法发送消息"); } } void MainWindow::createMenus() { // 直接创建菜单,不使用成员变量 QMenu *helpMenu = menuBar()->addMenu("帮助(&H)"); QAction *aboutAction = helpMenu->addAction("关于程序(&A)"); connect(aboutAction, &QAction::triggered, this, &MainWindow::showAbout); QAction *useGuideAction = helpMenu->addAction("使用说明(&U)"); connect(useGuideAction, &QAction::triggered, this, &MainWindow::showUseGuide); // 添加分隔线 helpMenu->addSeparator(); // 添加手动检查更新菜单项 QAction *checkUpdateAction = helpMenu->addAction("检查更新(&C)"); connect(checkUpdateAction, &QAction::triggered, this, &MainWindow::checkForUpdates); // 添加更新设置菜单项 QAction *updateSettingsAction = helpMenu->addAction("更新设置(&S)"); connect(updateSettingsAction, &QAction::triggered, this, &MainWindow::onUpdateSettingsClicked); } void MainWindow::showAbout() { QMessageBox::about(this, "关于程序", "兔喜MQTT测试程序\n\n" "版本: 1.5.2\n" "构建日期: 2025-09-12\n\n" "功能特性:\n" "• 新增灯条备份恢复和程序ota升级功能\n" "• 修复清空全部sn未生效的问题\n" "• 修复label匹配值错误的问题\n" "• 匹配系统颜色。修复浅色模式下字体看不清\n" "• 增加窗口版本显示\n"); } void MainWindow::showUseGuide() { QMessageBox::about(this, "使用说明", "切换主题后看不清文字,请重启应用!!\n" "1. 连接MQTT服务器(用户名默认:TJ251679787196)\n" "2. 输入需要测试的设备SN\n" "3. 首次使用先升级版本,1.1.16及之前的版本可能不支持升级\n" "4. 可以正常操作其他指令,如:查询版本、全部点亮、搜索灯带、灯带SN管理\n\n" ); } // 新增:检测系统主题并返回适合的文字颜色 QString MainWindow::getTextColorForTheme() const { // 获取系统调色板 QPalette palette = QApplication::palette(); // 检查窗口背景色的亮度来判断是否为深色主题 QColor backgroundColor = palette.color(QPalette::Window); // 计算亮度 (使用相对亮度公式) double luminance = (0.299 * backgroundColor.red() + 0.587 * backgroundColor.green() + 0.114 * backgroundColor.blue()) / 255.0; // 如果背景较暗(亮度小于0.5),使用白色文字;否则使用黑色文字 return (luminance < 0.5) ? "white" : "black"; } // 更新功能实现 void MainWindow::checkForUpdates() { if (updateCheckReply) { updateCheckReply->abort(); updateCheckReply = nullptr; } QNetworkRequest request{QUrl(updateServerUrl)}; request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); updateCheckReply = updateNetworkManager->get(request); connect(updateCheckReply, &QNetworkReply::finished, this, &MainWindow::onUpdateCheckFinished); } void MainWindow::onUpdateCheckFinished() { if (!updateCheckReply) return; if (updateCheckReply->error() == QNetworkReply::NoError) { QByteArray data = updateCheckReply->readAll(); QJsonDocument doc = QJsonDocument::fromJson(data); QJsonObject obj = doc.object(); QString latestVersion = obj["version"].toString(); QString changelog = obj["changelog"].toString(); // 根据平台选择合适的下载URL QString downloadUrl; #ifdef Q_OS_WIN downloadUrl = obj["downloadUrlWindows"].toString(); if (downloadUrl.isEmpty()) { // 如果没有Windows专用URL,回退到通用URL downloadUrl = obj["downloadUrl"].toString(); } #else downloadUrl = obj["downloadUrl"].toString(); if (downloadUrl.isEmpty()) { // 如果没有Linux专用URL,尝试Windows URL作为备选 downloadUrl = obj["downloadUrlWindows"].toString(); } #endif if (compareVersions(currentVersion, latestVersion) < 0) { showUpdateDialog(latestVersion, downloadUrl, changelog); } else { QMessageBox::information(this, "检查更新", "当前已是最新版本!"); } } else { QMessageBox::warning(this, "检查更新失败", QString("无法连接到更新服务器:%1").arg(updateCheckReply->errorString())); } updateCheckReply->deleteLater(); updateCheckReply = nullptr; } void MainWindow::showUpdateDialog(const QString &version, const QString &downloadUrl, const QString &changelog) { QMessageBox msgBox(this); msgBox.setWindowTitle("发现新版本"); msgBox.setText(QString("发现新版本 %1").arg(version)); msgBox.setDetailedText(changelog); msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); QAbstractButton *yesButton = msgBox.button(QMessageBox::Yes); QAbstractButton *noButton = msgBox.button(QMessageBox::No); if (yesButton) yesButton->setText("立即更新"); if (noButton) noButton->setText("稍后更新"); if (msgBox.exec() == QMessageBox::Yes) { downloadUpdate(downloadUrl); } } void MainWindow::downloadUpdate(const QString &downloadUrl) { if (updateDownloadReply) { updateDownloadReply->abort(); updateDownloadReply = nullptr; } QNetworkRequest request{QUrl(downloadUrl)}; updateDownloadReply = updateNetworkManager->get(request); connect(updateDownloadReply, &QNetworkReply::downloadProgress, this, &MainWindow::onUpdateDownloadProgress); connect(updateDownloadReply, &QNetworkReply::finished, this, &MainWindow::onUpdateDownloadFinished); // 显示下载进度对话框 QProgressDialog *progressDialog = new QProgressDialog("正在下载更新...", "取消", 0, 100, this); progressDialog->setWindowModality(Qt::WindowModal); progressDialog->show(); connect(progressDialog, &QProgressDialog::canceled, [this]() { if (updateDownloadReply) { updateDownloadReply->abort(); } }); } void MainWindow::onUpdateDownloadProgress(qint64 bytesReceived, qint64 bytesTotal) { if (bytesTotal > 0) { int progress = static_cast((bytesReceived * 100) / bytesTotal); // 更新进度条 QProgressDialog *progressDialog = findChild(); if (progressDialog) { progressDialog->setValue(progress); } } } void MainWindow::onUpdateDownloadFinished() { if (!updateDownloadReply) return; QProgressDialog *progressDialog = findChild(); if (progressDialog) { progressDialog->close(); progressDialog->deleteLater(); } if (updateDownloadReply->error() == QNetworkReply::NoError) { // 保存下载的文件 QByteArray data = updateDownloadReply->readAll(); QString fileName = QUrl(updateDownloadReply->url()).fileName(); // 确保文件名不为空 if (fileName.isEmpty()) { fileName = "QtDemo.exe"; // 默认文件名 } downloadedUpdatePath = QStandardPaths::writableLocation(QStandardPaths::TempLocation) + "/" + fileName; // 如果文件已存在,先删除 if (QFile::exists(downloadedUpdatePath)) { QFile::remove(downloadedUpdatePath); } QFile file(downloadedUpdatePath); if (file.open(QIODevice::WriteOnly)) { qint64 bytesWritten = file.write(data); file.close(); // 验证文件是否完整写入 if (bytesWritten != data.size()) { QMessageBox::critical(this, "下载失败", QString("文件写入不完整\n期望:%1 字节\n实际:%2 字节") .arg(data.size()).arg(bytesWritten)); QFile::remove(downloadedUpdatePath); // 清理不完整的文件 return; } // 验证下载的文件是否存在且可读 QFileInfo fileInfo(downloadedUpdatePath); if (!fileInfo.exists() || !fileInfo.isReadable()) { QMessageBox::critical(this, "下载失败", QString("下载的文件无法访问:%1").arg(downloadedUpdatePath)); return; } QMessageBox msgBox(this); msgBox.setWindowTitle("下载完成"); msgBox.setText(QString("更新包下载完成(%1 MB),是否立即安装?") .arg(QString::number(fileInfo.size() / 1024.0 / 1024.0, 'f', 2))); msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); QAbstractButton *yesButton = msgBox.button(QMessageBox::Yes); QAbstractButton *noButton = msgBox.button(QMessageBox::No); if (yesButton) yesButton->setText("立即安装"); if (noButton) noButton->setText("稍后安装"); if (msgBox.exec() == QMessageBox::Yes) { installUpdate(); } } else { QMessageBox::critical(this, "下载失败", QString("无法保存更新文件到:%1\n错误:%2") .arg(downloadedUpdatePath) .arg(file.errorString())); } } else { QMessageBox::warning(this, "下载失败", QString("下载更新失败:%1").arg(updateDownloadReply->errorString())); } updateDownloadReply->deleteLater(); updateDownloadReply = nullptr; } void MainWindow::installUpdate() { if (downloadedUpdatePath.isEmpty() || !QFile::exists(downloadedUpdatePath)) { QMessageBox::warning(this, "安装失败", "找不到更新文件!"); return; } // 根据文件类型执行不同的安装逻辑 if (downloadedUpdatePath.endsWith(".deb")) { // Debian包安装 QProcess::startDetached("pkexec", QStringList() << "dpkg" << "-i" << downloadedUpdatePath); } else if (downloadedUpdatePath.endsWith(".rpm")) { // RPM包安装 QProcess::startDetached("pkexec", QStringList() << "rpm" << "-i" << downloadedUpdatePath); } else if (downloadedUpdatePath.endsWith(".AppImage")) { // AppImage替换 QString currentPath = QCoreApplication::applicationFilePath(); QString backupPath = currentPath + ".backup"; // 备份当前程序 if (!QFile::copy(currentPath, backupPath)) { QString errorDetail = QFile(currentPath).errorString(); if (errorDetail.isEmpty()) { errorDetail = "Unknown error"; } // 在Windows上,可能是因为文件正在使用中 #ifdef Q_OS_WIN // 尝试检查文件是否被锁定 QFile testFile(currentPath); if (!testFile.open(QIODevice::ReadOnly)) { errorDetail += "\n可能原因:程序文件正在使用中,请确保没有其他实例在运行"; } else { testFile.close(); // 检查目标目录权限 QFileInfo dirInfo(QFileInfo(backupPath).absolutePath()); if (!dirInfo.isWritable()) { errorDetail += "\n可能原因:目标目录没有写入权限,请以管理员身份运行"; } } #endif QMessageBox::warning(this, "安装失败", QString("无法备份当前程序:%1").arg(errorDetail)); return; } // 替换程序文件 QFile::remove(currentPath); QFile::copy(downloadedUpdatePath, currentPath); QFile::setPermissions(currentPath, QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner | QFile::ReadGroup | QFile::ExeGroup | QFile::ReadOther | QFile::ExeOther); QMessageBox::information(this, "安装完成", "更新安装完成,请重启程序!"); QApplication::quit(); } else { // 普通可执行文件替换(如QtDemo) QString currentPath = QCoreApplication::applicationFilePath(); QString currentDir = QFileInfo(currentPath).absolutePath(); QString currentFileName = QFileInfo(currentPath).fileName(); QString backupPath = currentPath + ".backup"; QString tempPath = currentPath + ".new"; // 备份当前程序 #ifdef Q_OS_WIN // Windows平台:正在运行的exe文件被锁定,需要特殊处理 // 先检查备份文件是否已存在,如果存在则删除 if (QFile::exists(backupPath)) { if (!QFile::remove(backupPath)) { QMessageBox::warning(this, "安装失败", QString("无法删除旧的备份文件:%1\n错误:%2") .arg(backupPath) .arg(QFile(backupPath).errorString())); return; } } // 在Windows上,尝试使用系统命令复制正在运行的文件 QProcess copyProcess; QString copyCmd = QString("copy /Y \"%1\" \"%2\"") .arg(QDir::toNativeSeparators(currentPath)) .arg(QDir::toNativeSeparators(backupPath)); copyProcess.start("cmd.exe", QStringList() << "/c" << copyCmd); copyProcess.waitForFinished(5000); if (copyProcess.exitCode() != 0 || !QFile::exists(backupPath)) { // 如果系统命令也失败,尝试使用Qt的方式,但先尝试解锁文件 QFile currentFile(currentPath); if (!currentFile.copy(backupPath)) { QMessageBox::warning(this, "安装失败", QString("无法备份当前程序\n当前程序:%1\n备份位置:%2\n\n这可能是因为程序正在运行被系统锁定。\n建议:\n1. 关闭所有程序实例\n2. 以管理员身份运行\n3. 或手动复制程序文件进行备份") .arg(currentPath) .arg(backupPath)); return; } } #else // Linux/Unix平台的备份逻辑 // 先检查备份文件是否已存在,如果存在则删除 if (QFile::exists(backupPath)) { if (!QFile::remove(backupPath)) { QMessageBox::warning(this, "安装失败", QString("无法删除旧的备份文件:%1\n错误:%2") .arg(backupPath) .arg(QFile(backupPath).errorString())); return; } } if (!QFile::copy(currentPath, backupPath)) { QString errorDetail = QFile(currentPath).errorString(); if (errorDetail.isEmpty()) { errorDetail = "Unknown error"; } // 添加Linux平台的详细诊断信息 QFileInfo currentFileInfo(currentPath); QFileInfo backupDirInfo(QFileInfo(backupPath).absolutePath()); QString diagnosticInfo = QString("\n\n诊断信息:\n"); diagnosticInfo += QString("- 当前程序路径:%1\n").arg(currentPath); diagnosticInfo += QString("- 备份路径:%1\n").arg(backupPath); diagnosticInfo += QString("- 当前程序是否存在:%1\n").arg(currentFileInfo.exists() ? "是" : "否"); diagnosticInfo += QString("- 当前程序是否可读:%1\n").arg(currentFileInfo.isReadable() ? "是" : "否"); diagnosticInfo += QString("- 备份目录是否可写:%1\n").arg(backupDirInfo.isWritable() ? "是" : "否"); diagnosticInfo += QString("- 当前程序大小:%1 字节\n").arg(currentFileInfo.size()); // 尝试更详细的错误检查 QFile sourceFile(currentPath); QFile targetFile(backupPath); // 检查源文件是否可以打开 if (!sourceFile.open(QIODevice::ReadOnly)) { diagnosticInfo += QString("- 源文件打开失败:%1\n").arg(sourceFile.errorString()); } else { sourceFile.close(); diagnosticInfo += "- 源文件可以正常打开\n"; } // 检查目标文件是否可以创建 if (!targetFile.open(QIODevice::WriteOnly)) { diagnosticInfo += QString("- 目标文件创建失败:%1\n").arg(targetFile.errorString()); } else { targetFile.close(); targetFile.remove(); // 清理测试文件 diagnosticInfo += "- 目标文件可以正常创建\n"; } // 检查是否有权限问题 if (!backupDirInfo.isWritable()) { diagnosticInfo += "\n可能的解决方案:\n"; diagnosticInfo += "1. 检查目录写入权限\n"; diagnosticInfo += "2. 尝试以sudo权限运行程序\n"; diagnosticInfo += "3. 更改程序安装目录的权限"; } // 检查磁盘空间 QStorageInfo storage(backupDirInfo.absolutePath()); if (storage.isValid()) { qint64 availableBytes = storage.bytesAvailable(); qint64 requiredBytes = currentFileInfo.size(); diagnosticInfo += QString("- 可用磁盘空间:%1 字节\n").arg(availableBytes); if (availableBytes < requiredBytes) { diagnosticInfo += "\n错误:磁盘空间不足!"; } } // 尝试使用系统命令作为备选方案 diagnosticInfo += "\n尝试使用系统命令备份...\n"; QProcess cpProcess; QString cpCmd = QString("cp \"%1\" \"%2\"").arg(currentPath).arg(backupPath); cpProcess.start("sh", QStringList() << "-c" << cpCmd); cpProcess.waitForFinished(5000); if (cpProcess.exitCode() == 0 && QFile::exists(backupPath)) { diagnosticInfo += "系统命令备份成功!继续安装过程...\n"; // 如果系统命令成功,继续执行后续逻辑 } else { diagnosticInfo += QString("系统命令也失败了,退出码:%1\n").arg(cpProcess.exitCode()); diagnosticInfo += QString("系统命令错误输出:%1\n").arg(QString::fromLocal8Bit(cpProcess.readAllStandardError())); QMessageBox::warning(this, "安装失败", QString("无法备份当前程序:%1%2").arg(errorDetail).arg(diagnosticInfo)); return; } } #endif // 先将新文件复制到临时位置 // 确保临时文件不存在 if (QFile::exists(tempPath)) { if (!QFile::remove(tempPath)) { QMessageBox::warning(this, "安装失败", QString("无法删除旧的临时文件:%1").arg(tempPath)); return; } } if (!QFile::copy(downloadedUpdatePath, tempPath)) { QString errorMsg = QString("无法复制更新文件到临时位置\n源文件:%1\n目标文件:%2\n错误:%3") .arg(downloadedUpdatePath) .arg(tempPath) .arg(QFile(downloadedUpdatePath).errorString()); QMessageBox::warning(this, "安装失败", errorMsg); return; } // 验证复制的文件 QFileInfo sourceInfo(downloadedUpdatePath); QFileInfo tempInfo(tempPath); if (!tempInfo.exists() || tempInfo.size() != sourceInfo.size()) { QMessageBox::warning(this, "安装失败", QString("复制的临时文件不完整\n源文件大小:%1 字节\n临时文件大小:%2 字节") .arg(sourceInfo.size()) .arg(tempInfo.exists() ? tempInfo.size() : 0)); QFile::remove(tempPath); // 清理不完整的文件 return; } #ifdef Q_OS_WIN // Windows平台的更新和重启逻辑 // 在Windows上,检查目标目录是否可写 QFileInfo dirInfo(currentDir); if (!dirInfo.isWritable()) { QMessageBox::warning(this, "安装失败", QString("目标目录没有写入权限:%1\n请以管理员身份运行程序或选择其他安装位置。") .arg(currentDir)); QFile::remove(tempPath); // 清理临时文件 return; } QString scriptPath = currentDir + "/update_and_restart.bat"; QFile scriptFile(scriptPath); if (scriptFile.open(QIODevice::WriteOnly | QIODevice::Text)) { QTextStream out(&scriptFile); out << "@echo off\r\n"; out << "timeout /t 2 /nobreak >nul\r\n"; // 等待当前进程完全退出 out << "REM 移除旧文件并重命名新文件\r\n"; out << "del /f /q \"" << QDir::toNativeSeparators(currentPath) << "\"\r\n"; out << "move \"" << QDir::toNativeSeparators(tempPath) << "\" \"" << QDir::toNativeSeparators(currentPath) << "\"\r\n"; out << "cd /d \"" << QDir::toNativeSeparators(currentDir) << "\"\r\n"; out << "REM 启动新程序\r\n"; out << "start \"\" \"" << QDir::toNativeSeparators(currentFileName) << "\"\r\n"; out << "REM 清理脚本文件\r\n"; out << "timeout /t 1 /nobreak >nul\r\n"; out << "del /f /q \"" << QDir::toNativeSeparators(scriptPath) << "\"\r\n"; scriptFile.close(); // 启动更新脚本并退出当前程序 QProcess::startDetached("cmd.exe", QStringList() << "/c" << QDir::toNativeSeparators(scriptPath)); QMessageBox::information(this, "安装完成", "更新安装完成,程序将自动重启!"); // 延迟退出,确保脚本有时间启动 QTimer::singleShot(500, []() { QApplication::quit(); }); } else { QMessageBox::warning(this, "安装失败", QString("无法创建更新脚本:%1\n错误:%2") .arg(scriptPath) .arg(scriptFile.errorString())); QFile::remove(tempPath); // 清理临时文件 } #else // Linux/Unix平台的更新和重启逻辑 // 设置临时文件权限 QFile::setPermissions(tempPath, QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner | QFile::ReadGroup | QFile::ExeGroup | QFile::ReadOther | QFile::ExeOther); // 创建更新脚本 QString scriptPath = currentDir + "/update_and_restart.sh"; QFile scriptFile(scriptPath); if (scriptFile.open(QIODevice::WriteOnly | QIODevice::Text)) { QTextStream out(&scriptFile); out << "#!/bin/bash\n"; out << "sleep 2\n"; // 等待当前进程完全退出 out << "# 移除旧文件并重命名新文件\n"; out << "rm -f \"" << currentPath << "\"\n"; out << "mv \"" << tempPath << "\" \"" << currentPath << "\"\n"; out << "chmod +x \"" << currentPath << "\"\n"; out << "cd \"" << currentDir << "\"\n"; out << "# 启动新程序\n"; out << "nohup ./" << currentFileName << " > /dev/null 2>&1 &\n"; out << "# 清理脚本文件\n"; out << "sleep 1\n"; out << "rm -f \"" << scriptPath << "\"\n"; scriptFile.close(); // 设置脚本可执行权限 QFile::setPermissions(scriptPath, QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner | QFile::ReadGroup | QFile::ExeGroup | QFile::ReadOther | QFile::ExeOther); // 启动更新脚本并退出当前程序 QProcess::startDetached("/bin/bash", QStringList() << scriptPath); QMessageBox::information(this, "安装完成", "更新安装完成,程序将自动重启!"); // 延迟退出,确保脚本有时间启动 QTimer::singleShot(500, []() { QApplication::quit(); }); } else { QMessageBox::warning(this, "安装失败", "无法创建更新脚本!"); QFile::remove(tempPath); // 清理临时文件 } #endif } } int MainWindow::compareVersions(const QString &version1, const QString &version2) { QStringList v1Parts = version1.split('.'); QStringList v2Parts = version2.split('.'); int maxParts = qMax(v1Parts.size(), v2Parts.size()); for (int i = 0; i < maxParts; ++i) { int v1Part = (i < v1Parts.size()) ? v1Parts[i].toInt() : 0; int v2Part = (i < v2Parts.size()) ? v2Parts[i].toInt() : 0; if (v1Part < v2Part) return -1; if (v1Part > v2Part) return 1; } return 0; } void MainWindow::onUpdateSettingsClicked() { UpdateSettingsDialog dialog(this); if (dialog.exec() == QDialog::Accepted) { // 更新本地设置 updateServerUrl = dialog.getUpdateServerUrl(); settings->setValue("updateServerUrl", updateServerUrl); settings->setValue("autoCheckUpdates", dialog.isAutoCheckEnabled()); settings->setValue("checkInterval", dialog.getCheckInterval()); QMessageBox::information(this, "设置保存", "更新设置已保存!"); } }