diff --git a/EDMarketConnector.py b/EDMarketConnector.py index 2110d203..150502cd 100755 --- a/EDMarketConnector.py +++ b/EDMarketConnector.py @@ -81,7 +81,7 @@ class AppWindow: style.configure('TRadiobutton', font=font) style.configure('TEntry', font=font) - frame = ttk.Frame(self.w) + frame = ttk.Frame(self.w, name=appname.lower()) frame.grid(sticky=tk.NSEW) frame.columnconfigure(1, weight=1) frame.rowconfigure(4, weight=1) @@ -93,8 +93,8 @@ class AppWindow: self.cmdr = ttk.Label(frame, width=-21) self.system = HyperlinkLabel(frame, compound=tk.RIGHT, url = self.system_url, popup_copy = True) self.station = HyperlinkLabel(frame, url = self.station_url, popup_copy = lambda x: x!=self.STATION_UNDOCKED) - self.button = ttk.Button(frame, text=_('Update'), command=self.getandsend, default=tk.ACTIVE, state=tk.DISABLED) # Update button in main window - self.status = ttk.Label(frame, width=-25) + self.button = ttk.Button(frame, name='update', text=_('Update'), command=self.getandsend, default=tk.ACTIVE, state=tk.DISABLED) # Update button in main window + self.status = ttk.Label(frame, name='status', width=-25) self.w.bind('', self.getandsend) self.w.bind('', self.getandsend) @@ -134,8 +134,7 @@ class AppWindow: else: file_menu = self.view_menu = tk.Menu(menubar, tearoff=tk.FALSE) file_menu.add_command(label=_('Status'), state=tk.DISABLED, command=lambda:stats.StatsDialog(self.w, self.session)) # Menu item - if platform == 'win32': - file_menu.add_command(label=_("Check for Updates..."), command=lambda:self.updater.checkForUpdates()) + file_menu.add_command(label=_("Check for Updates..."), command=lambda:self.updater.checkForUpdates()) file_menu.add_command(label=_("Settings"), command=lambda:prefs.PreferencesDialog(self.w, self.login)) # Item in the File menu on Windows file_menu.add_separator() file_menu.add_command(label=_("Exit"), command=self.onexit) # Item in the File menu on Windows @@ -153,6 +152,7 @@ class AppWindow: style.lookup('TButton.label', 'background', ['active'])) menubar.configure( fg = fg, bg = bg, activeforeground = afg, activebackground = abg) file_menu.configure(fg = fg, bg = bg, activeforeground = afg, activebackground = abg) + self.edit_menu.configure(fg = fg, bg = bg, activeforeground = afg, activebackground = abg) self.w['menu'] = menubar # update geometry @@ -217,6 +217,8 @@ class AppWindow: if not self.status['text'] and monitor.restart_required(): self.status['text'] = _('Re-start Elite: Dangerous for automatic log entries') # Status bar message on launch + elif not getattr(sys, 'frozen', False): + self.updater.checkForUpdates() # Sparkle / WinSparkle does this automatically for packaged apps self.cooldown() diff --git a/config.py b/config.py index 34edd3a1..7e3a6229 100644 --- a/config.py +++ b/config.py @@ -9,6 +9,9 @@ applongname = 'E:D Market Connector' appcmdname = 'EDMC' appversion = '1.8.8.1' +update_feed = 'http://marginal.org.uk/edmarketconnector.xml' +update_interval = 47*60*60 + if platform=='darwin': from Foundation import NSBundle, NSUserDefaults, NSSearchPathForDirectoriesInDomains, NSApplicationSupportDirectory, NSDocumentDirectory, NSLibraryDirectory, NSUserDomainMask @@ -144,7 +147,7 @@ class Config: if disposition.value == REG_CREATED_NEW_KEY: buf = ctypes.create_unicode_buffer('1') RegSetValueEx(sparklekey, 'CheckForUpdates', 0, 1, buf, len(buf)*2) - buf = ctypes.create_unicode_buffer(unicode(47*60*60)) + buf = ctypes.create_unicode_buffer(unicode(update_interval)) RegSetValueEx(sparklekey, 'UpdateInterval', 0, 1, buf, len(buf)*2) RegCloseKey(sparklekey) diff --git a/setup.py b/setup.py index ab63e28d..ec519677 100755 --- a/setup.py +++ b/setup.py @@ -17,6 +17,10 @@ import shutil import sys from tempfile import gettempdir +from config import appname as APPNAME, applongname as APPLONGNAME, appcmdname as APPCMDNAME, appversion as VERSION +from config import update_feed, update_interval + + if sys.platform=='win32': assert platform.architecture()[0]=='32bit', 'Assumes a Python built for 32bit' import py2exe @@ -52,10 +56,6 @@ if sys.platform=='darwin': APP = 'EDMarketConnector.py' APPCMD = 'EDMC.py' -APPNAME = re.search(r"^appname\s*=\s*'(.+)'", file('config.py').read(), re.MULTILINE).group(1) -APPLONGNAME = re.search(r"^applongname\s*=\s*'(.+)'", file('config.py').read(), re.MULTILINE).group(1) -APPCMDNAME = re.search(r"^appcmdname\s*=\s*'(.+)'", file('config.py').read(), re.MULTILINE).group(1) -VERSION = re.search(r"^appversion\s*=\s*'(.+)'", file('config.py').read(), re.MULTILINE).group(1) SHORTVERSION = ''.join(VERSION.split('.')[:3]) if sys.platform=='darwin': @@ -80,8 +80,8 @@ if sys.platform=='darwin': 'SUEnableAutomaticChecks': True, 'SUShowReleaseNotes': True, 'SUAllowsAutomaticUpdates': False, - 'SUFeedURL': 'http://marginal.org.uk/edmarketconnector.xml', - 'SUScheduledCheckInterval': 47*60*60, + 'SUFeedURL': update_feed, + 'SUScheduledCheckInterval': update_interval, }, 'graph': True, # output dependency graph in dist } diff --git a/update.py b/update.py index a305b941..3f482369 100644 --- a/update.py +++ b/update.py @@ -1,35 +1,52 @@ import os from os.path import dirname, join import sys - +from time import time +import threading # ensure registry is set up on Windows before we start -import config - -class NullUpdater: - - def __init__(self, master): - pass - - def checkForUpdates(self): - pass - - def close(self): - pass +from config import appname, appversion, update_feed, update_interval, config if not getattr(sys, 'frozen', False): - class Updater(NullUpdater): - pass + # quick and dirty version comparison assuming "strict" numeric only version numbers + def versioncmp(versionstring): + return map(int, versionstring.split('.')) + + class Updater(): + + def __init__(self, master): + self.root = master + + def checkForUpdates(self): + thread = threading.Thread(target = self.worker, name = 'update worker') + thread.daemon = True + thread.start() + + def worker(self): + import requests + from xml.etree import ElementTree + + r = requests.get(update_feed, timeout = 20) + feed = ElementTree.fromstring(r.text) + items = dict([(item.find('enclosure').attrib.get('{http://www.andymatuschak.org/xml-namespaces/sparkle}version'), + item.find('title').text) for item in feed.findall('channel/item')]) + lastversion = sorted(items, key=versioncmp)[-1] + if versioncmp(lastversion) > versioncmp(appversion): + self.root.nametowidget('.%s.%s' % (appname.lower(), 'status'))['text'] = items[lastversion] + ' is available' + self.root.update_idletasks() + + def close(self): + pass elif sys.platform=='darwin': import objc - class Updater(NullUpdater): + class Updater(): - # https://github.com/sparkle-project/Sparkle/wiki/Customization + # http://sparkle-project.org/documentation/customization/ def __init__(self, master): try: @@ -57,7 +74,7 @@ elif sys.platform=='win32': def shutdown_request(): root.event_generate('<>', when="tail") - class Updater(NullUpdater): + class Updater(): # https://github.com/vslavik/winsparkle/wiki/Basic-Setup @@ -65,7 +82,7 @@ elif sys.platform=='win32': try: sys.frozen # don't want to try updating python.exe self.updater = ctypes.cdll.WinSparkle - self.updater.win_sparkle_set_appcast_url('http://marginal.org.uk/edmarketconnector.xml') # py2exe won't let us embed this in resources + self.updater.win_sparkle_set_appcast_url(update_feed) # py2exe won't let us embed this in resources # set up shutdown callback global root @@ -98,9 +115,3 @@ elif sys.platform=='win32': if self.updater: updater.win_sparkle_cleanup() self.updater = None - -else: - - class Updater(NullUpdater): - pass -