基于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.mzph.cn/news/741135.shtml

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

相关文章

<机器学习初识>——《机器学习》

目录 一、人工智能概述 1 人工智能应用场景 2 人工智能发展必备三要素 3 人工智能、机器学习和深度学习 二、人工智能发展历程 1 人工智能的起源 1.1 图灵测试 1.2 达特茅斯会议 2 发展历程 三、 人工智能主要分支 1 主要分支介绍 1.1 分支一&#xff1a;计算机视觉…

linux 启动命令

添加权限 drwxr-xr-x -rwxr-xr-x <strong>第一位&#xff1a;-代表文件&#xff0c;d代表目录<br> 用户、组用户、其他用户都是rwx形式&#xff0c;其中r表示读、w表示写、x表示可执行&#xff0c;-表示没有权限&#xff0c;拿用户组举例&#xff0c;r只能出…

c++中string的模拟实现(超详细!!!)

1.string的成员变量、&#xff08;拷贝&#xff09;构造、析构函数 1.1.成员变量 private:char* _str;size_t _size; //string中有效字符个数size_t _capacity; //string中能存储有效字符个数的大小 1.2&#xff08;拷贝&#xff09;构造函数 //构造函数string(const char* …

【Linux进阶之路】HTTP协议

文章目录 一、基本概念1.HTTP2.域名3.默认端口号4.URL 二、请求与响应1.抓包工具2.基本框架3.简易实现3.1 HttpServer3.2 HttpRequest3.2.1 version13.2.2 version23.2.3 version3 总结尾序 一、基本概念 常见的应用层协议&#xff1a; HTTPS (HyperText Transfer Protocol Sec…

C# 8.0+版本项目 string不可为空

1.在某一次新建项目的时候发现&#xff0c;新建的项目&#xff0c;写的测试接口&#xff0c;接口的入参有string的参数&#xff0c; 但是调用接口的时候string的参数没有传报了400&#xff0c;很奇怪&#xff0c;也没有语法错误之类的。 2.解决办法 在项目上右键->属性->…

鸿蒙Harmony应用开发—ArkTS声明式开发(基础手势:Span)

作为Text组件的子组件&#xff0c;用于显示行内文本的组件。 说明&#xff1a; 该组件从API Version 7开始支持。后续版本如有新增内容&#xff0c;则采用上角标单独标记该内容的起始版本。 该组件从API Version 10开始支持继承父组件Text的属性&#xff0c;即如果子组件未设置…

单片机开发-实现Zigbee的LED灯交替闪烁

前言 前提须知&#xff1a; 1.本文所讲的单片机开发的代码&#xff0c;使用软件是IAR Embedded Workbench&#xff1b; 2.本文所讲的内容与全国职业院校技能大赛“物联网应用开发”赛项Zigbee模块题目类似&#xff1b; 3.单片机Zigbee内容以收入到我的专栏“单片机Zigbee当…

C# 数据结构初始化长度

在C#中&#xff0c;各个数据结构的初始化长度是动态的&#xff0c;下面是一个例子&#xff0c;展示了如何初始化各个数据结构并演示它们的长度&#xff1a; using System; using System.Collections.Generic;class Program {static void Main(){// 初始化数组int[] intArray …

直播美颜SDK的商业化应用:如何为直播平台带来更多商业机会?

直播过程中的自然环境和摄像头本身的限制可能会影响用户的体验&#xff0c;因此直播美颜SDK的商业化应用应运而生&#xff0c;它为直播平台带来了更多商业机会。 直播美颜SDK是一种集成在直播平台中的软件开发工具包&#xff0c;它能够对直播过程中的视频流进行实时的美颜处理…

双指针算法———C++

首先说明双指针&#xff0c;何为双指针顾名思义就是序列中有两个指针来分别配合调控遍历这序列。 与其生硬讲解&#xff0c;不如举个例子。 例题&#xff1a;给定一段序列&#xff0c;里面有一些单词&#xff0c;并且每个单词都是以空格隔开。 题目要求是将每个单词为一行重…

WPF制作带图标和文字的按钮模板(通过附件属性实现)

1.界面模板代码部分 <Window.Resources><Style x:Key"IconButton" TargetType"Button"><Setter Property"Template"><Setter.Value><ControlTemplate TargetType"Button"><Border x:Name"borde…

算法D45 | 动态规划7 | 70. 爬楼梯 (进阶) 322. 零钱兑换 279.完全平方数

70. 爬楼梯 &#xff08;进阶&#xff09; 这道题目 爬楼梯之前我们做过&#xff0c;这次再用完全背包的思路来分析一遍 代码随想录 Python: 翻译成背包问题&#xff0c;即&#xff1a;在容量为n的背包里&#xff0c;装入重量为1/2的物品&#xff0c;可以重复利用物品&#x…

快速安装mysql 5.7 服务步骤(包含clinet)及常见问题

1、centos上如何安装MySQL&#xff1a; 1.1.下载并安装mysql yum Repo&#xff1a; # 下载 wget https://dev.mysql.com/get/mysql57-community-release-el7-9.noarch.rpm # 安装 rpm -ivh mysql57-community-release-el7-9.noarch.rpm 1.2.进入yum.repos.d目录&#xff0c;安…

C语言每日一题(63)复写零

题目链接 力扣网 1089 复写零 题目描述 给你一个长度固定的整数数组 arr &#xff0c;请你将该数组中出现的每个零都复写一遍&#xff0c;并将其余的元素向右平移。 注意&#xff1a;请不要在超过该数组长度的位置写入元素。请对输入的数组 就地 进行上述修改&#xff0c;不…

【C++ Primer Plus学习记录】简单文件输入/输出

有时候&#xff0c;通过键盘输入并非最好的选择。例如&#xff0c;假设您编写了一个股票分析程序&#xff0c;并下载了一个文件&#xff0c;其中包含1000种股票的价格。在这种情况下&#xff0c;让程序直接读取文件&#xff0c;而不是手工输入文件中所有的值&#xff0c;将方便…

惬意了解 —— 前端发展史

下拉底部&#xff0c;参与投票&#xff5e;&#xff5e; 前端发展史&#xff1a;从洪荒时代到现代 前端开发已经走过了将近20年的历程&#xff0c;从最早的纯静态页面到如今的现代前端框架&#xff0c;我们见证了前端技术的蓬勃发展。让我们一起回顾这段历史。 洪荒时代&…

深入联合文件系统

Union File System&#xff08;联合文件系统&#xff0c;UnionFS&#xff09;是一种轻量级的高性能分层文件系统&#xff0c;它支持将文件系统中的修改信息作为一次提交&#xff0c;并层层叠加&#xff0c;同时可以将不同目录挂载到同一个虚拟文件系统下&#xff0c;应用看到的…

2024年A特种设备相关管理(锅炉压力容器压力管道)证考试题库及A特种设备相关管理(锅炉压力容器压力管道)试题解析

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2024年A特种设备相关管理&#xff08;锅炉压力容器压力管道&#xff09;证考试题库及A特种设备相关管理&#xff08;锅炉压力容器压力管道&#xff09;试题解析是安全生产模拟考试一点通结合&#xff08;安监局&#…

【天池课堂】零基础入门数据挖掘-课程汇总

写在前面&#xff1a; 如果你现在很迷茫&#xff0c;但是又对数据挖掘感兴趣&#xff0c;建议先看看以下两个视频直播&#xff0c;两位大佬亲身讲述自己和数据挖掘的前世今生。 《如何入门数据挖掘竞赛》 鱼遇雨欲语与余。天池明星选手&#xff0c;武汉大学硕士&#xff0c;天…

前端框架的演进之路:从静态网页到现代交互体验的探索

前端框架的发展史 随着互联网的快速发展&#xff0c;前端技术也在不断进步&#xff0c;前端框架作为前端开发的重要工具&#xff0c;经历了从简单到复杂、从单一到多元的演变过程。本文将回顾前端框架的发展史&#xff0c;探讨其变迁背后的原因和趋势。 一、静态网页时代 在…