在Python中我们有很多种方案来编写桌面图形用户界面程序,譬如内置的
然后,在程序启动的时候,调用一个子线程,向远程服务器发起请求,以获得是否需要进行更新。
主程序通过一个子线程使用
程序文件的下载,我们可以直接使用
这样,我们就实现了Python编写的桌面图形界面程序的更新检测、下载和覆盖安装。
Tkinter
、强大的
PyQt5
和
PySide2
,还有
wxPython
。借助这些或内置或第三方的模块,我们可以轻松地编写跨平台的桌面图形界面程序。同时,我们还能直接使用
PyInstaller
、
Nuitka
、
cx_Freeze
等
Python
第三方程序打包模块,将编写好桌面图形界面程序打包为各个平台的二进制可执行程序文件,比如
Windows
下的
exe
程序执行文件。通常我们会将程序按需打包为单个的
exe可执行文件
或包含整个文件夹的可执行文件。
程序更新
随着版本的迭代和功能的优化,我们编写的程序会需要进行更新。 在 Web 应用中,这个问题几乎不需要处理,因为只要服务器更新了,浏览器打开的页面或功能都将是最新的。而在桌面图形界面程序中,则有点棘手。因为我们打包好的程序都是离线分发的,按照以往的流程,我们需要重新打包程序,然后,在某个页面放上新版本程序的下载链接,最后提醒使用者下载更新。如果使用者没有注意到,那么就凉拌了。如何解决呢? 我们需要在编写的桌面图形界面程序中,额外添加一个更新机制,用于检测更新、下载更新和完成更新。下面我们来介绍一下这个流程。检测更新
要实现程序的更新,我们首先需要让程序能够检测到软件存在更新。 在这里,我们需要一个远程的更新服务器,来提供更新的检测响应。![37deb17d5efb9832e5bb6f6998e32d4e.png](https://i-blog.csdnimg.cn/blog_migrate/e207f2e3cd821c71eb5a9e8cfe1beedb.png)
下载更新
如果没有程序的更新,自然就略过了。如果有程序的更新,我们就需要对新版本的程序进行下载。下载有两种方式:直接通过主程序进行下载;
编写一个单独的更新程序,主程序调用更新程序进行下载;
![97e9a11078c7b4ddd5571bc7de8a8871.png](https://i-blog.csdnimg.cn/blog_migrate/005c1fb6842d3e6b7ae9eca554740d9c.png)
subprocess.Popen()
调用打包为单个文件的更新程序。然后更新程序再核对一遍是否存在版本更新,如果存在更新,那么继续请求远程服务器,下载新版本的程序。
![6f9e900c7c788d54418a7b7579c09bad.png](https://i-blog.csdnimg.cn/blog_migrate/cb2795b2dfb99ab8911856ce3dac67ce.png)
requests
模块,示例代码如下所示:
def work(self):
# 新建一个文件对象
temp_file = os.path.join(self.download_path,'update.zip') # 指定更新包下载的路径
logger.info("下载临时文件路径为:{}".format(temp_file))
with open(temp_file,'wb') as self.fileobj:
# 请求更新文件包
f = requests.get(self.download_url, stream=True)
# 写入文件
offset = 0
for chunk in f.iter_content(chunk_size=self.buffer):
if not chunk:
break
self.fileobj.seek(offset)
self.fileobj.write(chunk)
offset = offset + len(chunk)
proess = offset / int(self.filesize) * 100
# logger.info("写入块:{},{}".format(offset,proess))
self.download_proess_signal.emit(int(proess))
解压覆盖
新版本的程序下载完,我们就需要将其解压到原有的文件夹了。这时候,更新程序和主程序分开打包的好处就出来了, 我们通过更新程序将正在运行的主程序进程结束掉,然后将新版本的程序压缩包解压覆盖现有程序所在的文件夹:def work(self):
# 1.杀死主程序进程
for proc in psutil.process_iter():
if proc.name() == 'xxx.exe':
proc.kill()
# 2.获取程序本地路径
current_path = os.path.dirname(os.path.abspath(__file__))
logger.info("当前程序路径:{}".format(current_path))
# 3.解压新版本压缩包到程序本地路径
update_file_path = os.path.join(self.download_temp_path, 'update.zip')
shutil.unpack_archive(
filename=update_file_path,
extract_dir=current_path
)
# 4.删除临时文件夹和文件
os.remove(update_file_path)
shutil.rmtree(self.download_temp_path)
![3a2d17dcddf97045bd218d324846a623.png](https://i-blog.csdnimg.cn/blog_migrate/c7cd8372287d5b63410e066b0268f13a.png)