kopia lustrzana https://github.com/inkstitch/inkstitch
272 wiersze
11 KiB
Python
272 wiersze
11 KiB
Python
# Authors: see git history
|
|
#
|
|
# Copyright (c) 2023 Authors
|
|
# Licensed under the GNU GPL version 3.0 or later. See the file LICENSE for details.
|
|
|
|
import json
|
|
from copy import copy
|
|
|
|
import inkex
|
|
import wx
|
|
import wx.adv
|
|
|
|
from ...elements import FillStitch, nodes_to_elements
|
|
from ...exceptions import InkstitchException, format_uncaught_exception
|
|
from ...i18n import _
|
|
from ...stitch_plan import stitch_groups_to_stitch_plan
|
|
from ...svg.tags import INKSTITCH_TARTAN
|
|
from ...tartan.fill_element import prepare_tartan_fill_element
|
|
from ...tartan.palette import Palette
|
|
from ...tartan.svg import TartanSvgGroup
|
|
from ...utils import DotDict
|
|
from ...utils.threading import ExitThread, check_stop_flag
|
|
from .. import PresetsPanel, PreviewRenderer, WarningPanel
|
|
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()):
|
|
self.parent = parent
|
|
self.simulator = simulator
|
|
self.elements = elements
|
|
self.cancel_hook = on_cancel
|
|
self.palette = Palette()
|
|
self.metadata = metadata or dict()
|
|
self.output_groups = output_groups
|
|
|
|
super().__init__(parent, wx.ID_ANY)
|
|
|
|
self.SetWindowStyle(wx.FRAME_FLOAT_ON_PARENT | wx.DEFAULT_FRAME_STYLE)
|
|
|
|
# preview
|
|
self.preview_renderer = PreviewRenderer(self.render_stitch_plan, self.on_stitch_plan_rendered)
|
|
self.presets_panel = PresetsPanel(self)
|
|
# warnings
|
|
self.warning_panel = WarningPanel(self)
|
|
self.warning_panel.Hide()
|
|
# notebook
|
|
self.notebook_sizer = wx.BoxSizer(wx.VERTICAL)
|
|
self.notebook = wx.Notebook(self, wx.ID_ANY)
|
|
self.notebook_sizer.Add(self.warning_panel, 0, wx.EXPAND | wx.ALL, 10)
|
|
self.notebook_sizer.Add(self.notebook, 1, wx.EXPAND, 0)
|
|
# customize
|
|
self.customize_panel = CustomizePanel(self.notebook, self)
|
|
self.notebook.AddPage(self.customize_panel, _('Customize'))
|
|
self.customize_panel.SetupScrolling() # scroll_x=False)
|
|
# code
|
|
self.code_panel = CodePanel(self.notebook, self)
|
|
self.notebook.AddPage(self.code_panel, _("Palette Code"))
|
|
# embroidery settings
|
|
self.embroidery_panel = EmbroideryPanel(self.notebook, self)
|
|
self.notebook.AddPage(self.embroidery_panel, _("Embroidery Settings"))
|
|
# help
|
|
help_panel = HelpPanel(self.notebook)
|
|
self.notebook.AddPage(help_panel, _("Help"))
|
|
# apply and cancel buttons
|
|
apply_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
|
self.cancel_button = wx.Button(self, label=_("Cancel"))
|
|
self.cancel_button.Bind(wx.EVT_BUTTON, self.cancel)
|
|
self.apply_button = wx.Button(self, label=_("Apply"))
|
|
self.apply_button.Bind(wx.EVT_BUTTON, self.apply)
|
|
apply_sizer.Add(self.cancel_button, 0, wx.RIGHT | wx.BOTTOM, 5)
|
|
apply_sizer.Add(self.apply_button, 0, wx.RIGHT | wx.BOTTOM, 10)
|
|
|
|
self.notebook_sizer.Add(self.presets_panel, 0, wx.EXPAND | wx.ALL, 10)
|
|
self.notebook_sizer.Add(apply_sizer, 0, wx.ALIGN_RIGHT | wx.ALL, 10)
|
|
|
|
self.SetSizer(self.notebook_sizer)
|
|
|
|
self.load_settings()
|
|
self.apply_settings()
|
|
|
|
self.Layout()
|
|
self.SetMinSize(self.notebook_sizer.CalcMin())
|
|
|
|
def update_from_code(self):
|
|
self.palette.update_from_code(self.code_panel.threadcount_text.GetValue())
|
|
self.customize_panel.symmetry_checkbox.SetValue(self.palette.symmetry)
|
|
self.customize_panel.warp_weft_checkbox.SetValue(self.palette.equal_warp_weft)
|
|
self.code_panel.threadcount_text.SetValue(self.palette.palette_code)
|
|
self.customize_panel.update_stripes(self.palette.palette_stripes)
|
|
self.customize_panel.update_symmetry()
|
|
self.customize_panel.update_warp_weft()
|
|
self.customize_panel.FitInside()
|
|
self.update_preview()
|
|
|
|
def update_from_stripes(self):
|
|
sizers = [self.customize_panel.warp_sizer]
|
|
if not self.customize_panel.warp_weft_checkbox.GetValue():
|
|
sizers.append(self.customize_panel.weft_sizer)
|
|
self.palette.update_from_stripe_sizer(
|
|
sizers,
|
|
self.customize_panel.symmetry_checkbox.GetValue(),
|
|
self.customize_panel.warp_weft_checkbox.GetValue()
|
|
)
|
|
self.update_code_text()
|
|
self.update_preview()
|
|
|
|
def update_code_text(self):
|
|
self.code_panel.threadcount_text.SetValue(self.palette.palette_code)
|
|
self.settings['palette'] = self.palette.palette_code
|
|
|
|
def load_settings(self):
|
|
"""Load the settings saved into the SVG element"""
|
|
self.settings = DotDict({
|
|
"symmetry": True,
|
|
"equal_warp_weft": True,
|
|
"rotate": 0.0,
|
|
"scale": 100,
|
|
"offset_x": 0.0,
|
|
"offset_y": 0.0,
|
|
"palette": "K/10 W/?10",
|
|
"output": "embroidery",
|
|
"stitch_type": "legacy_fill",
|
|
"row_spacing": 1.0,
|
|
"angle_warp": 0.0,
|
|
"angle_weft": 90.0,
|
|
"min_stripe_width": 1.0,
|
|
"bean_stitch_repeats": 0
|
|
})
|
|
|
|
try:
|
|
self.settings.update(json.loads(self.elements[0].get(INKSTITCH_TARTAN)))
|
|
except (TypeError, ValueError, IndexError):
|
|
pass
|
|
|
|
def apply_settings(self):
|
|
"""Make the settings in self.settings visible in the UI."""
|
|
self.customize_panel.rotate.SetValue(self.settings.rotate)
|
|
self.customize_panel.scale.SetValue(int(self.settings.scale))
|
|
self.customize_panel.offset_x.SetValue(self.settings.offset_x)
|
|
self.customize_panel.offset_y.SetValue(self.settings.offset_y)
|
|
self.code_panel.threadcount_text.SetValue(self.settings.palette)
|
|
self.embroidery_panel.set_output(self.settings.output)
|
|
self.embroidery_panel.set_stitch_type(self.settings.stitch_type)
|
|
self.embroidery_panel.svg_row_spacing.SetValue(self.settings.row_spacing)
|
|
self.embroidery_panel.angle_warp.SetValue(self.settings.angle_warp)
|
|
self.embroidery_panel.angle_weft.SetValue(self.settings.angle_weft)
|
|
self.embroidery_panel.min_stripe_width.SetValue(self.settings.min_stripe_width)
|
|
self.embroidery_panel.svg_bean_stitch_repeats.SetValue(self.settings.bean_stitch_repeats)
|
|
self.embroidery_panel.stitch_angle.SetValue(self.elements[0].get('inkstitch:tartan_angle', -45))
|
|
self.embroidery_panel.rows_per_thread.SetValue(self.elements[0].get('inkstitch:rows_per_thread', 2))
|
|
self.embroidery_panel.row_spacing.SetValue(self.elements[0].get('inkstitch:row_spacing_mm', 0.25))
|
|
underlay = self.elements[0].get('inkstitch:fill_underlay', "True").lower() in ('yes', 'y', 'true', 't', '1')
|
|
self.embroidery_panel.underlay.SetValue(underlay)
|
|
self.embroidery_panel.herringbone.SetValue(self.elements[0].get('inkstitch:herringbone_width_mm', 0))
|
|
self.embroidery_panel.bean_stitch_repeats.SetValue(self.elements[0].get('inkstitch:bean_stitch_repeats', '0'))
|
|
|
|
self.update_from_code()
|
|
|
|
self.customize_panel.symmetry_checkbox.SetValue(bool(self.settings.symmetry))
|
|
self.palette.update_symmetry(self.settings.symmetry)
|
|
self.customize_panel.warp_weft_checkbox.SetValue(bool(self.settings.equal_warp_weft))
|
|
self.customize_panel.update_warp_weft()
|
|
|
|
def save_settings(self):
|
|
"""Save the settings into the SVG elements."""
|
|
for element in self.elements:
|
|
element.set(INKSTITCH_TARTAN, json.dumps(self.settings))
|
|
|
|
def get_preset_data(self):
|
|
# called by self.presets_panel
|
|
settings = dict(self.settings)
|
|
return settings
|
|
|
|
def _hide_warning(self):
|
|
self.warning_panel.clear()
|
|
self.warning_panel.Hide()
|
|
self.Layout()
|
|
|
|
def _show_warning(self, warning_text):
|
|
self.warning_panel.set_warning_text(warning_text)
|
|
self.warning_panel.Show()
|
|
self.Layout()
|
|
|
|
def update_preview(self, event=None):
|
|
self.preview_renderer.update()
|
|
|
|
def apply_preset_data(self, preset_data):
|
|
settings = DotDict(preset_data)
|
|
self.settings = settings
|
|
self.apply_settings()
|
|
|
|
def get_preset_suite_name(self):
|
|
# called by self.presets_panel
|
|
return "tartan"
|
|
|
|
def close(self):
|
|
self.GetTopLevelParent().Close()
|
|
|
|
def cancel(self, event):
|
|
if self.cancel_hook:
|
|
self.cancel_hook()
|
|
self.close()
|
|
|
|
def apply(self, event):
|
|
self.update_tartan()
|
|
self.save_settings()
|
|
self.close()
|
|
|
|
def render_stitch_plan(self):
|
|
if self.settings['output'] == 'svg':
|
|
self.update_tartan()
|
|
stitch_groups = self._get_svg_stitch_groups()
|
|
else:
|
|
self.save_settings()
|
|
stitch_groups = []
|
|
previous_stitch_group = None
|
|
for element in self.elements:
|
|
try:
|
|
# copy the embroidery element to drop the cache
|
|
stitch_groups.extend(copy(FillStitch(element)).embroider(previous_stitch_group))
|
|
if stitch_groups:
|
|
previous_stitch_group = stitch_groups[-1]
|
|
except (SystemExit, ExitThread):
|
|
raise
|
|
except InkstitchException as exc:
|
|
wx.CallAfter(self._show_warning, str(exc))
|
|
except Exception:
|
|
wx.CallAfter(self._show_warning, format_uncaught_exception())
|
|
|
|
if stitch_groups:
|
|
return stitch_groups_to_stitch_plan(
|
|
stitch_groups,
|
|
collapse_len=self.metadata['collapse_len_mm'],
|
|
min_stitch_len=self.metadata['min_stitch_len_mm']
|
|
)
|
|
|
|
def _get_svg_stitch_groups(self):
|
|
stitch_groups = []
|
|
previous_stitch_group = None
|
|
for element in self.elements:
|
|
parent = element.getparent()
|
|
embroidery_elements = nodes_to_elements(parent.iterdescendants())
|
|
for embroidery_element in embroidery_elements:
|
|
check_stop_flag()
|
|
if embroidery_element.node == element:
|
|
continue
|
|
try:
|
|
# copy the embroidery element to drop the cache
|
|
stitch_groups.extend(copy(embroidery_element).embroider(previous_stitch_group))
|
|
if stitch_groups:
|
|
previous_stitch_group = stitch_groups[-1]
|
|
except InkstitchException:
|
|
pass
|
|
except Exception:
|
|
pass
|
|
return stitch_groups
|
|
|
|
def update_tartan(self):
|
|
for element in self.elements:
|
|
check_stop_flag()
|
|
if self.settings['output'] == 'svg':
|
|
TartanSvgGroup(self.settings).generate(element)
|
|
else:
|
|
prepare_tartan_fill_element(element)
|
|
|
|
def on_stitch_plan_rendered(self, stitch_plan):
|
|
self.simulator.stop()
|
|
self.simulator.load(stitch_plan)
|
|
self.simulator.go()
|