From 1ff677722844e311f64d10079ff96b725b00676e Mon Sep 17 00:00:00 2001 From: Kaalleen <36401965+kaalleen@users.noreply.github.com> Date: Sun, 15 Oct 2023 07:18:26 +0200 Subject: Add element info extension (#2544) --- lib/extensions/__init__.py | 2 + lib/extensions/element_info.py | 164 +++++++++++++++++++++++++++++++++++++++++ lib/gui/element_info.py | 93 +++++++++++++++++++++++ lib/stitch_plan/color_block.py | 6 ++ lib/stitch_plan/stitch_plan.py | 4 + 5 files changed, 269 insertions(+) create mode 100644 lib/extensions/element_info.py create mode 100644 lib/gui/element_info.py (limited to 'lib') diff --git a/lib/extensions/__init__.py b/lib/extensions/__init__.py index 7987a717..79759c6d 100644 --- a/lib/extensions/__init__.py +++ b/lib/extensions/__init__.py @@ -17,6 +17,7 @@ from .cut_satin import CutSatin from .cutwork_segmentation import CutworkSegmentation from .density_map import DensityMap from .duplicate_params import DuplicateParams +from .element_info import ElementInfo from .fill_to_stroke import FillToStroke from .flip import Flip from .generate_palette import GeneratePalette @@ -70,6 +71,7 @@ __all__ = extensions = [ApplyThreadlist, CutworkSegmentation, DensityMap, DuplicateParams, + ElementInfo, FillToStroke, Flip, GeneratePalette, diff --git a/lib/extensions/element_info.py b/lib/extensions/element_info.py new file mode 100644 index 00000000..a086108b --- /dev/null +++ b/lib/extensions/element_info.py @@ -0,0 +1,164 @@ +# Authors: see git history +# +# Copyright (c) 2023 Authors +# Licensed under the GNU GPL version 3.0 or later. See the file LICENSE for details. + +from ..elements import FillStitch, SatinColumn, Stroke +from ..gui.element_info import ElementInfoApp +from ..i18n import _ +from ..stitch_plan import stitch_groups_to_stitch_plan +from ..svg import PIXELS_PER_MM +from .base import InkstitchExtension + + +class ElementInfo(InkstitchExtension): + + def effect(self): + if not self.svg.selection or not self.get_elements(): + return + + self.metadata = self.get_inkstitch_metadata() + self.list_items = [] + + self._general_info() + + previous_stitch_group = None + for element in self.elements: + previous_stitch_group = self._element_info(element, previous_stitch_group) + + app = ElementInfoApp(self.list_items) + app.MainLoop() + + def _element_info(self, element, previous_stitch_group): + stitch_groups = element.to_stitch_groups(previous_stitch_group) + stitch_plan = stitch_groups_to_stitch_plan( + stitch_groups, + collapse_len=self.metadata['collapse_len'], + min_stitch_len=self.metadata['min_stitch_len'] + ) + + self.list_items.append(ListItem( + name=f"{ element.node.label } ({ element.node.get_id() })", + headline=True + )) + self.list_items.append(ListItem( + name=_("Type"), + value=element.element_name + )) + if isinstance(element, FillStitch): + fill_method = next((method.name for method in element._fill_methods if method.id == element.fill_method), "") + self.list_items.append(ListItem( + name=_("Fill Method"), + value=fill_method + )) + + if isinstance(element, SatinColumn): + satin_method = next((method.name for method in element._satin_methods if method.id == element.satin_method), "") + self.list_items.append(ListItem( + name=_("Satin Method"), + value=satin_method + )) + + if isinstance(element, Stroke): + stroke_method = next((method.name for method in element._stroke_methods if method.id == element.stroke_method), "") + self.list_items.append(ListItem( + name=_("Satin Method"), + value=stroke_method + )) + + self.list_items.append(ListItem( + name=_("Dimensions (mm)"), + value="{:.2f} x {:.2f}".format(stitch_plan.dimensions_mm[0], stitch_plan.dimensions_mm[1]) + )) + + stitch_lengths = [] + for group in stitch_groups: + stitches = group.stitches + + previous_stitch = stitches[0] + for stitch in stitches[1:]: + st = stitch - previous_stitch + length = st.length() / PIXELS_PER_MM + if length <= self.metadata['min_stitch_len_mm']: + continue + stitch_lengths.append(length) + previous_stitch = stitch + + stitches_per_group = "" + if len(stitch_groups) > 1: + stitches_per_group = f" ({', '.join([str(len(group.stitches)) for group in stitch_groups]) })" + + self.list_items.append(ListItem( + name=_("Stitches"), + value=str(stitch_plan.num_stitches - stitch_plan.num_jumps) + stitches_per_group + )) + self.list_items.append(ListItem( + name=_("Jumps"), + value=str(stitch_plan.num_jumps - 1) + )) + self.list_items.append(ListItem( + name=_("Max stitch length"), + value="{:.2f}".format(max(stitch_lengths)) + )) + self.list_items.append(ListItem( + name=_("Min stitch length"), + value="{:.2f}".format(min(stitch_lengths)) + )) + self.list_items.append(ListItem()) + return stitch_groups[0] + + def _general_info(self): + stitch_groups = self.elements_to_stitch_groups(self.elements) + stitch_plan = stitch_groups_to_stitch_plan( + stitch_groups, + collapse_len=self.metadata['collapse_len'], + min_stitch_len=self.metadata['min_stitch_len'] + ) + + self.list_items.append(ListItem( + name=_("All Selected Elements"), + headline=True + )) + self.list_items.append(ListItem( + name=_("Dimensions (mm)"), + value="{:.2f} x {:.2f}".format(stitch_plan.dimensions_mm[0], stitch_plan.dimensions_mm[1]) + )) + self.list_items.append(ListItem( + name=_("Colors"), + value=str(stitch_plan.num_colors) + )) + self.list_items.append(ListItem( + name=_("Color Changes"), + value=str(stitch_plan.num_color_blocks - 1) + )) + self.list_items.append(ListItem( + name=_("Jumps"), + value=str(stitch_plan.num_jumps) + )) + self.list_items.append(ListItem( + name=_("Trims"), + value=str(stitch_plan.num_trims) + )) + self.list_items.append(ListItem( + name=_("Stops"), + value=str(stitch_plan.num_stops) + )) + self.list_items.append(ListItem( + name=_("Stitches"), + value=str(stitch_plan.num_stitches - stitch_plan.num_jumps) + )) + self.list_items.append(ListItem( + name=_("Filter stitches smaller than (mm)"), + value=str(self.metadata['min_stitch_len_mm']) + )) + self.list_items.append(ListItem()) + + +class ListItem: + def __init__(self, name="", value="", headline=False): + self.name: str = name + self.value: str = value + self.headline: bool = headline + + def __repr__(self): + return "ListItem(%s, %s, %s)" % (self.name, self.value, self.headline) diff --git a/lib/gui/element_info.py b/lib/gui/element_info.py new file mode 100644 index 00000000..dd339081 --- /dev/null +++ b/lib/gui/element_info.py @@ -0,0 +1,93 @@ +# 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 wx +import wx.adv + +from ..i18n import _ + + +class ElementInfoFrame(wx.Frame): + + def __init__(self, *args, **kwargs): + self.list_items = kwargs.pop("list_items") + self.index = 0 + wx.Frame.__init__(self, None, wx.ID_ANY, _("Element Info"), *args, **kwargs) + + self.main_panel = wx.Panel(self, wx.ID_ANY) + + notebook_sizer = wx.BoxSizer(wx.VERTICAL) + self.notebook = wx.Notebook(self.main_panel, wx.ID_ANY) + notebook_sizer.Add(self.notebook, 1, wx.EXPAND, 0) + + self.info = wx.Panel(self.notebook, wx.ID_ANY) + self.notebook.AddPage(self.info, _("Info")) + + info_sizer = wx.BoxSizer(wx.VERTICAL) + + self.info_list = wx.ListCtrl(self.info, wx.ID_ANY, style=wx.BORDER_SUNKEN | wx.LC_HRULES | wx.LC_REPORT | wx.LC_VRULES) + self.info_list.AppendColumn(_("Name"), format=wx.LIST_FORMAT_LEFT) + self.info_list.AppendColumn(_("Value"), format=wx.LIST_FORMAT_LEFT) + self._fill_info_list() + self.info_list.SetColumnWidth(0, -1) + self.info_list.SetColumnWidth(1, -1) + info_sizer.Add(self.info_list, 1, wx.ALL | wx.EXPAND, 10) + + self.help = wx.Panel(self.notebook, wx.ID_ANY) + self.notebook.AddPage(self.help, _("Help")) + + help_sizer = wx.BoxSizer(wx.VERTICAL) + + help_text = wx.StaticText( + self.help, + wx.ID_ANY, + _("This extension informs about various parameters of selected stitch elements."), + style=wx.ALIGN_LEFT + ) + help_text.Wrap(500) + help_sizer.Add(help_text, 0, wx.ALL, 8) + + help_sizer.Add((20, 20), 0, 0, 0) + + website_info = wx.StaticText(self.help, wx.ID_ANY, _("More information on our website:")) + help_sizer.Add(website_info, 0, wx.ALL, 8) + + self.website_link = wx.adv.HyperlinkCtrl( + self.help, + wx.ID_ANY, + _("https://inkstitch.org/troubleshoot#element-info"), + _("https://inkstitch.org/troubleshoot#element-info") + ) + help_sizer.Add(self.website_link, 0, wx.ALL, 8) + + self.help.SetSizer(help_sizer) + self.info.SetSizer(info_sizer) + self.main_panel.SetSizer(notebook_sizer) + + self.SetSizeHints(notebook_sizer.CalcMin()) + + self.Layout() + + def _fill_info_list(self): + for item in self.list_items: + self.info_list.InsertItem(self.index, item.name) + if item.headline: + self.info_list.SetItemBackgroundColour(self.index, "black") + self.info_list.SetItemTextColour(self.index, "white") + else: + self.info_list.SetItem(self.index, 1, item.value) + self.index += 1 + + +class ElementInfoApp(wx.App): + def __init__(self, list_items): + self.list_items = list_items + super().__init__() + + def OnInit(self): + self.frame = ElementInfoFrame(list_items=self.list_items) + self.SetTopWindow(self.frame) + self.frame.Show() + return True diff --git a/lib/stitch_plan/color_block.py b/lib/stitch_plan/color_block.py index fdef5eb8..a0f9d43f 100644 --- a/lib/stitch_plan/color_block.py +++ b/lib/stitch_plan/color_block.py @@ -79,6 +79,12 @@ class ColorBlock(object): return sum(1 for stitch in self if stitch.trim) + @property + def num_jumps(self): + """Number of jumps in this color block.""" + + return sum(1 for stitch in self if stitch.jump) + @property def stop_after(self): if self.last_stitch is not None: diff --git a/lib/stitch_plan/stitch_plan.py b/lib/stitch_plan/stitch_plan.py index 68d52699..8067749a 100644 --- a/lib/stitch_plan/stitch_plan.py +++ b/lib/stitch_plan/stitch_plan.py @@ -172,6 +172,10 @@ class StitchPlan(object): def num_stitches(self): return sum(block.num_stitches for block in self) + @property + def num_jumps(self): + return sum(block.num_jumps for block in self) + @property def bounding_box(self): color_block_bounding_boxes = [cb.bounding_box for cb in self] -- cgit v1.2.3