pyqt应用程序执行更新

时间:2013-05-14 17:30:50

标签: pyqt updates

我准备好发布PyQt个应用程序了。一切都运作良好,我还有一件事要结束。我喜欢自我更新的软件:

  • 检查网址是否有新版本;
  • 找到新版本;
  • 通知用户更新(点击)→更新

问题是我不知道如何执行此更新。我检查,我发现新版本,我下载它然后我必须关闭应用程序并执行新版本的安装程序。如果我关闭它然后我不能执行任何其他操作,如果我执行安装程序我无法关闭应用程序。

根据一些用户选择,我的程序还会下载并安装一些需要相同功能的第三方软件:安装程序前关闭,安装程序后重启。

3 个答案:

答案 0 :(得分:6)

下载新版本的安装程序后,您可以使用atexit.register()os.exec*()来运行安装程序,例如atexit.register(os.execl, "installer.exe", "installer.exe")。这将使安装程序在应用程序即将退出时启动。 os.exec*()调用后,应用程序将立即退出,因此不会出现竞争条件。

答案 1 :(得分:2)

我喜欢接受的答案,但我有两条建议可供您考虑使用。它需要消化很多文本,但我希望它会很有趣,而且 - 最重要的是 - 有用。我要写的内容基本上是两种基于观察更新软件的方法,其中有多少其他应用程序可以工作。这两种情况都需要重新启动应用程序

  • 在运行时更换组件 - 一旦启动,应用程序就会加载到计算机的内存中并驻留在那里。除非必须以某种方式使用文件系统(例如打开并更改某些配置文件),否则应该能够在应用程序仍在运行时替换文件。在Unix / Linux平台上,就你可以改变什么而不是什么而言,事情会变得更加紧张。通常在Unix / Linux中,除非取消链接,否则无法更改正在运行的可执行文件(请参阅here)。我已经完成了几次,这太重要了。如果您不更新可执行文件,您甚至可以避免所有这些,只需替换其余文件(配置文件,库等)而不会出现任何问题。更新完成后,您可以提示用户重新启动应用程序,以便将新内容加载到内存中。我不确定它是否可能但是Qt插件基础结构可能允许在主应用程序仍在运行时添加新组件(还没有编写很多Qt插件,所以我不知道)。您可以使用接受的答案描述的内容重新启动,或继续阅读并应用第二种更新方法的部分内容。

  • 使用专用于更新主应用程序的外部流程进行更新 - Qt具有良好的基础架构来管理流程。如果你不喜欢它,你总是可以回到Python,这也提供了一种非常类似的做事方式。基本上我们可以将流程类型缩小到我们的业务场景的两种类型 - 附加(称为子流程)和分离(简称独立)。在你的情况下,我们可以排除第一个案例,因为 - 作为术语"子进程"可能会告诉你 - 一旦主要过程退出所有孩子也退出。我们不希望这样。我们想要的是一个独立的过程(你会看到一些原因)。分离进程的问题是这些......井......已经分离。这意味着分离的进程必须能够自行消亡或(如果需要)您需要恢复对它的控制并自行完成。否则,该过程将继续存在于您的记忆中,这可能不是我们想要的。在您的情况下,外部进程将是您的更新程序(在PyQt中再次编写,某些shell脚本或任何其他允许生成分离进程的内容)。这是你可以做的(我自己做了,甚至更多,它就像一个魅力):

    1. PyQt应用程序已完成下载文件夹X中的更新(位置应与更新程序应用程序查找新文件的位置一致)

    2. 使用

      生成分离的过程
      res, pid = QtCore.QProcess.startDetached('YOUR_EXTERNAL_UPDATING_PROGRAM')
      

      startDetached()的工作方式是,如果已成功启动,它将返回已启动外部进程的PID。对于我目前正在编写的应用程序,我实际上需要PID(为了在我的PyQt应用程序死于我的情况下恢复对生成进程的控制)所以我​​将它存储在一个文本文件中,该文件在我的主应用程序中读取再次发射(在崩溃或正常退出后)。这是我的场景的要求,因为即使UI崩溃并且UI必须在崩溃之前必须恢复到其声明(包括控制衍生进程的UI控制实体),生成的进程必须继续运行。您的更新程序不需要其中任何一个,因此您可以检查是否在成功启动进程的情况下返回res == TrueTrue)。 但是您可能希望将应用程序本身调用的PID存储在

      `QtCore.QCoreApplication.applicationPid()`
      

      你可能会问为什么?好吧,因为你想知道你的应用程序何时不再运行,然后启动更新程序(这是一个可靠的保险,当更新程序覆盖/删除/重命名应用程序的文件,包括可执行文件时不会发生冲突)。检查的最简单但非常不可靠的方法是在开始修补应用程序的文件之前,以等待特定时间的方式编写更新程序。这里的一个大问题是,要预测应用程序退出所需的时间以及从系统内存中刷新数据并不容易。所以另一种方式(有其他方式但不太可靠)是存储您要更新的应用程序的PID。一旦updater进程启动,它将只运行一个检查(在一个简单的while循环中)是否具有PID == 1234(例如)的进程仍在运行。为此,您有大量工具,包括您的平台提供的工具(有关使用shell命令的示例,请参阅(此处)[How to check if a process id (PID) exists)。一旦更新程序确保您的应用程序没有运行(如果操作系统是关于它的,我们无能为力;))它可以退出循环并启动实际的更新过程。此时,我们可以通过对话框窗口通知用户,例如"您的应用程序需要重新启动才能完成更新? [是] / [无]&#34 ;.如果用户选择NO,我们可以终止在后台运行的更新程序进程。否则我们可以退出应用程序并让更新程序执行它的操作。

    3. 更新程序更新应用程序的文件 - 更新程序现在正在快乐地运行。使用我们应用程序存储的PID,它还确保应用程序的进程不再运行。是时候做魔术了。此时你可以做任何你想做的事。当然,请记住,您可能需要访问权限才能更改文件。如果该流程尚未以适当的权限启动,则无法执行任何操作。确保这个部门的一切都很好。您可以使用提升的权限生成进程。这可能需要输入一些密码,在这种情况下你也必须处理这个问题。

    4. 更新程序已完成更新应用程序的文件 - 在更改了所有必需文件后,我们不再需要更新程序并且我们还想再次启动应用程序。如果菜单上没有重新启动应用程序,也可以跳过此步骤。请记住,虽然许多应用程序确实在更新时提供自动重启,因为它增加了用户体验 - 用户不必再次手动启动应用程序。你可以选择(甚至更好),这肯定更灵活。如果需要重新启动,您基本上可以执行与从应用程序启动更新程序所使用的完全相同的过程,但这次您以相反的方式执行此操作 - 在更新程序内启动应用程序作为分离的进程表单,然后只需退出更新程序。

即使这是很多文本阅读实际的实现(特别是第二个)并不困难。

希望这有助于某人。

答案 2 :(得分:0)

这就是为什么这么多公司在您的计算机上安装单独的更新服务应用程序的原因。 Adobe这样做,谷歌做到了,似乎每个人都在这样做。避免这种情况的一种方法是通过首先检查主应用程序更新的'启动器'应用程序启动应用程序,如果没有更新则启动主应用程序,但如果有更新,则先应用它然后再应用它推出主应用程序。

由于您正在使用pyqt,您可以做的另一件事是在您的app动态加载的python脚本文件中提供应用程序的一些功能。这对py2exe来说相当容易。就py2exe而言,在“脚本”或“插件”文件夹中将python脚本文件视为捆绑数据文件,并在运行时从文件夹中导入它们。然后,您的应用可以在加载之前检查更新版本,下载并更新脚本。