1
0
mirror of https://github.com/EDCD/EDMarketConnector.git synced 2025-04-17 01:22:19 +03:00

Merge branch 'auto_flight_log'

This commit is contained in:
Jonathan Harris 2015-11-07 05:12:03 +00:00
commit 71060835c3
6 changed files with 438 additions and 36 deletions

View File

@ -30,6 +30,7 @@ import eddb
import prefs
from config import appname, applongname, config
from hotkey import hotkeymgr
from monitor import monitor
l10n.Translations().install()
EDDB = eddb.EDDB()
@ -169,6 +170,12 @@ class AppWindow:
self.w.bind_all('<<Invoke>>', self.getandsend) # user-generated
hotkeymgr.register(self.w, config.getint('hotkey_code'), config.getint('hotkey_mods'))
# Install log monitoring
monitor.set_callback(self.system_change)
if (config.getint('output') & config.OUT_LOG_AUTO) and (config.getint('output') & (config.OUT_LOG_AUTO|config.OUT_LOG_EDSM)):
monitor.enable_logging()
monitor.start()
# call after credentials have changed
def login(self):
self.status['text'] = _('Logging in...')
@ -362,6 +369,29 @@ class AppWindow:
except:
pass
def system_change(self, timestamp, system):
if self.system['text'] != system:
try:
self.system['text'] = system
self.system['image'] = ''
self.station['text'] = EDDB.system(system) and self.STATION_UNDOCKED or ''
if config.getint('output') & config.OUT_LOG_FILE:
flightlog.writelog(timestamp, system)
if config.getint('output') & config.OUT_LOG_EDSM:
self.status['text'] = _('Sending data to EDSM...')
self.w.update_idletasks()
edsm.writelog(timestamp, system, lambda:self.edsm.lookup(system, EDDB.system(system))) # Do EDSM lookup during EDSM export
else:
self.edsm.start_lookup(system, EDDB.system(system))
self.edsmpoll()
self.status['text'] = strftime(_('Last updated at {HH}:{MM}:{SS}').format(HH='%H', MM='%M', SS='%S').encode('utf-8'), localtime(timestamp)).decode('utf-8')
except Exception as e:
if __debug__: print_exc()
self.status['text'] = unicode(e)
if not config.getint('hotkey_mute'):
hotkeymgr.play_bad()
def edsmpoll(self):
result = self.edsm.result
if result['done']:
@ -398,6 +428,7 @@ class AppWindow:
self.w.clipboard_append(self.station['text'] == self.STATION_UNDOCKED and self.system['text'] or '%s,%s' % (self.system['text'], self.station['text']))
def onexit(self, event=None):
flightlog.close()
if platform!='darwin' or self.w.winfo_rooty()>0: # http://core.tcl.tk/tk/tktview/c84f660833546b1b84e7
config.set('geometry', '+{1}+{2}'.format(*self.w.geometry().split('+')))
config.close()

View File

@ -75,6 +75,7 @@ class Config:
#OUT_STAT = 64 # No longer available
OUT_SHIP_CORIOLIS = 128
OUT_LOG_EDSM = 256
OUT_LOG_AUTO = 512
if platform=='darwin':

View File

@ -109,11 +109,16 @@ def export(data, edsmlookupfn):
querytime = config.getint('querytime') or int(time.time())
writelog(querytime, data['lastSystem']['name'], edsmlookupfn)
def writelog(timestamp, system, edsmlookupfn):
try:
# Look up the system before adding it to the log, since adding it to the log has the side-effect of creating it
edsmlookupfn()
r = requests.get('http://www.edsm.net/api-logs-v1/set-log?commanderName=%s&apiKey=%s&systemName=%s&dateVisited=%s' % (urllib.quote(config.get('edsm_cmdrname')), urllib.quote(config.get('edsm_apikey')), urllib.quote(data['lastSystem']['name']), urllib.quote(time.strftime('%Y-%m-%d %H:%M:%S', time.gmtime(querytime)))), timeout=EDSM._TIMEOUT)
r = requests.get('http://www.edsm.net/api-logs-v1/set-log?commanderName=%s&apiKey=%s&systemName=%s&dateVisited=%s' % (urllib.quote(config.get('edsm_cmdrname')), urllib.quote(config.get('edsm_apikey')), urllib.quote(system), urllib.quote(time.strftime('%Y-%m-%d %H:%M:%S', time.gmtime(timestamp)))), timeout=EDSM._TIMEOUT)
r.raise_for_status()
reply = r.json()
(msgnum, msg) = reply['msgnum'], reply['msg']

View File

@ -13,6 +13,8 @@ from shipyard import ship_map
logfile = None
last_timestamp = last_system = last_ship = None
last_commodities = {}
def openlog():
@ -42,24 +44,55 @@ def export(data):
querytime = config.getint('querytime') or int(time.time())
openlog()
commodities = defaultdict(int)
for item in data['ship'].get('cargo',{}).get('items',[]):
if item['commodity'] != 'drones':
commodities[commodity_map.get(item['commodity'], item['commodity'])] += item['qty']
writelog(querytime,
data['lastSystem']['name'],
data['commander']['docked'] and data['lastStarport']['name'],
ship_map.get(data['ship']['name'].lower(), data['ship']['name']),
commodities)
def writelog(timestamp, system, station=None, ship=None, commodities={}):
global last_timestamp, last_system, last_ship, last_commodities
if last_system and last_system != system:
_writelog(last_timestamp, last_system, None, last_ship, last_commodities)
if not station:
# If not docked, hold off writing log entry until docked or until system changes
last_timestamp, last_system, last_ship, last_commodities = timestamp, system, ship, commodities
else:
last_system = None
_writelog(timestamp, system, station, ship, commodities)
def _writelog(timestamp, system, station=None, ship=None, commodities={}):
openlog()
logfile.write('%s,%s,%s,%s,%s,%s\r\n' % (
time.strftime('%Y-%m-%d', time.localtime(querytime)),
time.strftime('%H:%M:%S', time.localtime(querytime)),
data['lastSystem']['name'],
data['commander']['docked'] and data['lastStarport']['name'] or '',
ship_map.get(data['ship']['name'].lower(), data['ship']['name']),
time.strftime('%Y-%m-%d', time.localtime(timestamp)),
time.strftime('%H:%M:%S', time.localtime(timestamp)),
system,
station or '',
ship or '',
','.join([('%d %s' % (commodities[k], k)) for k in sorted(commodities)])))
logfile.flush()
def close():
global last_timestamp, last_system
if last_system:
_writelog(last_timestamp, last_system, None, last_ship, last_commodities)
last_system = None
# return log as list of (timestamp, system_name)
def logs():
entries = []

304
monitor.py Normal file
View File

@ -0,0 +1,304 @@
import re
import threading
from os import listdir, pardir, rename, unlink
from os.path import exists, isdir, isfile, join
from platform import machine
from sys import platform
from time import strptime, localtime, mktime, sleep, time
from datetime import datetime
if __debug__:
from traceback import print_exc
if platform=='darwin':
from AppKit import NSWorkspace
from Foundation import NSSearchPathForDirectoriesInDomains, NSApplicationSupportDirectory, NSUserDomainMask
elif platform=='win32':
import ctypes
CSIDL_LOCAL_APPDATA = 0x001C
CSIDL_PROGRAM_FILESX86 = 0x002A
# _winreg that ships with Python 2 doesn't support unicode, so do this instead
from ctypes.wintypes import *
HKEY_LOCAL_MACHINE = 0x80000002
KEY_READ = 0x00020019
REG_SZ = 1
RegOpenKeyEx = ctypes.windll.advapi32.RegOpenKeyExW
RegOpenKeyEx.restype = LONG
RegOpenKeyEx.argtypes = [HKEY, LPCWSTR, DWORD, DWORD, ctypes.POINTER(HKEY)]
RegCloseKey = ctypes.windll.advapi32.RegCloseKey
RegCloseKey.restype = LONG
RegCloseKey.argtypes = [HKEY]
RegQueryValueEx = ctypes.windll.advapi32.RegQueryValueExW
RegQueryValueEx.restype = LONG
RegQueryValueEx.argtypes = [HKEY, LPCWSTR, LPCVOID, ctypes.POINTER(DWORD), LPCVOID, ctypes.POINTER(DWORD)]
RegEnumKeyEx = ctypes.windll.advapi32.RegEnumKeyExW
RegEnumKeyEx.restype = LONG
RegEnumKeyEx.argtypes = [HKEY, DWORD, LPWSTR, ctypes.POINTER(DWORD), ctypes.POINTER(DWORD), LPWSTR, ctypes.POINTER(DWORD), ctypes.POINTER(FILETIME)]
WNDENUMPROC = ctypes.WINFUNCTYPE(BOOL, HWND, ctypes.POINTER(DWORD))
EnumWindows = ctypes.windll.user32.EnumWindows
EnumWindows.argtypes = [WNDENUMPROC, LPARAM]
GetWindowText = ctypes.windll.user32.GetWindowTextW
GetWindowText.argtypes = [HWND, LPWSTR, ctypes.c_int]
GetWindowTextLength = ctypes.windll.user32.GetWindowTextLengthW
@WNDENUMPROC
def EnumWindowsProc(hwnd, lParam):
l = GetWindowTextLength(hwnd) + 1
buf = ctypes.create_unicode_buffer(l)
if GetWindowText(hwnd, buf, l) and buf.value.startswith('Elite - Dangerous'):
lParam[0] = 1
return False # stop enumeration
return True
class EDLogs:
def __init__(self):
self.logdir = self._logdir()
self.logging_enabled = self._logging_enabled
self._restart_required = False
self.observer = None
self.callback = None
def set_callback(self, callback):
self.callback = callback
def enable_logging(self):
if self.logging_enabled():
return True
elif self._enable_logging():
self._restart_required = self._ED_is_running()
return True
else:
return False
def restart_required(self):
if not self._ED_is_running():
self._restart_required = False
return self._restart_required
def logging_enabled_in_file(self, appconf):
if not isfile(appconf):
return False
with open(appconf, 'rU') as f:
content = f.read().lower()
start = content.find('<network')
end = content.find('</network>')
if start >= 0 and end >= 0:
return bool(re.search('verboselogging\s*=\s*\"1\"', content[start+8:end]))
else:
return False
def enable_logging_in_file(self, appconf):
try:
if not exists(appconf):
with open(appconf, 'wt') as f:
f.write('<AppConfig>\n\t<Network\n\t\tVerboseLogging="1"\n\t>\n\t</Network>\n</AppConfig>\n')
return True
with open(appconf, 'rU') as f:
content = f.read()
f.close()
backup = appconf[:-4] + '_backup.xml'
if exists(backup):
unlink(backup)
rename(appconf, backup)
with open(appconf, 'wt') as f:
start = content.lower().find('<network')
if start >= 0:
f.write(content[:start+8] + '\n\t\tVerboseLogging="1"' + content[start+8:])
else:
start = content.lower().find("</appconfig>")
if start >= 0:
f.write(content[:start] + '\t<Network\n\t\tVerboseLogging="1"\n\t>\n\t</Network>\n' + content[start:])
else:
f.write(content) # eh ?
return False
assert self._logging_enabled()
return self.logging_enabled_in_file(appconf)
except:
if __debug__: print_exc()
return False
def start(self):
if not self.logdir or not self.callback:
self.stop()
return False
if self.running():
return True
self.observer = threading.Thread(target = self.worker, name = 'netLog worker')
self.observer.daemon = True
self.observer.start()
return True
def stop(self):
self.observer = None # Orphan the worker thread
def running(self):
return self.observer and self.observer.is_alive()
def worker(self):
regexp = re.compile('{(.+)} System:[^\(]*\(([^\)]+)')
# Seek to the end of the latest log file
logfiles = sorted([x for x in listdir(self.logdir) if x.startswith('netLog.')])
logfile = logfiles and logfiles[-1] or None
if logfile:
loghandle = open(join(self.logdir, logfile), 'rt')
loghandle.seek(0, 2) # seek to EOF
else:
loghandle = None
while True:
# Check whether we're still supposed to be running
if threading.current_thread() != self.observer:
return # Terminate
# Check whether new log file started, e.g. client restarted. Assumes logs sort alphabetically.
logfiles = sorted([x for x in listdir(self.logdir) if x.startswith('netLog.')])
newlogfile = logfiles and logfiles[-1] or None
if logfile != newlogfile:
logfile = newlogfile
if loghandle:
loghandle.close()
loghandle = open(join(self.logdir, logfile), 'rt')
if logfile:
system = visited = None
loghandle.seek(0, 1) # reset EOF flag
for line in loghandle:
match = regexp.match(line)
if match:
system, visited = match.group(2), match.group(1)
if system:
self._restart_required = False # clearly logging is working
# Convert local time string to UTC date and time
visited_struct = strptime(visited, '%H:%M:%S')
now = localtime()
if now.tm_hour == 0 and visited_struct.tm_hour == 23:
# Crossed midnight between timestamp and poll
now = localtime(time()-12*60%60) # yesterday
time_struct = datetime(now.tm_year, now.tm_mon, now.tm_mday, visited_struct.tm_hour, visited_struct.tm_min, visited_struct.tm_sec).timetuple() # still local time
self.callback(mktime(time_struct), system)
sleep(10) # New system gets posted to log file before hyperspace ends, so don't need to poll too often
if platform=='darwin':
def _logdir(self):
# https://support.frontier.co.uk/kb/faq.php?id=97
suffix = join('Frontier Developments', 'Elite Dangerous')
paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, True)
if len(paths) and isdir(paths[0]) and isfile(join(paths[0], suffix, 'AppNetCfg.xml')) and isdir(join(paths[0], suffix, 'Logs')):
return join(paths[0], suffix, 'Logs')
else:
return None
def _logging_enabled(self):
return self.logdir and self.logging_enabled_in_file(join(self.logdir, pardir, 'AppConfigLocal.xml'))
def _enable_logging(self):
return self.logdir and self.enable_logging_in_file(join(self.logdir, pardir, 'AppConfigLocal.xml'))
def _ED_is_running(self):
for x in NSWorkspace.sharedWorkspace().runningApplications():
if x.bundleIdentifier() == 'uk.co.frontier.EliteDangerous':
return True
else:
return False
elif platform=='win32':
def _logdir(self):
# First try under the Launcher
key = HKEY()
if not RegOpenKeyEx(HKEY_LOCAL_MACHINE,
machine().endswith('64') and
r'SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall' or # Assumes that the launcher is a 32bit process
r'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall',
0, KEY_READ, ctypes.byref(key)):
buf = ctypes.create_unicode_buffer(MAX_PATH)
i = 0
while True:
size = DWORD(MAX_PATH)
if RegEnumKeyEx(key, i, buf, ctypes.byref(size), None, None, None, None):
break
subkey = HKEY()
if not RegOpenKeyEx(key, buf, 0, KEY_READ, ctypes.byref(subkey)):
valtype = DWORD()
valsize = DWORD((len('Frontier Developments')+1)*2)
valbuf = ctypes.create_unicode_buffer(valsize.value / 2)
if not RegQueryValueEx(subkey, 'Publisher', 0, ctypes.byref(valtype), valbuf, ctypes.byref(valsize)) and valtype.value == REG_SZ and valbuf.value == 'Frontier Developments':
if not RegQueryValueEx(subkey, 'InstallLocation', 0, ctypes.byref(valtype), None, ctypes.byref(valsize)) and valtype.value == REG_SZ:
valbuf = ctypes.create_unicode_buffer(valsize.value / 2)
if not RegQueryValueEx(subkey, 'InstallLocation', 0, ctypes.byref(valtype), valbuf, ctypes.byref(valsize)):
custpath = join(valbuf.value, 'Products')
if isdir(custpath):
for d in listdir(custpath):
if d.startswith('FORC-FDEV-D-1') and isfile(join(custpath, d, 'AppConfig.xml')) and isdir(join(custpath, d, 'Logs')):
RegCloseKey(subkey)
RegCloseKey(key)
return join(custpath, d, 'Logs')
RegCloseKey(subkey)
i += 1
RegCloseKey(key)
# https://support.elitedangerous.com/kb/faq.php?id=108
programs = ctypes.create_unicode_buffer(MAX_PATH)
ctypes.windll.shell32.SHGetSpecialFolderPathW(0, programs, CSIDL_PROGRAM_FILESX86, 0)
applocal = ctypes.create_unicode_buffer(MAX_PATH)
ctypes.windll.shell32.SHGetSpecialFolderPathW(0, applocal, CSIDL_LOCAL_APPDATA, 0)
for base in [join(programs.value, 'Steam', 'steamapps', 'common', 'Elite Dangerous', 'Products'),
join(programs.value, 'Frontier', 'Products'),
join(applocal.value, 'Frontier_Developments', 'Products')]:
if isdir(base):
for d in listdir(base):
if d.startswith('FORC-FDEV-D-1') and isfile(join(base, d, 'AppConfig.xml')) and isdir(join(base, d, 'Logs')):
return join(base, d, 'Logs')
return None
def _logging_enabled(self):
return self.logdir and (self.logging_enabled_in_file(join(self.logdir, pardir, 'AppConfigLocal.xml')) or
self.logging_enabled_in_file(join(self.logdir, pardir, 'AppConfig.xml')))
def _enable_logging(self):
return self.logdir and self.enable_logging_in_file(isfile(join(self.logdir, pardir, 'AppConfigLocal.xml')) and join(self.logdir, pardir, 'AppConfigLocal.xml') or join(self.logdir, pardir, 'AppConfig.xml'))
def _ED_is_running(self):
retval = DWORD(0)
EnumWindows(EnumWindowsProc, ctypes.addressof(retval))
return bool(retval)
elif platform=='linux2':
def _logdir(self):
return None
def _logging_enabled(self):
return False
def _enable_logging(self):
return False
def _ED_is_running(self):
return False
# singleton
monitor = EDLogs()

View File

@ -11,6 +11,7 @@ from ttkHyperlinkLabel import HyperlinkLabel
from config import applongname, config
from hotkey import hotkeymgr
from monitor import monitor
if platform == 'darwin':
@ -71,7 +72,7 @@ class PreferencesDialog(tk.Toplevel):
frame.grid(sticky=tk.NSEW)
credframe = ttk.LabelFrame(frame, text=_('Credentials')) # Section heading in settings
credframe.grid(padx=10, pady=10, sticky=tk.NSEW)
credframe.grid(padx=10, pady=5, sticky=tk.NSEW)
credframe.columnconfigure(1, weight=1)
ttk.Label(credframe, text=_('Please log in with your Elite: Dangerous account details')).grid(row=0, columnspan=2, sticky=tk.W) # Use same text as E:D Launcher's login dialog
@ -90,30 +91,35 @@ class PreferencesDialog(tk.Toplevel):
child.grid_configure(padx=5, pady=3)
outframe = ttk.LabelFrame(frame, text=_('Output')) # Section heading in settings
outframe.grid(padx=10, pady=10, sticky=tk.NSEW)
outframe.grid(padx=10, pady=5, sticky=tk.NSEW)
output = config.getint('output') or (config.OUT_EDDN | config.OUT_SHIP_EDS)
ttk.Label(outframe, text=_('Please choose what data to save')).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=_('Send station data to the Elite Dangerous Data Network'), variable=self.out_eddn, command=self.outvarchanged).grid(row=1, columnspan=2, padx=5, sticky=tk.W)
self.out_csv = tk.IntVar(value = (output & config.OUT_CSV ) and 1 or 0)
ttk.Checkbutton(outframe, text=_('Market data in CSV format file'), variable=self.out_csv, command=self.outvarchanged).grid(row=2, columnspan=2, padx=5, sticky=tk.W)
self.out_bpc = tk.IntVar(value = (output & config.OUT_BPC ) and 1 or 0)
ttk.Checkbutton(outframe, text=_("Market data in Slopey's BPC format file"), variable=self.out_bpc, command=self.outvarchanged).grid(row=3, columnspan=2, padx=5, sticky=tk.W)
self.out_td = tk.IntVar(value = (output & config.OUT_TD ) and 1 or 0)
ttk.Checkbutton(outframe, text=_('Market data in Trade Dangerous format file'), variable=self.out_td, command=self.outvarchanged).grid(row=4, columnspan=2, padx=5, sticky=tk.W)
self.out_ship_eds= tk.IntVar(value = (output & config.OUT_SHIP_EDS) and 1 or 0)
ttk.Checkbutton(outframe, text=_('Ship loadout in E:D Shipyard format file'), variable=self.out_ship_eds, command=self.outvarchanged).grid(row=5, columnspan=2, padx=5, pady=(5,0), sticky=tk.W)
self.out_ship_coriolis= tk.IntVar(value = (output & config.OUT_SHIP_CORIOLIS) and 1 or 0)
ttk.Checkbutton(outframe, text=_('Ship loadout in Coriolis format file'), variable=self.out_ship_coriolis, command=self.outvarchanged).grid(row=6, columnspan=2, padx=5, sticky=tk.W)
self.out_log_edsm = tk.IntVar(value = (output & config.OUT_LOG_EDSM ) and 1 or 0)
ttk.Checkbutton(outframe, text=_('Send flight log to Elite Dangerous Star Map'), variable=self.out_log_edsm, command=self.outvarchanged).grid(row=7, columnspan=2, padx=5, pady=(5,0), sticky=tk.W)
self.out_log_file = tk.IntVar(value = (output & config.OUT_LOG_FILE ) and 1 or 0)
ttk.Checkbutton(outframe, text=_('Flight log in CSV format file'), variable=self.out_log_file, command=self.outvarchanged).grid(row=8, columnspan=2, padx=5, sticky=tk.W)
ttk.Label(outframe, text=_('Please choose what data to save')).grid(row=0, padx=5, pady=3, sticky=tk.W)
self.out_eddn= tk.IntVar(value = (output & config.OUT_EDDN) and 1)
ttk.Checkbutton(outframe, text=_('Send station data to the Elite Dangerous Data Network'), variable=self.out_eddn, command=self.outvarchanged).grid(row=1, padx=5, sticky=tk.W)
self.out_csv = tk.IntVar(value = (output & config.OUT_CSV ) and 1)
ttk.Checkbutton(outframe, text=_('Market data in CSV format file'), variable=self.out_csv, command=self.outvarchanged).grid(row=2, padx=5, sticky=tk.W)
self.out_bpc = tk.IntVar(value = (output & config.OUT_BPC ) and 1)
ttk.Checkbutton(outframe, text=_("Market data in Slopey's BPC format file"), variable=self.out_bpc, command=self.outvarchanged).grid(row=3, padx=5, sticky=tk.W)
self.out_td = tk.IntVar(value = (output & config.OUT_TD ) and 1)
ttk.Checkbutton(outframe, text=_('Market data in Trade Dangerous format file'), variable=self.out_td, command=self.outvarchanged).grid(row=4, padx=5, sticky=tk.W)
self.out_ship_eds= tk.IntVar(value = (output & config.OUT_SHIP_EDS) and 1)
ttk.Checkbutton(outframe, text=_('Ship loadout in E:D Shipyard format file'), variable=self.out_ship_eds, command=self.outvarchanged).grid(row=5, padx=5, pady=(5,0), sticky=tk.W)
self.out_ship_coriolis= tk.IntVar(value = (output & config.OUT_SHIP_CORIOLIS) and 1)
ttk.Checkbutton(outframe, text=_('Ship loadout in Coriolis format file'), variable=self.out_ship_coriolis, command=self.outvarchanged).grid(row=6, padx=5, sticky=tk.W)
self.out_log_edsm = tk.IntVar(value = (output & config.OUT_LOG_EDSM) and 1)
ttk.Checkbutton(outframe, text=_('Send flight log to Elite Dangerous Star Map'), variable=self.out_log_edsm, command=self.outvarchanged).grid(row=7, padx=5, pady=(5,0), sticky=tk.W)
self.out_log_file = tk.IntVar(value = (output & config.OUT_LOG_FILE) and 1)
ttk.Checkbutton(outframe, text=_('Flight log in CSV format file'), variable=self.out_log_file, command=self.outvarchanged).grid(row=8, padx=5, sticky=tk.W)
self.out_log_auto = tk.IntVar(value = monitor.logdir and (output & config.OUT_LOG_AUTO) and 1 or 0)
if monitor.logdir:
self.out_log_auto_button = ttk.Checkbutton(outframe, text=_('Automatically make a log entry on entering a system'), variable=self.out_log_auto, command=self.outvarchanged) # Output setting
self.out_log_auto_button.grid(row=9, padx=5, sticky=tk.W)
self.out_log_auto_text = ttk.Label(outframe)
self.dir_label = ttk.Label(frame, text=_('File location'), foreground=style.lookup('TLabelframe.Label', 'foreground')) # Section heading in settings
dirframe = ttk.LabelFrame(frame, labelwidget = self.dir_label)
dirframe.grid(padx=10, pady=10, sticky=tk.NSEW)
dirframe.grid(padx=10, pady=5, sticky=tk.NSEW)
dirframe.columnconfigure(0, weight=1)
self.outdir = ttk.Entry(dirframe, takefocus=False)
@ -128,7 +134,7 @@ class PreferencesDialog(tk.Toplevel):
self.edsm_label = HyperlinkLabel(frame, text=_('Elite Dangerous Star Map credentials'), disabledforeground=style.lookup('TLabelframe.Label', 'foreground'), url='http://www.edsm.net/settings/api', underline=True) # Section heading in settings
edsmframe = ttk.LabelFrame(frame, labelwidget = self.edsm_label)
edsmframe.grid(padx=10, pady=10, sticky=tk.NSEW)
edsmframe.grid(padx=10, pady=5, sticky=tk.NSEW)
edsmframe.columnconfigure(1, weight=1)
ttk.Label(edsmframe, text=_('Cmdr name')).grid(row=0, sticky=tk.W) # EDSM & privacy setting
@ -151,7 +157,7 @@ class PreferencesDialog(tk.Toplevel):
self.hotkey_play = tk.IntVar(value = not config.getint('hotkey_mute'))
hotkeyframe = ttk.LabelFrame(frame, text=platform == 'darwin' and _('Keyboard shortcut') or # Section heading in settings on OSX
_('Hotkey')) # Section heading in settings on Windows
hotkeyframe.grid(padx=10, pady=10, sticky=tk.NSEW)
hotkeyframe.grid(padx=10, pady=5, sticky=tk.NSEW)
hotkeyframe.columnconfigure(1, weight=1)
if platform == 'darwin' and not was_accessible_at_launch:
if AXIsProcessTrusted():
@ -171,10 +177,10 @@ class PreferencesDialog(tk.Toplevel):
self.hotkey_play_btn.grid(row=2, columnspan=2, padx=5, sticky=tk.W)
privacyframe = ttk.LabelFrame(frame, text=_('Privacy')) # Section heading in settings
privacyframe.grid(padx=10, pady=10, sticky=tk.NSEW)
privacyframe.grid(padx=10, pady=5, sticky=tk.NSEW)
self.out_anon= tk.IntVar(value = config.getint('anonymous') and 1)
ttk.Label(privacyframe, text=_('How do you want to be identified in the saved data')).grid(row=0, columnspan=2, padx=5, sticky=tk.W)
ttk.Label(privacyframe, text=_('How do you want to be identified in the saved data')).grid(row=0, padx=5, sticky=tk.W)
ttk.Radiobutton(privacyframe, text=_('Cmdr name'), variable=self.out_anon, value=0).grid(padx=5, sticky=tk.W) # Privacy setting
ttk.Radiobutton(privacyframe, text=_('Pseudo-anonymized ID'), variable=self.out_anon, value=1).grid(padx=5, sticky=tk.W) # Privacy setting
@ -182,10 +188,12 @@ class PreferencesDialog(tk.Toplevel):
self.protocol("WM_DELETE_WINDOW", self.apply) # close button applies changes
else:
buttonframe = ttk.Frame(frame)
buttonframe.grid(padx=10, pady=10, sticky=tk.NSEW)
buttonframe.grid(padx=10, pady=5, sticky=tk.NSEW)
buttonframe.columnconfigure(0, weight=1)
ttk.Label(buttonframe).grid(row=0, column=0) # spacer
ttk.Button(buttonframe, text=_('OK'), command=self.apply).grid(row=0, column=1, sticky=tk.E)
button = ttk.Button(buttonframe, text=_('OK'), command=self.apply)
button.grid(row=0, column=1, sticky=tk.E)
button.bind("<Return>", lambda event:self.apply())
self.protocol("WM_DELETE_WINDOW", self._destroy)
# Selectively disable buttons depending on output settings
@ -205,6 +213,20 @@ class PreferencesDialog(tk.Toplevel):
self.outbutton['state'] = local and tk.NORMAL or tk.DISABLED
self.outdir['state'] = local and 'readonly' or tk.DISABLED
if monitor.logdir:
log = self.out_log_edsm.get() or self.out_log_file.get()
self.out_log_auto_button['state'] = log and tk.NORMAL or tk.DISABLED
self.out_log_auto_text['text'] = ''
if log and self.out_log_auto.get():
if not monitor.enable_logging():
self.out_log_auto_text['text'] = "Can't enable automatic logging!" # Shouldn't happen - don't translate
self.out_log_auto_text.grid(row=10, padx=(25,5), sticky=tk.EW)
elif monitor.restart_required():
self.out_log_auto_text['text'] = _('Re-start Elite: Dangerous to use this feature') # Output settings prompt
self.out_log_auto_text.grid(row=10, padx=(25,5), sticky=tk.EW)
edsm = self.out_log_edsm.get()
self.edsm_label['state'] = edsm and tk.NORMAL or tk.DISABLED
self.edsm_cmdr['state'] = edsm and tk.NORMAL or tk.DISABLED
@ -290,7 +312,7 @@ class PreferencesDialog(tk.Toplevel):
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) + (self.out_ship_eds.get() and config.OUT_SHIP_EDS or 0) + (self.out_log_file.get() and config.OUT_LOG_FILE or 0) + (self.out_ship_coriolis.get() and config.OUT_SHIP_CORIOLIS or 0) + (self.out_log_edsm.get() and config.OUT_LOG_EDSM or 0))
config.set('output', (self.out_eddn.get() and config.OUT_EDDN) + (self.out_bpc.get() and config.OUT_BPC) + (self.out_td.get() and config.OUT_TD) + (self.out_csv.get() and config.OUT_CSV) + (self.out_ship_eds.get() and config.OUT_SHIP_EDS) + (self.out_log_file.get() and config.OUT_LOG_FILE) + (self.out_ship_coriolis.get() and config.OUT_SHIP_CORIOLIS) + (self.out_log_edsm.get() and config.OUT_LOG_EDSM) + (self.out_log_auto.get() and config.OUT_LOG_AUTO))
config.set('outdir', expanduser(self.outdir.get()))
config.set('edsm_cmdrname', self.edsm_cmdr.get().strip())
@ -309,8 +331,13 @@ class PreferencesDialog(tk.Toplevel):
self.callback()
def _destroy(self):
# Re-enable hotkey monitoring before exit
# Re-enable hotkey and log monitoring before exit
hotkeymgr.register(self.parent, config.getint('hotkey_code'), config.getint('hotkey_mods'))
if (config.getint('output') & config.OUT_LOG_AUTO) and (config.getint('output') & (config.OUT_LOG_AUTO|config.OUT_LOG_EDSM)):
monitor.enable_logging()
monitor.start()
else:
monitor.stop()
self.destroy()
if platform == 'darwin':
@ -371,6 +398,7 @@ class AuthenticationDialog(tk.Toplevel):
self.code.focus_set()
ttk.Label(frame).grid(row=1, column=2) # spacer
self.button = ttk.Button(frame, text=_('OK'), command=self.apply, state=tk.DISABLED)
self.button.bind("<Return>", lambda event:self.apply())
self.button.grid(row=1, column=3, sticky=tk.E)
for child in frame.winfo_children():