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

作者:

文章目录
  • 在使用 PyQt5 + PyInstaller 开发 Windows 桌面工具时,一个非常现实的问题是: 程序如何自动检测新版本,并完成在线升级? 本文基于一个真实可用、已落地的更新方案,实现了: 远程版本号检测 语义化版本对比 更新提示弹窗 子线程下载更新包 实时下载进度条 ZIP 解压 EXE 自动替换 旧版本自动备份 更新完成后安全退出 非常适合 工具类 / 内部系统 / 单机 EXE 程序。
  • 整体更新流程如下: 点击「获取最新版本」 请求远程 version.txt 发现新版本 → 弹窗提示 用户确认 → 下载 update.zip 显示下载进度条 解压更新文件 备份旧 EXE 替换新 EXE 提示升级完成并退出程序
  • 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程序更新内容请搜索风君子博客以前的文章或继续浏览下面的相关文章希望大家以后多多支持风君子博客! 您可能感兴趣的文章: 基于Python开发Windows自动更新控制工具 Python更新所有安装的包的实现方式 Python包的版本切换和更新方式 Python+Tkinter实现软件自动更新与提醒 在python3中实现更新界面
  • 目录
    • 一、前言
    • 二、运行效果说明
    • 三、环境与依赖说明
      • Python 版本
      • pip 依赖安装
    • 四、服务器端准备
      • 版本号文件(version.txt)
      • 更新包(update.zip)
    • 五、完整代码
      • 六、关键设计说明
        • 七、适用场景
          • 八、总结

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

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

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

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

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

            整体更新流程如下:

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

            Python 3.8+

            pip install PyQt5
            pip install requests
            pip install packaging
            

            (如需打包 EXE)

            pip install pyinstaller
            

            1.2.0

            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程序更新内容请搜索风君子博客以前的文章或继续浏览下面的相关文章希望大家以后多多支持风君子博客!

            您可能感兴趣的文章:

            • 基于Python开发Windows自动更新控制工具
            • Python更新所有安装的包的实现方式
            • Python包的版本切换和更新方式
            • Python+Tkinter实现软件自动更新与提醒
            • 在python3中实现更新界面

            站内搜索