Added initial implementation to WXUpdater class
This commit is contained in:
parent
ec214bc387
commit
42b60d19a1
143
updater/wxupdater.py
Normal file
143
updater/wxupdater.py
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
""" Updater implementation that supports WXPython phoenix.
|
||||||
|
|
||||||
|
This module allows you to perform authomatic updates via the updater package by instantiating the :py:class:`WXUpdater` class and calling to the :py:func:`WXUpdater.check_for_updates` function, which retrieves update information from the provided server, ask the user to authorize the update if needed, and displays a progress bar while the update downloads.
|
||||||
|
|
||||||
|
:note:
|
||||||
|
You need to have the WXPython library installed in your system. If you are not using this graphical user interface, probably you might use another update implementation.
|
||||||
|
|
||||||
|
Automatic updates can be implemented, within a WXPython application, in the following way:
|
||||||
|
|
||||||
|
>>> import wx
|
||||||
|
>>> from updater.wxupdater import WXUpdater
|
||||||
|
>>> app = wx.App()
|
||||||
|
>>> updater = WXUpdater(app_name="My app", current_version="0.1", endpoint="https://some_url.com")
|
||||||
|
>>> updater.check_for_updates()
|
||||||
|
>>> app.MainLoop()
|
||||||
|
|
||||||
|
Also, you can customize messages in the update dialogs via the parameters :py:data:`WXUpdater.new_update_title`, :py:data:`WXUpdater.new_update_msg`, :py:data:`WXUpdater.update_progress_title`, :py:data:`WXUpdater.update_progress_msg`, :py:data:`WXUpdater.update_almost_complete_title` and :py:data:`WXUpdater.update_almost_complete_msg`,
|
||||||
|
|
||||||
|
>>> import wx
|
||||||
|
>>> from updater.wxupdater import WXUpdater
|
||||||
|
>>> app = wx.App()
|
||||||
|
>>> updater = WXUpdater(app_name="My app", current_version="0.1", endpoint="https://some_url.com")
|
||||||
|
>>> updater.new_update_title = "New version of my awesome app is available"
|
||||||
|
>>> updater.new_update_msg = "Do you want to get it right now?"
|
||||||
|
>>> updater.check_for_updates()
|
||||||
|
>>> app.MainLoop()
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import tempfile
|
||||||
|
import wx # type: ignore
|
||||||
|
import logging
|
||||||
|
from typing import Optional, Any, cast
|
||||||
|
from pubsub import pub # type: ignore
|
||||||
|
from platform_utils import paths # type: ignore
|
||||||
|
from . import core, utils
|
||||||
|
|
||||||
|
log = logging.getLogger("updater.WXUpdater")
|
||||||
|
|
||||||
|
class WXUpdater(core.UpdaterCore):
|
||||||
|
""" Class to implement updates via a WXPython interface.
|
||||||
|
|
||||||
|
:ivar new_update_title: Title to display in the dialog when a new update is available.
|
||||||
|
:ivar new_update_msg: Text to display to users when an update is available. This text is displayed in a message box. It supports the following variables: {app_name}, {update_version} and {description} which are provided by the update information.
|
||||||
|
:ivar update_progress_title: Title to display when the update is in progress. Available variables are {total_downloaded} and {total_size}, which are human readable strings of data downloaded.
|
||||||
|
:ivar update_progress_msg: Text to display while update is downloading. Available variables are {total_downloaded} and {total_size}, which are human readable strings of data downloaded.
|
||||||
|
:ivar update_almost_complete_title: Title of the message to display to users when the update is about to be installed.
|
||||||
|
:ivar update_almost_complete_msg: Message to explain to users about the application restart, after updates are applied.
|
||||||
|
"""
|
||||||
|
|
||||||
|
new_update_title: str = "New version for {app_name}"
|
||||||
|
new_update_msg: str = "There's a new {app_name} version available. Would you like to download it now?\n\n {app_name} version: {update_version}\n\nChanges:\n{update_description}"
|
||||||
|
update_progress_title: str = "Downloading update..."
|
||||||
|
update_progress_msg: str = "Updating... {total_downloaded} of {total_size}"
|
||||||
|
update_almost_complete_title: str = "Done"
|
||||||
|
update_almost_complete_msg: str = "The update is about to be installed in your system. After being installed, the application will restart. Press OK to continue."
|
||||||
|
|
||||||
|
def __init__(self, new_update_title: Optional[str] = None, new_update_msg: Optional[str] = None, update_progress_title: Optional[str] = None, update_progress_msg: Optional[str] = None, update_almost_complete_title: Optional[str] = None, update_almost_complete_msg: Optional[str] = None, *args, **kwargs):
|
||||||
|
""" class constructor.
|
||||||
|
|
||||||
|
It accepts all parameters required by :py:class:`updater.core.UpdaterCore`, plus the following:
|
||||||
|
|
||||||
|
:param new_update_title: Title to display in the dialog when a new update is available.
|
||||||
|
:type new_update_title: str
|
||||||
|
:param new_update_msg: Text to display to users when an update is available. This text is displayed in a message box. It supports the following variables: {app_name}, {update_version} and {description} which are provided by the update information.
|
||||||
|
:type new_update_msg: str
|
||||||
|
:param update_progress_title: Title to display when the update is in progress. Available variables are {total_downloaded} and {total_size}, which are human readable strings of data downloaded.
|
||||||
|
:type update_progress_title: str
|
||||||
|
:param update_progress_msg: Text to display while update is downloading. Available variables are {total_downloaded} and {total_size}, which are human readable strings of data downloaded.
|
||||||
|
:type update_progress_msg: str
|
||||||
|
:param update_almost_complete_title: Title of the message to display to users when the update is about to be installed.
|
||||||
|
:type update_almost_complete_title: str
|
||||||
|
:param update_almost_complete_msg: Message to explain to users about the application restart, after updates are applied.
|
||||||
|
:type update_almost_complete_msg: str
|
||||||
|
"""
|
||||||
|
super(WXUpdater, self).__init__(*args, **kwargs)
|
||||||
|
if new_update_title:
|
||||||
|
self.new_update_title = new_update_title
|
||||||
|
if new_update_msg:
|
||||||
|
self.new_update_msg = new_update_msg
|
||||||
|
if update_progress_title:
|
||||||
|
self.update_progress_title = update_progress_title
|
||||||
|
if update_progress_msg:
|
||||||
|
self.update_progress_msg
|
||||||
|
if update_almost_complete_title:
|
||||||
|
self.update_almost_complete_title = update_almost_complete_title
|
||||||
|
if update_almost_complete_msg:
|
||||||
|
self.update_almost_complete_msg = update_almost_complete_msg
|
||||||
|
self.progress_dialog: Any = None
|
||||||
|
|
||||||
|
def initialize(self) -> None:
|
||||||
|
pub.subscribe(self.on_update_progress, "updater.update-progress")
|
||||||
|
|
||||||
|
def create_progress_dialog(self) -> None:
|
||||||
|
self.progress_dialog = wx.ProgressDialog(self.update_progress_msg.format(total_downloaded="0", total_size="0"), self.update_progress_title, parent=None, maximum=100)
|
||||||
|
|
||||||
|
def on_new_update_available(self) -> bool:
|
||||||
|
dialog = wx.MessageDialog(None, self.new_update_msg, self.new_update_title, style=wx.YES|wx.NO|wx.ICON_WARNING)
|
||||||
|
response = dialog.ShowModal()
|
||||||
|
dialog.Destroy()
|
||||||
|
if response == wx.ID_YES:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def on_update_progress(self, total_downloaded: int, total_size: int) -> None:
|
||||||
|
if self.progress_dialog == None:
|
||||||
|
self.create_progress_dialog()
|
||||||
|
self.progress_dialog.Show()
|
||||||
|
if total_downloaded == total_size:
|
||||||
|
self.progress_dialog.Destroy()
|
||||||
|
else:
|
||||||
|
self.progress_dialog.Update((total_downloaded*100)/total_size, self.update_progress_msg.format(total_downloaded=utils.convert_bytes(total_downloaded), total_size=utils.convert_bytes(total_size)))
|
||||||
|
self.progress_dialog.SetTitle(self.update_progress_msg.format(total_downloaded=utils.convert_bytes(total_downloaded), total_size=utils.convert_bytes(total_size)))
|
||||||
|
|
||||||
|
def on_update_almost_complete(self) -> None:
|
||||||
|
ms = wx.MessageDialog(None, self.update_almost_complete_msg, self.update_almost_complete_title)
|
||||||
|
return ms.ShowModal()
|
||||||
|
|
||||||
|
def check_for_updates(self) -> None:
|
||||||
|
self.create_session()
|
||||||
|
self.initialize()
|
||||||
|
update_info = self.get_update_information()
|
||||||
|
version_data = self.get_version_data(update_info)
|
||||||
|
if version_data[0] == False:
|
||||||
|
return None
|
||||||
|
response = self.on_new_update_available()
|
||||||
|
if response == False:
|
||||||
|
return None
|
||||||
|
base_path = tempfile.mkdtemp()
|
||||||
|
print(base_path)
|
||||||
|
download_path = os.path.join(base_path, 'update.zip')
|
||||||
|
downloaded = self.download_update(cast(str, version_data[2]), download_path)
|
||||||
|
update_path = os.path.join(base_path, 'update')
|
||||||
|
extraction_path = self.extract_update(downloaded, destination=update_path)
|
||||||
|
bootstrap_exe = self.move_bootstrap(extraction_path)
|
||||||
|
source_path = os.path.join(paths.app_path(), "sandbox")
|
||||||
|
self.on_update_almost_complete()
|
||||||
|
self.execute_bootstrap(bootstrap_exe, source_path)
|
||||||
|
|
||||||
|
def __del__(self) -> None:
|
||||||
|
pub.unsubscribe(self.on_update_progress, "updater.update-progress")
|
Loading…
Reference in New Issue
Block a user