From 2636ecbdb9ada97439629afb3c6933081db0106d Mon Sep 17 00:00:00 2001 From: Daniel Seiller Date: Fri, 20 Sep 2019 17:40:56 +0200 Subject: [PATCH] chore: Setup tox+appveyor, refomat python code, prepare rust code to add branch pruning to search --- .appveyor.yml | 23 ++ .cargo/config | 2 + .gitignore | 3 +- LICENSE | 2 +- MANIFEST.in | 2 +- Pipfile | 12 + README.md | 10 +- appveyor.yml | 23 ++ build.bat | 21 +- build.py | 66 ++++ clean.bat | 2 +- docs/.vscode/settings.json | 2 +- docs/Makefile | 2 +- docs/filters/multifilter.py | 66 ++-- docs/src/img_out.py | 9 +- ed_lrr_gui/__init__.py | 4 +- ed_lrr_gui/__main__.py | 641 ++++++------------------------------ ed_lrr_gui/config.py | 3 +- ed_lrr_gui/gui/__init__.py | 1 + ed_lrr_gui/gui/ed_lrr.py | 2 +- ed_lrr_gui/gui/main.py | 546 ++++++++++++++++++++++++++++++ ed_lrr_gui/preprocess.py | 7 +- ed_lrr_gui/router.py | 14 +- icon/icon.ico | Bin 0 -> 376773 bytes icon/make.py | 115 +++++++ icon/out/icon_1.svg | 2 + icon/out/icon_1_small.svg | 2 + installer/ED_LRR.iss | 22 ++ rust/.cargo/config | 2 +- rust/Cargo.lock | 392 +++++++--------------- rust/Cargo.toml | 59 ++-- rust/src/lib.rs | 7 +- rust/src/route.rs | 37 ++- setup.py | 40 ++- tox.ini | 30 ++ 35 files changed, 1245 insertions(+), 926 deletions(-) create mode 100644 .appveyor.yml create mode 100644 .cargo/config create mode 100644 Pipfile create mode 100644 appveyor.yml create mode 100644 build.py create mode 100644 ed_lrr_gui/gui/main.py create mode 100644 icon/icon.ico create mode 100644 icon/make.py create mode 100644 icon/out/icon_1.svg create mode 100644 icon/out/icon_1_small.svg create mode 100644 installer/ED_LRR.iss create mode 100644 tox.ini diff --git a/.appveyor.yml b/.appveyor.yml new file mode 100644 index 0000000..7ccaef9 --- /dev/null +++ b/.appveyor.yml @@ -0,0 +1,23 @@ +image: Visual Studio 2019 +platform: x64 +version: 0.1.{build} +branches: + only: + - pyqt_gui + +environment: + VCVARS: C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvarsall.bat + VCVARSARG: x64 + +install: + - appveyor DownloadFile https://win.rustup.rs/ -FileName rustup-init.exe + - rustup-init -yv --default-toolchain nightly --default-host x86_64-pc-windows-msvc + - pip install tox + + +before_build: + - if defined VCVARS call "%VCVARS%" %VCVARSARG% + - conda activate + +test_script: + - tox \ No newline at end of file diff --git a/.cargo/config b/.cargo/config new file mode 100644 index 0000000..7a30eca --- /dev/null +++ b/.cargo/config @@ -0,0 +1,2 @@ +[build] +target-dir = "build\\temp.win-amd64-3.7\\ed_lrr_gui" diff --git a/.gitignore b/.gitignore index 5b0dd33..f0cdfd5 100644 --- a/.gitignore +++ b/.gitignore @@ -15,4 +15,5 @@ __pycache__ build *.pdf .history -pip-wheel-metadata \ No newline at end of file +.tox +pip-wheel-metadata diff --git a/LICENSE b/LICENSE index ea8c62e..99ee78e 100644 --- a/LICENSE +++ b/LICENSE @@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file +SOFTWARE. diff --git a/MANIFEST.in b/MANIFEST.in index fe1501c..2f28226 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,3 +1,3 @@ include rust/Cargo.toml include rust/.cargo/config -recursive-include rust/src * \ No newline at end of file +recursive-include rust/src * diff --git a/Pipfile b/Pipfile new file mode 100644 index 0000000..d3f6c2a --- /dev/null +++ b/Pipfile @@ -0,0 +1,12 @@ +[[source]] +name = "pypi" +url = "https://pypi.org/simple" +verify_ssl = true + +[dev-packages] + +[packages] +ed-lrr-gui = {path = "."} + +[requires] +python_version = "3.7" diff --git a/README.md b/README.md index 3afaad9..2723c76 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,9 @@ +# Prerequisites + +- conda (miniconda/anaconda) +- Visual Studio 2019 +- nightly rust compiler (`x86_64-pc-windows-msvc`) + # Testing ```bash @@ -14,7 +20,7 @@ rs_gui_test conda create -n ed_lrr_gui_env python=3 conda activate ed_lrr_gui_env python build_gui.py -pip install setuptools_rust +pip install setuptools_rust pyinstaller pip install . python setup.py build python setup.py bdist_wheel @@ -38,4 +44,4 @@ cd .. # TODO - integrate callbacks into the GUI: WIP - - QTimer pulls from queue updates UI (every 100ms) \ No newline at end of file + - QTimer pulls from queue updates UI (every 100ms) diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 0000000..7ccaef9 --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,23 @@ +image: Visual Studio 2019 +platform: x64 +version: 0.1.{build} +branches: + only: + - pyqt_gui + +environment: + VCVARS: C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvarsall.bat + VCVARSARG: x64 + +install: + - appveyor DownloadFile https://win.rustup.rs/ -FileName rustup-init.exe + - rustup-init -yv --default-toolchain nightly --default-host x86_64-pc-windows-msvc + - pip install tox + + +before_build: + - if defined VCVARS call "%VCVARS%" %VCVARSARG% + - conda activate + +test_script: + - tox \ No newline at end of file diff --git a/build.bat b/build.bat index 9487f53..7f064ec 100644 --- a/build.bat +++ b/build.bat @@ -1,11 +1,10 @@ -rm -rf build dist *.egg-info exe -python build_gui.py -pip uninstall -y ed_lrr_gui -pip install -I . setuptools_rust -python setup.py build -python setup.py bdist_wheel -python setup.py sdist -mkdir exe -cd exe -pyinstaller --noupx --noconsole --key="ED_LRR_GUI" --name ED_LRR_GUI ..\ed_lrr_gui\__main__.py -cd .. \ No newline at end of file +@echo off +:: TODO: convert to python script +:: set up new conda env, install packages, build, remove conda env +call conda create -y -n ed_lrr python=3 pycrypto || exit /b 1 +call conda activate ed_lrr || exit /b 1 +call conda install -y -c conda-forge nuitka +vcvars x64 +python build.py +call conda deactivate || exit /b 1 +call conda env remove -n ed_lrr || exit /b 1 \ No newline at end of file diff --git a/build.py b/build.py new file mode 100644 index 0000000..8bce8ab --- /dev/null +++ b/build.py @@ -0,0 +1,66 @@ +import subprocess as SP +from glob import glob +import os +import shutil +import pkg_resources as pkg +from contextlib import contextmanager + +@contextmanager +def in_dir(name,remove=False): + pwd=os.getcwd() + if os.path.isdir(name): + shutil.rmtree(name) + os.makedirs(name) + os.chdir(name) + yield + os.chdir(pwd) + if remove: + shutil.rmtree(name) + +SP.check_call(["pip", "install", "PyQt5"]) + +ui_path = os.path.dirname(os.path.abspath(__file__)) +for root, folders, files in os.walk(ui_path): + for file in files: + file = os.path.join(root, file) + outfile, ext = os.path.splitext(file) + if ext == ".ui": + outfile = outfile + ".py" + print(os.path.basename(file), "->", os.path.basename(outfile)) + SP.check_call(["pyuic5", "--from-imports", "-o", outfile, file]) + +SP.check_call(["pip", "install", ".[dev]"]) +main_py=os.path.abspath("ed_lrr_gui\__main__.py") +with in_dir("exe"): + with in_dir("pyinstaller"): + SP.check_call( + [ + "pyinstaller", + "--clean", + "--noupx", + "-c", + '--key="ED_LRR_GUI"', + "--name", + "ED_LRR", + main_py, + ] + ) + with in_dir("nuitka"): + SP.check_call( + [ + "python", + "-m", + "nuitka", + "--plugin-enable=multiprocessing", + "--plugin-enable=qt-plugins", + "--standalone", + "--follow-imports", + main_py, + ] + ) + + +# with in_dir("installer"): +# shutil.rmtree("Output") +# SP.check_call(["iscc", "/QP", "ED_LRR.iss"]) + diff --git a/clean.bat b/clean.bat index 098a107..20a9545 100644 --- a/clean.bat +++ b/clean.bat @@ -2,4 +2,4 @@ rm -rfv _*.pyd *.egg-info pip-wheel-metadata dist exe build __pycache__ cd rust cargo clean cargo clean --release -cd .. \ No newline at end of file +cd .. diff --git a/docs/.vscode/settings.json b/docs/.vscode/settings.json index e674f84..f02fbe4 100644 --- a/docs/.vscode/settings.json +++ b/docs/.vscode/settings.json @@ -8,4 +8,4 @@ "latex", "plaintext" ] -} \ No newline at end of file +} diff --git a/docs/Makefile b/docs/Makefile index 60a308c..f0dbc9f 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -45,4 +45,4 @@ watch: watchexec -w src -w data -w filters -w Makefile make all clean: - -rm $(PDFS) $(IMGS) \ No newline at end of file + -rm $(PDFS) $(IMGS) diff --git a/docs/filters/multifilter.py b/docs/filters/multifilter.py index 2cc9ff0..3ae005c 100644 --- a/docs/filters/multifilter.py +++ b/docs/filters/multifilter.py @@ -1,18 +1,19 @@ -from panflute import * -import tempfile -import sys -from jinja2 import Template, Environment, PackageLoader, select_autoescape import contextlib -import io -import hashlib -from dateutil.parser import parse as dateparse -from functools import partial -import subprocess as SP -import panflute as pf -import os import csv import datetime +import hashlib +import io +import os import re +import subprocess as SP +import sys +import tempfile +from functools import partial + +import panflute as pf +from dateutil.parser import parse as dateparse +from jinja2 import Environment, PackageLoader, Template, select_autoescape +from panflute import * def remove_pound(elem, doc): @@ -29,10 +30,10 @@ def fix_color(elem, doc): def update_date(elem, doc): if type(elem) == MetaMap: - datefmt = doc.get_metadata('datefmt', "%Y-%m-%d") + datefmt = doc.get_metadata("datefmt", "%Y-%m-%d") today = datetime.date.today().strftime(datefmt) - date = dateparse(doc.get_metadata('date', today)).date() - elem['date'] = MetaInlines(Str(date.strftime(datefmt))) + date = dateparse(doc.get_metadata("date", today)).date() + elem["date"] = MetaInlines(Str(date.strftime(datefmt))) return elem @@ -42,8 +43,7 @@ def csv_table(elem, doc): ext = os.path.splitext(elem.url)[1][1:] if ext == "csv": caption = elem.content - has_header = elem.attributes.get( - 'has-header', "false").lower() == "true" + has_header = elem.attributes.get("has-header", "false").lower() == "true" with open(elem.url) as f: reader = csv.reader(f) body = [] @@ -62,7 +62,12 @@ def code_refs(elem, doc): label = label.text filename = re.findall(r"^\[@lst:(.*)\]$", label) or [None] if filename[0] in doc.inc_files: - return [RawInline("\\hyperref[{}]{{{}}}".format(filename[0], filename[0]), format="tex")] + return [ + RawInline( + "\\hyperref[{}]{{{}}}".format(filename[0], filename[0]), + format="tex", + ) + ] def include_code(elem, doc): @@ -71,9 +76,8 @@ def include_code(elem, doc): filepath = elem.attributes.pop("include") filename = os.path.split(filepath)[-1] try: - elem.text += elem.text + \ - open(filepath, encoding="utf-8").read() - elem.attributes['caption'] = filename + elem.text += elem.text + open(filepath, encoding="utf-8").read() + elem.attributes["caption"] = filename doc.inc_files.append(filename) except Exception as e: elem.text += "Error: {}".format(e) @@ -92,28 +96,26 @@ def jinja_py_filt(doc, file): env = {} code = open(file, encoding="utf-8").read() exec(code, env) - return env['main'](doc) + return env["main"](doc) def prepare(doc): doc.inc_files = [] doc.env = Environment() doc.pyenv = {} - filters = {'py': partial(jinja_py_filt, doc)} + filters = {"py": partial(jinja_py_filt, doc)} doc.env.filters.update(filters) def process_templates(elem, doc): if type(elem) == CodeBlock: if elem.classes == ["@"]: - args = {'meta': doc.get_metadata()} + args = {"meta": doc.get_metadata()} return convert_text(doc.env.from_string(elem.text).render(args)) def yaml_filt(elem, doc): - tags = { - 'eval': py_eval, - } + tags = {"eval": py_eval} return yaml_filter(elem, doc, tags=tags, strict_yaml=True) @@ -138,8 +140,16 @@ def checkboxes(elem, doc): def main(doc=None): - f = [process_templates, update_date, csv_table, include_code, - fix_color, code_refs, yaml_filt, checkboxes] + f = [ + process_templates, + update_date, + csv_table, + include_code, + fix_color, + code_refs, + yaml_filt, + checkboxes, + ] return run_filters(f, prepare=prepare, doc=doc) diff --git a/docs/src/img_out.py b/docs/src/img_out.py index 7179b78..deb32bc 100644 --- a/docs/src/img_out.py +++ b/docs/src/img_out.py @@ -1,8 +1,9 @@ -import sys -import pylab as PL -import numpy as np -from scipy.spatial.ckdtree import cKDTree import heapq +import sys + +import numpy as np +import pylab as PL +from scipy.spatial.ckdtree import cKDTree exit() diff --git a/ed_lrr_gui/__init__.py b/ed_lrr_gui/__init__.py index 9bce23f..ce16d39 100644 --- a/ed_lrr_gui/__init__.py +++ b/ed_lrr_gui/__init__.py @@ -1,4 +1,4 @@ from _ed_lrr import * -from . import gui -from .router import Router + from .preprocess import Preprocessor +from .router import Router diff --git a/ed_lrr_gui/__main__.py b/ed_lrr_gui/__main__.py index 153f408..be94950 100644 --- a/ed_lrr_gui/__main__.py +++ b/ed_lrr_gui/__main__.py @@ -1,562 +1,111 @@ import sys -import os -import requests as RQ -from datetime import datetime, timedelta -from PyQt5.QtCore import QTimer, QThread, pyqtSignal, Qt, QObject -from PyQt5.QtWidgets import ( - QMainWindow, - QApplication, - QFileDialog, - QProgressDialog, - QTreeWidgetItem, - QAction, - QMessageBox, -) -from urllib.request import Request, urlopen -import gzip -import pathlib -from PyQt5.QtGui import QPalette, QColor -import ed_lrr_gui -import ed_lrr_gui.config as cfg -from ed_lrr_gui.gui.ed_lrr import Ui_ED_LRR -from ed_lrr_gui import Router, Preprocessor import multiprocessing as MP import queue -import csv -import _ed_lrr +import ctypes +from math import floor +import click +from click_default_group import DefaultGroup +import requests as RQ +from ed_lrr_gui import Router +from ed_lrr_gui import Preprocessor +import ed_lrr_gui.gui as ED_LRR_GUI +CONTEXT_SETTINGS = dict(help_option_names=['-h', '--help']) - -def sizeof_fmt(num, suffix="B"): - for unit in ["", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei", "Zi"]: - if abs(num) < 1024.0: - return "{:.02f}{}{}".format(num, unit, suffix) - num /= 1024.0 - return "{:.02f}{}{}".format(num, "Yi", suffix) - - -def t_round(dt): - return dt - dt % timedelta(seconds=1) - - -class ProgressDialog(QProgressDialog): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.setWindowModality(Qt.WindowModal) - - -class Job(QObject): - progress = pyqtSignal("PyQt_PyObject") - - def __init__(self, app, cls, *args, build_progress=None, **kwargs): - super().__init__() - self.job = cls(*args, **kwargs) - self.timer = QTimer(app) - self.app = app - self.timer.timeout.connect(self.interval) - self.timer.start(100) - self.last_val = None - self.build_progress = build_progress - self.progress_dialog = None - self.state = {} - - def start(self): - """ - if self.diag_prog is None: - self.diag_prog = ProgressDialog("", "Cancel", 0, 1000, self.main_window) - if self.dl_thread: - self.diag_prog.canceled.connect(self.dl_canceled) - self.diag_prog.show() - t_elapsed = datetime.today() - self.dl_started - rate = args["done"] / t_elapsed.total_seconds() - remaining = (args["size"] - args["done"]) / rate - rate = round(rate, 2) - # print(rate, remaining) - try: - t_rem = timedelta(seconds=remaining) - except OverflowError: - t_rem = "-" - msg = "Downloading {} [{}/{}] ({}/s)\n[{}/{}]".format( - filename, - sizeof_fmt(args["done"]), - sizeof_fmt(args["size"]), - sizeof_fmt(rate), - t_round(t_elapsed), - t_round(t_rem), - ) - self.diag_prog.setLabelText(msg) - self.diag_prog.setWindowTitle("Downloading EDSM Dumps") - self.diag_prog.setValue((args["done"] * 1000) // args["size"]) - """ - self.started = datetime.today() - if self.build_progress: - self.progress_dialog = ProgressDialog("", "Cancel", 0, 1000, self.app) - self.progress_dialog.canceled.connect(self.cancel) - self.progress_dialog.show() - self.progress.connect(self.__build_progress) - else: - self.progress.connect( - lambda *args, **kwargs: print("PROGRESS:", *args, **kwargs) - ) - return self.job.start() - - def __build_progress(self, *args, **kwargs): - kwargs["self"] = self - return self.build_progress(*args, **kwargs) - - def cancel(self): - self.job.terminate() - self.job = None - - def done(self): - return (self.job.is_alive() == False) and (self.job.queue.empty()) - - def interval(self): - while True: - try: - res = self.job.queue.get(True, 0.1) - except queue.Empty: - return - if res == self.last_val: - continue - self.state.update(res) - self.progress.emit(self.state) - self.last_val = res - - -class DownloadThread(QThread): - progress = pyqtSignal("PyQt_PyObject") - - def __init__(self, systems_url, systems_file, bodies_url, bodies_file): - super().__init__() - self.systems_url = systems_url - self.systems_file = systems_file - self.bodies_url = bodies_url - self.bodies_file = bodies_file - self.running = True - - def __del__(self): - self.wait() - - def stop(self): - self.running = False - - def run(self): - dl_jobs = [ - (self.systems_url, self.systems_file), - (self.bodies_url, self.bodies_file), - ] - for url, dest in dl_jobs: - outfile = url.split("/")[-1] - size = RQ.head(url, headers={"Accept-Encoding": "None"}) - size.raise_for_status() - size = int(size.headers.get("Content-Length", 0)) - with open(dest, "wb") as of: - resp = RQ.get(url, stream=True) - for chunk in resp.iter_content(1024 * 1024): - of.write(chunk) - self.progress.emit( - {"done": of.tell(), "size": size, "outfile": outfile} - ) - if not self.running: - return - - -class App(QApplication): - def __init__(self): - super().__init__(sys.argv) - self.setStyle("Fusion") - self.setup_styles() - - def set_style(self, style): - print("LOAD:", style) - self.setPalette(self.styles[style]) - - def setup_styles(self): - self.styles = {} - styles = { - "Dark": { - "Window": QColor(53, 53, 53), - "WindowText": Qt.white, - "Base": QColor(15, 15, 15), - "AlternateBase": QColor(53, 53, 53), - "ToolTipBase": Qt.white, - "ToolTipText": Qt.white, - "Text": Qt.white, - "Button": QColor(53, 53, 53), - "ButtonText": Qt.white, - "BrightText": Qt.red, - "Highlight": QColor(255, 128, 0), - "HighlightedText": Qt.black, - } - } - for style, colors in styles.items(): - palette = QPalette() - for entry, color in colors.items(): - palette.setColor(getattr(QPalette, entry), color) - if color == Qt.darkGray: - palette.setColor( - QPalette.Disabled, getattr(QPalette, entry), QColor(53, 53, 53) - ) - else: - palette.setColor( - QPalette.Disabled, getattr(QPalette, entry), Qt.darkGray - ) - self.styles[style] = palette - self.styles["Light"] = self.style().standardPalette() - - -class ED_LRR(Ui_ED_LRR): - dl_thread = None - diag_prog = None - dl_started = None - system_found = pyqtSignal("PyQt_PyObject") - - def __init__(self): - super().__init__() - self.config = cfg.load() - self.jobs = {} - - def start_job(self, cls, *args, **kwargs): - print("START JOB:", cls, args, kwargs) - name = cls.__name__ - if name in self.jobs and self.jobs[name].done(): - del self.jobs[name] - if not name in self.jobs: - self.jobs[name] = Job(self.app, cls, *args, **kwargs) - self.jobs[name].start() - - def get_open_file(self, filetypes, callback=None): - fileName, _ = QFileDialog.getOpenFileName( - self.main_window, - "Open file", - str(cfg.data_dir), - filetypes, - options=QFileDialog.DontUseNativeDialog, - ) - if callback: - return callback(fileName) - return fileName - - def get_save_file(self, filetypes, callback=None): - fileName, _ = QFileDialog.getSaveFileName( - self.main_window, - "Save file", - str(cfg.data_dir), - filetypes, - options=QFileDialog.DontUseNativeDialog, - ) - if callback: - return callback(fileName) - return fileName - - def set_sys_lst(self, path): - if path not in self.config.history_out_path: - self.config.history_out_path.append(path) - self.inp_sys_lst.addItem(path) - self.inp_out_pp.addItem(path) - self.inp_sys_lst.setCurrentText(path) - self.inp_out_pp.setCurrentText(path) - - def set_bodies_file(self, path): - if path not in self.config.history_bodies_path: - self.config.history_bodies_path.append(path) - self.inp_bodies_pp.addItem(path) - - def set_systems_file(self, path): - if path not in self.config.history_systems_path: - self.config.history_systems_path.append(path) - self.inp_systems_pp.addItem(path) - - def update_dropdowns(self): +@click.group(invoke_without_command=True,context_settings=CONTEXT_SETTINGS) +@click.pass_context +def main(ctx): + "Elite: Dangerous long range router, command line interface" + if ctx.invoked_subcommand is None: + ctx.invoke(gui) return + return - def log(self, *args): - t = datetime.today() - msg_t = "[{}] {}".format(t, str.format(*args)) - self.txt_log.append(msg_t) +@main.command() +@click.option("--debug",help="Debug print",is_flag=True) +def gui(debug): + "Run the ED LRR GUI (default)" + if not debug: + ctypes.windll.kernel32.FreeConsole() + sys.stdin=open("NUL","rt") + sys.stdout=open("NUL","wt") + sys.stderr=open("NUL","wt") + sys.exit(ED_LRR_GUI.main()) - def set_comp_mode(self, _): - if self.rd_comp.isChecked(): - comp_mode = "Compute Route" - self.btn_add.setText("Add") - if self.rd_precomp.isChecked(): - comp_mode = "Precompute Graph" - self.btn_add.setText("Select") - self.log("COMP_MODE", comp_mode) - self.lst_sys.setEnabled(self.rd_comp.isChecked()) - self.btn_rm.setEnabled(self.rd_comp.isChecked()) - self.cmb_mode.setEnabled(self.rd_comp.isChecked()) - self.chk_permute.setEnabled(self.rd_comp.isChecked()) - self.lbl_keep.setEnabled(self.rd_comp.isChecked()) - self.lbl_mode.setEnabled(self.rd_comp.isChecked()) - self.chk_permute_keep_first.setEnabled(self.rd_comp.isChecked()) - self.chk_permute_keep_last.setEnabled(self.rd_comp.isChecked()) - self.set_route_mode(self.rd_precomp.isChecked() or None) +@main.command() +@click.option("--url","-u",help="Base URL",default="https://www.edsm.net/dump/",show_default=True) +@click.option("--systems","-s",help="Target path for systemsWithCoordinates.json",default="systemsWithCoordinates.json",show_default=True) +@click.option("--bodies","-b",help="Target path for bodies.json",default="bodies.json",show_default=True) +def download(*args,**kwargs): + "Download EDSM dumps" + print("Download:",args,kwargs) + click.pause() - def set_route_mode(self, mode=None): - if mode == None: - mode = self.cmb_mode.currentText() - self.lbl_greedyness.setEnabled(mode == "A*-Search") - self.sld_greedyness.setEnabled(mode == "A*-Search") +@main.command() +def preprocess(*args,**kwargs): + "Preprocess EDSM dumps" + print("PreProcess:",ctx,args,kwargs) + click.pause() - def set_greedyness(self, value): - self.lbl_greedyness.setText("Greedyness Factor ({:.0%})".format(value / 100)) - @property - def systems(self): - ret = [] - for n in range(self.lst_sys.topLevelItemCount()): - ret.append(self.sys_to_dict(n)) - return ret - - def sys_to_dict(self, n): - header = [ - self.lst_sys.headerItem().data(c, 0) - for c in range(self.lst_sys.headerItem().columnCount()) - ] - system = [ - self.lst_sys.topLevelItem(n).data(c, 0) - for c in range(self.lst_sys.topLevelItem(n).columnCount()) - ] - ret = dict(zip(header, system)) - ret["id"] = getattr(self.lst_sys.topLevelItem(n), "__id__", None) - ret.pop(None, None) - return ret - - def error(self, msg): - QMessageBox.critical(self.main_window, "ERROR!", msg) - - def get_sys_list(self): - if not self.inp_sys_lst.currentText(): - self.error("System list is required!") - return - path = pathlib.Path(self.inp_sys_lst.currentText()) - if not path.exists(): - self.error("System list does not exist, run download and preprocess first!") - return - return path - - def run(self): - if not all(s["Type"] for s in self.systems): - self.error('Not all systens have been resolved, please click "Search All"') - return - print(self.systems) - systems = [str(s["id"]) for s in self.systems] - jump_range = self.sb_range.value() - mode = self.cmb_mode.currentText() - primary = self.chk_primary.isChecked() - keep_first = self.chk_permute_keep_first.isChecked() - keep_last = self.chk_permute_keep_last.isChecked() - permute = self.chk_permute.isChecked() - greedyness = ( - self.sld_greedyness.value() / 100 - if self.sld_greedyness.isEnabled() - else None - ) - path = self.get_sys_list() - if path is None: - return - precomp = path.with_suffix(".idx") - path = str(path) - if not precomp.exists(): - precomp = None - else: - precomp = str(precomp) - mode = { - "Breadth-First Search": "bfs", - "A*-Search": "astar", - "Greedy-Search": "greedy", - }[mode] - print( - systems, - jump_range, - mode, - primary, - permute, - (keep_first, keep_last), - greedyness, - path, - precomp, - ) - self.start_job( - Router, - systems, - jump_range, - 0.1, - mode, - primary, - permute, - keep_first, - keep_last, - greedyness, - precomp, - path, - ) - - def find_sys_by_names(self, names): - t_s = datetime.today() - if not self.get_sys_list(): - return None - ret = _ed_lrr.find_sys(names, self.inp_sys_lst.currentText()) - print("Took:", datetime.today() - t_s) - return ret - - def resolve_systems(self): - names = [] - for n in range(self.lst_sys.topLevelItemCount()): - names.append(self.sys_to_dict(n)["Name"]) - systems = self.find_sys_by_names(names) - if systems is None: - return - for i, name in enumerate(names): - _, system = systems[name] - self.lst_sys.topLevelItem(i).setData(0, 0, system["system"]) - self.lst_sys.topLevelItem(i).setData(1, 0, system["star_type"]) - self.lst_sys.topLevelItem(i).__id__ = system["id"] - # diff, item = self.find_sys_by_name(name) - # print("Found", (diff, item)) - - def add_system(self): - name = self.inp_sys.text() - item = QTreeWidgetItem(self.lst_sys, [name, None]) - item.resolved = False - item.setFlags(item.flags() & ~Qt.ItemIsDropEnabled) - - def remove_system(self): - root = self.lst_sys.invisibleRootItem() - for item in self.lst_sys.selectedItems(): - root.removeChild(item) - - def dl_canceled(self): - if self.dl_thread: - print("Cancel!") +@main.command() +@click.option("--path","-i",required=True,metavar="",help="Path to stars.csv",default="./stars.csv",type=click.Path(exists=True,dir_okay=False),show_default=True ) +@click.option("--precomp_file","-pf",metavar="",help="Precomputed routing graph to use",type=click.Path(exists=True,dir_okay=False)) +@click.option("--range","-r",required=True,metavar="",help="Jump range (Ly)",type=click.FloatRange(min=0)) +@click.option("--prune","-d",default=(0,0),metavar=" ",help="Prune search branches",nargs=2,type=click.Tuple([click.IntRange(min=0),click.FloatRange(min=0)])) +@click.option("--permute","-p",type=click.Choice(["all","keep_first","keep_last","keep_both"]),default=None,help="Permute hops to find shortest route",show_default=True) +@click.option("--primary","-ps",is_flag=True,default=False,help="Only route through primary stars") +@click.option("--factor","-g",metavar="",default=0.5,help="Greedyness factor for A-Star",show_default=True) +@click.option("--mode","-m",default="bfs",help="Search mode",type=click.Choice(["bfs","a-star","greedy"]),show_default=True) +@click.argument('systems',nargs=-1) +def route(**kwargs): + "Compute a route" + if kwargs['prune']==(0,0): + kwargs['prune']=None + def to_string(state): + if state: + return "[{}] {}".format(state['depth'],state['system']) + keep_first,keep_last={ + "all":(False,False), + "keep_first":(True,False), + "keep_last":(False,True), + "keep_both":(True,True), + None: (False,False) + }[kwargs['permute']] + args=[kwargs['systems'],kwargs['range'],kwargs['prune'],kwargs['mode'],kwargs['primary'],kwargs['permute']!=None,keep_first,keep_last,kwargs['factor'],None,kwargs['path']] + with click.progressbar(length=100,label="Computing route",show_percent=True,item_show_func=to_string,width=50) as pbar: + router=Router(*args) + router.start() + state={} + pstate={} + while not (router.queue.empty() and router.is_alive()==False): try: - self.dl_thread.progress.disconnect() - except TypeError: + event = router.queue.get(True,0.1) + state.update(event) + if state!=pstate: + pbar.current_item=state.get("status") + if pbar.current_item: + pbar.pos=floor(pbar.current_item["prc_done"]*10)/10 + pbar.update(0) + pstate=state + except queue.Empty: pass - self.dl_thread.stop() - self.dl_thread.wait() - self.diag_prog.close() - self.dl_thread = None - self.diag_prog = None - self.dl_started = None + pbar.pos=100 + pbar.update(0) + print(state.get("result")) + print("DONE!") - def handle_dl_progress(self, args): - filename = os.path.split(args["outfile"])[-1] - if self.diag_prog is None: - self.diag_prog = ProgressDialog("", "Cancel", 0, 1000, self.main_window) - if self.dl_thread: - self.diag_prog.canceled.connect(self.dl_canceled) - self.diag_prog.show() - t_elapsed = datetime.today() - self.dl_started - rate = args["done"] / t_elapsed.total_seconds() - remaining = (args["size"] - args["done"]) / rate - rate = round(rate, 2) - # print(rate, remaining) - try: - t_rem = timedelta(seconds=remaining) - except OverflowError: - t_rem = "-" - msg = "Downloading {} [{}/{}] ({}/s)\n[{}/{}]".format( - filename, - sizeof_fmt(args["done"]), - sizeof_fmt(args["size"]), - sizeof_fmt(rate), - t_round(t_elapsed), - t_round(t_rem), - ) - self.diag_prog.setLabelText(msg) - self.diag_prog.setWindowTitle("Downloading EDSM Dumps") - self.diag_prog.setValue((args["done"] * 1000) // args["size"]) - - def run_download(self): - if self.dl_thread: - return - self.dl_started = datetime.today() - self.dl_thread = DownloadThread( - self.inp_systems_dl.currentText(), - self.inp_systems_dest_dl.currentText(), - self.inp_bodies_dl.currentText(), - self.inp_bodies_dest_dl.currentText(), - ) - self.dl_thread.progress.connect(self.handle_dl_progress) - self.dl_thread.start() - print(".") - - def update_permute_chk(self, state): - self.chk_permute_keep_first.setEnabled(state) - self.chk_permute_keep_last.setEnabled(state) - self.lbl_keep.setEnabled(state) - - def setup_signals(self): - self.btn_download.clicked.connect(self.run_download) - self.inp_systems_dest_dl.setCurrentText(r"D:\devel\rust\ed_lrr_gui\DL\s.json") - self.inp_bodies_dest_dl.setCurrentText(r"D:\devel\rust\ed_lrr_gui\DL\b.json") - self.set_greedyness(self.sld_greedyness.value()) - self.cmb_mode.currentTextChanged.connect(self.set_route_mode) - self.rd_comp.toggled.connect(self.set_comp_mode) - self.rd_precomp.toggled.connect(self.set_comp_mode) - self.sld_greedyness.valueChanged.connect(self.set_greedyness) - self.btn_go.clicked.connect(self.run) - self.btn_add.clicked.connect(self.add_system) - self.btn_rm.clicked.connect(self.remove_system) - self.chk_permute.stateChanged.connect(self.update_permute_chk) - self.btn_search.clicked.connect(self.resolve_systems) - self.btn_out_browse_pp.clicked.connect( - lambda: self.get_save_file("CSV File (*.csv)", self.set_sys_lst) - ) - self.btn_sys_lst_browse.clicked.connect( - lambda: self.get_open_file("CSV File (*.csv)", self.set_sys_lst) - ) - - self.btn_bodies_browse_pp.clicked.connect( - lambda: self.get_open_file("JSON File (*.json)", self.set_bodies_file) - ) - self.btn_bodies_dest_browse_dl.clicked.connect( - lambda: self.get_save_file("JSON File (*.json)", self.set_bodies_file) - ) - self.btn_systems_browse_pp.clicked.connect( - lambda: self.get_open_file("JSON File (*.json)", self.set_systems_file) - ) - self.btn_systems_dest_browse_dl.clicked.connect( - lambda: self.get_save_file("JSON File (*.json)", self.set_systems_file) - ) - - def handle_close(self): - cfg.write(self.config) - print("BYEEEEEE!") - - def setup_styles(self, win, app): - for name in app.styles: - action = QAction(app) - action.setObjectName("action_load_style_" + name) - action.setText(name) - action.triggered.connect(lambda _, name=name: app.set_style(name)) - self.menuStyle.addAction(action) - - def setupUi(self, MainWindow, app): - super().setupUi(MainWindow) - self.update_dropdowns() - self.main_window = MainWindow - self.app = app - self.setup_signals() - self.lst_sys.setHeaderLabels(["Name", "Type"]) - self.set_route_mode() - self.update_permute_chk(self.chk_permute.isChecked()) - self.setup_styles(MainWindow, app) +@main.command() +@click.option("--path","-i",required=True,help="Path to stars.csv",default="./stars.csv",type=click.Path(exists=True,dir_okay=False),show_default=True ) +@click.option("--precomp_file","-pc",help="Precomputed routing graph to use",type=click.Path(exists=True,dir_okay=False)) +@click.option("--range","-r",required=True,help="Jump range (Ly)",type=click.FloatRange(min=0)) +@click.option("--primary","-ps",help="Only route through primary stars") +@click.option("--output","-o",required=True,help="Output path",default="./stars.idx",type=click.Path(exists=False,dir_okay=False),show_default=True ) +@click.argument('systems',nargs=-1) +def precompute(*args,**kwargs): + "Precompute routing graph" + print("PreComp:",ctx,args,kwargs) -def main(): - app = App() - MainWindow = QMainWindow() - ui = ED_LRR() - ui.setupUi(MainWindow, app) - MainWindow.show() - ret = app.exec_() - ui.handle_close() - exit(ret) - - -if __name__ == "__main__": +if __name__ == '__main__': MP.freeze_support() - main() + main() \ No newline at end of file diff --git a/ed_lrr_gui/config.py b/ed_lrr_gui/config.py index 3e748b7..9720f2d 100644 --- a/ed_lrr_gui/config.py +++ b/ed_lrr_gui/config.py @@ -1,7 +1,8 @@ import pathlib +from collections import namedtuple + import appdirs import yaml -from collections import namedtuple config_dir = pathlib.Path(appdirs.user_config_dir("ED_LRR")) config_dir.mkdir(parents=True, exist_ok=True) diff --git a/ed_lrr_gui/gui/__init__.py b/ed_lrr_gui/gui/__init__.py index e69de29..b668da9 100644 --- a/ed_lrr_gui/gui/__init__.py +++ b/ed_lrr_gui/gui/__init__.py @@ -0,0 +1 @@ +from .main import main \ No newline at end of file diff --git a/ed_lrr_gui/gui/ed_lrr.py b/ed_lrr_gui/gui/ed_lrr.py index c33b639..0ce8074 100644 --- a/ed_lrr_gui/gui/ed_lrr.py +++ b/ed_lrr_gui/gui/ed_lrr.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'D:\devel\rust\ed_lrr_gui\ed_lrr_gui\gui\ed_lrr.ui' # -# Created by: PyQt5 UI code generator 5.13.0 +# Created by: PyQt5 UI code generator 5.13.1 # # WARNING! All changes made in this file will be lost! diff --git a/ed_lrr_gui/gui/main.py b/ed_lrr_gui/gui/main.py new file mode 100644 index 0000000..af409d7 --- /dev/null +++ b/ed_lrr_gui/gui/main.py @@ -0,0 +1,546 @@ +import csv +import gzip +import multiprocessing as MP +import os +import pathlib +import queue +import sys +from sys import exit +from datetime import datetime, timedelta +from urllib.request import Request, urlopen + +import _ed_lrr +import ed_lrr_gui +import ed_lrr_gui.config as cfg +import requests as RQ +from ed_lrr_gui import Preprocessor, Router +from ed_lrr_gui.gui.ed_lrr import Ui_ED_LRR +from PyQt5.QtCore import QObject, Qt, QThread, QTimer, pyqtSignal +from PyQt5.QtGui import QColor, QPalette,QIcon +from PyQt5.QtWidgets import ( + QAction, + QApplication, + QFileDialog, + QMainWindow, + QMessageBox, + QProgressDialog, + QTreeWidgetItem, +) + + +def sizeof_fmt(num, suffix="B"): + for unit in ["", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei", "Zi"]: + if abs(num) < 1024.0: + return "{:.02f}{}{}".format(num, unit, suffix) + num /= 1024.0 + return "{:.02f}{}{}".format(num, "Yi", suffix) + + +def t_round(dt): + return dt - dt % timedelta(seconds=1) + + +class ProgressDialog(QProgressDialog): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.setWindowModality(Qt.WindowModal) + + +class Job(QObject): + progress = pyqtSignal("PyQt_PyObject") + + def __init__(self, app, main_window, cls, *args, **kwargs): + super().__init__() + self.job = cls(*args, **kwargs) + self.timer = QTimer(app) + self.app = app + self.main_window = main_window + self.timer.timeout.connect(self.interval) + self.timer.start(100) + self.last_val = None + self.progress_dialog = None + self.handle_progess = None + self.state = {} + + def setup_progress(self, handle_progess): + self.progress.connect( + lambda *args, **kwargs: handle_progess(self, *args, **kwargs) + ) + + def start(self): + if self.progress_dialog is None: + self.progress.connect( + lambda *args, **kwargs: print("PROGRESS:", *args, **kwargs) + ) + self.started = datetime.today() + return self.job.start() + + def cancel(self): + self.job.terminate() + self.job = None + + def done(self): + return (self.job.is_alive() == False) and (self.job.queue.empty()) + + def interval(self): + while True: + try: + res = self.job.queue.get(True, 0.1) + except queue.Empty: + return + if res == self.last_val: + continue + self.state.update(res) + self.progress.emit(self.state) + self.last_val = res + + +class DownloadThread(QThread): + progress = pyqtSignal("PyQt_PyObject") + + def __init__(self, systems_url, systems_file, bodies_url, bodies_file): + super().__init__() + self.systems_url = systems_url + self.systems_file = systems_file + self.bodies_url = bodies_url + self.bodies_file = bodies_file + self.running = True + + def __del__(self): + self.wait() + + def stop(self): + self.running = False + + def run(self): + dl_jobs = [ + (self.systems_url, self.systems_file), + (self.bodies_url, self.bodies_file), + ] + for url, dest in dl_jobs: + outfile = url.split("/")[-1] + size = RQ.head(url, headers={"Accept-Encoding": "None"}) + size.raise_for_status() + size = int(size.headers.get("Content-Length", 0)) + with open(dest, "wb") as of: + resp = RQ.get(url, stream=True) + for chunk in resp.iter_content(1024 * 1024): + of.write(chunk) + self.progress.emit( + {"done": of.tell(), "size": size, "outfile": outfile} + ) + if not self.running: + return + + +class App(QApplication): + def __init__(self): + super().__init__(sys.argv) + self.setStyle("Fusion") + self.setup_styles() + + def set_style(self, style): + print("LOAD:", style) + self.setPalette(self.styles[style]) + + def setup_styles(self): + self.styles = {} + styles = { + "Dark": { + "Window": QColor(53, 53, 53), + "WindowText": Qt.white, + "Base": QColor(15, 15, 15), + "AlternateBase": QColor(53, 53, 53), + "ToolTipBase": Qt.white, + "ToolTipText": Qt.white, + "Text": Qt.white, + "Button": QColor(53, 53, 53), + "ButtonText": Qt.white, + "BrightText": Qt.red, + "Highlight": QColor(255, 128, 0), + "HighlightedText": Qt.black, + } + } + for style, colors in styles.items(): + palette = QPalette() + for entry, color in colors.items(): + palette.setColor(getattr(QPalette, entry), color) + if color == Qt.darkGray: + palette.setColor( + QPalette.Disabled, getattr(QPalette, entry), QColor(53, 53, 53) + ) + else: + palette.setColor( + QPalette.Disabled, getattr(QPalette, entry), Qt.darkGray + ) + self.styles[style] = palette + self.styles["Light"] = self.style().standardPalette() + + +class ED_LRR(Ui_ED_LRR): + dl_thread = None + diag_prog = None + dl_started = None + system_found = pyqtSignal("PyQt_PyObject") + + def __init__(self): + super().__init__() + self.config = cfg.load() + self.jobs = {} + + def new_job(self, cls, *args, **kwargs): + print("CREATE JOB:", cls, args, kwargs) + name = cls.__name__ + if name in self.jobs and self.jobs[name].done(): + del self.jobs[name] + if not name in self.jobs: + self.jobs[name] = Job(self.app, self.main_window, cls, *args, **kwargs) + return self.jobs[name] + + def get_open_file(self, filetypes, callback=None): + fileName, _ = QFileDialog.getOpenFileName( + self.main_window, + "Open file", + str(cfg.data_dir), + filetypes, + options=QFileDialog.DontUseNativeDialog, + ) + if callback: + return callback(fileName) + return fileName + + def get_save_file(self, filetypes, callback=None): + fileName, _ = QFileDialog.getSaveFileName( + self.main_window, + "Save file", + str(cfg.data_dir), + filetypes, + options=QFileDialog.DontUseNativeDialog, + ) + if callback: + return callback(fileName) + return fileName + + def set_sys_lst(self, path): + if path not in self.config.history_out_path: + self.config.history_out_path.append(path) + self.inp_sys_lst.addItem(path) + self.inp_out_pp.addItem(path) + self.inp_sys_lst.setCurrentText(path) + self.inp_out_pp.setCurrentText(path) + + def set_bodies_file(self, path): + if path not in self.config.history_bodies_path: + self.config.history_bodies_path.append(path) + self.inp_bodies_pp.addItem(path) + + def set_systems_file(self, path): + if path not in self.config.history_systems_path: + self.config.history_systems_path.append(path) + self.inp_systems_pp.addItem(path) + + def update_dropdowns(self): + return + + def log(self, *args): + t = datetime.today() + msg_t = "[{}] {}".format(t, str.format(*args)) + self.txt_log.append(msg_t) + + def set_comp_mode(self, _): + if self.rd_comp.isChecked(): + comp_mode = "Compute Route" + self.btn_add.setText("Add") + if self.rd_precomp.isChecked(): + comp_mode = "Precompute Graph" + self.btn_add.setText("Select") + self.log("COMP_MODE", comp_mode) + self.lst_sys.setEnabled(self.rd_comp.isChecked()) + self.btn_rm.setEnabled(self.rd_comp.isChecked()) + self.cmb_mode.setEnabled(self.rd_comp.isChecked()) + self.chk_permute.setEnabled(self.rd_comp.isChecked()) + self.lbl_keep.setEnabled(self.rd_comp.isChecked()) + self.lbl_mode.setEnabled(self.rd_comp.isChecked()) + self.chk_permute_keep_first.setEnabled(self.rd_comp.isChecked()) + self.chk_permute_keep_last.setEnabled(self.rd_comp.isChecked()) + self.set_route_mode(self.rd_precomp.isChecked() or None) + + def set_route_mode(self, mode=None): + if mode == None: + mode = self.cmb_mode.currentText() + self.lbl_greedyness.setEnabled(mode == "A*-Search") + self.sld_greedyness.setEnabled(mode == "A*-Search") + + def set_greedyness(self, value): + self.lbl_greedyness.setText("Greedyness Factor ({:.0%})".format(value / 100)) + + @property + def systems(self): + ret = [] + for n in range(self.lst_sys.topLevelItemCount()): + ret.append(self.sys_to_dict(n)) + return ret + + def sys_to_dict(self, n): + header = [ + self.lst_sys.headerItem().data(c, 0) + for c in range(self.lst_sys.headerItem().columnCount()) + ] + system = [ + self.lst_sys.topLevelItem(n).data(c, 0) + for c in range(self.lst_sys.topLevelItem(n).columnCount()) + ] + ret = dict(zip(header, system)) + ret["id"] = getattr(self.lst_sys.topLevelItem(n), "__id__", None) + ret.pop(None, None) + return ret + + def error(self, msg): + QMessageBox.critical(self.main_window, "ERROR!", msg) + + def get_sys_list(self): + if not self.inp_sys_lst.currentText(): + self.error("System list is required!") + return + path = pathlib.Path(self.inp_sys_lst.currentText()) + if not path.exists(): + self.error("System list does not exist, run download and preprocess first!") + return + return path + + def route_progress(self, job, state): + print("RP:", job, state) + + def run(self): + if not all(s["Type"] for s in self.systems): + self.error('Not all systens have been resolved, please click "Search All"') + return + print(self.systems) + systems = [str(s["id"]) for s in self.systems] + jump_range = self.sb_range.value() + mode = self.cmb_mode.currentText() + primary = self.chk_primary.isChecked() + keep_first = self.chk_permute_keep_first.isChecked() + keep_last = self.chk_permute_keep_last.isChecked() + permute = self.chk_permute.isChecked() + greedyness = ( + self.sld_greedyness.value() / 100 + if self.sld_greedyness.isEnabled() + else None + ) + path = self.get_sys_list() + if path is None: + return + precomp = None + path = str(path) + mode = { + "Breadth-First Search": "bfs", + "A*-Search": "astar", + "Greedy-Search": "greedy", + }[mode] + print( + systems, + jump_range, + mode, + primary, + permute, + (keep_first, keep_last), + greedyness, + path, + precomp, + ) + route_job = self.new_job( + Router, + systems, + jump_range, + 0.1, + mode, + primary, + permute, + keep_first, + keep_last, + greedyness, + precomp, + path, + ) + if route_job: + self.route_progress_dialog = ProgressDialog( + "Computing route...", "Cancel", 0, 100, self.main_window + ) + self.route_progress_dialog.canceled.connect(route_job.cancel) + route_job.start() + self.route_progress_dialog.show() + else: + self.error("Another route job is already running!") + + def find_sys_by_names(self, names): + t_s = datetime.today() + if not self.get_sys_list(): + return None + # TODO: start thread/subprocess + ret = _ed_lrr.find_sys(names, self.inp_sys_lst.currentText()) + print("Took:", datetime.today() - t_s) + return ret + + def resolve_systems(self): + # TODO: show spinner + names = [] + for n in range(self.lst_sys.topLevelItemCount()): + names.append(self.sys_to_dict(n)["Name"]) + systems = self.find_sys_by_names(names) + if systems is None: + return + for i, name in enumerate(names): + _, system = systems[name] + self.lst_sys.topLevelItem(i).setData(0, 0, system["system"]) + self.lst_sys.topLevelItem(i).setData(1, 0, system["star_type"]) + self.lst_sys.topLevelItem(i).__id__ = system["id"] + # diff, item = self.find_sys_by_name(name) + # print("Found", (diff, item)) + + def add_system(self): + name = self.inp_sys.text() + item = QTreeWidgetItem(self.lst_sys, [name, None]) + item.resolved = False + item.setFlags(item.flags() & ~Qt.ItemIsDropEnabled) + + def remove_system(self): + root = self.lst_sys.invisibleRootItem() + for item in self.lst_sys.selectedItems(): + root.removeChild(item) + + def dl_canceled(self): + if self.dl_thread: + print("Cancel!") + try: + self.dl_thread.progress.disconnect() + except TypeError: + pass + self.dl_thread.stop() + self.dl_thread.wait() + self.diag_prog.close() + self.dl_thread = None + self.diag_prog = None + self.dl_started = None + + def handle_dl_progress(self, args): + filename = os.path.split(args["outfile"])[-1] + if self.diag_prog is None: + self.diag_prog = ProgressDialog("", "Cancel", 0, 1000, self.main_window) + if self.dl_thread: + self.diag_prog.canceled.connect(self.dl_canceled) + self.diag_prog.show() + t_elapsed = datetime.today() - self.dl_started + rate = args["done"] / t_elapsed.total_seconds() + remaining = (args["size"] - args["done"]) / rate + rate = round(rate, 2) + # print(rate, remaining) + try: + t_rem = timedelta(seconds=remaining) + except OverflowError: + t_rem = "-" + msg = "Downloading {} [{}/{}] ({}/s)\n[{}/{}]".format( + filename, + sizeof_fmt(args["done"]), + sizeof_fmt(args["size"]), + sizeof_fmt(rate), + t_round(t_elapsed), + t_round(t_rem), + ) + self.diag_prog.setLabelText(msg) + self.diag_prog.setWindowTitle("Downloading EDSM Dumps") + self.diag_prog.setValue((args["done"] * 1000) // args["size"]) + + def run_download(self): + if self.dl_thread: + return + self.dl_started = datetime.today() + self.dl_thread = DownloadThread( + self.inp_systems_dl.currentText(), + self.inp_systems_dest_dl.currentText(), + self.inp_bodies_dl.currentText(), + self.inp_bodies_dest_dl.currentText(), + ) + self.dl_thread.progress.connect(self.handle_dl_progress) + self.dl_thread.start() + print(".") + + def update_permute_chk(self, state): + self.chk_permute_keep_first.setEnabled(state) + self.chk_permute_keep_last.setEnabled(state) + self.lbl_keep.setEnabled(state) + + def setup_signals(self): + self.btn_download.clicked.connect(self.run_download) + self.inp_systems_dest_dl.setCurrentText(r"D:\devel\rust\ed_lrr_gui\DL\s.json") + self.inp_bodies_dest_dl.setCurrentText(r"D:\devel\rust\ed_lrr_gui\DL\b.json") + self.set_greedyness(self.sld_greedyness.value()) + self.cmb_mode.currentTextChanged.connect(self.set_route_mode) + self.rd_comp.toggled.connect(self.set_comp_mode) + self.rd_precomp.toggled.connect(self.set_comp_mode) + self.sld_greedyness.valueChanged.connect(self.set_greedyness) + self.btn_go.clicked.connect(self.run) + self.btn_add.clicked.connect(self.add_system) + self.btn_rm.clicked.connect(self.remove_system) + self.chk_permute.stateChanged.connect(self.update_permute_chk) + self.btn_search.clicked.connect(self.resolve_systems) + self.btn_out_browse_pp.clicked.connect( + lambda: self.get_save_file("CSV File (*.csv)", self.set_sys_lst) + ) + self.btn_sys_lst_browse.clicked.connect( + lambda: self.get_open_file("CSV File (*.csv)", self.set_sys_lst) + ) + + self.btn_bodies_browse_pp.clicked.connect( + lambda: self.get_open_file("JSON File (*.json)", self.set_bodies_file) + ) + self.btn_bodies_dest_browse_dl.clicked.connect( + lambda: self.get_save_file("JSON File (*.json)", self.set_bodies_file) + ) + self.btn_systems_browse_pp.clicked.connect( + lambda: self.get_open_file("JSON File (*.json)", self.set_systems_file) + ) + self.btn_systems_dest_browse_dl.clicked.connect( + lambda: self.get_save_file("JSON File (*.json)", self.set_systems_file) + ) + + def handle_close(self): + cfg.write(self.config) + print("BYEEEEEE!") + + def setup_styles(self, win, app): + for name in app.styles: + action = QAction(app) + action.setObjectName("action_load_style_" + name) + action.setText(name) + action.triggered.connect(lambda _, name=name: app.set_style(name)) + self.menuStyle.addAction(action) + + def setupUi(self, MainWindow, app): + super().setupUi(MainWindow) + self.update_dropdowns() + self.main_window = MainWindow + self.app = app + self.setup_signals() + self.lst_sys.setHeaderLabels(["Name", "Type"]) + self.set_route_mode() + self.update_permute_chk(self.chk_permute.isChecked()) + self.setup_styles(MainWindow, app) + + +def main(): + MP.freeze_support() + app = App() + app.setWindowIcon(QIcon(r'D:\devel\rust\ed_lrr_gui\icon\icon.ico')) + MainWindow = QMainWindow() + MainWindow.setWindowIcon(QIcon(r'D:\devel\rust\ed_lrr_gui\icon\icon.ico')) + ui = ED_LRR() + ui.setupUi(MainWindow, app) + MainWindow.show() + ret = app.exec_() + ui.handle_close() + exit(ret) + + +if __name__ == "__main__": + main() diff --git a/ed_lrr_gui/preprocess.py b/ed_lrr_gui/preprocess.py index f59b2b4..a487833 100644 --- a/ed_lrr_gui/preprocess.py +++ b/ed_lrr_gui/preprocess.py @@ -1,8 +1,9 @@ -from multiprocessing import Process, Queue, freeze_support import queue -from datetime import datetime, timedelta -import _ed_lrr from collections import namedtuple +from datetime import datetime, timedelta +from multiprocessing import Process, Queue, freeze_support + +import _ed_lrr class Preprocessor(Process): diff --git a/ed_lrr_gui/router.py b/ed_lrr_gui/router.py index 4e5bbfe..c9f99ce 100644 --- a/ed_lrr_gui/router.py +++ b/ed_lrr_gui/router.py @@ -1,8 +1,16 @@ -from multiprocessing import Process, Queue, freeze_support import queue -from datetime import datetime, timedelta -import _ed_lrr from collections import namedtuple +from datetime import datetime, timedelta +from multiprocessing import Process, Queue, freeze_support + +import _ed_lrr +# from PyQt5.QtWidgets import QProgressDialog + + +# class RouteProgress(QProgressDialog): +# def __init__(self, *args, **kwargs): +# super().__init__(*args, **kwargs) +# self.setWindowModality(Qt.WindowModal) class Router(Process): diff --git a/icon/icon.ico b/icon/icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..e46e62b1dab3e045fb35732efe53facfd92b304f GIT binary patch literal 376773 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.8: + dwg.add(dwg.circle((px,py),r=base_r+random.random()*base_r,stroke_width=w,stroke='#0ae')).fill('#0ae') + else: + dwg.add(dwg.circle((px,py),r=base_r+random.random()*base_r,stroke_width=w,stroke=color)).fill(color) + r=base_r + for _ in range(random.randint(1,max_rings)): + if small: + random.random() + random.random() + continue + r+=ring_step(random.random()) + if random.random()>0.75: + circ=dwg.add(dwg.circle((px,py),r=r,stroke_width=w,stroke="#ea0")) + else: + circ=dwg.add(dwg.circle((px,py),r=r,stroke_width=w,stroke=color)) + circ.fill(color,opacity=0) + + dwg.save() + + + +seed=0 +generate(seed,"icon_1",small=False) +generate(seed,"icon_1_small",small=True) diff --git a/icon/out/icon_1.svg b/icon/out/icon_1.svg new file mode 100644 index 0000000..1fb9797 --- /dev/null +++ b/icon/out/icon_1.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/icon/out/icon_1_small.svg b/icon/out/icon_1_small.svg new file mode 100644 index 0000000..e31b894 --- /dev/null +++ b/icon/out/icon_1_small.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/installer/ED_LRR.iss b/installer/ED_LRR.iss new file mode 100644 index 0000000..8e31873 --- /dev/null +++ b/installer/ED_LRR.iss @@ -0,0 +1,22 @@ +[Setup] +AppName = "ED_LRR" +AppVersion ="0.1.0" +; WizardStyle = modern +DefaultDirName = {autopf}\ED_LRR +DefaultGroupName=ED_LRR +Compression = lzma/ultra +SolidCompression = true +InternalCompressLevel = ultra +OutputBaseFilename="ED_LRR Setup" +ChangesEnvironment = true + +[Files] +Source: "..\exe\dist\ED_LRR\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs + +[Icons] +Name: "{group}\ED_LRR"; Filename: "{app}\ED_LRR.exe"; WorkingDir: "{app}" +Name: "{group}\Uninstall ED_LRR"; Filename: "{uninstallexe}" + +[Run] +Filename: "{app}\ED_LRR.exe"; Description: "Launch ED_LRR"; Flags: postinstall nowait skipifsilent unchecked +Filename: "{app}\ED_LRR.exe"; Parameters: "download" \ No newline at end of file diff --git a/rust/.cargo/config b/rust/.cargo/config index f0b7483..a5af40a 100644 --- a/rust/.cargo/config +++ b/rust/.cargo/config @@ -1,2 +1,2 @@ [build] -rustflags = ["-C", "target-cpu=native"] \ No newline at end of file +rustflags = ["-C", "target-cpu=native"] diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 4ccdea1..5ce2860 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -8,15 +8,6 @@ dependencies = [ "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "atty" -version = "0.2.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "autocfg" version = "0.1.5" @@ -29,14 +20,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "autocfg 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "bitflags" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "block-buffer" version = "0.7.3" @@ -64,7 +50,7 @@ dependencies = [ "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "regex-automata 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -77,47 +63,6 @@ name = "byteorder" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "cfg-if" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "clicolors-control" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "cloudabi" -version = "0.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "console" -version = "0.7.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", - "clicolors-control 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "encode_unicode 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "termios 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "csv" version = "1.1.1" @@ -127,7 +72,7 @@ dependencies = [ "csv-core 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", "ryu 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -163,12 +108,11 @@ dependencies = [ "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "csv 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "indicatif 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "humantime 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "permutohedron 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", - "pyo3 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rstar 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)", + "pyo3 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rstar 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", "sha3 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", "strsim 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -179,11 +123,6 @@ name = "either" version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "encode_unicode" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "fnv" version = "1.0.6" @@ -209,42 +148,51 @@ dependencies = [ [[package]] name = "humantime" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "indicatif" -version = "0.11.0" +name = "indoc" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "console 0.7.7 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "number_prefix 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "indoc-impl 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-hack 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "indoc-impl" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro-hack 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "unindent 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "inventory" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "ctor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", "ghost 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "inventory-impl 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "inventory-impl 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "inventory-impl" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -272,41 +220,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "libc" -version = "0.2.60" +version = "0.2.62" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "lock_api" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "mashup" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "mashup-impl 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro-hack 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "mashup-impl" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro-hack 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "memchr" version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -317,41 +239,29 @@ dependencies = [ "autocfg 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "number_prefix" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "opaque-debug" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] -name = "parking_lot" -version = "0.9.0" +name = "paste" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "lock_api 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot_core 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "paste-impl 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-hack 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "parking_lot_core" -version = "0.6.2" +name = "paste-impl" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-hack 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -366,17 +276,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "proc-macro-hack" -version = "0.4.2" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro-hack-impl 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "proc-macro-hack-impl" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "proc-macro2" version = "0.4.30" @@ -386,39 +293,51 @@ dependencies = [ ] [[package]] -name = "pyo3" -version = "0.7.0" +name = "proc-macro2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "inventory 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", - "mashup 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "pyo3" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "indoc 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "inventory 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "pyo3cls 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "spin 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "paste 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "pyo3cls 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", + "spin 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "unindent 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "version_check 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "pyo3-derive-backend" -version = "0.7.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "pyo3cls" -version = "0.7.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "pyo3-derive-backend 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "pyo3-derive-backend 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -435,18 +354,21 @@ dependencies = [ ] [[package]] -name = "redox_syscall" -version = "0.1.56" +name = "quote" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "regex" -version = "1.2.1" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)", "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "regex-syntax 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", + "regex-syntax 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", "thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -460,12 +382,12 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.11" +version = "0.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "rstar" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -473,40 +395,14 @@ dependencies = [ "pdqselect 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "rustc_version" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "ryu" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "scopeguard" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "semver" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "semver-parser" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "serde" -version = "1.0.98" +version = "1.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "serde_derive 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)", @@ -529,7 +425,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", "ryu 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -544,14 +440,9 @@ dependencies = [ "opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "smallvec" -version = "0.6.10" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "spin" -version = "0.5.0" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -570,11 +461,13 @@ dependencies = [ ] [[package]] -name = "termios" -version = "0.3.1" +name = "syn" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -590,116 +483,85 @@ name = "typenum" version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "unicode-width" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "unicode-xid" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "unicode-xid" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "unindent" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "version_check" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "winapi" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" [metadata] "checksum aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "58fb5e95d83b38284460a5fda7d6470aa0b8844d283a0b614b8535e880800d2d" -"checksum atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "1803c647a3ec87095e7ae7acfca019e98de5ec9a7d01343f611cf3152ed71a90" "checksum autocfg 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "22130e92352b948e7e82a49cdb0aa94f2211761117f29e052dd397c1ac33542b" "checksum bincode 1.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "9f04a5e50dc80b3d5d35320889053637d15011aed5e66b66b37ae798c65da6f7" -"checksum bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3d155346769a6855b86399e9bc3814ab343cd3d62c7e985113d46a0ec3c281fd" "checksum block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" "checksum block-padding 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "6d4dc3af3ee2e12f3e5d224e5e1e3d73668abbeb69e566d361f7d5563a4fdf09" "checksum bstr 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "e0a692f1c740e7e821ca71a22cf99b9b2322dfa94d10f71443befb1797b3946a" "checksum byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" "checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5" -"checksum cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "b486ce3ccf7ffd79fdeb678eac06a9e6c09fc88d33836340becb8fffe87c5e33" -"checksum clicolors-control 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "73abfd4c73d003a674ce5d2933fca6ce6c42480ea84a5ffe0a2dc39ed56300f9" -"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" -"checksum console 0.7.7 (registry+https://github.com/rust-lang/crates.io-index)" = "8ca57c2c14b8a2bf3105bc9d15574aad80babf6a9c44b1058034cdf8bd169628" "checksum csv 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "37519ccdfd73a75821cac9319d4fce15a81b9fcf75f951df5b9988aa3a0af87d" "checksum csv-core 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "9b5cadb6b25c77aeff80ba701712494213f4a8418fcda2ee11b6560c3ad0bf4c" "checksum ctor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "3b4c17619643c1252b5f690084b82639dd7fac141c57c8e77a00e0148132092c" "checksum digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" "checksum either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5527cfe0d098f36e3f8839852688e63c8fff1c90b2b405aef730615f9a7bcf7b" -"checksum encode_unicode 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "90b2c9496c001e8cb61827acdefad780795c42264c137744cae6f7d9e3450abd" "checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" "checksum generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" "checksum ghost 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5297b71943dc9fea26a3241b178c140ee215798b7f79f7773fd61683e25bca74" -"checksum humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3ca7e5f2e110db35f93b837c81797f3714500b81d517bf20c431b16d3ca4f114" -"checksum indicatif 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2c60da1c9abea75996b70a931bba6c750730399005b61ccd853cee50ef3d0d0c" -"checksum inventory 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "21df85981fe094480bc2267723d3dc0fd1ae0d1f136affc659b7398be615d922" -"checksum inventory-impl 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8a877ae8bce77402d5e9ed870730939e097aad827b2a932b361958fa9d6e75aa" +"checksum humantime 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" +"checksum indoc 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "3f9553c1e16c114b8b77ebeb329e5f2876eed62a8d51178c8bc6bff0d65f98f8" +"checksum indoc-impl 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b714fc08d0961716390977cdff1536234415ac37b509e34e5a983def8340fb75" +"checksum inventory 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f4cece20baea71d9f3435e7bbe9adf4765f091c5fe404975f844006964a71299" +"checksum inventory-impl 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "c2869bf972e998977b1cb87e60df70341d48e48dca0823f534feb91ea44adaf9" "checksum itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5b8467d9c1cebe26feb08c640139247fac215782d35371ade9a2136ed6085358" "checksum itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "501266b7edd0174f8530248f87f99c88fbe60ca4ef3dd486835b8d8d53136f7f" "checksum keccak 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "67c21572b4949434e4fc1e1978b99c5f77064153c59d998bf13ecd96fb5ecba7" "checksum lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc5729f27f159ddd61f4df6228e827e86643d4d3e7c32183cb30a1c08f604a14" -"checksum libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)" = "d44e80633f007889c7eff624b709ab43c92d708caad982295768a7b13ca3b5eb" -"checksum lock_api 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f8912e782533a93a167888781b836336a6ca5da6175c05944c86cf28c31104dc" -"checksum mashup 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "f2d82b34c7fb11bb41719465c060589e291d505ca4735ea30016a91f6fc79c3b" -"checksum mashup-impl 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "aa607bfb674b4efb310512527d64266b065de3f894fc52f84efcbf7eaa5965fb" +"checksum libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)" = "34fcd2c08d2f832f376f4173a231990fa5aef4e99fb569867318a227ef4c06ba" "checksum memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e" "checksum num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "6ba9a427cfca2be13aa6f6403b0b7e7368fe982bfa16fccc450ce74c46cd9b32" -"checksum number_prefix 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "dbf9993e59c894e3c08aa1c2712914e9e6bf1fcbfc6bef283e2183df345a4fee" "checksum opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" -"checksum parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252" -"checksum parking_lot_core 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b876b1b9e7ac6e1a74a6da34d25c42e17e8862aa409cbbbdcfc8d86c6f3bc62b" +"checksum paste 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "423a519e1c6e828f1e73b720f9d9ed2fa643dce8a7737fb43235ce0b41eeaa49" +"checksum paste-impl 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "4214c9e912ef61bf42b81ba9a47e8aad1b2ffaf739ab162bf96d1e011f54e6c5" "checksum pdqselect 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4ec91767ecc0a0bbe558ce8c9da33c068066c57ecc8bb8477ef8c1ad3ef77c27" "checksum permutohedron 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b687ff7b5da449d39e418ad391e5e08da53ec334903ddbb921db208908fc372c" -"checksum proc-macro-hack 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "463bf29e7f11344e58c9e01f171470ab15c925c6822ad75028cc1c0e1d1eb63b" -"checksum proc-macro-hack-impl 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "38c47dcb1594802de8c02f3b899e2018c78291168a22c281be21ea0fb4796842" +"checksum proc-macro-hack 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e688f31d92ffd7c1ddc57a1b4e6d773c0f2a14ee437a4b0a4f5a69c80eb221c8" "checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" -"checksum pyo3 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d09e6e2d3fa5ae1a8af694f865e03e763e730768b16e3097851ff0b7f2276086" -"checksum pyo3-derive-backend 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d9d7ae8ab3017515cd7c82d88ce49b55e12a56c602dc69993e123da45c91b186" -"checksum pyo3cls 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c494f8161f5b73096cc50f00fbb90fe670f476cde5e59c1decff39b546d54f40" +"checksum proc-macro2 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e98a83a9f9b331f54b924e68a66acb1bb35cb01fb0a23645139967abefb697e8" +"checksum pyo3 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5862a106c576591645b9fa36b56764b6c894dda70800479892997e5b4fd41c0f" +"checksum pyo3-derive-backend 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "20d6d14afa2d06a63331dad47b4b40cac06c3be1e3d7de56d020eab2b3e9484b" +"checksum pyo3cls 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4e39529c2416febd394f7d032abbce5fa1915e32b2fc9b564e1d9d017acac78d" "checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0" "checksum quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" -"checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" -"checksum regex 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88c3d9193984285d544df4a30c23a4e62ead42edf70a4452ceb76dac1ce05c26" +"checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" +"checksum regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dc220bd33bdce8f093101afe22a037b8eb0e5af33592e6a9caafff0d4cb81cbd" "checksum regex-automata 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "92b73c2a1770c255c240eaa4ee600df1704a38dc3feaa6e949e7fcd4f8dc09f9" -"checksum regex-syntax 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)" = "b143cceb2ca5e56d5671988ef8b15615733e7ee16cd348e064333b251b89343f" -"checksum rstar 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e90bb34cd8efef7ed3ebfb29e713e51301c3e60fba37c3e9185a1afaf9ce643a" -"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +"checksum regex-syntax 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "11a7e20d1cce64ef2fed88b66d347f88bd9babb82845b2b858f3edbf59a4f716" +"checksum rstar 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "08c3cf91d6318ed050a8dda79bc857665f9c3d41524b6f70bbd0396c5d9d662d" "checksum ryu 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c92464b447c0ee8c4fb3824ecc8383b81717b9f1e74ba2e72540aef7b9f82997" -"checksum scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b42e15e59b18a828bbf5c58ea01debb36b9b096346de35d941dcb89009f24a0d" -"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" -"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" -"checksum serde 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)" = "7fe5626ac617da2f2d9c48af5515a21d5a480dbd151e01bb1c355e26a3e68113" +"checksum serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)" = "f4473e8506b213730ff2061073b48fa51dcc66349219e2e7c5608f0296a1d95a" "checksum serde_derive 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)" = "01e69e1b8a631f245467ee275b8c757b818653c6d704cdbcaeb56b56767b529c" "checksum serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)" = "051c49229f282f7c6f3813f8286cc1e3323e8051823fce42c7ea80fe13521704" "checksum sha3 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dd26bc0e7a2e3a7c959bc494caf58b72ee0c71d67704e9520f736ca7e4853ecf" -"checksum smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "ab606a9c5e214920bb66c458cd7be8ef094f813f20fe77a54cc7dbfff220d4b7" -"checksum spin 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "44363f6f51401c34e7be73db0db371c04705d35efbe9f7d6082e03a921a32c55" +"checksum spin 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" "checksum strsim 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "032c03039aae92b350aad2e3779c352e104d919cb192ba2fabbd7b831ce4f0f6" "checksum syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)" = "eadc09306ca51a40555dd6fc2b415538e9e18bc9f870e47b1a524a79fe2dcf5e" -"checksum termios 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "72b620c5ea021d75a735c943269bb07d30c9b77d6ac6b236bc8b5c496ef05625" +"checksum syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "66850e97125af79138385e9b88339cbcd037e3f28ceab8c5ad98e64f0f1f80bf" "checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" "checksum typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "612d636f949607bdf9b123b4a6f6d966dedf3ff669f7f045890d3a4a73948169" -"checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526" "checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" -"checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" -"checksum winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "f10e386af2b13e47c89e7236a7a14a086791a2b88ebad6df9bf42040195cf770" -"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" -"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" +"checksum unindent 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "c7d0d32a92c9ed197278e09140c32dec854ad5826f0e0e18c1d2a1690f15c8d5" +"checksum version_check 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "078775d0255232fb988e6fccf26ddc9d1ac274299aaedcedce21c6f72cc533ce" diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 2a579f7..df5607e 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -1,32 +1,27 @@ -[package] -name = "ed_lrr" -version = "0.1.0" -authors = ["Daniel Seiller "] -edition = "2018" -repository = "https://gitlab.com/Earthnuker/ed_lrr.git" -license = "WTFPL" - - -[lib] -crate-type = ["cdylib"] - -[profile.release] -#debug=true - -[dependencies] -csv = "1.1.1" -serde = { version = "1.0", features = ["derive"] } -rstar = "0.5.0" -humantime = "1.2.0" -permutohedron = "0.2.4" -serde_json = "1.0.40" -indicatif = "0.11.0" -fnv = "1.0.6" -bincode = "1.1.4" -sha3 = "0.8.2" -byteorder = "1.3.2" -strsim = "0.9.2" - -[dependencies.pyo3] -version = "0.7.0" -features = ["extension-module"] +[package] +name = "ed_lrr" +version = "0.1.0" +authors = ["Daniel Seiller "] +edition = "2018" +repository = "https://gitlab.com/Earthnuker/ed_lrr.git" +license = "WTFPL" + + +[lib] +crate-type = ["cdylib"] +name = "_ed_lrr" + +[dependencies] +pyo3 = { version = "0.8.0", features = ["extension-module"] } +csv = "1.1.1" +serde = { version = "1.0", features = ["derive"] } +rstar = "0.5.1" +humantime = "1.3.0" +permutohedron = "0.2.4" +serde_json = "1.0.40" +fnv = "1.0.6" +bincode = "1.1.4" +sha3 = "0.8.2" +byteorder = "1.3.2" +strsim = "0.9.2" + diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 43bf006..8204e74 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -114,7 +114,7 @@ pub fn _ed_lrr(_py: Python, m: &PyModule) -> PyResult<()> { py: Python<'static>, hops: Vec, range: f32, - radius_mult: f32, + prune: Option<(usize,f64)>, mode: String, primary: bool, permute: bool, @@ -145,13 +145,14 @@ pub fn _ed_lrr(_py: Python, m: &PyModule) -> PyResult<()> { state_dict.set_item("prc_done", state.prc_done)?; state_dict.set_item("n_seen", state.n_seen)?; state_dict.set_item("prc_seen", state.prc_seen)?; + state_dict.set_item("from", state.from.clone())?; + state_dict.set_item("to", state.to.clone())?; callback.call(py, (state_dict,), None) }; let mut systems = Vec::new(); for sys in hops { systems.push(route::SysEntry::parse(&sys)) } - println!("SYSTEMS: {:?}", systems); let opts = RouteOpts { systems, range: Some(range), @@ -165,7 +166,7 @@ pub fn _ed_lrr(_py: Python, m: &PyModule) -> PyResult<()> { keep_first, keep_last, primary, - radius_mult, + prune, }; let none = ().to_object(py); match route(opts) { diff --git a/rust/src/route.rs b/rust/src/route.rs index 3a7878f..247084b 100644 --- a/rust/src/route.rs +++ b/rust/src/route.rs @@ -20,6 +20,8 @@ pub struct SearchState { pub mode: String, pub system: String, pub body: String, + pub from: String, + pub to: String, pub depth: usize, pub queue_size: usize, pub d_rem: f32, @@ -55,7 +57,7 @@ pub struct RouteOpts { pub keep_last: bool, pub factor: Option, pub mode: Mode, - pub radius_mult: f32, + pub prune: Option<(usize,f64)>, pub systems: Vec, pub callback: Box PyResult>, } @@ -191,7 +193,8 @@ pub struct Router { range: f32, primary_only: bool, path: PathBuf, - radius_mult: f32, + prune: Option<(usize,f64)>, + prune_map: FnvHashMap, callback: Box PyResult>, } @@ -199,7 +202,7 @@ impl Router { pub fn new( path: &PathBuf, range: f32, - radius_mult: f32, + prune: Option<(usize,f64)>, primary_only: bool, callback: Box PyResult>, ) -> Result { @@ -245,7 +248,8 @@ impl Router { cache: LineCache::new(path), path: path.clone(), callback, - radius_mult, + prune, + prune_map: FnvHashMap::default() }; println!( "{} Systems loaded in {}", @@ -302,7 +306,8 @@ impl Router { primary_only: primary, path, callback, - radius_mult: 0f32, + prune: None, + prune_map: FnvHashMap::default() }, )) } @@ -317,11 +322,8 @@ impl Router { fn valid(&self, sys: &System, src: &System, dst: &System) -> bool { let scoopable = self.scoopable.contains(&sys.id); - if self.radius_mult == 0.0 { - return scoopable; - } - let df = src.distp(dst); - (sys.distp(src) + sys.distp(dst)) < (df * (1.0 + self.radius_mult)) + return scoopable; + // TODO: check prune map } pub fn best_multiroute( @@ -466,6 +468,8 @@ impl Router { prc_done: 0.0, n_seen: 0, prc_seen: 0.0, + from: src_name.clone(), + to: dst_name.clone(), body: start_sys.body.clone(), system: start_sys.system.clone(), }; @@ -569,6 +573,8 @@ impl Router { prc_done: 0.0, n_seen: 0, prc_seen: 0.0, + from: src_name.clone(), + to: dst_name.clone(), body: start_sys.body.clone(), system: start_sys.system.clone(), }; @@ -793,6 +799,8 @@ impl Router { prc_done: 0.0, n_seen: 0, prc_seen: 0.0, + from: src_name.clone(), + to: dst_name.clone(), system: start_sys.system.clone(), body: start_sys.body.clone(), }; @@ -879,6 +887,7 @@ impl Router { } } pub fn route(opts: RouteOpts) -> Result>, String> { + // TODO: implement pruning (check if dist to target improved by at least $n*jump_range$ Ly in the last $m$ steps) if opts.systems.is_empty() { if opts.precomp_file.is_some() { return Err("Error: Please specify exatly one system".to_owned()); @@ -898,7 +907,7 @@ pub fn route(opts: RouteOpts) -> Result>, String> { Router::new( &path, opts.range.unwrap(), - opts.radius_mult, + opts.prune, opts.primary, Box::new(opts.callback), )? @@ -906,14 +915,12 @@ pub fn route(opts: RouteOpts) -> Result>, String> { Router::new( &path, opts.range.unwrap(), - opts.radius_mult, + opts.prune, opts.primary, opts.callback, )? }; - let systems: Vec = router - .resolve_systems(&opts.systems)? - .to_vec(); + let systems: Vec = router.resolve_systems(&opts.systems)?.to_vec(); if opts.precompute { for sys in systems { router.route_tree = None; diff --git a/setup.py b/setup.py index 20e467c..af29aac 100644 --- a/setup.py +++ b/setup.py @@ -1,11 +1,21 @@ -from setuptools import setup,find_packages +import sys +import distutils.cmd +import distutils.log +from setuptools import find_packages, setup from setuptools_rust import Binding, RustExtension, Strip +with open("README.md", "r") as fh: + long_description = fh.read() + + setup( name="ed_lrr_gui", version="0.1.0", author="Daniel Seiller", author_email="earthnuker@gmail.com", + description="Elite: Dangerous long range route plotter", + long_description=long_description, + long_description_content_type="text/markdown", url="none yet", rust_extensions=[ RustExtension( @@ -17,14 +27,38 @@ setup( ) ], packages=find_packages(), - entry_points={"console_scripts": ["ed_lrr_gui=ed_lrr_gui.__main__:main"]}, + entry_points={ + "console_scripts": [ + "ed_lrr_gui_console = ed_lrr_gui.gui.__main__:main", + "ed_lrr = ed_lrr_gui.__main__:main", + ], + "gui_scripts": ["ed_lrr_gui = ed_lrr_gui.gui.__main__:main"], + }, install_requires=[ - "PyQt5", "appdirs", "PyYAML", "requests", "python-dateutil", "pyperclip", + "click", + "PyQt5", + "click-default-group" + ], + setup_requires=[ + "setuptools", + "setuptools-rust", + "wheel", + "pyinstaller", + "pytest-runner", + ], + tests_require=["pytest", "pytest-pep8", "pytest-cov"], + extras_require={ + "dev": ["black", "pyinstaller","jinja2","svgwrite","tsp"], + }, + classifiers=[ + "Programming Language :: Python :: 3", + "License :: OSI Approved :: MIT License", + "Operating System :: OS Independent", ], include_package_data=True, zip_safe=False, diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..3212d14 --- /dev/null +++ b/tox.ini @@ -0,0 +1,30 @@ +[tox] +envlist = py37 +requires = tox-conda + +[testenv] +recreate = True +skip_install = True +deps = + PyQt5 + setuptools_rust +conda_deps = + pycrypto + nuitka +passenv = + CARGO_HOME + RUSTUP_HOME + INCLUDE + LIB + MSSdk + DISTUTILS_USE_SDK +whitelist_externals = + cargo +conda_channels = + conda-forge +extras = + dev +commands = + python build_gui.py + pip install .[dev] + python -m nuitka --plugin-enable=multiprocessing --plugin-enable=qt-plugins --standalone --follow-imports --output-dir=exe ed_lrr_gui\__main__.py