基于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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/274810.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Pycharm的Project Structure (项目结构)

文章目录 一、Sources二、Tests三、Exeluded四、Namespace packages五、Templates六、Resources 一、Sources 源代码根目录&#xff1a;包含项目的主要源代码&#xff0c;它会在这个目录下搜索代码&#xff0c;然后自动补全和只能提示都通过这里的代码提供。若项目运行自定义代…

System类 --java学习笔记

System System代表程序所在的系统&#xff0c;也是一个工具类 常见System方法&#xff1a; 按照惯例&#xff0c;exit括号中非零状态码表示异常终止&#xff0c;填零则表示人为终止 currentTimeMillis&#xff08;&#xff09;返回的是long类型的时间毫秒值&#xff1a;指的…

重学SpringBoot3-WebMvcAutoConfiguration类

更多SpringBoot3内容请关注我的专栏&#xff1a;《SpringBoot3》 期待您的点赞&#x1f44d;收藏⭐评论✍ 重学SpringBoot3-WebMvcAutoConfiguration类 是什么什么用生效条件作用 自定义配置的三种方式自定义配置举例1. 自定义 DispatcherServlet 配置2. 静态资源配置3. 自定义…

前端 --- HTML

1. HTML 结构 1.1 HTML 文件基本结构 <html><head><title>第一个html程序</title></head><body>hello world!</body> </html> html 标签是整个 html 文件的根标签(最顶层标签)head 标签中写页面的属性.body 标签中写的是页…

设计模式一 ---单例设计模式(动力节点,JavaSE基础)

设计模式 1.什么是设计模式&#xff1f; 2.设计模式的分类 单例设计模式就是GoF模式中的一种。 3.GoF设计模式的分类&#xff1a; 单例设计模式&#xff1a; 顾名思义&#xff1a;单个实例的设计模式&#xff01;

k8s+wordpress+zabbix+elastic+filebeat+kibana服务搭建以及测试

一&#xff0c;环境&#xff1a;docker&#xff0c;k8s&#xff0c;zabbix&#xff0c;以及搭建worpdress&#xff0c;elasticsearch&#xff0c;filebeat&#xff0c;kibana 二&#xff0c;主机分配&#xff1a; 名称host详述个人博客3192.168.142.133 搭配mysql8.0.36的数据…

集合实现类研究底层(部分):手撕ArrayList底层源码、手撕LinkedList底层源码、手写单向链表和双向链表

day26上 集合框架图 标绿已经学习底层&#xff0c;深入底层主要是研究实现类底层 继承关系图 手撕ArrayList底层源码 ps:研究添加元素的过程 思路&#xff1a; 1.研究继承关系 2.研究属性 3.理解创建集合的过程 – 构造方法的底层原理 4.研究添加元素的过程 提升&#xff1a…

jmeter发送请求参数如何使用变量

问题描述 发送jmeter请求时&#xff0c;想设置请求参数为变量 解决方法

基于yolov5的草莓成熟度检测系统,可进行图像目标检测,也可进行视屏和摄像检测(pytorch框架)【python源码+UI界面+功能源码详解】

功能演示&#xff1a; 基于yolov5的草莓成熟度检测系统&#xff0c;系统既能够实现图像检测&#xff0c;也可以进行视屏和摄像实时检测_哔哩哔哩_bilibili &#xff08;一&#xff09;简介 基于yolov5的草莓成熟度系统是在pytorch框架下实现的&#xff0c;这是一个完整的项目…

基于YOLOv8/YOLOv7/YOLOv6/YOLOv5的条形码二维码检测系统(深度学习+UI界面+训练数据集+Python代码)

摘要&#xff1a;在物流和制造业中&#xff0c;开发一套高效的条形码与二维码识别系统显得尤为关键。本博文深入探讨了如何利用深度学习技术打造出一套先进的条形码及二维码检测系统&#xff0c;并且提供了一套完整的实施方案。该系统搭载了性能卓越的YOLOv8算法&#xff0c;并…

php7.3.4连接sqlserver(windows平台)

前言 有个项目需要手上laravel连接客户的sqlserver数据库读取数据&#xff0c;故在本地开发的lnmp环境中&#xff0c;php需要增加扩展 过程 从微软官网下载sqlsrv扩展,注意注意php版本&#xff0c;下载地址 解压的文件会有nts和ts两个版本&#xff0c;本地打开phpinfo查看 将…

如何在Linux系统安装SVN并配置固定公网地址远程访问【内网穿透】

文章目录 前言1. Ubuntu安装SVN服务2. 修改配置文件2.1 修改svnserve.conf文件2.2 修改passwd文件2.3 修改authz文件 3. 启动svn服务4. 内网穿透4.1 安装cpolar内网穿透4.2 创建隧道映射本地端口 5. 测试公网访问6. 配置固定公网TCP端口地址6.1 保留一个固定的公网TCP端口地址6…

C++开发基础——IO操作与文件流

一&#xff0c;基础概念 C的IO操作是基于字节流&#xff0c;并且IO操作与设备无关&#xff0c;同一种IO操作可以在不同类型的设备上使用。 C的流是指流入/流出程序的字节序列&#xff0c;在输入操作中数据从外部设备(键盘&#xff0c;文件&#xff0c;网络等)流入程序&#x…

关于c++的protected关键字

关于c的protected关键字 分类引言例子1&#xff09;错误的demo2&#xff09;改正的demo protected在c中的含义与作用 分类 c基础知识 引言 做了很业务&#xff0c;c基础知识却忘了很多&#xff0c;今天看了一个例子&#xff0c;唤醒了我关于c三大特性之一----封装&#xff0…

信息抽取在旅游行业的应用:以景点信息抽取为例

开源项目推荐 今天先给大家推荐一个开源项目&#xff0c;多模态AI能力引擎平台: 免费的自然语言处理、情感分析、实体识别、图像识别与分类、OCR识别、语音识别接口&#xff0c;功能强大&#xff0c;欢迎体验。 https://gitee.com/stonedtx/free-nlp-api 场景描述 在旅游行业…

echarts vue里画一个简单的环状饼图

<!--观察记录--><div class"teach-plan observe-record"><div class"title-common"><div class"title-common-left">观察记录</div></div><div class"teach-plan-cont"><div class"…

pytest生成allure的报告

首先要下载安装配置allure allure serve ./outputs/allure_report 可以生成html的文件自动在默认浏览器中打开

蓝桥杯每日一题 走迷宫bfs 超超详细解释!!!

昨天学习了bfs的基本概念&#xff0c;今天来做一道经典习题练练手吧&#xff01; bfs常用的两类题型 1.从A出发是否存在到达B的路径(dfs也可) 2.从A出发到B的最短路径&#xff08;数小:<20才能用dfs&#xff09; 遗留的那个问题的答案- 题目&#xff1a;走迷宫 答案&…

Biomedical knowledge graph-enhanced prompt generation for large language models

1. 生物医学知识图谱增强大语言模型提示生成 论文地址&#xff1a;[2311.17330] Biomedical knowledge graph-enhanced prompt generation for large language models (arxiv.org) 源码地址&#xff1a;https://github.com/BaranziniLab/KG_RAG 2. 摘要 大语言模型&#xff0…

【PHP+代码审计】PHP基础——流程控制

&#x1f36c; 博主介绍&#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是 hacker-routing &#xff0c;很高兴认识大家~ ✨主攻领域&#xff1a;【渗透领域】【应急响应】 【Java、PHP】 【VulnHub靶场复现】【面试分析】 &#x1f389;点赞➕评论➕收…