Python+PyQt5开发一个Windows EXE程序在线更新工具

 更新时间:2026年01月04日 09:33:03   作者:weixin_46244623  
这篇文章主要为大家详细介绍了Python+PyQt5开发一个Windows EXE程序在线更新工具,可以自动检测新版本并完成在线升级,感兴趣的小伙伴可以了解下

一、前言

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

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

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

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

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

二、运行效果说明

整体更新流程如下:

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

三、环境与依赖说明

Python 版本

Python 3.8+

pip 依赖安装

pip install PyQt5
pip install requests
pip install packaging

(如需打包 EXE)

pip install pyinstaller

四、服务器端准备

版本号文件(version.txt)

1.2.0

更新包(update.zip)

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

五、完整代码

SoftVesion.py
def current_version():
    """
    Returns the version of the package
    """
    return "1.1.0"
def latest_version():
    """
    Returns the latest version of the package
    """
    return "1.1.0"
def upgrade_date():
    """
    Returns the time of the upgrade
    """
    return "2024年1月19日"
def current_title():
    """
    Returns the title of the package
    """
    return "Excel常用小工具"
def remote_version_url():
    """
    Returns the url of the latest version
    """
    return "http://82.157.62.197:97/version.txt"
def update_url():
    """
    Returns the url of the version info
    """
    return "http://192.168.31.219:8080/update.zip"

UpdateApp.py

import os
import zipfile
import requests
from packaging import version
from PyQt5.QtCore import QThread, pyqtSignal, Qt
from PyQt5.QtWidgets import QApplication,QDialog, QVBoxLayout,QMainWindow, QPushButton, QMessageBox, QProgressDialog,QLabel,QAction,QDesktopWidget
import shutil
from .SoftVesion import current_version,latest_version,remote_version_url,update_url,current_title
from PyQt5.QtGui import QStandardItemModel, QStandardItem,QIcon
import sys
import time
import subprocess
class DownloadThread(QThread):
    progressChanged = pyqtSignal(int)
    downloadFinished = pyqtSignal()
    unableToFetchUpdateLink = pyqtSignal()

    def __init__(self, url, save_path):
        super().__init__()
        self.url = url
        self.save_path = save_path

    def run(self):
        try:
            response = requests.get(self.url, stream=True)
            if response.status_code == 200:
                total_size = int(response.headers.get('content-length', 0))

                downloaded_size = 0
                with open(self.save_path, 'wb') as file:
                    for data in response.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()
        except Exception as e:
            print("Error fetching update link:", e)
            self.unableToFetchUpdateLink.emit()

class UpdateApp(QDialog):
    def __init__(self,parent=None):
        super().__init__(parent)
        self.current_version = current_version()  # 当前应用程序版本号
        self.latest_version = latest_version()
        self.progress_dialog = None
        self.download_thread = None
        self.remote_version_url= remote_version_url()
        self.update_url = update_url()
        self.init_ui()
        #self.show_update_notification()
    def move_to_center(self, widget):
        # 将 widget 移动到屏幕中央
        desktop_center = QDesktopWidget().availableGeometry().center()
        widget_frame = widget.frameGeometry()
        widget_frame.moveCenter(desktop_center)
        widget.move(widget_frame.topLeft())
    def init_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)

        if self.download_thread:
            self.download_thread.unableToFetchUpdateLink.connect(self.show_unable_to_fetch_update_link)

    def show_unable_to_fetch_update_link(self):
        QMessageBox.critical(self, "无法获取更新链接", "无法获取更新链接,请检查网络连接。")

    def update_app(self):
        try:
            remote_version = self.get_remote_version()
            if version.parse(remote_version) > version.parse(self.current_version):
                if self.has_network_connection():
                    self.perform_upgrade()
                else:
                    QMessageBox.information(self, "无法获取版本号", "无法获取远程版本号,请检查网络连接。")
            else:
                QMessageBox.information(self, "无需升级", "当前应用程序已经是最新版本。")
        except:
            QMessageBox.information(self, "无法获取版本号", "无法获取远程版本号,请检查网络连接。")

    def get_remote_version(self):
        try:
            response = requests.get(self.remote_version_url)
            if response.status_code == 200:
                return response.text.strip()
            return self.latest_version
        except:
            return self.latest_version

    def perform_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()

    def update_progress_bar(self, progress):
        self.progress_dialog.setValue(progress)

    def handle_download_finished(self):
        self.progress_dialog.hide()
        if os.path.exists("temp_update.zip"):
            self.process_download("temp_update.zip")
            os.remove("temp_update.zip")
        sys.exit()

    def has_network_connection(self):
        try:
            requests.get(remote_version_url(), timeout=5)
            return True
        except:
            return False

    def show_update_notification(self):
        try:
            remote_version = self.get_remote_version()
            if version.parse(remote_version) > version.parse(self.current_version):
                reply = QMessageBox.question(
                    self, "版本更新提示",
                    f"有新版本可用:{remote_version}\n您当前的版本:{self.current_version}\n是否要更新?",
                    QMessageBox.Yes | QMessageBox.No
                )
                if reply == QMessageBox.Yes:
                    self.update_app()
            else:
                QMessageBox.information(self, "版本更新提示", "您当前的版本是最新版本。")
        except:
            pass

if __name__ == "__main__":
    app = QApplication([])
    window = UpdateApp()
    window.show()
    app.exec_()

六、关键设计说明

使用 QThread 防止界面卡死

使用 packaging.version 进行版本比较

ZIP 更新包支持扩展文件

EXE 覆盖前自动备份

更新完成后安全退出

七、适用场景

PyQt5 工具软件

内部办公系统

单机 EXE 程序

不依赖第三方更新框架

八、总结

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

到此这篇关于Python+PyQt5开发一个Windows EXE程序在线更新工具的文章就介绍到这了,更多相关Python EXE程序更新内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • GPU版本安装Pytorch的最新方法步骤

    GPU版本安装Pytorch的最新方法步骤

    最近深度学习需要用GPU版本的pytorch来加速运算,所以下面这篇文章主要给大家介绍了关于GPU版本安装Pytorch的最新方法步骤,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2023-02-02
  • Python实现输入二叉树的先序和中序遍历,再输出后序遍历操作示例

    Python实现输入二叉树的先序和中序遍历,再输出后序遍历操作示例

    这篇文章主要介绍了Python实现输入二叉树的先序和中序遍历,再输出后序遍历操作,涉及Python基于先序遍历和中序遍历构造二叉树,再后序遍历输出相关操作技巧,需要的朋友可以参考下
    2018-07-07
  • Python+Turtle制作独特的表白图

    Python+Turtle制作独特的表白图

    这篇文章主要利用Python和Turtle库绘制独特的表白图,文中的示例代码讲解详细,对我们学习Python有一定的帮助,感兴趣的可以了解一下
    2022-04-04
  • Python文件读写6大实用方法小结

    Python文件读写6大实用方法小结

    Python文件读写的6大实用方法涵盖了从基本读取到高级操作的不同场景,本文给大家介绍了是这些方法的具体使用,并通过代码示例介绍的非常详细,需要的朋友可以参考下
    2025-01-01
  • PyTorch Dataset与DataLoader使用超详细讲解

    PyTorch Dataset与DataLoader使用超详细讲解

    用于处理数据样本的代码可能会变得凌乱且难以维护;理想情况下,我们希望数据集代码与模型训练代码解耦,以获得更好的可读性和模块化。PyTorch提供的torch.utils.data.DataLoader和torch.utils.data.Dataset允许你使用预下载的数据集或自己制作的数据
    2022-10-10
  • 在python中实现同行输入/接收多个数据的示例

    在python中实现同行输入/接收多个数据的示例

    今天小编就为大家分享一篇在python中实现同行输入/接收多个数据的示例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2019-07-07
  • Python进行图像处理不可或缺的10种工具

    Python进行图像处理不可或缺的10种工具

    在图像处理领域,Python凭借其丰富的库和工具,成为了数据科学家和工程师们首选的编程语言,以下是图像处理中Python常用的10种工具,并附上简单的代码示例,需要的朋友可以参考下
    2025-12-12
  • Python将一个Excel拆分为多个Excel

    Python将一个Excel拆分为多个Excel

    这篇文章主要为大家详细介绍了Python将一个Excel拆分为多个Excel,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-11-11
  • Python 专题四 文件基础知识

    Python 专题四 文件基础知识

    本文主要讲述了Python文件基础知识,包括文件的打开、读写、关闭操作、使用循环读写文件及迭代器的知识。具有很好的参考价值。下面跟着小编一起来看下吧
    2017-03-03
  • python index() 与 rindex() 方法的使用示例详解

    python index() 与 rindex() 方法的使用示例详解

    这篇文章主要介绍了python index() 与 rindex() 方法的使用,需要的朋友可以参考下
    2022-12-12

最新评论