2013-05-14 98 views
5

我有一个PyQt应用程序准备发布。这是我第一次尝试这种与我无关的事情。一切工作都很好,我只有一件事要结束。我喜欢更新自己的软件:pyqt应用程序执行更新

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

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

基于某些用户选择,我的程序还会下载和安装一些需要相同功能的第三方软件:关闭安装程序,安装程序后重新启动。

回答

6

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

+0

还没有尝试过,但似乎是一个优雅的解决方案,所以我现在只需+1。 – 2013-11-13 21:42:01

0

这就是为什么这么多公司在您的计算机上安装独立的更新服务应用程序。 Adobe做到了,Google做到了,似乎每个人都在做。避免这种情况的一种方法是让你的应用程序由一个“启动器”应用程序启动,该应用程序首先检查主应用程序的更新,如果没有更新,它会启动主应用程序,但如果有更新,它会先应用它,然后应用它启动主应用程序。

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

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 == True(如果该过程已成功启动,则返回True)。 但是你可能要存储PID调用

      `QtCore.QCoreApplication.applicationPid()` 
      

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

    3. 更新程序更新应用程序的文件 - 更新程序现在开心地运行。使用我们应用程序的存储PID,它还确保应用程序的进程不再运行。时间去做魔术。在这一点上,你可以做任何你想做的事情。当然,请记住,您可能需要访问权限才能更改文件。如果这个过程还没有以适当的权利开始,它将无法做到这一点。确保这个部门的一切都很好。您可以用提升的权限产生一个进程。这可能需要输入一些密码,在这种情况下,您也必须处理此密码。

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

即使这是一个大量的文字阅读的实际执行情况(特别是第二个)并不困难。

希望这可以帮助别人。