PyQt5实现远程下载更新exe可执行文件
1、实现流程
1、获取远程http地址
2、获取需要更新的exe文件
3、点击更新
4、把exe强关闭
5、下载文件
6、更新
2、效果图
3、示例代码
conf.ini配置文件:
{"http_address_edit_value": "http://xxx.com/xxx/xxx.exe", "exe_name_edit_value": "\u83b7\u53d6IP.exe", "version_num_edit_value": "4.5.16"}
main.py文件:
# -*- coding: utf-8 -*-
"""
@contact: 微信 1257309054
@file: main.py
@time: 2024/3/9 21:16
@author: LDC
"""
import datetime
import json
import logging
import os
import subprocess
import sys
import time
import psutil as psutil
import requests
from PyQt5.QtCore import QThread, pyqtSignal
from PyQt5.QtWidgets import QMainWindow, QApplication
from update import Ui_MainWindow
class Window(QMainWindow, Ui_MainWindow):
def __init__(self):
super(QMainWindow, self).__init__()
self.log_msg = None # 更新日志
self.setup_ui() # 渲染画布
self.update_thread = UpdateThread(self) # 开启线程循环监控是否可以下载
self.connect_signals() # 绑定触发事件
def setup_ui(self):
self.setupUi(self) # 渲染pyqt5界面
self.get_conf_ini() # 获取当前参数
self.http_address_edit_value = self.conf['http_address_edit_value'] # 远程更新exe文件地址
self.http_address_edit.setText(self.http_address_edit_value)
self.exe_name_edit_value = self.conf['exe_name_edit_value'] # exe文件名
self.exe_name_edit.setText(self.exe_name_edit_value)
self.version_num_edit_value = self.conf['version_num_edit_value'] # 版本号
self.version_num_edit.setText(self.version_num_edit_value)
self.log_edit.setText('{},{}当前版本号{}'.format(
datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
self.exe_name_edit_value,
self.version_num_edit_value),
)
def get_conf_ini(self):
# 读取conf.ini
self.conf = {}
with open(r'conf.ini', 'r', encoding='utf-8') as f:
self.conf = json.loads(f.read())
def connect_signals(self):
# 绑定触发事件
self.btn_save.clicked.connect(self.btn_save_clicked)
self.btn_update.clicked.connect(self.btn_update_clicked)
# 输入框输入完成事件
self.http_address_edit.editingFinished.connect(self.http_address_edit_changed)
self.exe_name_edit.editingFinished.connect(self.exe_name_edit_changed)
self.version_num_edit.editingFinished.connect(self.version_num_edit_changed)
self.update_thread._signal_update.connect(self.update_threading_slot)
def http_address_edit_changed(self):
# 修改远程地址
self.http_address_edit_value = self.http_address_edit.text()
self.conf['http_address_edit_value'] = self.http_address_edit_value
def exe_name_edit_changed(self):
# 修改exe文件名
self.exe_name_edit_value = self.exe_name_edit.text()
self.conf['exe_name_edit_value'] = self.exe_name_edit_value
def version_num_edit_changed(self):
# 修改版本号
self.version_num_edit_value = self.version_num_edit.text()
self.conf['version_num_edit_value'] = self.version_num_edit_value
def btn_update_clicked(self):
# 更新exe文件
msg = '准备更新软件,正在关闭{}'.format(self.exe_name_edit_value)
print(msg)
self.log_edit.append(msg)
self.btn_update.setText('更新中...')
self.update_thread.start() # 开始下载
def btn_save_clicked(self):
# 保存
with open(r'conf.ini', 'w', encoding='utf-8') as f:
f.write(json.dumps(self.conf))
def save_log(self):
# 保存
if not self.log_msg:
return
with open(r'update_log.txt', 'a', encoding='utf-8') as f:
f.write(self.log_msg)
f.write('\n')
def update_threading_slot(self, data):
# 线程回调函数
data = json.loads(data)
is_update = data['is_update']
if is_update == 1:
msg = '{},更新成功,版本号:{}'.format(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
self.version_num_edit_value)
self.log_msg = msg
self.save_log()
self.log_edit.append(msg)
self.btn_update.setText('更新')
print('更新成功')
elif is_update == 2:
self.log_edit.append('{},开始更新'.format(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')))
print('开始更新')
else:
self.log_edit.append('{},更新失败,找不到远程文件'.format(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')))
self.btn_update.setText('更新')
print('更新失败')
class UpdateThread(QThread):
# 监控线程
_signal_update = pyqtSignal(str)
def __init__(self, parent=None):
super(UpdateThread, self).__init__(parent)
self.window = parent
def run(self):
'''
监控程序
'''
while 1:
time.sleep(1)
try:
while self.is_process_running(self.window.exe_name_edit_value):
os.popen('taskkill /f /im {}'.format(self.window.exe_name_edit_value)) # 关闭程序
time.sleep(2)
self._signal_update.emit(json.dumps({'is_update': 2})) # 发送信号给槽函数
# 下载远程exe文件
r = requests.get(self.window.http_address_edit_value)
with open(self.window.exe_name_edit_value, 'wb') as code:
code.write(r.content)
self._signal_update.emit(json.dumps({'is_update': 1})) # 发送信号给槽函数
except:
self._signal_update.emit(json.dumps({'is_update': 0})) # 发送信号给槽函数
break
def is_process_running(self, process_name):
# 查看程序是否还存活
for process in psutil.process_iter():
try:
if process.name() == process_name:
return True
except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
pass
return False
if __name__ == '__main__':
app = QApplication(sys.argv)
mywindow = Window()
mywindow.show()
sys.exit(app.exec_())
ui.py文件:
# -*- coding: utf-8 -*-
"""
@contact: 微信 1257309054
@file: main.py
@time: 2024/3/9 21:16
@author: LDC
"""
# Form implementation generated from reading ui file 'update.ui'
#
# Created by: PyQt5 UI code generator 5.15.4
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(714, 431)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.textBrowser = QtWidgets.QTextBrowser(self.centralwidget)
self.textBrowser.setGeometry(QtCore.QRect(15, 10, 681, 51))
self.textBrowser.setObjectName("textBrowser")
self.label = QtWidgets.QLabel(self.centralwidget)
self.label.setGeometry(QtCore.QRect(20, 80, 54, 31))
self.label.setObjectName("label")
self.http_address_edit = QtWidgets.QLineEdit(self.centralwidget)
self.http_address_edit.setGeometry(QtCore.QRect(80, 79, 501, 31))
self.http_address_edit.setObjectName("http_address_edit")
self.btn_save = QtWidgets.QPushButton(self.centralwidget)
self.btn_save.setGeometry(QtCore.QRect(590, 80, 75, 31))
self.btn_save.setObjectName("btn_save")
self.label_2 = QtWidgets.QLabel(self.centralwidget)
self.label_2.setGeometry(QtCore.QRect(20, 120, 54, 31))
self.label_2.setObjectName("label_2")
self.exe_name_edit = QtWidgets.QLineEdit(self.centralwidget)
self.exe_name_edit.setGeometry(QtCore.QRect(80, 120, 271, 31))
self.exe_name_edit.setObjectName("exe_name_edit")
self.btn_update = QtWidgets.QPushButton(self.centralwidget)
self.btn_update.setGeometry(QtCore.QRect(590, 120, 75, 31))
self.btn_update.setObjectName("btn_update")
self.label_3 = QtWidgets.QLabel(self.centralwidget)
self.label_3.setGeometry(QtCore.QRect(20, 160, 54, 31))
self.label_3.setObjectName("label_3")
self.label_4 = QtWidgets.QLabel(self.centralwidget)
self.label_4.setGeometry(QtCore.QRect(360, 130, 41, 21))
self.label_4.setObjectName("label_4")
self.version_num_edit = QtWidgets.QLineEdit(self.centralwidget)
self.version_num_edit.setGeometry(QtCore.QRect(400, 119, 181, 31))
self.version_num_edit.setObjectName("version_num_edit")
self.log_edit = QtWidgets.QTextBrowser(self.centralwidget)
self.log_edit.setGeometry(QtCore.QRect(80, 170, 501, 192))
self.log_edit.setObjectName("log_edit")
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 714, 23))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.textBrowser.setHtml(_translate("MainWindow", "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\">\n"
"<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\n"
"p, li { white-space: pre-wrap; }\n"
"</style></head><body style=\" font-family:\'SimSun\'; font-size:9pt; font-weight:400; font-style:normal;\">\n"
"<p align=\"center\" style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-size:26pt; color:#0000ff;\">远程更新exe可执行文件</span></p></body></html>"))
self.label.setText(_translate("MainWindow", "远程地址"))
self.btn_save.setText(_translate("MainWindow", "保存"))
self.label_2.setText(_translate("MainWindow", "exe文件名"))
self.btn_update.setText(_translate("MainWindow", "更新"))
self.label_3.setText(_translate("MainWindow", "更新日志"))
self.label_4.setText(_translate("MainWindow", "版本号"))