diff --git a/src/BarcodeWidget.cpp b/src/BarcodeWidget.cpp index ec1041c6..eb937de7 100644 --- a/src/BarcodeWidget.cpp +++ b/src/BarcodeWidget.cpp @@ -7,6 +7,7 @@ #include "version_info/version.h" #include #include +#include #include #include #include @@ -361,6 +362,8 @@ BarcodeWidget::BarcodeWidget(QWidget *parent) if (!styleSheet.isEmpty()) { this->setStyleSheet(styleSheet); } + // 进行版本检查 + QTimer::singleShot(0, this, &BarcodeWidget::checkUpdateOnStartup); } void BarcodeWidget::updateButtonStates() const { @@ -1119,6 +1122,46 @@ ZXing::BarcodeFormat BarcodeWidget::stringToBarcodeFormat(const QString &formatS return map.value(key, ZXing::BarcodeFormat::None); // 未匹配时返回None } +void BarcodeWidget::checkUpdateOnStartup() { + if (!m_updateChecker) { + m_updateChecker = new UpdateChecker(this); + } + // 后端接口地址 + QUrl apiUrl("http://106.14.192.58:10000/update/check_version"); + // 获取当前版本和架构 + //QString version = version::git_tag.data(); + //QString architecture = version::architecture.data(); + QString test_version = "v1.0"; + QString test_os = "windows-x64"; + UpdateCheckRequest request(test_version, test_os); + // 连接信号 + connect(m_updateChecker, &UpdateChecker::UpdateAvailable, this, [this](const UpdateInfo &info) { + QMessageBox box(this); + box.setWindowTitle("发现新版本"); + box.setText(QString("最新版本:%1\n\n更新日志:\n%2\n\n更新链接:%3\n") + .arg(info.latest, info.changeLog, info.downloadUrl)); + + QPushButton *btnUpdate = box.addButton("立即更新", QMessageBox::AcceptRole); + QPushButton *btnLater = box.addButton("稍后再说", QMessageBox::RejectRole); + + box.exec(); + + if (box.clickedButton() == btnUpdate) { + QDesktopServices::openUrl(QUrl(info.downloadUrl)); + } else if (box.clickedButton() == btnLater) { + // 关闭弹窗即可;exec() 返回后 box 会析构 + } + }); + connect(m_updateChecker, &UpdateChecker::NoUpdate, this, []() { spdlog::info("No update!"); }); + + connect(m_updateChecker, &UpdateChecker::ErrorOccured, this, [](const QString &msg) { + spdlog::error("UpdateCheck Error, Error Message: {}", msg.toStdString()); + }); + + // 发起检查 + m_updateChecker->Check(apiUrl, request); +} + void BarcodeWidget::setupLanguageAction() { LanguageManager &languageMgr = LanguageManager::instance(); diff --git a/src/BarcodeWidget.h b/src/BarcodeWidget.h index 9d9ef256..056272c4 100644 --- a/src/BarcodeWidget.h +++ b/src/BarcodeWidget.h @@ -8,6 +8,7 @@ #include #include "CameraWidget.h" +#include "UpdateChecker.h" #include "convert.h" #include "mqtt/MQTTMessageWidget.h" #include "mqtt/mqtt_client.h" @@ -122,6 +123,12 @@ private slots: */ static ZXing::BarcodeFormat stringToBarcodeFormat(const QString &formatStr); + /** + * @brief 在程序启动时与服务器通信判断是否需要更新 + * + */ + void checkUpdateOnStartup(); + private: /** * @brief 初始化语言切换的Action @@ -173,4 +180,5 @@ private slots: std::unique_ptr subscriber_; /**< MQTT订阅者实例 */ std::unique_ptr messageWidget; /**< MQTT消息展示窗口 */ CameraWidget preview; /**< 摄像头预览窗口 */ + UpdateChecker *m_updateChecker = nullptr; /**< 版本检查实例 */ }; diff --git a/src/UpdateChecker.cpp b/src/UpdateChecker.cpp new file mode 100644 index 00000000..e8043e13 --- /dev/null +++ b/src/UpdateChecker.cpp @@ -0,0 +1,53 @@ +#include "UpdateChecker.h" +#include +#include +#include +#include + +UpdateChecker::UpdateChecker(QObject *parent) + : QObject(parent) {} + +void UpdateChecker::Check(const QUrl &apiUrl, const UpdateCheckRequest &request) { + // 1. request + QNetworkRequest req(apiUrl); + req.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + QJsonDocument doc(request.toJson()); + QByteArray body = doc.toJson(QJsonDocument::Compact); + + // 2. 󣬳ʱ + QNetworkReply *reply = m_nam.post(req, body); + QTimer *timer = new QTimer(reply); + timer->setSingleShot(true); + timer->start(4000); + QObject::connect(timer, &QTimer::timeout, reply, [reply]() { reply->abort(); }); + + // 3. յذ + QObject::connect(reply, &QNetworkReply::finished, this, [this, reply]() { + const auto err = reply->error(); + const QByteArray body = reply->readAll(); + reply->deleteLater(); + // 3.1 + if (err != QNetworkReply::NoError) { + emit ErrorOccured(QString(": %1").arg(reply->errorString())); + return; + } + // 3.2 ȡذ + QJsonParseError pe{}; + QJsonDocument doc = QJsonDocument::fromJson(body, &pe); + if (pe.error != QJsonParseError::NoError || !doc.isObject()) { + emit ErrorOccured(QString("زǺϷ json")); + return; + } + QJsonObject o = doc.object(); + UpdateInfo info; + info.latest = o.value("version").toString(); + info.downloadUrl = o.value("update_url").toString(); + info.changeLog = o.value("update_log").toString(); + info.updateNeed = o.value("update_need").toBool(); + if (info.updateNeed) { + emit UpdateAvailable(info); + } else { + emit NoUpdate(); + } + }); +} diff --git a/src/UpdateChecker.h b/src/UpdateChecker.h new file mode 100644 index 00000000..6dc56b73 --- /dev/null +++ b/src/UpdateChecker.h @@ -0,0 +1,38 @@ +#pragma once +#include +#include +#include + +struct UpdateInfo { + QString latest; /**< °汾 */ + QString downloadUrl; /**< · */ + QString changeLog; /**< 汾־ */ + bool updateNeed = false; /**< ǷҪ */ +}; + +struct UpdateCheckRequest { + QString version; /**< ͻ˰汾 */ + QString osArch; /**< ͻ˼ܹ */ + UpdateCheckRequest(const QString _version, const QString _os) + : version(_version), osArch(_os) {} + // л JSON + QJsonObject toJson() const { + QJsonObject o; + o.insert("version", version); + o.insert("os-arch", osArch); + return o; + } +}; +class UpdateChecker : public QObject { + Q_OBJECT +public: + explicit UpdateChecker(QObject *parent = nullptr); + void Check(const QUrl &apiUrl, const UpdateCheckRequest &request); +signals: + void NoUpdate(); + void UpdateAvailable(const UpdateInfo &info); + void ErrorOccured(const QString &msg); + +private: + QNetworkAccessManager m_nam; +}; \ No newline at end of file diff --git a/translations/app_en_US.ts b/translations/app_en_US.ts index 9133b20d..978c8828 100644 --- a/translations/app_en_US.ts +++ b/translations/app_en_US.ts @@ -77,195 +77,195 @@ BarcodeWidget - - + + 帮助 help - - + + 工具 tool - - + + 设置 settings - - + + 关于软件 About the software - - + + MQTT实时消息监控窗口 MQTT Real-time Message Monitoring Window - - + + 打开摄像头扫码 Open the camera to scan the code - - + + Base64 Base64 - - + + 文本输入 Text input - - - + + + 选择一个文件或图片 Select a file or image - - + + 浏览 Browse - - + + 生成 generate - - + + 解码 decode - - + + 保存 save - - + + 请选择任意文件来生成条码 Please select any file to generate the barcode - - + + 可以解码PNG图片中的条码 It can decode barcodes in PNG images - - + + 选择条码类型: Select barcode type: - - + + 宽度: width: - - + + 高度: height: - + 输入要转换的文字 Enter the text to be converted - + 选择需要转换的文件或图片 Select the file or image you want to convert - - + + 生成图片失败 Failed to generate image - - - + + + 警告 Warning - - + + 无可处理文件 No files to process - + 无法打开文件: Unable to open the file: - + 无法加载图片文件: %1 Unable to load image file: %1 - + 无法识别条码或条码格式不正确 Unable to recognise the barcode or the barcode format is incorrect - + 没有可保存的内容。 There is no content available to save. - + 保存图片 Save the image - + 保存文件 Save the file - + 请选择保存文件夹 Please select a folder to save the file - + 数据为空或无效 The data is empty or invalid - + 写入失败 Failed to write - + 未知错误 Unknown error - + 操作完成。 总计处理: %1 成功: %2 @@ -276,7 +276,7 @@ Successful: %2 Failed: %3 - + [保存失败的文件]: @@ -287,17 +287,17 @@ Failed: %3 - + ...以及其他 %1 个文件 ...and %1 other files - + 保存结果 - 包含错误 Save results - including errors - + [文件列表]: @@ -308,48 +308,48 @@ Failed: %3 - + 保存成功 Saved successfully - + 当前模式:直接文本生成 请输入内容并点击生成 Current mode: Direct text generation. Please enter the content and click Generate - + 已选择 %1 个文件,准备处理: %1 files selected, ready for processing: - + [待解码] [Pending Decoding] - + [待生成] [Pending Generation] - + [不确定类型,默认待生成] [Uncertain type, pending generation] - - + + 请选择文件 或者键入内容 Please select a file or enter content - - + + 语言 language @@ -358,107 +358,107 @@ or enter content CameraWidget - + 摄像头预览 Camera preview - + 摄像头 Camera - + 显示设置 Display settings - + 二维码类型 QR code type - + 全选 Select All - + 清空 Clear - + 后处理 Post-processing - + 图像增强 Image enhancement - + 调试 Debug - + 保存识别帧 Save recognised frame - - + + 时间 Time - - + + 图像 Image - - + + 类型 Type - - + + 内容 Content - + [隐藏] PNG 数据 [Hidden] PNG Data - + [隐藏] 图片宽度 [隐藏] 图片宽度 - + [隐藏] 图片高度 [Hidden] Image Height @@ -479,25 +479,25 @@ or enter content - + 摄像头就绪... Camera ready... - + 导出 Export - + 导出 HTML (.html) Export HTML (.html) - + 导出 XLSX (.xlsx) Export XLSX (.xlsx) @@ -562,42 +562,42 @@ or enter content - + 错误 Error - + 无法打开摄像头 Unable to open the camera - + 摄像头已启动 Camera has started - + 摄像头已停止 Camera has stopped - + 摄像头运行中... Camera is running... - + 检测到 Detected - + code - + 条码类型: %1 内容: %2 时间: %3 @@ -610,7 +610,7 @@ Time: %3 - + 扫描结果 Scan results diff --git a/translations/app_zh_CN.ts b/translations/app_zh_CN.ts index e0884855..8e5304c2 100644 --- a/translations/app_zh_CN.ts +++ b/translations/app_zh_CN.ts @@ -77,195 +77,195 @@ BarcodeWidget - - + + 帮助 - - + + 工具 - - + + 设置 - - + + 关于软件 - - + + MQTT实时消息监控窗口 - - + + 打开摄像头扫码 - - + + Base64 - - + + 文本输入 - - - + + + 选择一个文件或图片 - - + + 浏览 - - + + 生成 - - + + 解码 - - + + 保存 - - + + 请选择任意文件来生成条码 - - + + 可以解码PNG图片中的条码 - - + + 选择条码类型: - - + + 宽度: - - + + 高度: - + 输入要转换的文字 - + 选择需要转换的文件或图片 - - + + 生成图片失败 - - - + + + 警告 - - + + 无可处理文件 - + 无法打开文件: - + 无法加载图片文件: %1 - + 无法识别条码或条码格式不正确 - + 没有可保存的内容。 - + 保存图片 - + 保存文件 - + 请选择保存文件夹 - + 数据为空或无效 - + 写入失败 - + 未知错误 - + 操作完成。 总计处理: %1 成功: %2 @@ -273,7 +273,7 @@ - + [保存失败的文件]: @@ -281,17 +281,17 @@ - + ...以及其他 %1 个文件 - + 保存结果 - 包含错误 - + [文件列表]: @@ -299,46 +299,46 @@ - + 保存成功 - + 当前模式:直接文本生成 请输入内容并点击生成 - + 已选择 %1 个文件,准备处理: - + [待解码] - + [待生成] - + [不确定类型,默认待生成] - - + + 请选择文件 或者键入内容 - - + + 语言 @@ -347,107 +347,107 @@ CameraWidget - + 摄像头预览 - + 摄像头 - + 显示设置 - + 二维码类型 - + 全选 - + 清空 - + 后处理 - + 图像增强 - + 调试 - + 保存识别帧 - - + + 时间 - - + + 图像 - - + + 类型 - - + + 内容 - + [隐藏] PNG 数据 - + [隐藏] 图片宽度 - + [隐藏] 图片高度 @@ -468,25 +468,25 @@ - + 摄像头就绪... - + 导出 - + 导出 HTML (.html) - + 导出 XLSX (.xlsx) @@ -547,42 +547,42 @@ - + 错误 - + 无法打开摄像头 - + 摄像头已启动 - + 摄像头已停止 - + 摄像头运行中... - + 检测到 - + - + 条码类型: %1 内容: %2 时间: %3 @@ -591,7 +591,7 @@ - + 扫描结果