我有一个PyQt准备发布的应用程序.一切都运作良好,我还有一件事要结束.我喜欢自我更新的软件:
问题是我不知道如何执行此更新.我检查,我发现新版本,我下载它然后我必须关闭应用程序并执行新版本的安装程序.如果我关闭它然后我不能执行任何其他,如果我执行安装程序我无法关闭应用程序.
基于一些用户选择我的程序还下载并安装了一些需要相同的第三方软件:安装程序前关闭,安装程序后重启.
下载新版本的安装程序后,您可以使用atexit.register()with os.exec*()来运行安装程序,例如atexit.register(os.execl, "installer.exe", "installer.exe").这将使安装程序在应用程序即将退出时启动.应用程序将在os.exec*()通话后立即退出,因此不会出现竞争条件.
我喜欢接受的答案,但是我有两个建议供您考虑使用。需要消化的文字很多,但我希望它会很有趣,而且最重要的是有帮助。我要写的基本上是两种更新软件的方法,纯粹基于观察有多少其他应用程序可以工作。这两种情况都需要重新启动应用程序
在仍在运行时更换组件- 一旦启动,应用程序就会加载到计算机内存中并驻留在其中。除非它必须以某种方式与文件系统一起工作(例如打开并更改某些配置文件),否则应该能够在应用程序仍在运行时替换文件。在 Unix/Linux 平台上,就可以更改和不可更改的内容而言,情况有点严格。通常,在 Unix/Linux 中,除非取消链接,否则无法更改正在运行的可执行文件(出于安全原因)(请参阅此处)。我已经做过好几次了,这实在是太了不起了。如果您不更新可执行文件,您甚至可以避免所有这些,只需替换其余文件(配置文件、库等),就不会出现任何问题。更新完成后,您可以提示用户重新启动应用程序,以便将新内容加载到内存中。我不确定这是否可能,但也许 Qt 插件基础设施允许在主应用程序仍在运行时添加新组件(还没有编写很多 Qt 插件,所以我不知道)。您可以使用已接受的答案中描述的内容进行重新启动,或者继续阅读并应用第二种更新方法的部分内容。
通过使用专用于更新主应用程序的外部进程进行更新- Qt 具有用于管理进程的良好基础设施。如果您不喜欢它,您可以随时使用 Python,它也提供了非常相似的做事方式。基本上,我们可以将场景的进程类型缩小为两种类型-附加(称为子进程)和分离(称为独立)。在您的情况下,我们可以排除第一种情况,因为正如术语“子进程”可能告诉您的那样,一旦主进程退出,所有子进程也会退出。我们不希望这样。我们想要的是一个独立的过程(稍后你就会明白为什么)。分离进程的问题是它们……嗯……分离。这意味着分离的进程必须能够自行终止,或者(如果需要)您需要恢复对它的控制并自行完成。否则该进程将继续驻留在您的内存中,这可能不是我们想要的。在你的情况下,外部进程将是你的更新程序(用 PyQt 再次编写,一些 shell 脚本或任何其他允许生成分离进程的东西)。这是你可以做的(我自己做过,甚至更重要的是,它就像一个魅力):
PyQt 应用程序已完成下载文件夹 X 中的更新(位置应与更新程序应用程序查找新文件的位置一致)
生成一个独立的进程
res, pid = QtCore.QProcess.startDetached('YOUR_EXTERNAL_UPDATING_PROGRAM')
Run Code Online (Sandbox Code Playgroud)
其工作原理startDetached()是,如果成功启动,它会返回已启动的外部进程的 PID。对于我当前正在编写的应用程序,我实际上需要 PID(为了恢复对生成进程的控制,以防我的 PyQt 应用程序死机),因此我将其存储在一个文本文件中,一旦我的主应用程序再次启动,该文件就会被读取(崩溃或正常退出后)。这是我的场景的要求,因为即使 UI 崩溃,生成的进程也必须保持运行,并且 UI 只需要恢复到崩溃之前的状态(包括控制生成的进程的 UI 控制实体)。您的更新程序不需要任何这些,因此您只需检查是否res == True(True在进程成功启动的情况下返回)。但是您可能想要存储应用程序本身调用的 PID
`QtCore.QCoreApplication.applicationPid()`
Run Code Online (Sandbox Code Playgroud)
You may ask why? Well, because you want to know WHEN your application is no longer running and THEN start the updater (this is a solid insurance that no conflicts will occur when the updater overwrites/remove/renames the application's files including the executable). The easiest but very unreliable way of checking that is to write your updater in such a way that it *waits* for a specific time before starting to tinker with the application's files. The big problem here is that it is not easy to predict how long your application will require to quit and for its data to be flushed from the system's memory. So the other way (there are others but not very reliable) is to store the PID of the application you want to update. Once the updater process has started it will just run a check (in a simple `while` loop) whether the process with PID == 1234 (for example) is still running or not. For that you have plenty of tools including those provided by your platform (see (here)[/sf/ask/213078491/] for an example using a shell command). Once the updater makes sure that your application is not running (if the OS lies about it there is nothing we can do about it ;)) it can exit the loop and start the actual update procedure. At this point we can notify the user with a dialog window such as "Your application needs to restart in order to complete an update? [yes]/[no]". If the user choses NO, we can kill the updater process that is running in the background. Otherwise we can quit the application and let the updater do its thing.
Run Code Online (Sandbox Code Playgroud)
更新程序更新您的应用程序的文件- 更新程序现在正在愉快地运行。使用我们应用程序存储的 PID,它还可以确保应用程序的进程不再运行。是时候施展魔法了。此时您可以做任何您想做的事情。当然请记住,您可能需要访问权限才能更改文件。如果该进程没有以适当的权限启动,它将无法执行任何操作。确保这个部门一切顺利。您可以生成具有提升权限的进程。这可能需要输入一些密码,在这种情况下您也必须处理这个问题。
更新程序已完成更新您的应用程序的文件- 更改所有必需的文件后,我们不再需要更新程序,并且我们还想再次启动应用程序。如果菜单上没有重新启动应用程序,您也可以跳过此步骤。请记住,许多应用程序确实在更新时提供自动重启功能,因为它增加了用户体验 - 用户无需再次手动启动应用程序。您可以将其设为可选(甚至更好),这绝对更加灵活。如果需要重新启动,您基本上可以执行与从应用程序启动更新程序完全相同的过程,但这次您以相反的方式执行 - 您将应用程序作为更新程序内的独立进程表单启动,然后只需退出更新程序即可。
尽管这是很多文本,但阅读实际实现(尤其是第二个)并不困难。
由于其依赖于平台的性质,我没有提到的第三个选项是服务。在 Linux、MacOS、Windows 等上您都有服务。您可以为您的应用程序创建更新程序服务(例如,Java 的更新程序服务会定期检查新更新并在发现更新后提示您)。此外,这与 Qt 无关,除非您的服务以某种方式使用 Qt。
希望这对某人有帮助。
| 归档时间: |
|
| 查看次数: |
1072 次 |
| 最近记录: |