mirror of
https://github.com/MCV-Software/TWBlue.git
synced 2024-11-26 12:53:12 -06:00
Updater has been refactored
This commit is contained in:
parent
10d0b8ba6b
commit
d9d0efab2d
@ -1,2 +1,12 @@
|
|||||||
import updater
|
import glob
|
||||||
import update_manager
|
import os.path
|
||||||
|
import platform
|
||||||
|
|
||||||
|
def find_datafiles():
|
||||||
|
system = platform.system()
|
||||||
|
if system == 'Windows':
|
||||||
|
file_ext = '*.exe'
|
||||||
|
else:
|
||||||
|
file_ext = '*.sh'
|
||||||
|
path = os.path.abspath(os.path.join(__path__[0], 'bootstrappers', file_ext))
|
||||||
|
return [('', glob.glob(path))]
|
||||||
|
114
src/updater/update.py
Normal file
114
src/updater/update.py
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
from logging import getLogger
|
||||||
|
logger = getLogger('update')
|
||||||
|
|
||||||
|
import contextlib
|
||||||
|
import io
|
||||||
|
import os
|
||||||
|
import platform
|
||||||
|
import requests
|
||||||
|
import tempfile
|
||||||
|
try:
|
||||||
|
import czipfile as zipfile
|
||||||
|
except ImportError:
|
||||||
|
import zipfile
|
||||||
|
|
||||||
|
from platform_utils import paths
|
||||||
|
|
||||||
|
def perform_update(endpoint, current_version, app_name='', password=None, update_available_callback=None, progress_callback=None, update_complete_callback=None):
|
||||||
|
requests_session = create_requests_session(app_name=app_name, version=current_version)
|
||||||
|
available_update = find_update(endpoint, requests_session=requests_session)
|
||||||
|
if not available_update:
|
||||||
|
return
|
||||||
|
available_version = available_update['current_version']
|
||||||
|
if not str(available_version) > str(current_version) or platform.system() not in available_update['downloads']:
|
||||||
|
return
|
||||||
|
available_description = available_update.get('description', None)
|
||||||
|
update_url = available_update ['downloads'][platform.system()]
|
||||||
|
logger.info("A new update is available. Version %s" % available_version)
|
||||||
|
if callable(update_available_callback) and not update_available_callback(version=available_version, description=available_description): #update_available_callback should return a falsy value to stop the process
|
||||||
|
logger.info("User canceled update.")
|
||||||
|
return
|
||||||
|
base_path = tempfile.mkdtemp()
|
||||||
|
download_path = os.path.join(base_path, 'update.zip')
|
||||||
|
update_path = os.path.join(base_path, 'update')
|
||||||
|
downloaded = download_update(update_url, download_path, requests_session=requests_session, progress_callback=progress_callback)
|
||||||
|
extracted = extract_update(downloaded, update_path, password=password)
|
||||||
|
bootstrap_path = move_bootstrap(extracted)
|
||||||
|
execute_bootstrap(bootstrap_path, extracted)
|
||||||
|
logger.info("Update prepared for installation.")
|
||||||
|
if callable(update_complete_callback):
|
||||||
|
update_complete_callback()
|
||||||
|
|
||||||
|
def create_requests_session(app_name=None, version=None):
|
||||||
|
user_agent = ''
|
||||||
|
session = requests.session()
|
||||||
|
if app_name:
|
||||||
|
user_agent = ' %s/%r' % (app_name, version)
|
||||||
|
session.headers['User-Agent'] = session.headers['User-Agent'] + user_agent
|
||||||
|
return session
|
||||||
|
|
||||||
|
def find_update(endpoint, requests_session):
|
||||||
|
response = requests_session.get(endpoint)
|
||||||
|
response.raise_for_status()
|
||||||
|
content = response.json()
|
||||||
|
return content
|
||||||
|
|
||||||
|
def download_update(update_url, update_destination, requests_session, progress_callback=None, chunk_size=io.DEFAULT_BUFFER_SIZE):
|
||||||
|
total_downloaded = total_size = 0
|
||||||
|
with io.open(update_destination, 'w+b') as outfile:
|
||||||
|
download = requests_session.get(update_url, stream=True)
|
||||||
|
total_size = int(download.headers.get('content-length', 0))
|
||||||
|
logger.debug("Total update size: %d" % total_size)
|
||||||
|
download.raise_for_status()
|
||||||
|
for chunk in download.iter_content(chunk_size):
|
||||||
|
outfile.write(chunk)
|
||||||
|
total_downloaded += len(chunk)
|
||||||
|
if callable(progress_callback):
|
||||||
|
call_callback(progress_callback, total_downloaded, total_size)
|
||||||
|
logger.debug("Update downloaded")
|
||||||
|
return update_destination
|
||||||
|
|
||||||
|
def extract_update(update_archive, destination, password=None):
|
||||||
|
"""Given an update archive, extracts it. Returns the directory to which it has been extracted"""
|
||||||
|
with contextlib.closing(zipfile.ZipFile(update_archive)) as archive:
|
||||||
|
if password:
|
||||||
|
archive.setpassword(password)
|
||||||
|
archive.extractall(path=destination)
|
||||||
|
logger.debug("Update extracted")
|
||||||
|
return destination
|
||||||
|
|
||||||
|
def move_bootstrap(extracted_path):
|
||||||
|
working_path = os.path.abspath(os.path.join(extracted_path, '..'))
|
||||||
|
if platform.system() == 'Darwin':
|
||||||
|
extracted_path = os.path.join(extracted_path, 'Contents', 'Resources')
|
||||||
|
downloaded_bootstrap = os.path.join(extracted_path, bootstrap_name())
|
||||||
|
new_bootstrap_path = os.path.join(working_path, bootstrap_name())
|
||||||
|
os.rename(downloaded_bootstrap, new_bootstrap_path)
|
||||||
|
return new_bootstrap_path
|
||||||
|
|
||||||
|
def execute_bootstrap(bootstrap_path, source_path):
|
||||||
|
arguments = r'"%s" "%s" "%s" "%s"' % (os.getpid(), source_path, paths.app_path(), paths.get_executable())
|
||||||
|
if platform.system() == 'Windows':
|
||||||
|
import win32api
|
||||||
|
win32api.ShellExecute(0, 'open', bootstrap_path, arguments, '', 5)
|
||||||
|
else:
|
||||||
|
import subprocess
|
||||||
|
make_executable(bootstrap_path)
|
||||||
|
subprocess.Popen(['%s %s' % (bootstrap_path, arguments)], shell=True)
|
||||||
|
logger.info("Bootstrap executed")
|
||||||
|
|
||||||
|
def bootstrap_name():
|
||||||
|
if platform.system() == 'Windows': return 'bootstrap.exe'
|
||||||
|
if platform.system() == 'Darwin': return 'bootstrap-mac.sh'
|
||||||
|
return 'bootstrap-lin.sh'
|
||||||
|
|
||||||
|
def make_executable(path):
|
||||||
|
import stat
|
||||||
|
st = os.stat(path)
|
||||||
|
os.chmod(path, st.st_mode | stat.S_IEXEC)
|
||||||
|
|
||||||
|
def call_callback(callback, *args, **kwargs):
|
||||||
|
# try:
|
||||||
|
callback(*args, **kwargs)
|
||||||
|
# except:
|
||||||
|
# logger.exception("Failed calling callback %r with args %r and kwargs %r" % (callback, args, kwargs))
|
@ -1,36 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
import os, sys, wx
|
|
||||||
from mysc import paths
|
|
||||||
import application, updater
|
|
||||||
from mysc.thread_utils import call_threaded
|
|
||||||
import logging as original_logger
|
|
||||||
log = original_logger.getLogger("update_manager")
|
|
||||||
|
|
||||||
def check_for_update(msg=False):
|
|
||||||
log.debug(u"Checking for updates...")
|
|
||||||
url = updater.find_update_url(application.update_url, application.version)
|
|
||||||
if url is None:
|
|
||||||
if msg == True:
|
|
||||||
wx.MessageDialog(None, _(u"Your TW Blue version is up to date"), _(u"Update"), style=wx.OK).ShowModal()
|
|
||||||
return
|
|
||||||
else:
|
|
||||||
log.debug(u"New version from %s " % url)
|
|
||||||
new_path = os.path.join(paths.app_path("delete-me"), 'updates', 'update.zip')
|
|
||||||
log.debug(u"Descargando actualización en %s" % new_path)
|
|
||||||
d = wx.MessageDialog(None, _(u"There's a new TW Blue version available. Would you like to download it now?"), _(u"New version for %s") % application.name, style=wx.YES|wx.NO|wx.ICON_WARNING)
|
|
||||||
if d.ShowModal() == wx.ID_YES:
|
|
||||||
progress = wx.ProgressDialog(_(u"Download in Progress"), _(u"Downloading the new version..."), parent=None, maximum=100, style = wx.PD_APP_MODAL)
|
|
||||||
def update(percent):
|
|
||||||
if percent == 100:
|
|
||||||
progress.Destroy()
|
|
||||||
else:
|
|
||||||
progress.Update(percent, _(u"Update"))
|
|
||||||
def update_complete():
|
|
||||||
wx.MessageDialog(None, _(u"The new TW Blue version has been downloaded and installed. Press OK to start the application."), _(u"Done!")).ShowModal()
|
|
||||||
sys.exit()
|
|
||||||
app_updater = updater.AutoUpdater(url, new_path, 'bootstrap.exe', app_path=paths.app_path(), postexecute=paths.app_path("TWBlue.exe"), finish_callback=update_complete, percentage_callback=update)
|
|
||||||
app_updater.start_update()
|
|
||||||
progress.ShowModal()
|
|
||||||
else:
|
|
||||||
return
|
|
||||||
|
|
@ -1,220 +1,12 @@
|
|||||||
#AutoUpdater
|
# -*- coding: utf-8 -*-
|
||||||
#Released under an MIT license
|
|
||||||
|
|
||||||
import logging
|
|
||||||
logger = logging.getLogger('updater')
|
|
||||||
|
|
||||||
import application
|
import application
|
||||||
from urllib import FancyURLopener, URLopener
|
import update
|
||||||
import urllib2
|
|
||||||
from functools import total_ordering
|
|
||||||
import hashlib
|
|
||||||
import os
|
|
||||||
try:
|
|
||||||
from czipfile import ZipFile
|
|
||||||
except ImportError:
|
|
||||||
from zipfile import ZipFile
|
|
||||||
import subprocess
|
|
||||||
import stat
|
|
||||||
import platform
|
import platform
|
||||||
import shutil
|
#if platform.system() == "Windows":
|
||||||
import json
|
from wxUpdater import *
|
||||||
if platform.system() == 'Windows':
|
|
||||||
import win32api
|
|
||||||
|
|
||||||
|
def do_update():
|
||||||
class AutoUpdater(object):
|
try:
|
||||||
|
update.perform_update(endpoint=application.update_url, current_version=application.version, app_name=application.name, update_available_callback=available_update_dialog, progress_callback=progress_callback, update_complete_callback=update_finished)
|
||||||
def __init__(self, URL, save_location, bootstrapper, app_path, postexecute=None, password=None, MD5=None, percentage_callback=None, finish_callback=None):
|
except:
|
||||||
"""Supply a URL/location/bootstrapper filename to download a zip file from
|
pass
|
||||||
The finish_callback argument should be a Python function it'll call when done"""
|
|
||||||
#Let's download the file using urllib
|
|
||||||
self.complete = 0
|
|
||||||
self.finish_callback = finish_callback #What to do on exit
|
|
||||||
self.percentage_callback = percentage_callback or self.print_percentage_callback
|
|
||||||
self.URL = URL
|
|
||||||
self.bootstrapper = bootstrapper
|
|
||||||
#The application path on the Mac should be 1 directory up from where the .app file is.
|
|
||||||
tempstr = ""
|
|
||||||
if (platform.system() == "Darwin"):
|
|
||||||
for x in (app_path.split("/")):
|
|
||||||
if (".app" in x):
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
tempstr = os.path.join(tempstr, x)
|
|
||||||
app_path = "/" + tempstr + "/"
|
|
||||||
#The post-execution path should include the .app file
|
|
||||||
tempstr = ""
|
|
||||||
for x in (postexecute.split("/")):
|
|
||||||
if (".app" in x):
|
|
||||||
tempstr = os.path.join(tempstr, x)
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
tempstr = os.path.join(tempstr, x)
|
|
||||||
postexecute = "/" + tempstr
|
|
||||||
self.app_path = app_path
|
|
||||||
self.postexecute = postexecute
|
|
||||||
logging.info("apppath: " + str(app_path))
|
|
||||||
logging.info("postexecute: " + str(postexecute))
|
|
||||||
self.password = password
|
|
||||||
self.MD5 = MD5
|
|
||||||
self.save_location = save_location
|
|
||||||
#self.save_location contains the full path, including the blabla.zip
|
|
||||||
self.save_directory = os.path.join(*os.path.split(save_location)[:-1])
|
|
||||||
#self.save_directory doesn't contain the blabla.zip
|
|
||||||
|
|
||||||
def prepare_staging_directory(self):
|
|
||||||
if not os.path.exists(self.save_directory):
|
|
||||||
#We need to make all folders but the last one
|
|
||||||
os.makedirs(self.save_directory)
|
|
||||||
logger.info("Created staging directory %s" % self.save_directory)
|
|
||||||
|
|
||||||
def transfer_callback(self, count, bSize, tSize):
|
|
||||||
"""Callback to update percentage of download"""
|
|
||||||
percent = int(count*bSize*100/tSize)
|
|
||||||
self.percentage_callback(percent)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def print_percentage_callback(percent):
|
|
||||||
print percent
|
|
||||||
|
|
||||||
def start_update(self):
|
|
||||||
"""Called to start the whole process"""
|
|
||||||
logger.debug("URL: %s SL: %s" % (self.URL, self.save_location))
|
|
||||||
self.prepare_staging_directory()
|
|
||||||
Listy = CustomURLOpener().retrieve(self.URL, self.save_location, reporthook=self.transfer_callback)
|
|
||||||
if self.MD5:
|
|
||||||
#Check the MD5
|
|
||||||
if self.MD5File(location) != self.MD5:
|
|
||||||
#ReDownload
|
|
||||||
self.start_update()
|
|
||||||
self.download_complete(Listy[0])
|
|
||||||
|
|
||||||
def MD5File(self, fileName):
|
|
||||||
"Custom function that will get the Md5 sum of our file"
|
|
||||||
file_reference=open(fileName, 'rb').read()
|
|
||||||
return hashlib.md5(file_reference).hexdigest()
|
|
||||||
|
|
||||||
def download_complete(self, location):
|
|
||||||
"""Called when the file is done downloading, and MD5 has been successfull"""
|
|
||||||
logger.debug("Download complete.")
|
|
||||||
zippy = ZipFile(location, mode='r')
|
|
||||||
extracted_path = os.path.join(self.save_directory, os.path.basename(location).strip(".zip"))
|
|
||||||
zippy.extractall(extracted_path, pwd=self.password)
|
|
||||||
bootstrapper_path = os.path.join(self.save_directory, self.bootstrapper) #where we will find our bootstrapper
|
|
||||||
old_bootstrapper_path = os.path.join(extracted_path, self.bootstrapper)
|
|
||||||
if os.path.exists(bootstrapper_path):
|
|
||||||
os.chmod(bootstrapper_path, 666)
|
|
||||||
os.remove(bootstrapper_path)
|
|
||||||
shutil.move(old_bootstrapper_path, self.save_directory) #move bootstrapper
|
|
||||||
os.chmod(bootstrapper_path, stat.S_IRUSR|stat.S_IXUSR)
|
|
||||||
if platform.system() == "Windows":
|
|
||||||
bootstrapper_command = r'%s' % bootstrapper_path
|
|
||||||
bootstrapper_args = r'"%s" "%s" "%s" "%s"' % (os.getpid(), extracted_path, self.app_path, self.postexecute)
|
|
||||||
win32api.ShellExecute(0, 'open', bootstrapper_command, bootstrapper_args, "", 5)
|
|
||||||
else:
|
|
||||||
#bootstrapper_command = [r'sh "%s" -l "%s" -d "%s" "%s"' % (bootstrapper_path, self.app_path, extracted_path, str(os.getpid()))]
|
|
||||||
bootstrapper_command = r'"%s" "%s" "%s" "%s" "%s"' % (bootstrapper_path, os.getpid(), extracted_path, self.app_path, self.postexecute)
|
|
||||||
shell = True
|
|
||||||
#logging.debug("Final bootstrapper command: %r" % bootstrapper_command)
|
|
||||||
subprocess.Popen([bootstrapper_command], shell=shell)
|
|
||||||
self.complete = 1
|
|
||||||
if callable(self.finish_callback):
|
|
||||||
self.finish_callback()
|
|
||||||
|
|
||||||
def cleanup(self):
|
|
||||||
"""Delete stuff"""
|
|
||||||
try:
|
|
||||||
shutil.rmtree(self.save_directory)
|
|
||||||
except any:
|
|
||||||
return
|
|
||||||
|
|
||||||
def find_update_url(URL, version):
|
|
||||||
"""Return a URL to an update of the application for the current platform at the given URL if one exists, or None""
|
|
||||||
Assumes Windows, Linux, or Mac"""
|
|
||||||
response = urllib2.urlopen(URL)
|
|
||||||
json_str = response.read().strip("\n")
|
|
||||||
json_p = json.loads(json_str)
|
|
||||||
if is_newer(version, json_p['current_version']):
|
|
||||||
if application.snapshot == False: return json_p['downloads'][platform.system()+platform.architecture()[0][:2]]
|
|
||||||
else: return json_p['downloads'][platform.system()]
|
|
||||||
|
|
||||||
|
|
||||||
def is_newer(local_version, remote_version):
|
|
||||||
"""Returns True if the remote version is newer than the local version."""
|
|
||||||
return Version(remote_version) > local_version
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@total_ordering
|
|
||||||
class Version(object):
|
|
||||||
VERSION_QUALIFIERS = {
|
|
||||||
'alpha': 1,
|
|
||||||
'beta': 2,
|
|
||||||
'rc': 3
|
|
||||||
}
|
|
||||||
|
|
||||||
def __init__(self, version):
|
|
||||||
self.version = version
|
|
||||||
self.version_qualifier = None
|
|
||||||
self.version_qualifier_num = None
|
|
||||||
self.sub_version = None
|
|
||||||
if isinstance(version, basestring):
|
|
||||||
version = version.lower()
|
|
||||||
if '-' not in version:
|
|
||||||
for q in self.VERSION_QUALIFIERS:
|
|
||||||
if q in version:
|
|
||||||
self.version_qualifier = q
|
|
||||||
self.version_qualifier_num = self.VERSION_QUALIFIERS[q]
|
|
||||||
split_version = version.split(q)
|
|
||||||
self.version_number = float(split_version[0])
|
|
||||||
if len(split_version) > 1:
|
|
||||||
self.sub_version = split_version[1]
|
|
||||||
return
|
|
||||||
self.version_number= float(version)
|
|
||||||
return
|
|
||||||
split_version = version.split('-')
|
|
||||||
self.version_number= float(split_version[0])
|
|
||||||
self.version_qualifier = split_version [1]
|
|
||||||
self.version_qualifier_num = self.VERSION_QUALIFIERS[self.version_qualifier]
|
|
||||||
if len(split_version) == 3:
|
|
||||||
self.sub_version = int(split_version[2])
|
|
||||||
else:
|
|
||||||
self.version_number= float(version)
|
|
||||||
|
|
||||||
def __lt__(self, other):
|
|
||||||
if not isinstance(other, self.__class__):
|
|
||||||
other = Version(other)
|
|
||||||
if other.version_qualifier == self.version_qualifier == None:
|
|
||||||
return self.version_number< other.version_number
|
|
||||||
if self.version_number < other.version_number:
|
|
||||||
return True
|
|
||||||
elif self.version_number > other.version_number:
|
|
||||||
return False
|
|
||||||
if other.version_number == self.version_number and not other.version_qualifier_num and self.version_qualifier_num:
|
|
||||||
return True
|
|
||||||
if other.version_number == self.version_number and self.version_qualifier_num == self.version_qualifier_num and self.sub_version < other.sub_version:
|
|
||||||
return True
|
|
||||||
return self.version_qualifier_num < other.version_qualifier_num
|
|
||||||
|
|
||||||
def __gt__(self, other):
|
|
||||||
if not isinstance(other, self.__class__):
|
|
||||||
other = Version(other)
|
|
||||||
if other.version_qualifier == self.version_qualifier == None:
|
|
||||||
return self.version_number > other.version_number
|
|
||||||
if self.version_number < other.version_number:
|
|
||||||
return False
|
|
||||||
elif self.version_number > other.version_number:
|
|
||||||
return True
|
|
||||||
if other.version_number == self.version_number and not other.version_qualifier_num and self.version_qualifier_num:
|
|
||||||
return False
|
|
||||||
if other.version_number == self.version_number and self.version_qualifier_num == self.version_qualifier_num and self.sub_version > other.sub_version:
|
|
||||||
return True
|
|
||||||
return self.version_qualifier_num > other.version_qualifier_num
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class CustomURLOpener(FancyURLopener):
|
|
||||||
def http_error_default(*a, **k):
|
|
||||||
return URLopener.http_error_default(*a, **k)
|
|
42
src/updater/utils.py
Normal file
42
src/updater/utils.py
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
def convert_bytes(n):
|
||||||
|
K, M, G, T, P = 1 << 10, 1 << 20, 1 << 30, 1 << 40, 1 << 50
|
||||||
|
if n >= P:
|
||||||
|
return '%.2fPb' % (float(n) / T)
|
||||||
|
elif n >= T:
|
||||||
|
return '%.2fTb' % (float(n) / T)
|
||||||
|
elif n >= G:
|
||||||
|
return '%.2fGb' % (float(n) / G)
|
||||||
|
elif n >= M:
|
||||||
|
return '%.2fMb' % (float(n) / M)
|
||||||
|
elif n >= K:
|
||||||
|
return '%.2fKb' % (float(n) / K)
|
||||||
|
else:
|
||||||
|
return '%d' % n
|
||||||
|
|
||||||
|
def seconds_to_string(seconds, precision=0):
|
||||||
|
day = seconds // 86400
|
||||||
|
hour = seconds // 3600
|
||||||
|
min = (seconds // 60) % 60
|
||||||
|
sec = seconds - (hour * 3600) - (min * 60)
|
||||||
|
sec_spec = "." + str(precision) + "f"
|
||||||
|
sec_string = sec.__format__(sec_spec)
|
||||||
|
string = ""
|
||||||
|
if day == 1:
|
||||||
|
string += _(u"%d day, ") % day
|
||||||
|
elif day >= 2:
|
||||||
|
string += _(u"%d days, ") % day
|
||||||
|
if (hour == 1):
|
||||||
|
string += _(u"%d hour, ") % hour
|
||||||
|
elif (hour >= 2):
|
||||||
|
string += _("%d hours, ") % hour
|
||||||
|
if (min == 1):
|
||||||
|
string += _(u"%d minute, ") % min
|
||||||
|
elif (min >= 2):
|
||||||
|
string += _(u"%d minutes, ") % min
|
||||||
|
if sec >= 0 and sec <= 2:
|
||||||
|
string += _(u"%s second") % sec_string
|
||||||
|
else:
|
||||||
|
string += _(u"%s seconds") % sec_string
|
||||||
|
return string
|
30
src/updater/wxUpdater.py
Normal file
30
src/updater/wxUpdater.py
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import wx
|
||||||
|
import application
|
||||||
|
import utils
|
||||||
|
|
||||||
|
progress_dialog = None
|
||||||
|
|
||||||
|
def available_update_dialog(version, description):
|
||||||
|
dialog = wx.MessageDialog(None, _(u"There's a new %s version available. Would you like to download it now?\n\n %s version: %s\n\nChanges:\n%s") % (application.name, application.name, version, description), _(u"New version for %s") % application.name, style=wx.YES|wx.NO|wx.ICON_WARNING)
|
||||||
|
if dialog.ShowModal() == wx.ID_YES:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def create_progress_dialog():
|
||||||
|
return wx.ProgressDialog(_(u"Download in Progress"), _(u"Downloading the new version..."), parent=None, maximum=100)
|
||||||
|
|
||||||
|
def progress_callback(total_downloaded, total_size):
|
||||||
|
global progress_dialog
|
||||||
|
if progress_dialog == None:
|
||||||
|
progress_dialog = create_progress_dialog()
|
||||||
|
progress_dialog.Show()
|
||||||
|
if total_downloaded == total_size:
|
||||||
|
progress_dialog.Destroy()
|
||||||
|
else:
|
||||||
|
progress_dialog.Update((total_downloaded*100)/total_size, _(u"Updating... %s of %s") % (str(utils.convert_bytes(total_downloaded)), str(utils.convert_bytes(total_size))))
|
||||||
|
|
||||||
|
def update_finished():
|
||||||
|
ms = wx.MessageDialog(None, _(u"The new Lees version has been downloaded and installed. Press OK to start the application."), _(u"Done!")).ShowModal()
|
Loading…
Reference in New Issue
Block a user