Merge pull request #10 from CMDR-Kiel42/spansh_package

Spansh package
This commit is contained in:
CMDR-Kiel42 2019-08-11 20:41:53 +02:00 committed by GitHub
commit ecc42f0367
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 554 additions and 440 deletions

View File

@ -2,6 +2,16 @@
All notable changes to this project will be documented in this file.
## 2.1.2
- Fixed conflicts when other plugins used similar file names
- Fixed plugin sometimes just breaking when nasty errors occured and actually recover from them
- Remove trailing whitespaces when plotting a route to avoid issues with Spansh
- Show plotting errors in the GUI (like unknown system name or invalid range)
- Fixed an issue with the systems list where it wouldn't disappear
- Fixed an issue when plotting from the system you're currently in (it should now *finally* start at the next waypoint)
- Keep previous entries in the *Route plotting* GUI when closing it
## 2.1.1
- Fixed an issue with CSV files containing blank lines

View File

@ -18,7 +18,7 @@ class AutoCompleter(Entry, PlaceHolder):
self.parent = parent
self.lb = Listbox(self.parent, **kw)
self.lb = Listbox(self.parent, selectmode=SINGLE, **kw)
self.lb_up = False
self.has_selected = False
self.queue = Queue.Queue()
@ -26,12 +26,22 @@ class AutoCompleter(Entry, PlaceHolder):
PlaceHolder.__init__(self, placeholder)
self.bind("<Any-Key>", self.keypressed)
self.lb.bind("<Any-Key>", self.keypressed)
self.bind('<Control-KeyRelease-a>', self.select_all)
self.bind('<Button-3>', self.paste)
self.lb.bind("<Double-Button-1>", self.selection)
self.bind("<FocusOut>", self.ac_foc_out)
self.lb.bind("<FocusOut>", self.ac_foc_out)
self.update_me()
def ac_foc_out(self, event):
x,y = self.parent.winfo_pointerxy()
widget_under_cursor = self.parent.winfo_containing(x,y)
if widget_under_cursor != self.lb and widget_under_cursor != self:
self.foc_out()
self.hide_list()
def paste(self, event):
self.foc_in()
self.insert(0, self.clipboard_get())
@ -39,9 +49,9 @@ class AutoCompleter(Entry, PlaceHolder):
def keypressed(self, event):
key=event.keysym
if key == 'Down':
self.down()
self.down(event.widget.widgetName)
elif key == 'Up':
self.up()
self.up(event.widget.widgetName)
elif key in ['Return', 'Right']:
if self.lb_up:
self.selection()
@ -66,7 +76,7 @@ class AutoCompleter(Entry, PlaceHolder):
self.hide_list()
self.icursor(END)
def up(self):
def up(self, widget):
if self.lb_up:
if self.lb.curselection() == ():
index = '0'
@ -76,9 +86,10 @@ class AutoCompleter(Entry, PlaceHolder):
self.lb.selection_clear(first=index)
index = str(int(index)-1)
self.lb.selection_set(first=index)
self.lb.activate(index)
if widget != "listbox":
self.lb.activate(index)
def down(self):
def down(self, widget):
if self.lb_up:
if self.lb.curselection() == ():
index = '0'
@ -89,7 +100,8 @@ class AutoCompleter(Entry, PlaceHolder):
index = str(int(index+1))
self.lb.selection_set(first=index)
self.lb.activate(index)
if widget != "listbox":
self.lb.activate(index)
else:
self.query_systems()
@ -118,7 +130,7 @@ class AutoCompleter(Entry, PlaceHolder):
self.lb_up = False
def query_systems(self):
inp = self.var.get()
inp = self.var.get().strip()
if inp != self.placeholder and inp.__len__() >= 3:
url = "https://spansh.co.uk/api/systems?"
try:
@ -130,7 +142,7 @@ class AutoCompleter(Entry, PlaceHolder):
lista = json.loads(results.content)
if lista:
self.write(lista)
except NameError:
except:
exc_type, exc_value, exc_traceback = sys.exc_info()
lines = traceback.format_exception(exc_type, exc_value, exc_traceback)
sys.stderr.write(''.join('!! ' + line for line in lines))

View File

@ -22,9 +22,10 @@ class PlaceHolder():
self['fg'] = self.placeholder_color
def foc_in(self, *args):
if self['fg'] == self.placeholder_color or self.get() == self.placeholder:
self.delete('0', 'end')
if self['fg'] == "red" or self['fg'] == self.placeholder_color:
self['fg'] = self.default_fg_color
if self.get() == self.placeholder:
self.delete('0', 'end')
def foc_out(self, *args):
if not self.get():

View File

@ -6,4 +6,5 @@ from PlaceHolder import PlaceHolder
class PlaceHolderEntry(Entry, PlaceHolder):
def __init__(self, parent, placeholder, **kw):
Entry.__init__(self, parent, **kw)
self.var = self["textvariable"] = StringVar()
PlaceHolder.__init__(self, placeholder)

492
SpanshRouter/SpanshRouter.py Executable file
View File

@ -0,0 +1,492 @@
#! /usr/bin/env python2
import os
import sys
import traceback
import csv
import subprocess
import webbrowser
import json
import requests
import Tkinter as tk
import tkFileDialog as filedialog
import tkMessageBox as confirmDialog
from time import sleep
from monitor import monitor
from . import AutoCompleter
from . import PlaceHolderEntry
from .updater import SpanshUpdater
class SpanshRouter():
def __init__(self, plugin_dir):
version_file = os.path.join(plugin_dir, "version.json")
with open(version_file, 'r') as version_fd:
self.plugin_version = version_fd.read()
self.update_available = False
self.next_stop = "No route planned"
self.route = []
self.next_wp_label = "Next waypoint: "
self.jumpcountlbl_txt = "Estimated jumps left: "
self.parent = None
self.plugin_dir = plugin_dir
self.save_route_path = os.path.join(plugin_dir, 'route.csv')
self.offset_file_path = os.path.join(plugin_dir, 'offset')
self.offset = 0
self.jumps_left = 0
self.error_txt = tk.StringVar()
self.plot_error = "Error while trying to plot a route, please try again."
# -- GUI part --
def init_gui(self, parent):
self.parent = parent
parentwidth = parent.winfo_width()
self.frame = tk.Frame(parent, borderwidth=2)
self.frame.grid(sticky=tk.NSEW, columnspan=2)
# Route info
self.waypoint_prev_btn = tk.Button(self.frame, text="^", command=self.goto_prev_waypoint)
self.waypoint_btn = tk.Button(self.frame, text=self.next_wp_label + self.next_stop, command=self.copy_waypoint)
self.waypoint_next_btn = tk.Button(self.frame, text="v", command=self.goto_next_waypoint)
self.jumpcounttxt_lbl = tk.Label(self.frame, text=self.jumpcountlbl_txt + str(self.jumps_left))
self.error_lbl = tk.Label(self.frame, textvariable=self.error_txt)
# Plotting GUI
self.source_ac = AutoCompleter(self.frame, "Source System", width=30)
self.dest_ac = AutoCompleter(self.frame, "Destination System", width=30)
self.range_entry = PlaceHolderEntry(self.frame, "Range (LY)", width=10)
self.efficiency_slider = tk.Scale(self.frame, from_=1, to=100, orient=tk.HORIZONTAL, label="Efficiency (%)")
self.efficiency_slider.set(60)
self.plot_gui_btn = tk.Button(self.frame, text="Plot route", command=self.show_plot_gui)
self.plot_route_btn = tk.Button(self.frame, text="Calculate", command=self.plot_route)
self.cancel_plot = tk.Button(self.frame, text="Cancel", command=lambda: self.show_plot_gui(False))
self.csv_route_btn = tk.Button(self.frame, text="Import CSV", command=self.plot_csv)
self.clear_route_btn = tk.Button(self.frame, text="Clear route", command=self.clear_route)
row = 0
self.waypoint_prev_btn.grid(row=row, columnspan=2)
row += 1
self.waypoint_btn.grid(row=row, columnspan=2)
row += 1
self.waypoint_next_btn.grid(row=row, columnspan=2)
row += 1
self.source_ac.grid(row=row,columnspan=2, pady=(10,0)) # The AutoCompleter takes two rows to show the list when needed, so we skip one
row += 2
self.dest_ac.grid(row=row,columnspan=2, pady=(10,0))
row += 2
self.range_entry.grid(row=row, pady=10, sticky=tk.W)
row += 1
self.efficiency_slider.grid(row=row, pady=10, columnspan=2, sticky=tk.EW)
row += 1
self.csv_route_btn.grid(row=row, pady=10, padx=0)
self.plot_route_btn.grid(row=row, pady=10, padx=0)
self.plot_gui_btn.grid(row=row, column=1, pady=10, padx=5, sticky=tk.W)
self.cancel_plot.grid(row=row, column=1, pady=10, padx=5, sticky=tk.E)
row += 1
self.clear_route_btn.grid(row=row,column=1)
row += 1
self.jumpcounttxt_lbl.grid(row=row, pady=5, sticky=tk.W)
row += 1
self.error_lbl.grid(row=row, columnspan=2)
self.error_lbl.grid_remove()
row += 1
# Check if we're having a valid range on the fly
self.range_entry.var.trace('w', self.check_range)
self.show_plot_gui(False)
if not self.route.__len__() > 0:
self.waypoint_prev_btn.grid_remove()
self.waypoint_btn.grid_remove()
self.waypoint_next_btn.grid_remove()
self.jumpcounttxt_lbl.grid_remove()
self.clear_route_btn.grid_remove()
if self.update_available:
update_txt = ("A SpanshRouter update is available.\n"
"It will be installed next time you start EDMC.\n"
"Click to dismiss this message, right click to see what's new.")
self.update_btn = tk.Button(self.frame, text=update_txt, command=lambda: self.update_btn.grid_forget())
self.update_btn.bind("<Button-3>", self.goto_changelog_page)
self.update_btn.grid(row=row, pady=5, columnspan=2)
row += 1
self.update_gui()
return self.frame
def show_plot_gui(self, show=True):
if show:
self.waypoint_prev_btn.grid_remove()
self.waypoint_btn.grid_remove()
self.waypoint_next_btn.grid_remove()
self.jumpcounttxt_lbl.grid_remove()
self.clear_route_btn.grid_remove()
self.plot_gui_btn.grid_remove()
self.csv_route_btn.grid_remove()
self.source_ac.grid()
self.dest_ac.grid()
self.range_entry.grid()
self.efficiency_slider.grid()
self.plot_route_btn.grid()
self.cancel_plot.grid()
# Workaround because EDMC keeps switching the placeholder to bright white
if self.source_ac.get() == self.source_ac.placeholder:
self.source_ac.force_placeholder_color()
if self.dest_ac.get() == self.dest_ac.placeholder:
self.dest_ac.force_placeholder_color()
if self.range_entry.get() == self.range_entry.placeholder:
self.range_entry.force_placeholder_color()
self.show_route_gui(False)
else:
if len(self.source_ac.var.get()) == 0:
self.source_ac.put_placeholder()
if len(self.dest_ac.var.get()) == 0:
self.dest_ac.put_placeholder()
self.source_ac.grid_remove()
self.dest_ac.grid_remove()
self.range_entry.grid_remove()
self.efficiency_slider.grid_remove()
self.plot_gui_btn.grid_remove()
self.plot_route_btn.grid_remove()
self.cancel_plot.grid_remove()
self.plot_gui_btn.grid()
self.csv_route_btn.grid()
self.show_route_gui(True)
def set_source_ac(self, text):
self.source_ac.delete(0, tk.END)
self.source_ac.insert(0, text)
self.source_ac["fg"] = self.source_ac.default_fg_color
def show_route_gui(self, show):
self.hide_error()
if not show or not self.route.__len__() > 0:
self.waypoint_prev_btn.grid_remove()
self.waypoint_btn.grid_remove()
self.waypoint_next_btn.grid_remove()
self.jumpcounttxt_lbl.grid_remove()
self.clear_route_btn.grid_remove()
else:
self.waypoint_btn["text"] = self.next_wp_label + self.next_stop
self.jumpcounttxt_lbl["text"] = self.jumpcountlbl_txt + str(self.jumps_left)
self.jumpcounttxt_lbl.grid()
self.waypoint_prev_btn.grid()
self.waypoint_btn.grid()
self.waypoint_next_btn.grid()
if self.offset == 0:
self.waypoint_prev_btn.config(state=tk.DISABLED)
else:
self.waypoint_prev_btn.config(state=tk.NORMAL)
if self.offset == self.route.__len__()-1:
self.waypoint_next_btn.config(state=tk.DISABLED)
else:
self.waypoint_next_btn.config(state=tk.NORMAL)
self.clear_route_btn.grid()
def update_gui(self):
self.show_route_gui(True)
def show_error(self, error):
self.error_txt.set(error)
self.error_lbl.grid()
def hide_error(self):
self.error_lbl.grid_remove()
def enable_plot_gui(self, enable):
if enable:
self.source_ac.config(state=tk.NORMAL)
self.source_ac.update_idletasks()
self.dest_ac.config(state=tk.NORMAL)
self.dest_ac.update_idletasks()
self.efficiency_slider.config(state=tk.NORMAL)
self.efficiency_slider.update_idletasks()
self.range_entry.config(state=tk.NORMAL)
self.range_entry.update_idletasks()
self.plot_route_btn.config(state=tk.NORMAL, text="Calculate")
self.plot_route_btn.update_idletasks()
self.cancel_plot.config(state=tk.NORMAL)
self.cancel_plot.update_idletasks()
else:
self.source_ac.config(state=tk.DISABLED)
self.source_ac.update_idletasks()
self.dest_ac.config(state=tk.DISABLED)
self.dest_ac.update_idletasks()
self.efficiency_slider.config(state=tk.DISABLED)
self.efficiency_slider.update_idletasks()
self.range_entry.config(state=tk.DISABLED)
self.range_entry.update_idletasks()
self.plot_route_btn.config(state=tk.DISABLED, text="Computing...")
self.plot_route_btn.update_idletasks()
self.cancel_plot.config(state=tk.DISABLED)
self.cancel_plot.update_idletasks()
# -- END GUI part --
def open_last_route(self):
try:
with open(self.save_route_path, 'r') as csvfile:
route_reader = csv.reader(csvfile)
for row in route_reader:
if row not in (None, "", []):
self.route.append(row)
try:
with open(self.offset_file_path, 'r') as offset_fh:
self.offset = int(offset_fh.readline())
except:
self.offset = 0
for row in self.route[self.offset:]:
self.jumps_left += int(row[1])
self.next_stop = self.route[self.offset][0]
self.copy_waypoint()
except:
print("No previously saved route.")
def copy_waypoint(self):
if sys.platform == "linux" or sys.platform == "linux2":
command = subprocess.Popen(["echo", "-n", self.next_stop], stdout=subprocess.PIPE)
subprocess.Popen(["xclip", "-selection", "c"], stdin=command.stdout)
else:
self.parent.clipboard_clear()
self.parent.clipboard_append(self.next_stop)
self.parent.update()
def goto_next_waypoint(self):
if self.offset < self.route.__len__()-1:
self.update_route(1)
def goto_prev_waypoint(self):
if self.offset > 0:
self.update_route(-1)
def update_route(self, direction=1):
if direction > 0:
self.jumps_left -= int(self.route[self.offset][1])
self.offset += 1
else:
self.offset -= 1
self.jumps_left += int(self.route[self.offset][1])
if self.offset >= self.route.__len__():
self.next_stop = "End of the road!"
self.update_gui()
else:
self.next_stop = self.route[self.offset][0]
self.update_gui()
self.copy_waypoint()
def goto_changelog_page(self):
changelog_url = 'https://github.com/CMDR-Kiel42/EDMC_SpanshRouter/blob/master/CHANGELOG.md#'
changelog_url += self.spansh_updater.version.replace('.', '')
webbrowser.open(changelog_url)
def plot_csv(self):
filename = filedialog.askopenfilename(filetypes = (("csv files", "*.csv"),)) # show an "Open" dialog box and return the path to the selected file
if filename.__len__() > 0:
with open(filename, 'r') as csvfile:
route_reader = csv.reader(csvfile)
# Skip the header
route_reader.next()
self.jumps_left = 0
for row in route_reader:
if row not in (None, "", []):
self.route.append([row[0], row[4]])
self.jumps_left += int(row[4])
self.offset = 0
self.next_stop = self.route[0][0]
self.copy_waypoint()
self.update_gui()
def plot_route(self):
self.hide_error()
try:
source = self.source_ac.get().strip()
dest = self.dest_ac.get().strip()
efficiency = self.efficiency_slider.get()
if ( source and source != self.source_ac.placeholder and
dest and dest != self.dest_ac.placeholder ):
try:
range_ly = float(self.range_entry.get())
except ValueError:
self.show_error("Invalid range")
return
job_url="https://spansh.co.uk/api/route?"
results = requests.post(job_url, params={
"efficiency": efficiency,
"range": range_ly,
"from": source,
"to": dest
}, headers={'User-Agent': "EDMC_SpanshRouter 1.0"})
if results.status_code == 202:
self.enable_plot_gui(False)
tries = 0
while(tries < 20):
response = json.loads(results.content)
job = response["job"]
results_url = "https://spansh.co.uk/api/results/" + job
route_response = requests.get(results_url, timeout=5)
if route_response.status_code != 202:
break
tries += 1
sleep(1)
if route_response:
if route_response.status_code == 200:
route = json.loads(route_response.content)["result"]["system_jumps"]
self.clear_route(show_dialog=False)
for waypoint in route:
self.route.append([waypoint["system"], str(waypoint["jumps"])])
self.jumps_left += waypoint["jumps"]
self.enable_plot_gui(True)
self.show_plot_gui(False)
self.offset = 1 if self.route[0][0] == monitor.system else 0
self.next_stop = self.route[self.offset][0]
self.copy_waypoint()
self.update_gui()
else:
sys.stderr.write("Failed to query plotted route from Spansh: code " + str(route_response.status_code) + route_response.text + '\n')
self.enable_plot_gui(True)
failure = json.loads(results.content)
if route_response.status_code == 400 and "error" in failure:
self.show_error(failure["error"])
if "starting system" in failure["error"]:
self.source_ac["fg"] = "red"
if "finishing system" in failure["error"]:
self.dest_ac["fg"] = "red"
else:
self.show_error(self.plot_error)
else:
sys.stderr.write("Query to Spansh timed out")
self.enable_plot_gui(True)
self.show_error("The query to Spansh was too long and timed out, please try again.")
else:
sys.stderr.write("Failed to query plotted route from Spansh: code " + str(results.status_code) + results.text + '\n')
self.enable_plot_gui(True)
failure = json.loads(results.content)
if results.status_code == 400 and "error" in failure:
self.show_error(failure["error"])
if "starting system" in failure["error"]:
self.source_ac["fg"] = "red"
if "finishing system" in failure["error"]:
self.dest_ac["fg"] = "red"
else:
self.show_error(self.plot_error)
except:
exc_type, exc_value, exc_traceback = sys.exc_info()
lines = traceback.format_exception(exc_type, exc_value, exc_traceback)
sys.stderr.write(''.join('!! ' + line for line in lines))
self.enable_plot_gui(True)
self.show_error(self.plot_error)
def clear_route(self, show_dialog=True):
clear = confirmDialog.askyesno("SpanshRouter","Are you sure you want to clear the current route?") if show_dialog else True
if clear:
self.offset = 0
self.route = []
self.next_waypoint = ""
self.jumps_left = 0
try:
os.remove(self.save_route_path)
except:
print("No route to delete")
try:
os.remove(self.offset_file_path)
except:
print("No offset file to delete")
self.update_gui()
def save_route(self):
if self.route.__len__() != 0:
with open(self.save_route_path, 'w') as csvfile:
writer = csv.writer(csvfile)
writer.writerows(self.route)
with open(self.offset_file_path, 'w') as offset_fh:
offset_fh.write(str(self.offset))
else:
try:
os.remove(self.save_route_path)
os.remove(self.offset_file_path)
except:
print("No route to delete")
def check_range(self, name, index, mode):
value = self.range_entry.var.get()
if value.__len__() > 0 and value != self.range_entry.placeholder:
try:
float(value)
self.range_entry['fg'] = self.range_entry.default_fg_color
self.hide_error()
except ValueError:
self.show_error("Invalid range")
self.range_entry['fg'] = "red"
def cleanup_old_version(self):
try:
if (os.path.exists(os.path.join(self.plugin_dir, "AutoCompleter.py"))
and os.path.exists(os.path.join(self.plugin_dir, "SpanshRouter"))):
files_list = os.listdir(self.plugin_dir)
for filename in files_list:
if (filename != "load.py"
and (filename.endswith(".py") or filename.endswith(".pyc") or filename.endswith(".pyo"))):
os.remove(os.path.join(self.plugin_dir, filename))
except:
exc_type, exc_value, exc_traceback = sys.exc_info()
lines = traceback.format_exception(exc_type, exc_value, exc_traceback)
sys.stderr.write(''.join('!! ' + line for line in lines))
def check_for_update(self):
self.cleanup_old_version()
url = "https://raw.githubusercontent.com/CMDR-Kiel42/EDMC_SpanshRouter/master/version.json"
try:
response = requests.get(url, timeout=2)
if response.status_code == 200:
if self.plugin_version != response.content:
self.update_available = True
self.spansh_updater = SpanshUpdater(response.content, self.plugin_dir)
if not self.spansh_updater.download_zip():
sys.stderr.write("Error when downloading the latest SpanshRouter update")
else:
sys.stderr.write("Could not query latest SpanshRouter version: " + str(response.status_code) + response.text)
except:
exc_type, exc_value, exc_traceback = sys.exc_info()
lines = traceback.format_exception(exc_type, exc_value, exc_traceback)
sys.stderr.write(''.join('!! ' + line for line in lines))
def install_update(self):
self.spansh_updater.install()

4
SpanshRouter/__init__.py Normal file
View File

@ -0,0 +1,4 @@
from updater import SpanshUpdater
from AutoCompleter import AutoCompleter
from PlaceHolderEntry import PlaceHolderEntry
from SpanshRouter import SpanshRouter

View File

@ -27,7 +27,7 @@ class SpanshUpdater():
else:
sys.stderr.write("Failed to fetch SpanchRouter update. Status code: " + str(r.status_code))
self.zip_downloaded = False
except NameError:
except:
exc_type, exc_value, exc_traceback = sys.exc_info()
lines = traceback.format_exception(exc_type, exc_value, exc_traceback)
sys.stderr.write(''.join('!! ' + line for line in lines))
@ -42,7 +42,7 @@ class SpanshUpdater():
zip_ref.extractall(self.plugin_dir)
os.remove(self.zip_path)
except NameError:
except:
exc_type, exc_value, exc_traceback = sys.exc_info()
lines = traceback.format_exception(exc_type, exc_value, exc_traceback)
sys.stderr.write(''.join('!! ' + line for line in lines))
sys.stderr.write(''.join('!! ' + line for line in lines))

442
load.py
View File

@ -1,435 +1,29 @@
import Tkinter as tk
import tkFileDialog as filedialog
import tkMessageBox as confirmDialog
import sys
import csv
import os
import json
import webbrowser
import requests
import traceback
import subprocess
from updater import SpanshUpdater
from time import sleep
from AutoCompleter import AutoCompleter
from PlaceHolderEntry import PlaceHolderEntry
this = sys.modules[__name__]
this.plugin_version = "2.1.1"
this.update_available = False
this.next_stop = "No route planned"
this.route = []
this.next_wp_label = "Next waypoint: "
this.jumpcountlbl_txt = "Estimated jumps left: "
this.parent = None
this.save_route_path = ""
this.offset_file_path = ""
this.offset = 0
this.jumps_left = 0
this.error_txt = tk.StringVar()
this.plot_error = "Error while trying to plot a route, please try again."
from SpanshRouter import SpanshRouter
spansh_router = None
def plugin_start(plugin_dir):
# Check for newer versions
url = "https://raw.githubusercontent.com/CMDR-Kiel42/EDMC_SpanshRouter/master/version.json"
try:
response = requests.get(url, timeout=2)
if response.status_code == 200:
if this.plugin_version != response.content:
this.update_available = True
this.spansh_updater = SpanshUpdater(response.content, plugin_dir)
if not this.spansh_updater.download_zip():
sys.stderr.write("Error when downloading the latest SpanshRouter update")
else:
sys.stderr.write("Could not query latest SpanshRouter version: " + str(response.status_code) + response.text)
except NameError:
exc_type, exc_value, exc_traceback = sys.exc_info()
lines = traceback.format_exception(exc_type, exc_value, exc_traceback)
sys.stderr.write(''.join('!! ' + line for line in lines))
finally:
this.save_route_path = os.path.join(plugin_dir, 'route.csv')
this.offset_file_path = os.path.join(plugin_dir, 'offset')
try:
# Open the last saved route
with open(this.save_route_path, 'r') as csvfile:
route_reader = csv.reader(csvfile)
for row in route_reader:
if row not in (None, "", []):
this.route.append(row)
try:
with open(this.offset_file_path, 'r') as offset_fh:
this.offset = int(offset_fh.readline())
except:
this.offset = 0
for row in this.route[this.offset:]:
this.jumps_left += int(row[1])
this.next_stop = this.route[this.offset][0]
copy_waypoint()
except:
print("No previously saved route.")
global spansh_router
spansh_router = SpanshRouter(plugin_dir)
spansh_router.check_for_update()
spansh_router.open_last_route()
def plugin_stop():
if this.route.__len__() != 0:
# Save route for next time
with open(this.save_route_path, 'w') as csvfile:
writer = csv.writer(csvfile)
writer.writerows(this.route)
global spansh_router
spansh_router.save_route()
with open(this.offset_file_path, 'w') as offset_fh:
offset_fh.write(str(this.offset))
else:
try:
os.remove(this.save_route_path)
os.remove(this.offset_file_path)
except:
print("No route to delete")
if this.update_available:
this.spansh_updater.install()
def show_error(error):
this.error_txt.set(error)
this.error_lbl.grid()
def hide_error():
this.error_lbl.grid_remove()
def show_route_gui(show):
hide_error()
if not show or not this.route.__len__() > 0:
this.waypoint_prev_btn.grid_remove()
this.waypoint_btn.grid_remove()
this.waypoint_next_btn.grid_remove()
this.jumpcounttxt_lbl.grid_remove()
this.clear_route_btn.grid_remove()
else:
this.waypoint_btn["text"] = this.next_wp_label + this.next_stop
this.jumpcounttxt_lbl["text"] = this.jumpcountlbl_txt + str(this.jumps_left)
this.jumpcounttxt_lbl.grid()
this.waypoint_prev_btn.grid()
this.waypoint_btn.grid()
this.waypoint_next_btn.grid()
if this.offset == 0:
this.waypoint_prev_btn.config(state=tk.DISABLED)
else:
this.waypoint_prev_btn.config(state=tk.NORMAL)
if this.offset == this.route.__len__()-1:
this.waypoint_next_btn.config(state=tk.DISABLED)
else:
this.waypoint_next_btn.config(state=tk.NORMAL)
this.clear_route_btn.grid()
def update_gui():
show_route_gui(True)
def show_plot_gui(show=True):
if show:
this.waypoint_prev_btn.grid_remove()
this.waypoint_btn.grid_remove()
this.waypoint_next_btn.grid_remove()
this.jumpcounttxt_lbl.grid_remove()
this.clear_route_btn.grid_remove()
this.plot_gui_btn.grid_remove()
this.csv_route_btn.grid_remove()
this.source_ac.grid()
this.dest_ac.grid()
this.range_entry.grid()
this.efficiency_slider.grid()
this.plot_route_btn.grid()
this.cancel_plot.grid()
# Workaround because EDMC keeps switching the placeholder to bright white
if this.source_ac.get() == this.source_ac.placeholder:
this.source_ac.force_placeholder_color()
if this.dest_ac.get() == this.dest_ac.placeholder:
this.dest_ac.force_placeholder_color()
if this.range_entry.get() == this.range_entry.placeholder:
this.range_entry.force_placeholder_color()
show_route_gui(False)
else:
this.source_ac.put_placeholder()
this.dest_ac.put_placeholder()
this.source_ac.grid_remove()
this.dest_ac.grid_remove()
this.range_entry.grid_remove()
this.efficiency_slider.grid_remove()
this.plot_gui_btn.grid_remove()
this.plot_route_btn.grid_remove()
this.cancel_plot.grid_remove()
this.plot_gui_btn.grid()
this.csv_route_btn.grid()
show_route_gui(True)
def copy_waypoint(self=None):
if sys.platform == "linux" or sys.platform == "linux2":
command = subprocess.Popen(["echo", "-n", this.next_stop], stdout=subprocess.PIPE)
subprocess.Popen(["xclip", "-selection", "c"], stdin=command.stdout)
else:
this.parent.clipboard_clear()
this.parent.clipboard_append(this.next_stop)
this.parent.update()
def goto_next_waypoint(self=None):
if this.offset < this.route.__len__()-1:
update_route(1)
def goto_prev_waypoint(self=None):
if this.offset > 0:
update_route(-1)
def plot_csv(self=None):
filename = filedialog.askopenfilename(filetypes = (("csv files", "*.csv"),)) # show an "Open" dialog box and return the path to the selected file
if filename.__len__() > 0:
with open(filename, 'r') as csvfile:
route_reader = csv.reader(csvfile)
# Skip the header
route_reader.next()
this.jumps_left = 0
for row in route_reader:
if row not in (None, "", []):
this.route.append([row[0], row[4]])
this.jumps_left += int(row[4])
this.offset = 0
this.next_stop = this.route[0][0]
copy_waypoint()
update_gui()
def enable_plot_gui(enable):
if enable:
this.source_ac.config(state=tk.NORMAL)
this.source_ac.update_idletasks()
this.dest_ac.config(state=tk.NORMAL)
this.dest_ac.update_idletasks()
this.efficiency_slider.config(state=tk.NORMAL)
this.efficiency_slider.update_idletasks()
this.range_entry.config(state=tk.NORMAL)
this.range_entry.update_idletasks()
this.plot_route_btn.config(state=tk.NORMAL, text="Calculate")
this.plot_route_btn.update_idletasks()
this.cancel_plot.config(state=tk.NORMAL)
this.cancel_plot.update_idletasks()
else:
this.source_ac.config(state=tk.DISABLED)
this.source_ac.update_idletasks()
this.dest_ac.config(state=tk.DISABLED)
this.dest_ac.update_idletasks()
this.efficiency_slider.config(state=tk.DISABLED)
this.efficiency_slider.update_idletasks()
this.range_entry.config(state=tk.DISABLED)
this.range_entry.update_idletasks()
this.plot_route_btn.config(state=tk.DISABLED, text="Computing...")
this.plot_route_btn.update_idletasks()
this.cancel_plot.config(state=tk.DISABLED)
this.cancel_plot.update_idletasks()
def plot_route(self=None):
hide_error()
try:
source = this.source_ac.get()
dest = this.dest_ac.get()
efficiency = this.efficiency_slider.get()
if ( source and source != this.source_ac.placeholder and
dest and dest != this.dest_ac.placeholder ):
range_ly = float(this.range_entry.get())
job_url="https://spansh.co.uk/api/route?"
results = requests.post(job_url, params={
"efficiency": efficiency,
"range": range_ly,
"from": source,
"to": dest
}, headers={'User-Agent': "EDMC_SpanshRouter 1.0"})
if results.status_code == 202:
enable_plot_gui(False)
tries = 0
while(tries < 20):
response = json.loads(results.content)
job = response["job"]
results_url = "https://spansh.co.uk/api/results/" + job
route_response = requests.get(results_url, timeout=5)
if route_response.status_code != 202:
break
tries += 1
sleep(1)
if route_response:
if route_response.status_code == 200:
route = json.loads(route_response.content)["result"]["system_jumps"]
clear_route(show_dialog=False)
for waypoint in route:
this.route.append([waypoint["system"], str(waypoint["jumps"])])
this.jumps_left += waypoint["jumps"]
enable_plot_gui(True)
show_plot_gui(False)
this.offset = 0
this.next_stop = this.route[0][0]
copy_waypoint()
update_gui()
else:
sys.stderr.write("Failed to query plotted route from Spansh: code " + str(route_response.status_code) + route_response.text)
enable_plot_gui(True)
show_error(this.plot_error)
else:
sys.stderr.write("Query to Spansh timed out")
enable_plot_gui(True)
show_error(this.plot_error)
else:
sys.stderr.write("Failed to query route from Spansh: code " + str(results.status_code) + results.text)
enable_plot_gui(True)
show_error(this.plot_error)
except NameError:
exc_type, exc_value, exc_traceback = sys.exc_info()
lines = traceback.format_exception(exc_type, exc_value, exc_traceback)
sys.stderr.write(''.join('!! ' + line for line in lines))
enable_plot_gui(True)
show_error(this.plot_error)
def clear_route(self=None, show_dialog=True):
clear = confirmDialog.askyesno("SpanshRouter","Are you sure you want to clear the current route?") if show_dialog else True
if clear:
this.offset = 0
this.route = []
this.next_waypoint = ""
this.jumps_left = 0
try:
os.remove(this.save_route_path)
except:
print("No route to delete")
try:
os.remove(this.offset_file_path)
except:
print("No offset file to delete")
update_gui()
def update_route(direction=1):
if direction > 0:
this.jumps_left -= int(this.route[this.offset][1])
this.offset += 1
else:
this.offset -= 1
this.jumps_left += int(this.route[this.offset][1])
if this.offset >= this.route.__len__():
this.next_stop = "End of the road!"
update_gui()
else:
this.next_stop = this.route[this.offset][0]
update_gui()
copy_waypoint(this.parent)
if spansh_router.update_available:
spansh_router.install_update()
def journal_entry(cmdr, is_beta, system, station, entry, state):
if (entry['event'] in ['FSDJump', 'Location', 'SupercruiseEntry', 'SupercruiseExit']) and entry["StarSystem"] == this.next_stop:
update_route()
this.source_ac.delete(0, tk.END)
this.source_ac.insert(0, entry["StarSystem"])
this.source_ac["fg"] = this.source_ac.default_fg_color
elif entry['event'] == 'FSSDiscoveryScan' and entry['SystemName'] == this.next_stop:
update_route()
def goto_changelog_page(self=None):
changelog_url = 'https://github.com/CMDR-Kiel42/EDMC_SpanshRouter/blob/master/CHANGELOG.md#'
changelog_url += this.spansh_updater.version.replace('.', '')
webbrowser.open(changelog_url)
global spansh_router
if (entry['event'] in ['FSDJump', 'Location', 'SupercruiseEntry', 'SupercruiseExit']) and entry["StarSystem"] == spansh_router.next_stop:
spansh_router.update_route()
spansh_router.set_source_ac(entry["StarSystem"])
elif entry['event'] == 'FSSDiscoveryScan' and entry['SystemName'] == spansh_router.next_stop:
spansh_router.update_route()
def plugin_app(parent):
this.parent = parent
parentwidth = parent.winfo_width()
this.frame = tk.Frame(parent, borderwidth=2)
this.frame.grid(sticky=tk.NSEW)
# Route info
this.waypoint_prev_btn = tk.Button(this.frame, text="^", command=goto_prev_waypoint)
this.waypoint_btn = tk.Button(this.frame, text=this.next_wp_label + this.next_stop, command=copy_waypoint)
this.waypoint_next_btn = tk.Button(this.frame, text="v", command=goto_next_waypoint)
this.jumpcounttxt_lbl = tk.Label(this.frame, text=this.jumpcountlbl_txt + str(this.jumps_left))
this.error_lbl = tk.Label(this.frame, textvariable=this.error_txt)
# Plotting GUI
this.source_ac = AutoCompleter(this.frame, "Source System", width=30)
this.dest_ac = AutoCompleter(this.frame, "Destination System", width=30)
this.range_entry = PlaceHolderEntry(this.frame, "Range (LY)", width=10)
this.efficiency_slider = tk.Scale(this.frame, from_=1, to=100, orient=tk.HORIZONTAL, label="Efficiency (%)")
this.efficiency_slider.set(60)
this.plot_gui_btn = tk.Button(this.frame, text="Plot route", command=show_plot_gui)
this.plot_route_btn = tk.Button(this.frame, text="Calculate", command=plot_route)
this.cancel_plot = tk.Button(this.frame, text="Cancel", command=lambda: show_plot_gui(False))
this.csv_route_btn = tk.Button(this.frame, text="Import CSV", command=plot_csv)
this.clear_route_btn = tk.Button(this.frame, text="Clear route", command=clear_route)
row = 0
this.waypoint_prev_btn.grid(row=row, columnspan=2)
row += 1
this.waypoint_btn.grid(row=row, columnspan=2)
row += 1
this.waypoint_next_btn.grid(row=row, columnspan=2)
row += 1
this.source_ac.grid(row=row,columnspan=2, pady=(10,0)) # The AutoCompleter takes two rows to show the list when needed, so we skip one
row += 2
this.dest_ac.grid(row=row,columnspan=2, pady=(10,0))
row += 2
this.range_entry.grid(row=row, pady=10, sticky=tk.W)
row += 1
this.efficiency_slider.grid(row=row, pady=10, columnspan=2, sticky=tk.EW)
row += 1
this.csv_route_btn.grid(row=row, pady=10, padx=0)
this.plot_route_btn.grid(row=row, pady=10, padx=0)
this.plot_gui_btn.grid(row=row, column=1, pady=10, padx=5, sticky=tk.W)
this.cancel_plot.grid(row=row, column=1, pady=10, padx=5, sticky=tk.E)
row += 1
this.clear_route_btn.grid(row=row,column=1)
row += 1
this.jumpcounttxt_lbl.grid(row=row, pady=5, sticky=tk.W)
row += 1
this.error_lbl.grid(row=row, columnspan=2)
this.error_lbl.grid_remove()
row += 1
show_plot_gui(False)
if not this.route.__len__() > 0:
this.waypoint_prev_btn.grid_remove()
this.waypoint_btn.grid_remove()
this.waypoint_next_btn.grid_remove()
this.jumpcounttxt_lbl.grid_remove()
this.clear_route_btn.grid_remove()
if this.update_available:
update_txt = ("A SpanshRouter update is available.\n"
"It will be installed next time you start EDMC.\n"
"Click to dismiss this message, right click to see what's new.")
this.update_btn = tk.Button(this.frame, text=update_txt, command=lambda: this.update_btn.grid_forget())
this.update_btn.bind("<Button-3>", goto_changelog_page)
this.update_btn.grid(row=row, pady=5, columnspan=2)
row += 1
update_gui()
return this.frame
global spansh_router
spansh_router.init_gui(parent)

View File

@ -1 +1 @@
2.1.1
2.1.2