当前位置: 首页 > news >正文

PyQt5 实现 Windows EXE 程序在线更新(自动下载 + 覆盖升级)

一、前言

在使用PyQt5 + PyInstaller开发 Windows 桌面工具时,一个非常现实的问题是:

👉程序如何自动检测新版本,并完成在线升级?

本文基于一个真实可用、已落地的更新方案,实现了:

  • ✅ 远程版本号检测
  • ✅ 语义化版本对比
  • ✅ 更新提示弹窗
  • ✅ 子线程下载更新包
  • ✅ 实时下载进度条
  • ✅ ZIP 解压
  • ✅ EXE 自动替换
  • ✅ 旧版本自动备份
  • ✅ 更新完成后安全退出

非常适合工具类 / 内部系统 / 单机 EXE 程序


二、运行效果说明

整体更新流程如下:

  1. 点击「获取最新版本」
  2. 请求远程version.txt
  3. 发现新版本 → 弹窗提示
  4. 用户确认 → 下载update.zip
  5. 显示下载进度条
  6. 解压更新文件
  7. 备份旧 EXE
  8. 替换新 EXE
  9. 提示升级完成并退出程序

三、环境与依赖说明

1️⃣ Python 版本

Python 3.8+

2️⃣ pip 依赖安装

pipinstallPyQt5 pipinstallrequests pipinstallpackaging

(如需打包 EXE)

pipinstallpyinstaller

四、服务器端准备

1️⃣ 版本号文件(version.txt)

1.2.0

2️⃣ 更新包(update.zip)

update.zip ├── update.exe ├── logo.png(可选)

五、完整代码

SoftVesion.pydefcurrent_version():""" Returns the version of the package """return"1.1.0"deflatest_version():""" Returns the latest version of the package """return"1.1.0"defupgrade_date():""" Returns the time of the upgrade """return"2024年1月19日"defcurrent_title():""" Returns the title of the package """return"Excel常用小工具"defremote_version_url():""" Returns the url of the latest version """return"http://82.157.62.197:97/version.txt"defupdate_url():""" Returns the url of the version info """return"http://192.168.31.219:8080/update.zip"

UpdateApp.py

importosimportzipfileimportrequestsfrompackagingimportversionfromPyQt5.QtCoreimportQThread,pyqtSignal,QtfromPyQt5.QtWidgetsimportQApplication,QDialog,QVBoxLayout,QMainWindow,QPushButton,QMessageBox,QProgressDialog,QLabel,QAction,QDesktopWidgetimportshutilfrom.SoftVesionimportcurrent_version,latest_version,remote_version_url,update_url,current_titlefromPyQt5.QtGuiimportQStandardItemModel,QStandardItem,QIconimportsysimporttimeimportsubprocessclassDownloadThread(QThread):progressChanged=pyqtSignal(int)downloadFinished=pyqtSignal()unableToFetchUpdateLink=pyqtSignal()def__init__(self,url,save_path):super().__init__()self.url=url self.save_path=save_pathdefrun(self):try:response=requests.get(self.url,stream=True)ifresponse.status_code==200:total_size=int(response.headers.get('content-length',0))downloaded_size=0withopen(self.save_path,'wb')asfile:fordatainresponse.iter_content(chunk_size=8192):file.write(data)downloaded_size+=len(data)progress=int((downloaded_size/total_size)*100)self.progressChanged.emit(progress)self.downloadFinished.emit()else:print("Error fetching update link:")self.unableToFetchUpdateLink.emit()exceptExceptionase:print("Error fetching update link:",e)self.unableToFetchUpdateLink.emit()classUpdateApp(QDialog):def__init__(self,parent=None):super().__init__(parent)self.current_version=current_version()# 当前应用程序版本号self.latest_version=latest_version()self.progress_dialog=Noneself.download_thread=Noneself.remote_version_url=remote_version_url()self.update_url=update_url()self.init_ui()#self.show_update_notification()defmove_to_center(self,widget):# 将 widget 移动到屏幕中央desktop_center=QDesktopWidget().availableGeometry().center()widget_frame=widget.frameGeometry()widget_frame.moveCenter(desktop_center)widget.move(widget_frame.topLeft())definit_ui(self):self.setFixedSize(300,200)self.setWindowTitle("检查更新")icon=QIcon("icon.png")self.setWindowIcon(icon)self.version_label=QLabel("当前版本号: "+self.current_version,self)self.version_label.setAlignment(Qt.AlignCenter)self.version_label.setGeometry(100,10,110,20)self.update_button=QPushButton("获取最新版本",self)self.update_button.setGeometry(100,60,110,40)self.update_button.clicked.connect(self.show_update_notification)ifself.download_thread:self.download_thread.unableToFetchUpdateLink.connect(self.show_unable_to_fetch_update_link)defshow_unable_to_fetch_update_link(self):QMessageBox.critical(self,"无法获取更新链接","无法获取更新链接,请检查网络连接。")defupdate_app(self):try:remote_version=self.get_remote_version()ifversion.parse(remote_version)>version.parse(self.current_version):ifself.has_network_connection():self.perform_upgrade()else:QMessageBox.information(self,"无法获取版本号","无法获取远程版本号,请检查网络连接。")else:QMessageBox.information(self,"无需升级","当前应用程序已经是最新版本。")except:QMessageBox.information(self,"无法获取版本号","无法获取远程版本号,请检查网络连接。")defget_remote_version(self):try:response=requests.get(self.remote_version_url)ifresponse.status_code==200:returnresponse.text.strip()returnself.latest_versionexcept:returnself.latest_versiondefperform_upgrade(self):update_url=self.update_url temp_zip_path="temp_update.zip"self.download_thread=DownloadThread(update_url,temp_zip_path)self.download_thread.progressChanged.connect(self.update_progress_bar)self.download_thread.downloadFinished.connect(self.handle_download_finished)self.progress_dialog=QProgressDialog("正在下载更新...",None,0,100,self)self.progress_dialog.setWindowModality(Qt.WindowModal)self.progress_dialog.setAutoClose(False)self.progress_dialog.setAutoReset(False)self.progress_dialog.setWindowTitle("下载进度")self.move_to_center(self.progress_dialog)self.progress_dialog.canceled.connect(self.download_thread.quit)self.download_thread.start()defupdate_progress_bar(self,progress):self.progress_dialog.setValue(progress)defhandle_download_finished(self):self.progress_dialog.hide()ifos.path.exists("temp_update.zip"):self.process_download("temp_update.zip")os.remove("temp_update.zip")sys.exit()defhas_network_connection(self):try:requests.get(remote_version_url(),timeout=5)returnTrueexcept:returnFalsedefshow_update_notification(self):try:remote_version=self.get_remote_version()ifversion.parse(remote_version)>version.parse(self.current_version):reply=QMessageBox.question(self,"版本更新提示",f"有新版本可用:{remote_version}\n您当前的版本:{self.current_version}\n是否要更新?",QMessageBox.Yes|QMessageBox.No)ifreply==QMessageBox.Yes:self.update_app()else:QMessageBox.information(self,"版本更新提示","您当前的版本是最新版本。")except:passif__name__=="__main__":app=QApplication([])window=UpdateApp()window.show()app.exec_()

六、关键设计说明

✔ 使用 QThread 防止界面卡死

✔ 使用 packaging.version 进行版本比较

✔ ZIP 更新包支持扩展文件

✔ EXE 覆盖前自动备份

✔ 更新完成后安全退出


七、适用场景

  • PyQt5 工具软件
  • 内部办公系统
  • 单机 EXE 程序
  • 不依赖第三方更新框架

八、总结

本文提供的是一个可直接落地、真实可用的 PyQt5 自动更新方案,
无需复杂服务器逻辑,维护成本极低。

http://www.jsqmd.com/news/191752/

相关文章:

  • HeyGem能否在Colab上运行?远程GPU租用可行性分析
  • 文物管理系统|基于java+ vue文物管理系统(源码+数据库+文档)
  • HTML+CSS构建HeyGem WebUI界面?前端架构猜想
  • HeyGem系统支持WebP图片格式作为头像贴图
  • Java小白求职面试:从Spring Boot到微服务的技术深度探讨
  • HeyGem系统AI伦理探讨:数字人是否会取代真人?
  • 解决HeyGem处理速度慢问题:GPU加速配置建议
  • C#跨平台日志收集实战(日志架构设计大揭秘)
  • HeyGem批量生成失败?检查这五个常见配置错误
  • HeyGem WebUI界面功能详解:按钮、标签与交互逻辑
  • ESP32音频分类小白指南:轻松上手的第一步教程
  • Kubernetes集群管理多个HeyGem实例?大规模生成架构
  • 你不知道的C#权限黑科技:让.NET Core应用安全运行在非Windows系统
  • HeyGem数字人唇形匹配准确率实测:接近真人表现
  • HeyGem系统多语言界面翻译计划启动支持国际化
  • 揭秘C#指针编程:如何安全高效地使用不安全类型提升系统性能
  • C#能否调用HeyGem API?未来扩展可能性探讨
  • 揭秘C#跨平台日志难题:5步实现.NET Core全栈日志聚合
  • 【C#交错数组深度解析】:掌握高效访问技巧的5大核心方法
  • 2026年终加气砖厂家推荐:主流厂商横向对比与5家高可靠性排名解析。 - 十大品牌推荐
  • Multisim中的模拟电路搭建:零基础实战指南
  • 科哥开发的HeyGem系统安全性如何?本地部署无数据泄露风险
  • 高新技术企业认定哪家机构更靠谱?2026年终5强服务商权威测评与最终推荐! - 十大品牌推荐
  • C#网络拦截器性能优化秘籍,让高并发场景下的监控不再拖慢系统
  • HeyGem生成视频保存路径揭秘:outputs目录使用说明
  • Arduino Uno模拟与数字引脚区别:核心要点解析
  • 【C#跨平台日志收集终极指南】:从零搭建高效统一的日志系统
  • C#交错数组访问优化:90%开发者忽略的3个关键细节
  • 计算机毕业设计|基于springboot + vue民宿平台管理系统(源码+数据库+文档)
  • 2026年评价高的PET打包带厂家最新权威实力榜 - 品牌宣传支持者