diff --git a/SpanshRouter/SpanshRouter.py b/SpanshRouter/SpanshRouter.py index 2ce7403..b0d7724 100755 --- a/SpanshRouter/SpanshRouter.py +++ b/SpanshRouter/SpanshRouter.py @@ -2,13 +2,19 @@ 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 . import AutoCompleter from . import PlaceHolderEntry +from .updater import SpanshUpdater class SpanshRouter(): def __init__(self, plugin_dir): @@ -22,6 +28,7 @@ class SpanshRouter(): 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 @@ -29,7 +36,7 @@ class SpanshRouter(): 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() @@ -50,11 +57,11 @@ class SpanshRouter(): 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=plot_route) + 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=plot_csv) - self.clear_route_btn = tk.Button(self.frame, text="Clear route", command=clear_route) + 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) @@ -84,7 +91,7 @@ class SpanshRouter(): self.error_lbl.grid_remove() row += 1 - show_plot_gui(False) + self.show_plot_gui(False) if not self.route.__len__() > 0: self.waypoint_prev_btn.grid_remove() @@ -98,14 +105,125 @@ class SpanshRouter(): "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("", goto_changelog_page) + self.update_btn.bind("", self.goto_changelog_page) self.update_btn.grid(row=row, pady=5, columnspan=2) row += 1 - update_gui() + 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: + self.source_ac.put_placeholder() + 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): + 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() def open_last_route(self): try: @@ -142,52 +260,173 @@ class SpanshRouter(): def goto_next_waypoint(self): if self.offset < self.route.__len__()-1: - update_route(1) + self.update_route(1) def goto_prev_waypoint(self): if self.offset > 0: - update_route(-1) - - 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() - show_route_gui(False) + 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.source_ac.put_placeholder() - 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() - show_route_gui(True) + 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(self.parent) -if __name__ == "__main__": - pass \ No newline at end of file + 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() + dest = self.dest_ac.get() + efficiency = self.efficiency_slider.get() + + if ( source and source != self.source_ac.placeholder and + dest and dest != self.dest_ac.placeholder ): + + range_ly = float(self.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: + 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 = 0 + self.next_stop = self.route[0][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) + self.enable_plot_gui(True) + self.show_error(self.plot_error) + else: + sys.stderr.write("Query to Spansh timed out") + self.enable_plot_gui(True) + self.show_error(self.plot_error) + else: + sys.stderr.write("Failed to query route from Spansh: code " + str(results.status_code) + results.text) + self.enable_plot_gui(True) + self.show_error(self.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)) + 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_for_update(self): + 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 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)) + + def install_update(self): + self.install() diff --git a/load.py b/load.py index ef61e53..93708ba 100644 --- a/load.py +++ b/load.py @@ -1,273 +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 time import sleep -from SpanshRouter import updater as SpanshUpdater -from SpanshRouter import AutoCompleter -from SpanshRouter import PlaceHolderEntry from SpanshRouter import SpanshRouter -this = sys.modules[__name__] spansh_router = None -# Done 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: - global spansh_router - spansh_router = SpanshRouter(plugin_dir) - spansh_router.open_last_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 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): + global spansh_router 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 + spansh_router.update_route() + spansh_router.set_source_ac(entry["StarSystem"]) 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) + spansh_router.update_route() def plugin_app(parent): - \ No newline at end of file + global spansh_router + spansh_router.init_gui(parent) \ No newline at end of file