summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorLex Neva <lexelby@users.noreply.github.com>2020-04-28 12:34:05 -0400
committerGitHub <noreply@github.com>2020-04-28 18:34:05 +0200
commitcb2b4e3522cb7f426ba5ad3e68deb9e6ccc581ec (patch)
tree2a2f38823c3c9f0b5439ce2aa7c9040299109292 /lib
parenteb526927e16954390d06929535d6f5c3766b5f6c (diff)
electron simulator (#531)
Diffstat (limited to 'lib')
-rw-r--r--lib/api/__init__.py1
-rw-r--r--lib/api/server.py110
-rw-r--r--lib/api/simulator.py3
-rw-r--r--lib/api/stitch_plan.py17
-rw-r--r--lib/extensions/__init__.py6
-rw-r--r--lib/extensions/simulator.py17
-rw-r--r--lib/stitch_plan/stitch.py3
-rw-r--r--lib/stitch_plan/stitch_plan.py11
-rw-r--r--lib/threads/color.py19
-rw-r--r--lib/utils/geometry.py3
-rw-r--r--lib/utils/json.py15
11 files changed, 200 insertions, 5 deletions
diff --git a/lib/api/__init__.py b/lib/api/__init__.py
new file mode 100644
index 00000000..f93e59a9
--- /dev/null
+++ b/lib/api/__init__.py
@@ -0,0 +1 @@
+from .server import APIServer \ No newline at end of file
diff --git a/lib/api/server.py b/lib/api/server.py
new file mode 100644
index 00000000..c57a1785
--- /dev/null
+++ b/lib/api/server.py
@@ -0,0 +1,110 @@
+import errno
+import logging
+import socket
+from threading import Thread
+import time
+
+from flask import Flask, request, g
+import requests
+
+from ..utils.json import InkStitchJSONEncoder
+from .simulator import simulator
+from .stitch_plan import stitch_plan
+
+
+class APIServer(Thread):
+ def __init__(self, *args, **kwargs):
+ self.extension = args[0]
+ Thread.__init__(self, *args[1:], **kwargs)
+ self.daemon = True
+ self.shutting_down = False
+ self.app = None
+ self.host = None
+ self.port = None
+ self.ready = False
+
+ self.__setup_app()
+
+ def __setup_app(self): # noqa: C901
+ self.app = Flask(__name__)
+ self.app.json_encoder = InkStitchJSONEncoder
+
+ self.app.register_blueprint(simulator, url_prefix="/simulator")
+ self.app.register_blueprint(stitch_plan, url_prefix="/stitch_plan")
+
+ @self.app.before_request
+ def store_extension():
+ # make the InkstitchExtension object available to the view handling
+ # this request
+ g.extension = self.extension
+
+ @self.app.route('/shutdown', methods=['POST'])
+ def shutdown():
+ self.shutting_down = True
+ request.environ.get('werkzeug.server.shutdown')()
+ return "shutting down"
+
+ @self.app.route('/ping')
+ def ping():
+ return "pong"
+
+ def stop(self):
+ # for whatever reason, shutting down only seems possible in
+ # the context of a flask request, so we'll just make one
+ requests.post("http://%s:%s/shutdown" % (self.host, self.port))
+
+ def disable_logging(self):
+ logging.getLogger('werkzeug').setLevel(logging.ERROR)
+
+ def run(self):
+ self.disable_logging()
+
+ self.host = "127.0.0.1"
+ self.port = 5000
+
+ while True:
+ try:
+ self.app.run(self.host, self.port, threaded=True)
+ except socket.error as e:
+ if e.errno == errno.EADDRINUSE:
+ self.port += 1
+ continue
+ else:
+ raise
+ else:
+ break
+
+ def ready_checker(self):
+ """Wait until the server is started.
+
+ Annoyingly, there's no way to get a callback to be run when the Flask
+ server starts. Instead, we'll have to poll.
+ """
+
+ while True:
+ if self.port:
+ try:
+ response = requests.get("http://%s:%s/ping" % (self.host, self.port))
+ if response.status_code == 200:
+ break
+ except socket.error, e:
+ if e.errno == errno.ECONNREFUSED:
+ pass
+ else:
+ raise
+
+ time.sleep(0.1)
+
+ def start_server(self):
+ """Start the API server.
+
+ returns: port (int) -- the port that the server is listening on
+ (on localhost)
+ """
+
+ checker = Thread(target=self.ready_checker)
+ checker.start()
+ self.start()
+ checker.join()
+
+ return self.port
diff --git a/lib/api/simulator.py b/lib/api/simulator.py
new file mode 100644
index 00000000..d11bd308
--- /dev/null
+++ b/lib/api/simulator.py
@@ -0,0 +1,3 @@
+from flask import Blueprint
+
+simulator = Blueprint('simulator', __name__)
diff --git a/lib/api/stitch_plan.py b/lib/api/stitch_plan.py
new file mode 100644
index 00000000..fd6bf9c9
--- /dev/null
+++ b/lib/api/stitch_plan.py
@@ -0,0 +1,17 @@
+from flask import Blueprint, g, jsonify
+
+from ..stitch_plan import patches_to_stitch_plan
+
+
+stitch_plan = Blueprint('stitch_plan', __name__)
+
+
+@stitch_plan.route('')
+def get_stitch_plan():
+ if not g.extension.get_elements():
+ return dict(colors=[], stitch_blocks=[], commands=[])
+
+ patches = g.extension.elements_to_patches(g.extension.elements)
+ stitch_plan = patches_to_stitch_plan(patches)
+
+ return jsonify(stitch_plan)
diff --git a/lib/extensions/__init__.py b/lib/extensions/__init__.py
index 6f9ef5c4..3c67700b 100644
--- a/lib/extensions/__init__.py
+++ b/lib/extensions/__init__.py
@@ -16,7 +16,7 @@ from output import Output
from params import Params
from print_pdf import Print
from remove_embroidery_settings import RemoveEmbroiderySettings
-from simulate import Simulate
+from simulator import Simulator
from stitch_plan_preview import StitchPlanPreview
from zip import Zip
@@ -25,7 +25,6 @@ __all__ = extensions = [Embroider,
Install,
Params,
Print,
- Simulate,
Input,
Output,
Zip,
@@ -40,4 +39,5 @@ __all__ = extensions = [Embroider,
Troubleshoot,
RemoveEmbroiderySettings,
BreakApart,
- ImportThreadlist]
+ ImportThreadlist,
+ Simulator]
diff --git a/lib/extensions/simulator.py b/lib/extensions/simulator.py
new file mode 100644
index 00000000..1c0627ba
--- /dev/null
+++ b/lib/extensions/simulator.py
@@ -0,0 +1,17 @@
+from ..api import APIServer
+from ..gui import open_url
+
+from .base import InkstitchExtension
+
+
+class Simulator(InkstitchExtension):
+ def __init__(self):
+ InkstitchExtension.__init__(self)
+
+ def effect(self):
+ api_server = APIServer(self)
+ port = api_server.start_server()
+ electron = open_url("/simulator?port=%d" % port)
+ electron.wait()
+ api_server.stop()
+ api_server.join()
diff --git a/lib/stitch_plan/stitch.py b/lib/stitch_plan/stitch.py
index 462634cb..ccbea12e 100644
--- a/lib/stitch_plan/stitch.py
+++ b/lib/stitch_plan/stitch.py
@@ -31,3 +31,6 @@ class Stitch(Point):
def copy(self):
return Stitch(self.x, self.y, self.color, self.jump, self.stop, self.trim, self.color_change, self.no_ties)
+
+ def __json__(self):
+ return vars(self)
diff --git a/lib/stitch_plan/stitch_plan.py b/lib/stitch_plan/stitch_plan.py
index ac6b3b8d..0b2f75cd 100644
--- a/lib/stitch_plan/stitch_plan.py
+++ b/lib/stitch_plan/stitch_plan.py
@@ -98,6 +98,14 @@ class StitchPlan(object):
def __repr__(self):
return "StitchPlan(%s)" % ", ".join(repr(cb) for cb in self.color_blocks)
+ def __json__(self):
+ return dict(color_blocks=self.color_blocks,
+ num_stops=self.num_stops,
+ num_trims=self.num_trims,
+ num_stitches=self.num_stitches,
+ bounding_box=self.bounding_box
+ )
+
@property
def num_colors(self):
"""Number of unique colors in the stitch plan."""
@@ -175,6 +183,9 @@ class ColorBlock(object):
def __delitem__(self, item):
del self.stitches[item]
+ def __json__(self):
+ return dict(color=self.color, stitches=self.stitches)
+
def has_color(self):
return self._color is not None
diff --git a/lib/threads/color.py b/lib/threads/color.py
index 8c596e44..ea9dab06 100644
--- a/lib/threads/color.py
+++ b/lib/threads/color.py
@@ -1,7 +1,8 @@
-import simplestyle
-import re
import colorsys
from pyembroidery.EmbThread import EmbThread
+import re
+
+import simplestyle
class ThreadColor(object):
@@ -27,6 +28,20 @@ class ThreadColor(object):
self.number = number
self.manufacturer = manufacturer
+ def __json__(self):
+ jsonified = self._as_dict()
+ jsonified["visible_on_white"] = self.visible_on_white._as_dict()
+
+ return jsonified
+
+ def _as_dict(self):
+ return dict(name=self.name,
+ number=self.number,
+ manufacturer=self.manufacturer,
+ rgb=self.rgb,
+ hex=self.to_hex_str(),
+ )
+
def __eq__(self, other):
if isinstance(other, ThreadColor):
return self.rgb == other.rgb
diff --git a/lib/utils/geometry.py b/lib/utils/geometry.py
index 647636c8..bae32c32 100644
--- a/lib/utils/geometry.py
+++ b/lib/utils/geometry.py
@@ -63,6 +63,9 @@ class Point:
self.x = x
self.y = y
+ def __json__(self):
+ return vars(self)
+
def __add__(self, other):
return Point(self.x + other.x, self.y + other.y)
diff --git a/lib/utils/json.py b/lib/utils/json.py
new file mode 100644
index 00000000..c670a46b
--- /dev/null
+++ b/lib/utils/json.py
@@ -0,0 +1,15 @@
+from flask.json import JSONEncoder
+
+
+class InkStitchJSONEncoder(JSONEncoder):
+ """JSON encoder class that runs __json__ on an object if available.
+
+ The __json__ method should return a JSON-compatible representation of the
+ object.
+ """
+
+ def default(self, obj):
+ try:
+ return obj.__json__()
+ except AttributeError:
+ return JSONEncoder.default(self, obj)