kopia lustrzana https://github.com/inkstitch/inkstitch
simulator fixes (#2844)
* fix slide and control panel rendering bugs * clear marker lists when clearing stitch plan * switch simulator back to wx * remove unused function * fix off-by-one error in color bar * avoid overlapping command symbols of different types * don't maximize simulator * adjust alignment * remove unused API server * bugfix * focus entire simulator panel * rename simulator/realistic preview -> simulator * experimental: background color picker * set pagecolor to background color by default * satisfy macos * toggle jumps on drawing canvas * clear frog family --------- Co-authored-by: Kaalleenpull/2878/head
rodzic
7af665806a
commit
3f0f04abec
|
@ -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
|
|
@ -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)
|
|
@ -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)
|
|
@ -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
|
|
@ -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__)
|
|
@ -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
|
|
@ -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
|
||||
)
|
||||
|
||||
|
|
|
@ -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
|
||||
)
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
|
@ -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">
|
||||
|
|
Ładowanie…
Reference in New Issue