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: Kaalleen
pull/2878/head
Lex Neva 2024-04-30 14:21:32 -04:00 zatwierdzone przez GitHub
rodzic 7af665806a
commit 3f0f04abec
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: B5690EEEBB952194
16 zmienionych plików z 166 dodań i 285 usunięć

Wyświetl plik

@ -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

Wyświetl plik

@ -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)

Wyświetl plik

@ -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)

Wyświetl plik

@ -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

Wyświetl plik

@ -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__)

Wyświetl plik

@ -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

Wyświetl plik

@ -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
)

Wyświetl plik

@ -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
)

Wyświetl plik

@ -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()

Wyświetl plik

@ -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
)

Wyświetl plik

@ -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

Wyświetl plik

@ -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)

Wyświetl plik

@ -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)

Wyświetl plik

@ -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)

Wyświetl plik

@ -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

Wyświetl plik

@ -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">