基于Qt 和python 的自动升级功能
需求:
公司内部的一个客户端工具,想加上一个自动升级功能。
服务端:
1,服务端使用python3.7 ,搭配 fastapi 和uvicorn 写一个简单的服务,开出一个get接口,用于客户端读取安装包的版本,描述,和路径。
2,使用 python 自带的 http.server 启动一个文件服务器,将安装包存入,并将地址写到步骤1的json文件中。
json文件长这个样子,每次客户端都解析这个文件,如果最新的版本大于当前的版本,则从url下载文件,并自动执行文件。
{"ver":"1.0.1","desc":"1.优化界面\n2.删除了什么东西\n3.增加了什么东西把\n4.添加了什么东西\n5.特别好用 试试吧","file":"test_1_0.exe","url":"http://xxx.xxx.xxx:8002/pkg/test/WinSCP.exe"
}
服务很简单, 主要就是提供一个get接口。
from fastapi import FastAPI
import jsonclass MyApp:def __init__(self, title: str = "UpgradeServer", version: str = "1.0.0"):self.app = FastAPI(title=title, version=version)# Additional initialization or configuration can be done heredef configure_routes(self):@self.app.get("/")def root():return {"不为无益之事,何以遣有涯之生!"}@self.app.get("/cur_ver/{item}")def cur_ver(item:str=None):path = "pkg/"+item+"/"+item+".json"with open(path, 'r') as file:# 从文件中加载JSON数据data = json.load(file)print(data['ver'])return datadef run(self, host: str = "0.0.0.0", port: int = 8001):import uvicornuvicorn.run(self.app, host=host, port=port)if __name__ == "__main__":my_app = MyApp()my_app.configure_routes()my_app.run()
客户端:
1,客户端是一个 QDialog,每次启动时 从服务端获取最新的版本号,大于则开始下载安装包,下载完成后,则执行安装包。

2,使用的时候 将客户端放到main函数中,并传入当前的版本号。

//.h 文件
#ifndef UPGRADECLIENT_H
#define UPGRADECLIENT_H#include <QDialog>
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QThread>
#include <QMutex>
#include <QEventLoop>
#include <QFile>namespace Ui {
class UpgradeClient;
}enum class Status{Init,Error,NeedUpgrade,NoUpgradde,Abandon,DownloadFinished,
};class UpgradeClient : public QDialog
{Q_OBJECTpublic:explicit UpgradeClient(const QString& ver,QWidget *parent = nullptr);~UpgradeClient();int start();private slots:void on_laterBtn_clicked();void on_nowBtn_clicked();private:Ui::UpgradeClient *ui;QNetworkAccessManager manager;QNetworkReply *verReply{nullptr};QNetworkReply *downloadReply{nullptr};//当前版本QString curVer;//最新版本 描述 名称 urlQString latestVer;QString pkgDesc;QString pkgName;QString pkgUrl;//判断当前状态Status curStatus{Status::Init};//安装包存储文件QFile pkgFile;//事件循环 用于等待版本检擦QEventLoop eventLoop;private://检查当前版本void checkVer();//下载安装包void downloadPkg(const QString& _name,const QString& _url);//解析json数据void parseJson(const QByteArray &jsonData);//比较版本bool compareVer(int lMajor,int lMinor,int lPath,int cMajor,int cMinor,int cPath);//运行安装包bool runPkg(const QString& filename);protected:void closeEvent(QCloseEvent *event) override;};#endif // UPGRADECLIENT_H//cpp 文件
#include "upgradeclient.h"
#include "ui_upgradeclient.h"
#include <QFile>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QtConcurrent>
#include <chrono>//检查版本 url
const QString checkVerUrl{"http://xxx.xxx.xxx:8001/cur_ver/test"};UpgradeClient::UpgradeClient(const QString& ver,QWidget *parent) :QDialog (parent),ui(new Ui::UpgradeClient),curVer(ver)
{ui->setupUi(this);this->setWindowTitle(QStringLiteral("检测到需要升级"));ui->progressBar->setHidden(true);
}UpgradeClient::~UpgradeClient()
{qDebug()<<"~UpgradeClient()";delete ui;
}int UpgradeClient::start()
{checkVer();eventLoop.exec();if(curStatus==Status::NeedUpgrade){this->exec();if(curStatus==Status::DownloadFinished){return 0;}}else{this->reject();}return -1;
}void UpgradeClient::on_laterBtn_clicked()
{curStatus = Status::Abandon;this->reject();
}void UpgradeClient::on_nowBtn_clicked()
{if(pkgName.isEmpty())return;downloadPkg(pkgName,pkgUrl);ui->laterBtn->setEnabled(false);ui->nowBtn->setEnabled(false);
}void UpgradeClient::checkVer()
{curStatus = Status::Init;QUrl url;url.setUrl(checkVerUrl);QNetworkRequest request(url);verReply = manager.get(request);QObject::connect(verReply, &QNetworkReply::finished, this,[&]() {if (verReply->error() == QNetworkReply::NoError) {QByteArray responseData = verReply->readAll();qDebug() << "Response:" << responseData;parseJson(responseData);} else {qDebug() << "Error:" << verReply->errorString();curStatus = Status::Error;}eventLoop.quit();});
}void UpgradeClient::downloadPkg(const QString& _name,const QString& _url)
{QUrl url;url.setUrl(_url);QNetworkRequest request(url);QString currentPath = QCoreApplication::applicationDirPath();QString filename = currentPath+"/"+_name;pkgFile.setFileName(filename);if (pkgFile.open(QIODevice::WriteOnly)){downloadReply = manager.get(request);connect(downloadReply, &QNetworkReply::downloadProgress, this, [&](qint64 bytesReceived, qint64 bytesTotal){if(bytesTotal!=0){int progress = static_cast<int>((bytesReceived * 100) / bytesTotal);qDebug()<<"process "<<progress;ui->progressBar->setHidden(false);ui->progressBar->setValue(progress);}});connect(downloadReply,&QNetworkReply::readyRead,this,[&](){pkgFile.write(downloadReply->readAll());});connect(downloadReply, &QNetworkReply::finished, this, [&,filename](){if(curStatus==Status::Abandon)return;if (verReply->error() == QNetworkReply::NoError){pkgFile.flush();pkgFile.close();if(ui->progressBar->value()<98){curStatus = Status::Error;ui->logLabel->setText(QStringLiteral("下载安装包出错!"));}else{if(!this->runPkg(filename)){curStatus = Status::Error;ui->logLabel->setText(QStringLiteral("安装程序执行失败!"));}else{curStatus = Status::DownloadFinished;this->accept();}}}else{curStatus = Status::Error;qDebug() << "Error:" << downloadReply->errorString();ui->logLabel->setText(QStringLiteral("下载出错:")+downloadReply->errorString());}});}else {qDebug() << "Error: Could not open file for writing";curStatus = Status::Error;this->reject();}
}void UpgradeClient::parseJson(const QByteArray &jsonData)
{QJsonDocument jsonDocument = QJsonDocument::fromJson(jsonData);if (!jsonDocument.isNull()) {if (jsonDocument.isObject()) {QJsonObject jsonObject = jsonDocument.object();latestVer = jsonObject["ver"].toString();pkgDesc = jsonObject["desc"].toString();pkgName = jsonObject["file"].toString();pkgUrl = jsonObject["url"].toString();qDebug()<<"curVer:"<<curVer<<" "<<"latestVer:"<<latestVer;QStringList latestV = latestVer.split(".");QStringList curV = curVer.split(".");if(latestV.size()==3&&curV.size()==3){int lMajorV = latestV.at(0).toUInt();int lMinorV = latestV.at(1).toUInt();int lPathV = latestV.at(2).toUInt();int cMajorV = curV.at(0).toUInt();int cMinorV = curV.at(1).toUInt();int cPathV = curV.at(2).toUInt();if(compareVer(lMajorV,lMinorV,lPathV,cMajorV,cMinorV,cPathV)){ui->textEdit->append(QStringLiteral("最新版本:%1\n").arg(latestVer));ui->textEdit->append(pkgDesc);curStatus = Status::NeedUpgrade;}else{curStatus = Status::NoUpgradde;}}else{curStatus = Status::Error;}}else{curStatus = Status::Error;}} else {qDebug() << "Error: Failed to parse JSON data";curStatus = Status::Error;}
}bool UpgradeClient::compareVer(int lMajor, int lMinor, int lPath, int cMajor, int cMinor, int cPath)
{int localVersion[3]{cMajor,cMinor,cPath};int latestVersion[3]{lMajor,lMinor,lPath};int k = memcmp(localVersion,latestVersion,sizeof(int)*3);qDebug()<<"compareVer "<<k;if(k==0){return false;}else if(k<0){return true;}else{return false;}
}bool UpgradeClient::runPkg(const QString &filename)
{QStringList arguments;bool success = QProcess::startDetached(filename,arguments);if (success) {qDebug() << "External program started as a detached process.";} else {qDebug() << "Failed to start external program.";}return success;
}void UpgradeClient::closeEvent(QCloseEvent *event)
{qDebug()<<"closeEvent";curStatus = Status::Abandon;if(verReply){verReply->close();verReply->deleteLater();}if(downloadReply){downloadReply->close();downloadReply->deleteLater();}if(pkgFile.isOpen()){pkgFile.close();}QDialog::closeEvent(event);
}//ui文件
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0"><class>UpgradeClient</class><widget class="QWidget" name="UpgradeClient"><property name="geometry"><rect><x>0</x><y>0</y><width>409</width><height>240</height></rect></property><property name="windowTitle"><string>Form</string></property><layout class="QGridLayout" name="gridLayout"><item row="0" column="0"><widget class="QTextEdit" name="textEdit"><property name="readOnly"><bool>true</bool></property></widget></item><item row="1" column="0"><widget class="QProgressBar" name="progressBar"><property name="value"><number>0</number></property><property name="textVisible"><bool>true</bool></property><property name="invertedAppearance"><bool>false</bool></property><property name="format"><string>%p%</string></property></widget></item><item row="2" column="0"><layout class="QHBoxLayout" name="horizontalLayout"><item><widget class="QLabel" name="logLabel"><property name="text"><string/></property></widget></item><item><spacer name="horizontalSpacer"><property name="orientation"><enum>Qt::Horizontal</enum></property><property name="sizeHint" stdset="0"><size><width>40</width><height>20</height></size></property></spacer></item><item><widget class="QPushButton" name="nowBtn"><property name="text"><string>现在</string></property></widget></item><item><widget class="QPushButton" name="laterBtn"><property name="text"><string>稍后</string></property></widget></item></layout></item></layout></widget><resources/><connections/>
</ui>
效果:
1,启动检测升级。

2, 点击 【现在】 开始下载 安装包。

docker 部署一下服务端:
1. 下载镜像:docker pull python3.7
2. 启动容器:docker run -it -p 8001:8001 -p 8002:8002 --name upgrade python:3.7 /bin/bash
3. 安装环境:pip3.7 install fastapi &pip3.7 install uvicorn
4. 拷贝文件:docker cp upgrade upgrade:/home
5. 退出容器:Ctrl+P+Q
相关文章:
基于Qt 和python 的自动升级功能
需求: 公司内部的一个客户端工具,想加上一个自动升级功能。 服务端: 1,服务端使用python3.7 ,搭配 fastapi 和uvicorn 写一个简单的服务,开出一个get接口,用于客户端读取安装包的版本&#…...
【论文阅读】IEEE Access 2019 BadNets:评估深度神经网络的后门攻击
文章目录 一.论文信息二.论文内容1.摘要2.引言3.主要图表4.结论 一.论文信息 论文题目: BadNets: Evaluating Backdooring Attacks on Deep Neural Networks(BadNets:评估深度神经网络的后门攻击) 论文来源: 2019-IEEE Access …...
Unity 让角色动起来(动画控制器)
下载素材: 导入后,找到预制体和动画。 新建动画控制器,拖动到预制体的新版动画组件上。 建立动画关系 创建脚本,挂载到预制体上。 using System.Collections; using System.Collections.Generic; using UnityEngine;public c…...
ubuntu22.04环境中安装pylint
ubuntu22.04环境中安装pylint sudo apt-get install python3-pipsudo aptitude install python3-pipsudo pip install pylint sudo apt-get install python3-pip 在安装pylint的时候,需要使用pip命令,在ubuntu22.04环境中命令如下: $ sudo …...
主流数据库的区别
几个主流的数据库有: 1. MySQL:MySQL是一种关系型数据库管理系统,常用于Web应用程序开发和数据存储。 2. Oracle:Oracle是一种关系型数据库管理系统,由Oracle Corporation开发和销售。它广泛用于企业级应用程序中。 …...
veeam备份基础
veeam的安装 将文件动态连接文件复制到veeam的安装目录中,替换掉新的文件 重新启动服务 为veeam添加证书 为veeam添加存储 其他 第一次完整备份时间会比较久 备份预览,transferred和processing date的区别 transferred后面数据为压缩比...
Flink并行度
1、Task flink中每个算子就是一个Task,比如flatMap、map、sum是一个Task。 2、SubTask 算子有几个并行度SubTask的数量就是几,比如 3、算子并行度 算子并行度指的是每个算子的并行度,可用env.setParallelism(1);设置所有算子的并行度&am…...
这届留学生是懂作弊的,ChatGPT震惊教授一整年!
ChatGPT,一款全新聊天机器人模型,成为北美科技圈的新时髦。 图片来源:New York Post 有人和它“探讨”人生,畅聊哲学,但也有人起了歪心思,用它进行学术作弊。这类新型学术不端事件引发人们关于教育的再思考…...
CVE-2023-38836 BoidCMSv.2.0.0 后台文件上传漏洞
漏洞简介 BoidCMS是一个免费的开源平面文件 CMS,用于构建简单的网站和博客,使用 PHP 开发并使用 JSON 作为数据库。它的安装无需配置或安装任何关系数据库(如 MySQL)。您只需要一个支持PHP 的Web服务器。在 BoidCMS v.2.0.0 中存…...
pf4j插件实践验证
Java系统实现插件机制,可自行通过classloader实现,亦可使用成熟的框架。pf4j是一款轻量级,扩展性强的插件,可实现插件的开发管理(插件开发、加载、卸载、更新),省略了一些基础代码的开发&#x…...
计算机组成原理之运算方法和运算器
文章目录 数据格式定点数浮点数 机器码表示原码反码补码数的补码与真值 移码IEEE754标准 数据格式 定点数 定点数就是数据的小数点的位置是固定不变的,通常将数据表示成纯小数或纯整数以 n 1 n1 n1 位数表示定点数,以 X n Xn Xn表示定点数的正负&#…...
Redux Toolkit
本文作者为 360 奇舞团前端开发工程师 阅读本文章前,需要先了解下 redux 的基本概念与用法,Redux Toolkit 是建立在 Redux 基础之上的工具包,因此需要对 Redux 的基本概念有一定的了解,包括 Action、Reducer、Store、Middleware 等…...
基于YOLOv8/YOLOv7/YOLOv6/YOLOv5的商品识别系统(深度学习+UI界面+训练数据集+Python代码)
摘要:在零售行业的技术进步中,开发商品识别系统扮演着关键角色。本博文详细阐述了如何利用深度学习技术搭建一个高效的商品识别系统,并分享了一套完整的代码实现。系统采用了性能强劲的YOLOv8算法,同时对YOLOv7、YOLOv6、YOLOv5等…...
在亚马逊云EC2上启动PopOS
CloudEndure遇到的挑战 自从使用CloudEndure导入win11后就一发不可收拾,然后就可以尝试新的操作系统,比如system76的Pop!_OS,虽然上是基于ubuntu进行开发的,但是在使用安装CloudEndure 的时候还是遇到的了问题,可能是因为内核很新,也可能其他的一些原因. 如果说严格按照兼容性…...
Linux运维:磁盘分区与挂载详解
Linux运维:磁盘分区与挂载详解 1、磁盘分区的原理2、查看系统中所有的磁盘设备及其分区信息3、进行磁盘分区(对于sdb新磁盘)4、格式化分区5、挂载分区(临时挂载、永久挂载)6、取消挂载分区7、删除分区 💖Th…...
jeecg 项目 springcloud 项目有一个模块 没加载进来 只需要 把这个模块放到 可以加载到模块的位置 刷新依赖
springcloud 项目有一个模块 没加载进来 只需要 把这个模块放到 可以加载到模块的位置 刷新依赖...
spring boot使用mybatisplus访问mysql的配置流程
网上教程大多教人新建一个带对应组件的项目,本文记录如何在一个已有springboot2.x项目中,配置使用mybatisplus来访问mysql。包括使用wrapper和自己写mapper.xml的自定义函数两种和数据库交互的方式。 关于项目的创建,参考创建springboot 2.x…...
git 如何将多个提交点合并为一个提交点 commit
文章目录 核心命令详细使用模式总结示例 核心命令 git merge branch2 是将分支branch2的提交点合并到本地当前分支。 而在执行这条命令的时候,加一个选项--squash就表示在合并的时候将多个提交点合并为一个提交点。 git merge --squash branch2 先看squash单词的意…...
[C语言] 数据存储
类型意义: 1.类型决定内存空间大小(大小决定了使用范围) 2.如何看待内存空间的视角 类型分类 整形 类型大小(字节)short2int4long4long8 浮点型 类型大小(字节)float4double8long double12 构造类型 数组结构性struct联合union枚举enum 指…...
LoadBalancer负载均衡服务调用
LoadBalancer负载均衡服务调用 1、Ribbon目前也进入维护 Spring Cloud Ribbon是基于Netflix Ribbon实现的一套客户端 负载均衡的工具。 简单的说,Ribbon是Netflix发布的开源项目,主要功能是**提供客户端的软件负载均衡算法和服务调用。**Ribbon…...
[2025CVPR]DeepVideo-R1:基于难度感知回归GRPO的视频强化微调框架详解
突破视频大语言模型推理瓶颈,在多个视频基准上实现SOTA性能 一、核心问题与创新亮点 1.1 GRPO在视频任务中的两大挑战 安全措施依赖问题 GRPO使用min和clip函数限制策略更新幅度,导致: 梯度抑制:当新旧策略差异过大时梯度消失收敛困难:策略无法充分优化# 传统GRPO的梯…...
React 第五十五节 Router 中 useAsyncError的使用详解
前言 useAsyncError 是 React Router v6.4 引入的一个钩子,用于处理异步操作(如数据加载)中的错误。下面我将详细解释其用途并提供代码示例。 一、useAsyncError 用途 处理异步错误:捕获在 loader 或 action 中发生的异步错误替…...
Cesium1.95中高性能加载1500个点
一、基本方式: 图标使用.png比.svg性能要好 <template><div id"cesiumContainer"></div><div class"toolbar"><button id"resetButton">重新生成点</button><span id"countDisplay&qu…...
C# SqlSugar:依赖注入与仓储模式实践
C# SqlSugar:依赖注入与仓储模式实践 在 C# 的应用开发中,数据库操作是必不可少的环节。为了让数据访问层更加简洁、高效且易于维护,许多开发者会选择成熟的 ORM(对象关系映射)框架,SqlSugar 就是其中备受…...
【python异步多线程】异步多线程爬虫代码示例
claude生成的python多线程、异步代码示例,模拟20个网页的爬取,每个网页假设要0.5-2秒完成。 代码 Python多线程爬虫教程 核心概念 多线程:允许程序同时执行多个任务,提高IO密集型任务(如网络请求)的效率…...
汇编常见指令
汇编常见指令 一、数据传送指令 指令功能示例说明MOV数据传送MOV EAX, 10将立即数 10 送入 EAXMOV [EBX], EAX将 EAX 值存入 EBX 指向的内存LEA加载有效地址LEA EAX, [EBX4]将 EBX4 的地址存入 EAX(不访问内存)XCHG交换数据XCHG EAX, EBX交换 EAX 和 EB…...
Java线上CPU飙高问题排查全指南
一、引言 在Java应用的线上运行环境中,CPU飙高是一个常见且棘手的性能问题。当系统出现CPU飙高时,通常会导致应用响应缓慢,甚至服务不可用,严重影响用户体验和业务运行。因此,掌握一套科学有效的CPU飙高问题排查方法&…...
【Java学习笔记】BigInteger 和 BigDecimal 类
BigInteger 和 BigDecimal 类 二者共有的常见方法 方法功能add加subtract减multiply乘divide除 注意点:传参类型必须是类对象 一、BigInteger 1. 作用:适合保存比较大的整型数 2. 使用说明 创建BigInteger对象 传入字符串 3. 代码示例 import j…...
sipsak:SIP瑞士军刀!全参数详细教程!Kali Linux教程!
简介 sipsak 是一个面向会话初始协议 (SIP) 应用程序开发人员和管理员的小型命令行工具。它可以用于对 SIP 应用程序和设备进行一些简单的测试。 sipsak 是一款 SIP 压力和诊断实用程序。它通过 sip-uri 向服务器发送 SIP 请求,并检查收到的响应。它以以下模式之一…...
vulnyx Blogger writeup
信息收集 arp-scan nmap 获取userFlag 上web看看 一个默认的页面,gobuster扫一下目录 可以看到扫出的目录中得到了一个有价值的目录/wordpress,说明目标所使用的cms是wordpress,访问http://192.168.43.213/wordpress/然后查看源码能看到 这…...
