summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/api/__init__.py6
-rw-r--r--lib/api/lang.py11
-rw-r--r--lib/api/page_specs.py36
-rw-r--r--lib/api/server.py121
-rw-r--r--lib/api/simulator.py8
-rw-r--r--lib/api/stitch_plan.py29
-rw-r--r--lib/extensions/lettering.py3
-rw-r--r--lib/extensions/params.py8
-rw-r--r--lib/extensions/simulator.py32
-rw-r--r--lib/extensions/tartan.py3
-rw-r--r--lib/gui/__init__.py2
-rw-r--r--lib/gui/lettering.py3
-rw-r--r--lib/gui/simulator.py174
-rw-r--r--lib/gui/tartan/main_panel.py3
-rw-r--r--lib/utils/svg_data.py10
-rw-r--r--templates/simulator.xml2
16 files changed, 166 insertions, 285 deletions
diff --git a/lib/api/__init__.py b/lib/api/__init__.py
deleted file mode 100644
index 35e411a7..00000000
--- a/lib/api/__init__.py
+++ /dev/null
@@ -1,6 +0,0 @@
-# Authors: see git history
-#
-# Copyright (c) 2010 Authors
-# Licensed under the GNU GPL version 3.0 or later. See the file LICENSE for details.
-
-from .server import APIServer \ No newline at end of file
diff --git a/lib/api/lang.py b/lib/api/lang.py
deleted file mode 100644
index 73c190f4..00000000
--- a/lib/api/lang.py
+++ /dev/null
@@ -1,11 +0,0 @@
-import os
-
-from flask import Blueprint, jsonify
-
-languages = Blueprint('languages', __name__)
-
-
-@languages.route('')
-def get_lang():
- languages = dict(os.environ)
- return jsonify(languages)
diff --git a/lib/api/page_specs.py b/lib/api/page_specs.py
deleted file mode 100644
index 8d3aee49..00000000
--- a/lib/api/page_specs.py
+++ /dev/null
@@ -1,36 +0,0 @@
-# Authors: see git history
-#
-# Copyright (c) 2010 Authors
-# Licensed under the GNU GPL version 3.0 or later. See the file LICENSE for details.
-
-from flask import Blueprint, g, jsonify
-
-page_specs = Blueprint('page_specs', __name__)
-
-
-@page_specs.route('')
-def get_page_specs():
- svg = g.extension.document.getroot()
- width = svg.get('width', 0)
- height = svg.get('height', 0)
- pagecolor = "white"
- deskcolor = "white"
- bordercolor = "black"
- showpageshadow = True
-
- namedview = svg.namedview
- if namedview is not None:
- pagecolor = namedview.get('pagecolor', pagecolor)
- deskcolor = namedview.get('inkscape:deskcolor', deskcolor)
- bordercolor = namedview.get('bordercolor', bordercolor)
- showpageshadow = namedview.get('inkscape:showpageshadow', showpageshadow)
-
- page_specs = {
- "width": width,
- "height": height,
- "pagecolor": pagecolor,
- "deskcolor": deskcolor,
- "bordercolor": bordercolor,
- "showpageshadow": showpageshadow
- }
- return jsonify(page_specs)
diff --git a/lib/api/server.py b/lib/api/server.py
deleted file mode 100644
index 5625d77d..00000000
--- a/lib/api/server.py
+++ /dev/null
@@ -1,121 +0,0 @@
-# Authors: see git history
-#
-# Copyright (c) 2010 Authors
-# Licensed under the GNU GPL version 3.0 or later. See the file LICENSE for details.
-
-import errno
-import logging
-import socket
-import sys
-import time
-from threading import Thread
-from contextlib import closing
-
-import requests
-from flask import Flask, g
-from werkzeug.serving import make_server
-
-from ..utils.json import InkStitchJSONProvider
-from .simulator import simulator
-from .stitch_plan import stitch_plan
-from .page_specs import page_specs
-from .lang import languages
-# this for electron axios
-from flask_cors import CORS
-
-
-class APIServer(Thread):
- def __init__(self, *args, **kwargs):
- self.extension = args[0]
- Thread.__init__(self, *args[1:], **kwargs)
- self.daemon = True
- self.app = None
- self.host = None
- self.port = None
- self.ready = False
-
- self.__setup_app()
- self.flask_server = None
- self.server_thread = None
-
- def __setup_app(self): # noqa: C901
- # Disable warning about using a development server in a production environment
- cli = sys.modules['flask.cli']
- cli.show_server_banner = lambda *x: None
-
- self.app = Flask(__name__)
- CORS(self.app)
- self.app.json = InkStitchJSONProvider(self.app)
-
- self.app.register_blueprint(simulator, url_prefix="/simulator")
- self.app.register_blueprint(stitch_plan, url_prefix="/stitch_plan")
- self.app.register_blueprint(page_specs, url_prefix="/page_specs")
- self.app.register_blueprint(languages, url_prefix="/languages")
-
- @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('/ping')
- def ping():
- return "pong"
-
- def stop(self):
- self.flask_server.shutdown()
- self.server_thread.join()
-
- def disable_logging(self):
- logging.getLogger('werkzeug').setLevel(logging.ERROR)
-
- # https://github.com/aluo-x/Learning_Neural_Acoustic_Fields/blob/master/train.py
- # https://github.com/pytorch/pytorch/issues/71029
- def find_free_port(self):
- with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as s:
- s.bind(('localhost', 0))
- return s.getsockname()[1]
-
- def run(self):
- self.disable_logging()
-
- self.host = "127.0.0.1"
- self.port = self.find_free_port()
- self.flask_server = make_server(self.host, self.port, self.app)
- self.server_thread = Thread(target=self.flask_server.serve_forever)
- self.server_thread.start()
-
- 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 as 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
deleted file mode 100644
index 26c0246c..00000000
--- a/lib/api/simulator.py
+++ /dev/null
@@ -1,8 +0,0 @@
-# Authors: see git history
-#
-# Copyright (c) 2010 Authors
-# Licensed under the GNU GPL version 3.0 or later. See the file LICENSE for details.
-
-from flask import Blueprint
-
-simulator = Blueprint('simulator', __name__)
diff --git a/lib/api/stitch_plan.py b/lib/api/stitch_plan.py
deleted file mode 100644
index 0267a70a..00000000
--- a/lib/api/stitch_plan.py
+++ /dev/null
@@ -1,29 +0,0 @@
-# Authors: see git history
-#
-# Copyright (c) 2010 Authors
-# Licensed under the GNU GPL version 3.0 or later. See the file LICENSE for details.
-
-from flask import Blueprint, g, jsonify
-
-from ..exceptions import InkstitchException, format_uncaught_exception
-from ..stitch_plan import stitch_groups_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=[])
-
- try:
- metadata = g.extension.get_inkstitch_metadata()
- collapse_len = metadata['collapse_len_mm']
- min_stitch_len = metadata['min_stitch_len_mm']
- stitch_groups = g.extension.elements_to_stitch_groups(g.extension.elements)
- stitch_plan = stitch_groups_to_stitch_plan(stitch_groups, collapse_len=collapse_len, min_stitch_len=min_stitch_len)
- return jsonify(stitch_plan)
- except InkstitchException as exc:
- return jsonify({"error_message": str(exc)}), 500
- except Exception:
- return jsonify({"error_message": format_uncaught_exception()}), 500
diff --git a/lib/extensions/lettering.py b/lib/extensions/lettering.py
index 43ff424d..76821902 100644
--- a/lib/extensions/lettering.py
+++ b/lib/extensions/lettering.py
@@ -14,6 +14,7 @@ from ..gui.simulator import SplitSimulatorWindow
from ..i18n import _
from ..svg import get_correction_transform
from ..svg.tags import INKSCAPE_LABEL, INKSTITCH_LETTERING, SVG_GROUP_TAG
+from ..utils.svg_data import get_pagecolor
from .commands import CommandsExtension
@@ -59,6 +60,7 @@ class Lettering(CommandsExtension):
def effect(self):
metadata = self.get_inkstitch_metadata()
+ background_color = get_pagecolor(self.svg.namedview)
app = wx.App()
frame = SplitSimulatorWindow(
title=_("Ink/Stitch Lettering"),
@@ -66,6 +68,7 @@ class Lettering(CommandsExtension):
group=self.get_or_create_group(),
on_cancel=self.cancel,
metadata=metadata,
+ background_color=background_color,
target_duration=1
)
diff --git a/lib/extensions/params.py b/lib/extensions/params.py
index ea19672f..0a593fdc 100644
--- a/lib/extensions/params.py
+++ b/lib/extensions/params.py
@@ -15,7 +15,6 @@ from secrets import randbelow
import wx
from wx.lib.scrolledpanel import ScrolledPanel
-from .base import InkstitchExtension
from ..commands import is_command, is_command_symbol
from ..elements import (Clone, EmbroideryElement, FillStitch, Polyline,
SatinColumn, Stroke)
@@ -28,7 +27,9 @@ from ..stitch_plan import stitch_groups_to_stitch_plan
from ..svg.tags import SVG_POLYLINE_TAG
from ..utils import get_resource_dir
from ..utils.param import ParamOption
+from ..utils.svg_data import get_pagecolor
from ..utils.threading import ExitThread, check_stop_flag
+from .base import InkstitchExtension
def grouper(iterable_obj, count, fillvalue=None):
@@ -473,10 +474,11 @@ class ParamsTab(ScrolledPanel):
class SettingsPanel(wx.Panel):
- def __init__(self, parent, tabs_factory=None, on_cancel=None, metadata=None, simulator=None):
+ def __init__(self, parent, tabs_factory=None, on_cancel=None, metadata=None, background_color='white', simulator=None):
self.tabs_factory = tabs_factory
self.cancel_hook = on_cancel
self.metadata = metadata
+ self.background_color = background_color
self.simulator = simulator
self.parent = parent
@@ -782,12 +784,14 @@ class Params(InkstitchExtension):
try:
app = wx.App()
metadata = self.get_inkstitch_metadata()
+ background_color = get_pagecolor(self.svg.namedview)
frame = SplitSimulatorWindow(
title=_("Embroidery Params"),
panel_class=SettingsPanel,
tabs_factory=self.create_tabs,
on_cancel=self.cancel,
metadata=metadata,
+ background_color=background_color,
target_duration=5
)
diff --git a/lib/extensions/simulator.py b/lib/extensions/simulator.py
index 3b532bf2..57978b73 100644
--- a/lib/extensions/simulator.py
+++ b/lib/extensions/simulator.py
@@ -3,9 +3,11 @@
# Copyright (c) 2010 Authors
# Licensed under the GNU GPL version 3.0 or later. See the file LICENSE for details.
-from ..api import APIServer
-from ..gui import open_url
+import wx
+from ..gui.simulator import SimulatorWindow
+from ..stitch_plan import stitch_groups_to_stitch_plan
+from ..utils.svg_data import get_pagecolor
from .base import InkstitchExtension
@@ -16,9 +18,23 @@ class Simulator(InkstitchExtension):
def effect(self):
if not self.get_elements():
return
- api_server = APIServer(self)
- port = api_server.start_server()
- electron = open_url("/simulator", port)
- electron.wait()
- api_server.stop()
- api_server.join()
+
+ metadata = self.get_inkstitch_metadata()
+ collapse_len = metadata['collapse_len_mm']
+ min_stitch_len = metadata['min_stitch_len_mm']
+ stitch_groups = self.elements_to_stitch_groups(self.elements)
+ stitch_plan = stitch_groups_to_stitch_plan(stitch_groups, collapse_len=collapse_len, min_stitch_len=min_stitch_len)
+ background_color = get_pagecolor(self.svg.namedview)
+
+ app = wx.App()
+ current_screen = wx.Display.GetFromPoint(wx.GetMousePosition())
+ display = wx.Display(current_screen)
+ screen_rect = display.GetClientArea()
+ height = int(screen_rect[3] * 0.8)
+ simulator = SimulatorWindow(size=(0, height), background_color=background_color)
+ wx.CallLater(100, simulator.Centre)
+ app.SetTopWindow(simulator)
+ simulator.Show()
+ simulator.load(stitch_plan)
+ simulator.go()
+ app.MainLoop()
diff --git a/lib/extensions/tartan.py b/lib/extensions/tartan.py
index a9b34dfb..7f8c582a 100644
--- a/lib/extensions/tartan.py
+++ b/lib/extensions/tartan.py
@@ -13,6 +13,7 @@ from ..gui.simulator import SplitSimulatorWindow
from ..gui.tartan import TartanMainPanel
from ..i18n import _
from ..svg.tags import EMBROIDERABLE_TAGS, INKSTITCH_TARTAN, SVG_GROUP_TAG
+from ..utils.svg_data import get_pagecolor
from .base import InkstitchExtension
@@ -58,6 +59,7 @@ class Tartan(InkstitchExtension):
errormsg(_("To create a tartan pattern please select at least one element with a fill color."))
return
metadata = self.get_inkstitch_metadata()
+ background_color = get_pagecolor(self.svg.namedview)
app = wx.App()
frame = SplitSimulatorWindow(
@@ -66,6 +68,7 @@ class Tartan(InkstitchExtension):
elements=list(self.elements),
on_cancel=self.cancel,
metadata=metadata,
+ background_color=background_color,
target_duration=1
)
diff --git a/lib/gui/__init__.py b/lib/gui/__init__.py
index 4343e4d1..8766b5cc 100644
--- a/lib/gui/__init__.py
+++ b/lib/gui/__init__.py
@@ -6,5 +6,5 @@
from .dialogs import confirm_dialog, info_dialog
from .electron import open_url
from .presets import PresetsPanel
-from .simulator import PreviewRenderer, show_simulator
+from .simulator import PreviewRenderer
from .warnings import WarningPanel
diff --git a/lib/gui/lettering.py b/lib/gui/lettering.py
index edbebb20..57d8a85d 100644
--- a/lib/gui/lettering.py
+++ b/lib/gui/lettering.py
@@ -25,12 +25,13 @@ from . import PresetsPanel, PreviewRenderer, info_dialog
class LetteringPanel(wx.Panel):
DEFAULT_FONT = "small_font"
- def __init__(self, parent, simulator, group, on_cancel=None, metadata=None):
+ def __init__(self, parent, simulator, group, on_cancel=None, metadata=None, background_color='white'):
self.parent = parent
self.simulator = simulator
self.group = group
self.cancel_hook = on_cancel
self.metadata = metadata or dict()
+ self.background_color = background_color
super().__init__(parent, wx.ID_ANY)
diff --git a/lib/gui/simulator.py b/lib/gui/simulator.py
index 423802f8..79f3d95d 100644
--- a/lib/gui/simulator.py
+++ b/lib/gui/simulator.py
@@ -8,14 +8,15 @@ import time
from threading import Event, Thread
import wx
+from numpy import split
from wx.lib.intctrl import IntCtrl
from lib.debug import debug
from lib.utils import get_resource_dir
from lib.utils.settings import global_settings
from lib.utils.threading import ExitThread
+
from ..i18n import _
-from ..stitch_plan import stitch_plan_from_file
from ..svg import PIXELS_PER_MM
# L10N command label at bottom of simulator window
@@ -43,8 +44,8 @@ class ControlPanel(wx.Panel):
wx.Panel.__init__(self, parent, *args, **kwargs)
self.drawing_panel = None
- self.num_stitches = 1
- self.current_stitch = 1
+ self.num_stitches = 0
+ self.current_stitch = 0
self.speed = 1
self.direction = 1
self._last_color_block_end = 0
@@ -98,12 +99,15 @@ class ControlPanel(wx.Panel):
self.slider.Bind(wx.EVT_SLIDER, self.on_slider)
self.stitchBox = IntCtrl(self, -1, value=1, min=1, max=2, limited=True, allow_none=True,
size=((100, -1)), style=wx.TE_PROCESS_ENTER)
+ self.stitchBox.Clear()
self.stitchBox.Bind(wx.EVT_LEFT_DOWN, self.on_stitch_box_focus)
self.stitchBox.Bind(wx.EVT_SET_FOCUS, self.on_stitch_box_focus)
self.stitchBox.Bind(wx.EVT_TEXT_ENTER, self.on_stitch_box_focusout)
self.stitchBox.Bind(wx.EVT_KILL_FOCUS, self.on_stitch_box_focusout)
self.Bind(wx.EVT_LEFT_DOWN, self.on_stitch_box_focusout)
- self.totalstitchText = wx.StaticText(self, -1, label="/ ________")
+ self.totalstitchText = wx.StaticText(self, -1, label="")
+ extent = self.totalstitchText.GetTextExtent("0000000")
+ self.totalstitchText.SetMinSize(extent)
self.btnJump = wx.BitmapToggleButton(self, -1, style=self.button_style)
self.btnJump.SetToolTip(_('Show jump stitches'))
self.btnJump.SetBitmap(self.load_icon('jump'))
@@ -120,6 +124,9 @@ class ControlPanel(wx.Panel):
self.btnColorChange.SetToolTip(_('Show color changes'))
self.btnColorChange.SetBitmap(self.load_icon('color_change'))
self.btnColorChange.Bind(wx.EVT_TOGGLEBUTTON, lambda event: self.on_marker_button('color_change', event))
+ self.btnBackgroundColor = wx.ColourPickerCtrl(self, -1, colour='white', size=((40, -1)))
+ self.btnBackgroundColor.SetToolTip(_("Change background color"))
+ self.btnBackgroundColor.Bind(wx.EVT_COLOURPICKER_CHANGED, self.on_update_background_color)
if self.detach_callback:
self.btnDetachSimulator = wx.BitmapButton(self, -1, style=self.button_style)
self.btnDetachSimulator.SetToolTip(_('Detach/attach simulator window'))
@@ -129,8 +136,10 @@ class ControlPanel(wx.Panel):
# Layout
self.hbSizer1 = wx.BoxSizer(wx.HORIZONTAL)
self.hbSizer1.Add(self.slider, 1, wx.EXPAND | wx.RIGHT, 10)
- self.hbSizer1.Add(self.stitchBox, 0, wx.ALIGN_CENTER | wx.Right, 10)
- self.hbSizer1.Add(self.totalstitchText, 0, wx.ALIGN_CENTER | wx.LEFT, 10)
+ self.hbSizer1.Add(self.stitchBox, 0, wx.ALIGN_TOP | wx.TOP, 25)
+ self.hbSizer1.Add((1, 1), 0, wx.RIGHT, 10)
+ self.hbSizer1.Add(self.totalstitchText, 0, wx.ALIGN_TOP | wx.TOP, 25)
+ self.hbSizer1.Add((1, 1), 0, wx.RIGHT, 10)
self.controls_sizer = wx.StaticBoxSizer(wx.StaticBox(self, wx.ID_ANY, _("Controls")), wx.HORIZONTAL)
self.controls_inner_sizer = wx.BoxSizer(wx.HORIZONTAL)
@@ -147,11 +156,12 @@ class ControlPanel(wx.Panel):
self.show_sizer = wx.StaticBoxSizer(wx.StaticBox(self, wx.ID_ANY, _("Show")), wx.HORIZONTAL)
self.show_inner_sizer = wx.BoxSizer(wx.HORIZONTAL)
- self.show_inner_sizer.Add(self.btnNpp, 0, wx.EXPAND | wx.ALL, 2)
+ self.show_inner_sizer.Add(self.btnNpp, 0, wx.ALL, 2)
self.show_inner_sizer.Add(self.btnJump, 0, wx.ALL, 2)
self.show_inner_sizer.Add(self.btnTrim, 0, wx.ALL, 2)
self.show_inner_sizer.Add(self.btnStop, 0, wx.ALL, 2)
self.show_inner_sizer.Add(self.btnColorChange, 0, wx.ALL, 2)
+ self.show_inner_sizer.Add(self.btnBackgroundColor, 0, wx.EXPAND | wx.ALL, 2)
if self.detach_callback:
self.show_inner_sizer.Add(self.btnDetachSimulator, 0, wx.ALL, 2)
self.show_sizer.Add((1, 1), 1)
@@ -226,7 +236,6 @@ class ControlPanel(wx.Panel):
self.accel_table = wx.AcceleratorTable(self.accel_entries)
self.SetAcceleratorTable(self.accel_table)
- self.SetFocus()
# wait for layouts so that panel size is set
if self.stitch_plan:
@@ -241,27 +250,33 @@ class ControlPanel(wx.Panel):
# otherwise the slider and intctrl get mad
num_stitches = 2
self.num_stitches = num_stitches
+ self.stitchBox.SetValue(1)
self.stitchBox.SetMax(num_stitches)
self.slider.SetMax(num_stitches)
self.totalstitchText.SetLabel(f"/ { num_stitches }")
self.choose_speed()
- def add_color(self, color, num_stitches):
- start = self._last_color_block_end + 1
- self.slider.add_color_section(ColorSection(color.rgb, start, start + num_stitches - 1))
- self._last_color_block_end = self._last_color_block_end + num_stitches
+ def clear(self):
+ self.stitches = []
+ self._set_num_stitches(0)
+ self.slider.clear()
+ self.stitchBox.Clear()
+ self.totalstitchText.SetLabel("")
def load(self, stitch_plan):
+ self.clear()
self.stitches = []
self._set_num_stitches(stitch_plan.num_stitches)
stitch_num = 0
+ last_block_end = 1
for color_block in stitch_plan.color_blocks:
self.stitches.extend(color_block.stitches)
start = stitch_num + 1
- end = start + color_block.num_stitches
- self.slider.add_color_section(color_block.color.rgb, start, end)
+ end = start + color_block.num_stitches - 1
+ self.slider.add_color_section(color_block.color.rgb, last_block_end, end)
+ last_block_end = end
for stitch_num, stitch in enumerate(color_block.stitches, start):
if stitch.trim:
@@ -280,6 +295,16 @@ class ControlPanel(wx.Panel):
def on_marker_button(self, marker_type, event):
self.slider.enable_marker_list(marker_type, event.GetEventObject().GetValue())
+ if marker_type == 'jump':
+ self.drawing_panel.Refresh()
+
+ def on_update_background_color(self, event):
+ self.set_background_color(event.Colour)
+
+ def set_background_color(self, color):
+ self.btnBackgroundColor.SetColour(color)
+ self.drawing_panel.SetBackgroundColour(color)
+ self.drawing_panel.Refresh()
def choose_speed(self):
if self.target_duration:
@@ -539,18 +564,18 @@ class DrawingPanel(wx.Panel):
last_stitch = None
start = time.time()
- for pen, stitches in zip(self.pens, self.stitch_blocks):
+ for pen, stitches, jumps in zip(self.pens, self.stitch_blocks, self.jumps):
canvas.SetPen(pen)
if stitch + len(stitches) < self.current_stitch:
stitch += len(stitches)
if len(stitches) > 1:
- canvas.StrokeLines(stitches)
+ self.draw_stitch_lines(canvas, pen, stitches, jumps)
self.draw_needle_penetration_points(canvas, pen, stitches)
last_stitch = stitches[-1]
else:
stitches = stitches[:self.current_stitch - stitch]
if len(stitches) > 1:
- canvas.StrokeLines(stitches)
+ self.draw_stitch_lines(canvas, pen, stitches, jumps)
self.draw_needle_penetration_points(canvas, pen, stitches)
last_stitch = stitches[-1]
break
@@ -607,6 +632,16 @@ class DrawingPanel(wx.Panel):
canvas.EndLayer()
+ def draw_stitch_lines(self, canvas, pen, stitches, jumps):
+ render_jumps = self.control_panel.btnJump.GetValue()
+ if render_jumps:
+ canvas.StrokeLines(stitches)
+ else:
+ stitch_blocks = split(stitches, jumps)
+ for i, block in enumerate(stitch_blocks):
+ if len(block) > 1:
+ canvas.StrokeLines(block)
+
def draw_needle_penetration_points(self, canvas, pen, stitches):
if self.control_panel.btnNpp.GetValue():
npp_pen = wx.Pen(pen.GetColour(), width=int(0.5 * PIXELS_PER_MM * self.PIXEL_DENSITY))
@@ -669,6 +704,7 @@ class DrawingPanel(wx.Panel):
def parse_stitch_plan(self, stitch_plan):
self.pens = []
self.stitch_blocks = []
+ self.jumps = []
# There is no 0th stitch, so add a place-holder.
self.commands = [None]
@@ -676,6 +712,8 @@ class DrawingPanel(wx.Panel):
for color_block in stitch_plan:
pen = self.color_to_pen(color_block.color)
stitch_block = []
+ jumps = []
+ stitch_index = 0
for stitch in color_block:
# trim any whitespace on the left and top and scale to the
@@ -687,6 +725,7 @@ class DrawingPanel(wx.Panel):
self.commands.append(TRIM)
elif stitch.jump:
self.commands.append(JUMP)
+ jumps.append(stitch_index)
elif stitch.stop:
self.commands.append(STOP)
elif stitch.color_change:
@@ -698,10 +737,16 @@ class DrawingPanel(wx.Panel):
self.pens.append(pen)
self.stitch_blocks.append(stitch_block)
stitch_block = []
+ self.jumps.append(jumps)
+ jumps = []
+ stitch_index = 0
+ else:
+ stitch_index += 1
if stitch_block:
self.pens.append(pen)
self.stitch_blocks.append(stitch_block)
+ self.jumps.append(jumps)
def set_speed(self, speed):
self.speed = speed
@@ -805,11 +850,12 @@ class DrawingPanel(wx.Panel):
class MarkerList(list):
- def __init__(self, icon_name, stitch_numbers=()):
+ def __init__(self, icon_name, offset=0, stitch_numbers=()):
super().__init__(self)
icons_dir = get_resource_dir("icons")
self.icon_name = icon_name
self.icon = wx.Image(os.path.join(icons_dir, f"{icon_name}.png")).ConvertToBitmap()
+ self.offset = offset
self.enabled = False
self.extend(stitch_numbers)
@@ -828,32 +874,32 @@ class ColorSection:
class SimulatorSlider(wx.Panel):
PROXY_EVENTS = (wx.EVT_SLIDER,)
- def __init__(self, parent, id=wx.ID_ANY, minValue=0, maxValue=1, **kwargs):
+ def __init__(self, parent, id=wx.ID_ANY, minValue=1, maxValue=2, **kwargs):
super().__init__(parent, id)
kwargs['style'] = wx.SL_HORIZONTAL | wx.SL_VALUE_LABEL | wx.SL_TOP | wx.ALIGN_TOP
- self._height = self.GetTextExtent("M").y * 4
+ self._height = self.GetTextExtent("M").y * 6
self.SetMinSize((self._height, self._height))
self.marker_lists = {
"trim": MarkerList("trim"),
- "stop": MarkerList("stop"),
- "jump": MarkerList("jump"),
- "color_change": MarkerList("color_change"),
+ "jump": MarkerList("jump", 0.17),
+ "stop": MarkerList("stop", 0.34),
+ "color_change": MarkerList("color_change", 0.34),
}
self.marker_pen = wx.Pen(wx.Colour(0, 0, 0))
self.color_sections = []
self.margin = 15
self.tab_start = 0
- self.tab_width = 0.2
- self.tab_height = 0.2
- self.color_bar_start = 0.3
- self.color_bar_thickness = 0.25
+ self.tab_width = 0.15
+ self.tab_height = 0.15
+ self.color_bar_start = 0.22
+ self.color_bar_thickness = 0.17
self.marker_start = self.color_bar_start
- self.marker_end = 0.75
- self.marker_icon_start = 0.75
- self.marker_icon_size = self._height // 4
+ self.marker_end = 0.5
+ self.marker_icon_start = 0.5
+ self.marker_icon_size = self._height // 6
self._min = minValue
self._max = maxValue
@@ -884,6 +930,16 @@ class SimulatorSlider(wx.Panel):
def GetValue(self):
return self._value
+ def clear(self):
+ self.color_sections = []
+ self._min = 1
+ self._max = 2
+ self._value = 0
+ self._tab_rect = None
+
+ for marker_list in self.marker_lists.values():
+ marker_list.clear()
+
def add_color_section(self, color, start, end):
self.color_sections.append(ColorSection(color, start, end))
@@ -912,6 +968,9 @@ class SimulatorSlider(wx.Panel):
dc.Clear()
gc = wx.GraphicsContext.Create(dc)
+ if self._value < self._min:
+ return
+
width, height = self.GetSize()
min_value = self._min
max_value = self._max
@@ -952,11 +1011,11 @@ class SimulatorSlider(wx.Panel):
x = _value_to_x(value)
gc.StrokeLine(
x, height * self.marker_start,
- x, height * self.marker_end
+ x, height * (self.marker_end + marker_list.offset)
)
gc.DrawBitmap(
marker_list.icon,
- x - self.marker_icon_size / 2, height * self.marker_icon_start,
+ x - self.marker_icon_size / 2, height * (self.marker_icon_start + marker_list.offset),
self.marker_icon_size, self.marker_icon_size
)
@@ -1011,7 +1070,7 @@ class SimulatorSlider(wx.Panel):
class SimulatorPanel(wx.Panel):
""""""
- def __init__(self, parent, stitch_plan=None, target_duration=5, stitches_per_second=16, detach_callback=None):
+ def __init__(self, parent, stitch_plan=None, background_color='white', target_duration=5, stitches_per_second=16, detach_callback=None):
""""""
super().__init__(parent, style=wx.BORDER_SUNKEN)
@@ -1022,6 +1081,7 @@ class SimulatorPanel(wx.Panel):
detach_callback=detach_callback)
self.dp = DrawingPanel(self, stitch_plan=stitch_plan, control_panel=self.cp)
self.cp.set_drawing_panel(self.dp)
+ self.cp.set_background_color(wx.Colour(background_color))
vbSizer = wx.BoxSizer(wx.VERTICAL)
vbSizer.Add(self.dp, 1, wx.EXPAND | wx.ALL, 2)
@@ -1040,10 +1100,12 @@ class SimulatorPanel(wx.Panel):
def clear(self):
self.dp.clear()
+ self.cp.clear()
class SimulatorWindow(wx.Frame):
def __init__(self, panel=None, parent=None, **kwargs):
+ background_color = kwargs.pop('background_color', 'white')
super().__init__(None, title=_("Embroidery Simulation"), **kwargs)
self.SetWindowStyle(wx.FRAME_FLOAT_ON_PARENT | wx.DEFAULT_FRAME_STYLE)
@@ -1062,8 +1124,8 @@ class SimulatorWindow(wx.Frame):
self.panel.Show()
else:
self.is_child = False
- self.simulator_panel = SimulatorPanel(self)
- self.sizer.Add(self.simulator_panel, 1, wx.EXPAND)
+ self.panel = SimulatorPanel(self, background_color=background_color)
+ self.sizer.Add(self.panel, 1, wx.EXPAND)
self.SetSizer(self.sizer)
self.Layout()
@@ -1072,6 +1134,8 @@ class SimulatorWindow(wx.Frame):
if self.is_child:
self.Bind(wx.EVT_CLOSE, self.on_close)
+ else:
+ self.Maximize()
def detach_simulator_panel(self):
self.sizer.Detach(self.panel)
@@ -1079,6 +1143,12 @@ class SimulatorWindow(wx.Frame):
def on_close(self, event):
self.parent.attach_simulator()
+ def load(self, stitch_plan):
+ self.panel.load(stitch_plan)
+
+ def go(self):
+ self.panel.go()
+
class SplitSimulatorWindow(wx.Frame):
def __init__(self, panel_class, title, target_duration=None, **kwargs):
@@ -1088,7 +1158,13 @@ class SplitSimulatorWindow(wx.Frame):
self.detached_simulator_frame = None
self.splitter = wx.SplitterWindow(self, style=wx.SP_LIVE_UPDATE)
- self.simulator_panel = SimulatorPanel(self.splitter, target_duration=target_duration, detach_callback=self.toggle_detach_simulator)
+ background_color = kwargs.pop('background_color', 'white')
+ self.simulator_panel = SimulatorPanel(
+ self.splitter,
+ background_color=background_color,
+ target_duration=target_duration,
+ detach_callback=self.toggle_detach_simulator
+ )
self.settings_panel = panel_class(self.splitter, simulator=self.simulator_panel, **kwargs)
self.splitter.SplitVertically(self.settings_panel, self.simulator_panel)
@@ -1105,6 +1181,7 @@ class SplitSimulatorWindow(wx.Frame):
self.SetMinSize(self.sizer.CalcMin())
+ self.simulator_panel.SetFocus()
self.Maximize()
self.Show()
wx.CallLater(100, self.set_sash_position)
@@ -1146,7 +1223,7 @@ class SplitSimulatorWindow(wx.Frame):
self.detached_simulator_frame = None
self.Maximize()
self.splitter.UpdateSize()
- self.SetFocus()
+ self.simulator_panel.SetFocus()
self.Raise()
wx.CallLater(100, self.set_sash_position)
global_settings['pop_out_simulator'] = False
@@ -1228,26 +1305,3 @@ class PreviewRenderer(Thread):
except: # noqa: E722
import traceback
debug.log("unhandled exception in PreviewRenderer.render_stitch_plan(): " + traceback.format_exc())
-
-
-def show_simulator(stitch_plan):
- app = wx.App()
- current_screen = wx.Display.GetFromPoint(wx.GetMousePosition())
- display = wx.Display(current_screen)
- screen_rect = display.GetClientArea()
-
- simulator_pos = (screen_rect[0], screen_rect[1])
-
- # subtract 1 because otherwise the window becomes maximized on Linux
- width = screen_rect[2] - 1
- height = screen_rect[3] - 1
-
- frame = SimulatorWindow(pos=simulator_pos, size=(width, height), stitch_plan=stitch_plan)
- app.SetTopWindow(frame)
- frame.Show()
- app.MainLoop()
-
-
-if __name__ == "__main__":
- stitch_plan = stitch_plan_from_file(sys.argv[1])
- show_simulator(stitch_plan)
diff --git a/lib/gui/tartan/main_panel.py b/lib/gui/tartan/main_panel.py
index 238c8901..82638170 100644
--- a/lib/gui/tartan/main_panel.py
+++ b/lib/gui/tartan/main_panel.py
@@ -26,13 +26,14 @@ from . import CodePanel, CustomizePanel, EmbroideryPanel, HelpPanel
class TartanMainPanel(wx.Panel):
- def __init__(self, parent, simulator, elements, on_cancel=None, metadata=None, output_groups=inkex.Group()):
+ def __init__(self, parent, simulator, elements, on_cancel=None, metadata=None, background_color='white', output_groups=inkex.Group()):
self.parent = parent
self.simulator = simulator
self.elements = elements
self.cancel_hook = on_cancel
self.palette = Palette()
self.metadata = metadata or dict()
+ self.background_color = background_color
self.output_groups = output_groups
super().__init__(parent, wx.ID_ANY)
diff --git a/lib/utils/svg_data.py b/lib/utils/svg_data.py
new file mode 100644
index 00000000..c3ef672a
--- /dev/null
+++ b/lib/utils/svg_data.py
@@ -0,0 +1,10 @@
+# Authors: see git history
+#
+# Copyright (c) 2024 Authors
+# Licensed under the GNU GPL version 3.0 or later. See the file LICENSE for details.
+
+def get_pagecolor(namedview, default_color='white'):
+ pagecolor = default_color
+ if namedview is not None:
+ pagecolor = namedview.get('pagecolor', pagecolor)
+ return pagecolor
diff --git a/templates/simulator.xml b/templates/simulator.xml
index 813b09b5..94568c3f 100644
--- a/templates/simulator.xml
+++ b/templates/simulator.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<inkscape-extension translationdomain="inkstitch" xmlns="http://www.inkscape.org/namespace/inkscape/extension">
- <name>Simulator / Realistic Preview</name>
+ <name>Simulator</name>
<id>org.{{ id_inkstitch }}.simulator</id>
<param name="extension" type="string" gui-hidden="true">simulator</param>
<effect implements-custom-gui="true">