2013-05-14 29 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. 更新程序已完成更新應用程序的文件 - 在所有必需的文件被更改後,我們不再需要更新程序,並且我們還希望再次啓動應用程序。如果重新啓動應用程序不在菜單上,您也可以跳過此步驟。但請記住,許多應用程序在更新時提供自動重新啓動,因爲它增加了用戶體驗 - 用戶不必再次手動啓動應用程序。你可以選擇它(甚至更好),這是肯定更靈活。如果需要重新啓動,則基本上可以執行與從應用程序啓動更新程序完全相同的過程,但這次您以相反的方式執行 - 您將應用程序作爲更新程序內的分離進程窗體啓動,並簡單地退出更新程序。

即使這是一個大量的文字閱讀的實際執行情況(特別是第二個)並不困難。

希望這可以幫助別人。