diff --git a/EDMarketConnector.png b/EDMarketConnector.png new file mode 100644 index 00000000..e94af958 Binary files /dev/null and b/EDMarketConnector.png differ diff --git a/EDMarketConnector.py b/EDMarketConnector.py index 4e18ecbb..d4253753 100755 --- a/EDMarketConnector.py +++ b/EDMarketConnector.py @@ -25,7 +25,7 @@ class AppWindow: def __init__(self, master): - self.holdofftime = (config.read('querytime') or 0) + companion.holdoff + self.holdofftime = config.getint('querytime') + companion.holdoff self.session = companion.Session() self.w = master @@ -33,6 +33,13 @@ class AppWindow: self.w.rowconfigure(0, weight=1) self.w.columnconfigure(0, weight=1) + if platform == 'win32': + self.w.wm_iconbitmap(default='EDMarketConnector.ico') + elif platform == 'linux2': + from PIL import Image, ImageTk + icon = ImageTk.PhotoImage(Image.open("EDMarketConnector.png")) + root.tk.call('wm', 'iconphoto', root, '-default', icon) + frame = ttk.Frame(self.w) frame.grid(sticky=tk.NSEW) frame.columnconfigure(1, weight=1) @@ -74,20 +81,18 @@ class AppWindow: menubar.add_cascade(label="File", menu=file_menu) root.protocol("WM_DELETE_WINDOW", self.onexit) - if platform=='win32': - self.w.wm_iconbitmap(default='EDMarketConnector.ico') - # update geometry - if config.read('geometry'): - self.w.geometry(config.read('geometry')) + if config.get('geometry'): + self.w.geometry(config.get('geometry')) self.w.update_idletasks() self.w.wait_visibility() (w, h) = (self.w.winfo_width(), self.w.winfo_height()) - self.w.minsize(w, h) # Minimum size = initial size - self.w.maxsize(-1, h) # Maximum height = initial height + self.w.minsize(w, h) # Minimum size = initial size + if platform != 'linux2': # update_idletasks() doesn't allow for the menubar on Linux + self.w.maxsize(-1, h) # Maximum height = initial height # First run - if not config.read('username') or not config.read('password'): + if not config.get('username') or not config.get('password'): prefs.PreferencesDialog(self.w, self.login) else: self.login() @@ -98,7 +103,7 @@ class AppWindow: self.button['state'] = tk.DISABLED self.w.update_idletasks() try: - self.session.login(config.read('username'), config.read('password')) + self.session.login(config.get('username'), config.get('password')) self.status['text'] = '' except companion.VerificationRequired: # don't worry about authentication now @@ -134,7 +139,7 @@ class AppWindow: self.system['text'] = data.get('lastSystem') and data.get('lastSystem').get('name') or '' self.station['text'] = data.get('commander') and data.get('commander').get('docked') and data.get('lastStarport') and data.get('lastStarport').get('name') or '-' - config.write('querytime', querytime) + config.set('querytime', querytime) self.holdofftime = querytime + companion.holdoff # Validation @@ -147,16 +152,16 @@ class AppWindow: elif not data.get('lastStarport') or not data['lastStarport'].get('commodities'): raise Exception("Station doesn't have a market!") - if config.read('output') & config.OUT_CSV: + if config.getint('output') & config.OUT_CSV: bpc.export(data, True) - if config.read('output') & config.OUT_TD: + if config.getint('output') & config.OUT_TD: td.export(data) - if config.read('output') & config.OUT_BPC: + if config.getint('output') & config.OUT_BPC: bpc.export(data, False) - if config.read('output') & config.OUT_EDDN: + if config.getint('output') & config.OUT_EDDN: eddn.export(data, self.setstatus) except companion.VerificationRequired: @@ -185,7 +190,7 @@ class AppWindow: self.w.update_idletasks() def onexit(self): - config.write('geometry', '+{1}+{2}'.format(*self.w.geometry().split('+'))) + config.set('geometry', '+{1}+{2}'.format(*self.w.geometry().split('+'))) config.close() self.session.close() self.w.destroy() diff --git a/bpc.py b/bpc.py index bf240f1a..884bc459 100644 --- a/bpc.py +++ b/bpc.py @@ -10,9 +10,9 @@ from companion import commoditymap, bracketmap def export(data, csv=False): - querytime = config.read('querytime') or int(time.time()) + querytime = config.getint('querytime') or int(time.time()) - filename = join(config.read('outdir'), '%s.%s.%s.%s' % (data['lastSystem']['name'].strip(), data['lastStarport']['name'].strip(), time.strftime('%Y-%m-%dT%H.%M.%S', time.localtime(querytime)), csv and 'csv' or 'bpc')) + filename = join(config.get('outdir'), '%s.%s.%s.%s' % (data['lastSystem']['name'].strip(), data['lastStarport']['name'].strip(), time.strftime('%Y-%m-%dT%H.%M.%S', time.localtime(querytime)), csv and 'csv' or 'bpc')) timestamp = time.strftime('%Y-%m-%dT%H:%M:%S', time.gmtime(querytime)) if csv: diff --git a/config.py b/config.py index 0edea2ba..c9a73097 100644 --- a/config.py +++ b/config.py @@ -1,11 +1,18 @@ import sys -from os import mkdir -from os.path import basename, isdir, join +from os import getenv, makedirs, mkdir +from os.path import expanduser, dirname, isdir, join from sys import platform -if platform=='win32': +if platform=='darwin': + from Foundation import NSBundle, NSUserDefaults, NSSearchPathForDirectoriesInDomains, NSApplicationSupportDirectory, NSDocumentDirectory, NSLibraryDirectory, NSUserDomainMask +elif platform=='win32': + import ctypes.wintypes import numbers import _winreg +elif platform=='linux2': + import codecs + # requires python-iniparse package - ConfigParser that ships with Python < 3.2 doesn't support unicode + from iniparse import RawConfigParser appname = 'EDMarketConnector' @@ -23,8 +30,6 @@ class Config: if platform=='darwin': def __init__(self): - from Foundation import NSBundle, NSUserDefaults, NSSearchPathForDirectoriesInDomains, NSApplicationSupportDirectory, NSDocumentDirectory, NSLibraryDirectory, NSUserDomainMask - self.app_dir = join(NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, True)[0], appname) if not isdir(self.app_dir): mkdir(self.app_dir) @@ -35,13 +40,19 @@ class Config: self.settings = dict(settings) # Check out_dir exists - if not self.read('outdir') or not isdir(self.read('outdir')): - self.write('outdir', NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, True)[0]) + if not self.get('outdir') or not isdir(self.get('outdir')): + self.set('outdir', NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, True)[0]) - def read(self, key): + def get(self, key): return self.settings.get(key) - def write(self, key, val): + def getint(self, key): + try: + return int(self.settings.get(key, 0)) # should already be int, but check by casting + except: + return 0 + + def set(self, key, val): self.settings[key] = val def close(self): @@ -52,7 +63,6 @@ class Config: elif platform=='win32': def __init__(self): - import ctypes.wintypes CSIDL_PERSONAL = 0x0005 CSIDL_LOCAL_APPDATA = 0x001C buf = ctypes.create_unicode_buffer(ctypes.wintypes.MAX_PATH) @@ -63,17 +73,23 @@ class Config: self.handle = _winreg.CreateKey(_winreg.HKEY_CURRENT_USER, r'Software\%s' % appname) - if not self.read('outdir') or not isdir(self.read('outdir')): + if not self.get('outdir') or not isdir(self.get('outdir')): ctypes.windll.shell32.SHGetSpecialFolderPathW(0, buf, CSIDL_PERSONAL, 0) - self.write('outdir', buf.value) + self.set('outdir', buf.value) - def read(self, key): + def get(self, key): try: return _winreg.QueryValueEx(self.handle, key)[0] except: return None - def write(self, key, val): + def getint(self, key): + try: + return int(_winreg.QueryValueEx(self.handle, key)[0]) # should already be int, but check by casting + except: + return 0 + + def set(self, key, val): if isinstance(val, basestring): _winreg.SetValueEx(self.handle, key, 0, _winreg.REG_SZ, val) elif isinstance(val, numbers.Integral): @@ -85,7 +101,51 @@ class Config: _winreg.CloseKey(self.handle) self.handle = None - else: # unix + elif platform=='linux2': + + def __init__(self): + # http://standards.freedesktop.org/basedir-spec/latest/ar01s03.html + + self.app_dir = join(getenv('XDG_DATA_HOME', expanduser('~/.local/share')), appname) + if not isdir(self.app_dir): + makedirs(self.app_dir) + + self.filename = join(getenv('XDG_CONFIG_HOME', expanduser('~/.config')), appname, '%s.ini' % appname) + if not isdir(dirname(self.filename)): + makedirs(dirname(self.filename)) + + self.config = RawConfigParser() + try: + self.config.readfp(codecs.open(self.filename, 'r', 'utf-8')) + # XXX handle missing? + except: + self.config.add_section('DEFAULT') + + if not self.get('outdir') or not isdir(self.get('outdir')): + self.set('outdir', expanduser('~')) + + def set(self, key, val): + self.config.set('DEFAULT', key, val) + + def get(self, key): + try: + return self.config.get('DEFAULT', key) # all values are stored as strings + except: + return None + + def getint(self, key): + try: + return int(self.config.get('DEFAULT', key)) # all values are stored as strings + except: + return 0 + + def close(self): + h = codecs.open(self.filename, 'w', 'utf-8') + h.write(unicode(self.config.data)) + h.close() + self.config = None + + else: # ??? def __init__(self): raise NotImplementedError('Implement me') diff --git a/eddn.py b/eddn.py index ec8829c1..3e6d2e6f 100644 --- a/eddn.py +++ b/eddn.py @@ -17,7 +17,7 @@ def export(data, callback): callback('Sending data to EDDN...') - querytime = config.read('querytime') or int(time.time()) + querytime = config.getint('querytime') or int(time.time()) header = { 'softwareName': '%s [%s]' % (applongname, platform=='darwin' and "Mac OS" or system()), 'softwareVersion': appversion, diff --git a/prefs.py b/prefs.py index 65c27f43..fa580762 100644 --- a/prefs.py +++ b/prefs.py @@ -46,11 +46,11 @@ class PreferencesDialog(tk.Toplevel): ttk.Label(credframe, text="Password").grid(row=2, sticky=tk.W) self.username = ttk.Entry(credframe) - self.username.insert(0, config.read('username') or '') + self.username.insert(0, config.get('username') or '') self.username.grid(row=1, column=1, sticky=tk.NSEW) self.username.focus_set() self.password = ttk.Entry(credframe, show=u'•') - self.password.insert(0, config.read('password') or '') + self.password.insert(0, config.get('password') or '') self.password.grid(row=2, column=1, sticky=tk.NSEW) for child in credframe.winfo_children(): @@ -60,7 +60,7 @@ class PreferencesDialog(tk.Toplevel): outframe.grid(padx=10, pady=10, sticky=tk.NSEW) outframe.columnconfigure(0, weight=1) - output = config.read('output') or config.OUT_EDDN + output = config.getint('output') or config.OUT_EDDN ttk.Label(outframe, text="Please choose where you want the market data saved").grid(row=0, columnspan=2, padx=5, pady=3, sticky=tk.W) self.out_eddn= tk.IntVar(value = (output & config.OUT_EDDN) and 1 or 0) ttk.Checkbutton(outframe, text="Online to the Elite Dangerous Data Network (EDDN)", variable=self.out_eddn).grid(row=1, columnspan=2, padx=5, sticky=tk.W) @@ -74,7 +74,7 @@ class PreferencesDialog(tk.Toplevel): self.outbutton = ttk.Button(outframe, text=(platform=='darwin' and 'Change...' or 'Browse...'), command=self.outbrowse) self.outbutton.grid(row=5, column=1, padx=5, pady=(5,0), sticky=tk.NSEW) self.outdir = ttk.Entry(outframe) - self.outdir.insert(0, config.read('outdir')) + self.outdir.insert(0, config.get('outdir')) self.outdir.grid(row=6, columnspan=2, padx=5, pady=5, sticky=tk.EW) self.outvarchanged() @@ -106,13 +106,13 @@ class PreferencesDialog(tk.Toplevel): self.outdir['state'] = 'readonly' def apply(self): - credentials = (config.read('username'), config.read('password')) - config.write('username', self.username.get().strip()) - config.write('password', self.password.get().strip()) - config.write('output', (self.out_eddn.get() and config.OUT_EDDN or 0) + (self.out_bpc.get() and config.OUT_BPC or 0) + (self.out_td.get() and config.OUT_TD or 0) + (self.out_csv.get() and config.OUT_CSV or 0)) - config.write('outdir', self.outdir.get().strip()) + credentials = (config.get('username'), config.get('password')) + config.set('username', self.username.get().strip()) + config.set('password', self.password.get().strip()) + config.set('output', (self.out_eddn.get() and config.OUT_EDDN or 0) + (self.out_bpc.get() and config.OUT_BPC or 0) + (self.out_td.get() and config.OUT_TD or 0) + (self.out_csv.get() and config.OUT_CSV or 0)) + config.set('outdir', self.outdir.get().strip()) self.destroy() - if credentials != (config.read('username'), config.read('password')) and self.callback: + if credentials != (config.get('username'), config.get('password')) and self.callback: self.callback() diff --git a/setup.py b/setup.py index 68a4b921..63d21433 100755 --- a/setup.py +++ b/setup.py @@ -54,6 +54,7 @@ SHORTVERSION = ''.join(VERSION.split('.')[:3]) PY2APP_OPTIONS = {'dist_dir': dist_dir, 'optimize': 2, 'packages': [ 'requests' ], + 'excludes': [ 'PIL' ], 'iconfile': '%s.icns' % APPNAME, 'semi_standalone': True, 'site_packages': False, @@ -71,6 +72,7 @@ PY2APP_OPTIONS = {'dist_dir': dist_dir, PY2EXE_OPTIONS = {'dist_dir': dist_dir, 'optimize': 2, 'packages': [ 'requests' ], + 'excludes': [ 'PIL' ], } if sys.platform=='win32': diff --git a/td.py b/td.py index bc97828d..8a15a191 100644 --- a/td.py +++ b/td.py @@ -13,9 +13,9 @@ from companion import categorymap, commoditymap, bracketmap def export(data): - querytime = config.read('querytime') or int(time.time()) + querytime = config.getint('querytime') or int(time.time()) - filename = join(config.read('outdir'), '%s.%s.%s.prices' % (data['lastSystem']['name'].strip(), data['lastStarport']['name'].strip(), time.strftime('%Y-%m-%dT%H.%M.%S', time.localtime(querytime)))) + filename = join(config.get('outdir'), '%s.%s.%s.prices' % (data['lastSystem']['name'].strip(), data['lastStarport']['name'].strip(), time.strftime('%Y-%m-%dT%H.%M.%S', time.localtime(querytime)))) timestamp = time.strftime('%Y-%m-%d %H:%M:%S', time.gmtime(querytime))