summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLex Neva <lexelby@users.noreply.github.com>2018-09-29 13:22:05 -0600
committerGitHub <noreply@github.com>2018-09-29 13:22:05 -0600
commit5139c13fd7f43bb4d829e3c8225c51268e4d27d1 (patch)
tree8d89cf6fc362aa261baf9f63d68653c50fe72aa5
parentee7ed8f679dd493bd8991ede51bc741c47567b51 (diff)
parent5e9cb83a9375a2b6312f390e84e4ac8e778d04cb (diff)
Merge pull request #316 from inkstitch/lexelby/bug-fixes
more bug fixes
-rw-r--r--Makefile2
-rw-r--r--lib/extensions/params.py127
-rw-r--r--lib/stitch_plan/stitch.py28
-rw-r--r--lib/stitch_plan/ties.py34
-rw-r--r--lib/svg/units.py5
5 files changed, 108 insertions, 88 deletions
diff --git a/Makefile b/Makefile
index 79d930a8..9d806509 100644
--- a/Makefile
+++ b/Makefile
@@ -60,4 +60,4 @@ locales:
.PHONY: style
style:
- flake8 . --count --max-complexity=10 --max-line-length=150 --statistics --exclude=pyembroidery,__init__.py,simulator.py,params.py
+ flake8 . --count --max-complexity=10 --max-line-length=150 --statistics --exclude=pyembroidery,__init__.py
diff --git a/lib/extensions/params.py b/lib/extensions/params.py
index b1db1b31..1f3032ca 100644
--- a/lib/extensions/params.py
+++ b/lib/extensions/params.py
@@ -4,20 +4,18 @@ import os
import sys
import json
import traceback
-import time
from threading import Thread, Event
from copy import copy
import wx
from wx.lib.scrolledpanel import ScrolledPanel
from collections import defaultdict
-from functools import partial
from itertools import groupby
from .base import InkstitchExtension
from ..i18n import _
from ..stitch_plan import patches_to_stitch_plan
from ..elements import EmbroideryElement, Fill, AutoFill, Stroke, SatinColumn
-from ..utils import save_stderr, restore_stderr, get_resource_dir
+from ..utils import get_resource_dir
from ..simulator import EmbroiderySimulator
from ..commands import is_command
@@ -39,7 +37,7 @@ def load_presets():
with open(presets_path(), 'r') as presets:
presets = json.load(presets)
return presets
- except:
+ except IOError:
return {}
@@ -64,14 +62,14 @@ def delete_preset(name):
save_presets(presets)
-def confirm_dialog(parent, question, caption = 'ink/stitch'):
+def confirm_dialog(parent, question, caption='ink/stitch'):
dlg = wx.MessageDialog(parent, question, caption, wx.YES_NO | wx.ICON_QUESTION)
result = dlg.ShowModal() == wx.ID_YES
dlg.Destroy()
return result
-def info_dialog(parent, message, caption = 'ink/stitch'):
+def info_dialog(parent, message, caption='ink/stitch'):
dlg = wx.MessageDialog(parent, message, caption, wx.OK | wx.ICON_INFORMATION)
dlg.ShowModal()
dlg.Destroy()
@@ -298,12 +296,12 @@ class ParamsTab(ScrolledPanel):
self.description.SetLabel(self.description_text)
self.description_container = box
self.Bind(wx.EVT_SIZE, self.resized)
- sizer.Add(self.description, proportion=0, flag=wx.EXPAND|wx.ALL, border=5)
+ sizer.Add(self.description, proportion=0, flag=wx.EXPAND | wx.ALL, border=5)
box.Add(sizer, proportion=0, flag=wx.ALL, border=5)
if self.toggle:
toggle_sizer = wx.BoxSizer(wx.HORIZONTAL)
- toggle_sizer.Add(self.create_change_indicator(self.toggle.name), proportion = 0, flag=wx.ALIGN_CENTER_VERTICAL|wx.RIGHT, border=5)
+ toggle_sizer.Add(self.create_change_indicator(self.toggle.name), proportion=0, flag=wx.ALIGN_CENTER_VERTICAL | wx.RIGHT, border=5)
toggle_sizer.Add(self.toggle_checkbox, proportion=0, flag=wx.ALIGN_CENTER_VERTICAL)
box.Add(toggle_sizer, proportion=0, flag=wx.BOTTOM, border=10)
@@ -312,7 +310,7 @@ class ParamsTab(ScrolledPanel):
description = wx.StaticText(self, label=param.description)
description.SetToolTip(param.tooltip)
- self.settings_grid.Add(description, proportion=1, flag=wx.EXPAND|wx.RIGHT|wx.ALIGN_CENTER_VERTICAL|wx.TOP, border=5)
+ self.settings_grid.Add(description, proportion=1, flag=wx.EXPAND | wx.RIGHT | wx.ALIGN_CENTER_VERTICAL | wx.TOP, border=5)
if param.type == 'boolean':
@@ -336,7 +334,7 @@ class ParamsTab(ScrolledPanel):
self.param_inputs[param.name] = input
- self.settings_grid.Add(input, proportion=1, flag=wx.ALIGN_CENTER_VERTICAL|wx.EXPAND|wx.LEFT, border=40)
+ self.settings_grid.Add(input, proportion=1, flag=wx.ALIGN_CENTER_VERTICAL | wx.EXPAND | wx.LEFT, border=40)
self.settings_grid.Add(wx.StaticText(self, label=param.unit or ""), proportion=1, flag=wx.ALIGN_CENTER_VERTICAL)
self.inputs_to_params = {v: k for k, v in self.param_inputs.iteritems()}
@@ -365,6 +363,7 @@ class ParamsTab(ScrolledPanel):
# end of class SatinPane
+
class SettingsFrame(wx.Frame):
def __init__(self, *args, **kwargs):
# begin wxGlade: MyFrame.__init__
@@ -471,14 +470,14 @@ class SettingsFrame(wx.Frame):
stitch_plan=stitch_plan,
on_close=self.simulate_window_closed,
target_duration=5)
- except:
+ except Exception:
error = traceback.format_exc()
try:
# a window may have been created, so we need to destroy it
# or the app will never exit
- wx.Window.FindWindowByName("Preview").Destroy()
- except:
+ wx.Window.FindWindowByName(_("Preview")).Destroy()
+ except Exception:
pass
info_dialog(self, error, _("Internal Error"))
@@ -517,7 +516,7 @@ class SettingsFrame(wx.Frame):
patches.extend(copy(node).embroider(None))
except SystemExit:
raise
- except:
+ except Exception:
# Ignore errors. This can be things like incorrect paths for
# satins or division by zero caused by incorrect param values.
pass
@@ -578,7 +577,6 @@ class SettingsFrame(wx.Frame):
def overwrite_preset(self, event):
self.add_preset(event, overwrite=True)
-
def _load_preset(self, preset_name):
preset = self.check_and_load_preset(preset_name)
if not preset:
@@ -587,7 +585,6 @@ class SettingsFrame(wx.Frame):
for tab in self.tabs:
tab.load_preset(preset)
-
def load_preset(self, event):
preset_name = self.get_preset_name()
if not preset_name:
@@ -597,7 +594,6 @@ class SettingsFrame(wx.Frame):
event.Skip()
-
def delete_preset(self, event):
preset_name = self.get_preset_name()
if not preset_name:
@@ -649,30 +645,32 @@ class SettingsFrame(wx.Frame):
def __do_layout(self):
# begin wxGlade: MyFrame.__do_layout
sizer_1 = wx.BoxSizer(wx.VERTICAL)
- #self.sizer_3_staticbox.Lower()
+ # self.sizer_3_staticbox.Lower()
sizer_2 = wx.StaticBoxSizer(self.presets_box, wx.HORIZONTAL)
sizer_3 = wx.BoxSizer(wx.HORIZONTAL)
for tab in self.tabs:
self.notebook.AddPage(tab, tab.name)
- sizer_1.Add(self.notebook, 1, wx.EXPAND|wx.LEFT|wx.TOP|wx.RIGHT, 10)
- sizer_2.Add(self.preset_chooser, 1, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5)
- sizer_2.Add(self.load_preset_button, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5)
- sizer_2.Add(self.add_preset_button, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5)
- sizer_2.Add(self.overwrite_preset_button, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5)
- sizer_2.Add(self.delete_preset_button, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5)
- sizer_1.Add(sizer_2, 0, flag=wx.EXPAND|wx.ALL, border=10)
- sizer_3.Add(self.cancel_button, 0, wx.ALIGN_RIGHT|wx.RIGHT, 5)
- sizer_3.Add(self.use_last_button, 0, wx.ALIGN_RIGHT|wx.RIGHT|wx.BOTTOM, 5)
- sizer_3.Add(self.apply_button, 0, wx.ALIGN_RIGHT|wx.RIGHT|wx.BOTTOM, 5)
+ sizer_1.Add(self.notebook, 1, wx.EXPAND | wx.LEFT | wx.TOP | wx.RIGHT, 10)
+ sizer_2.Add(self.preset_chooser, 1, wx.ALIGN_CENTER_VERTICAL | wx.ALL, 5)
+ sizer_2.Add(self.load_preset_button, 0, wx.ALIGN_CENTER_VERTICAL | wx.ALL, 5)
+ sizer_2.Add(self.add_preset_button, 0, wx.ALIGN_CENTER_VERTICAL | wx.ALL, 5)
+ sizer_2.Add(self.overwrite_preset_button, 0, wx.ALIGN_CENTER_VERTICAL | wx.ALL, 5)
+ sizer_2.Add(self.delete_preset_button, 0, wx.ALIGN_CENTER_VERTICAL | wx.ALL, 5)
+ sizer_1.Add(sizer_2, 0, flag=wx.EXPAND | wx.ALL, border=10)
+ sizer_3.Add(self.cancel_button, 0, wx.ALIGN_RIGHT | wx.RIGHT, 5)
+ sizer_3.Add(self.use_last_button, 0, wx.ALIGN_RIGHT | wx.RIGHT | wx.BOTTOM, 5)
+ sizer_3.Add(self.apply_button, 0, wx.ALIGN_RIGHT | wx.RIGHT | wx.BOTTOM, 5)
sizer_1.Add(sizer_3, 0, wx.ALIGN_RIGHT, 0)
self.SetSizer(sizer_1)
sizer_1.Fit(self)
self.Layout()
# end wxGlade
+
class NoValidObjects(Exception):
pass
+
class Params(InkstitchExtension):
def __init__(self, *args, **kwargs):
self.cancelled = False
@@ -728,6 +726,41 @@ class Params(InkstitchExtension):
return groupby(sorted(params, key=by_group_and_sort_index), by_group)
+ def sort_tabs(self, tabs):
+ def tab_sort_key(tab):
+ parent = tab.parent_tab or tab
+
+ sort_key = (
+ # For Stroke and SatinColumn, place the one that's
+ # enabled first. Place dependent tabs first too.
+ parent.toggle and parent.toggle_checkbox.IsChecked(),
+
+ # If multiple tabs are enabled, make sure dependent
+ # tabs are grouped with the parent.
+ parent,
+
+ # Within parent/dependents, put the parent first.
+ tab == parent
+ )
+
+ return sort_key
+
+ tabs.sort(key=tab_sort_key, reverse=True)
+
+ def pair_tabs(self, tabs):
+ for tab in tabs:
+ if tab.toggle and tab.toggle.inverse:
+ for other_tab in tabs:
+ if other_tab != tab and other_tab.toggle.name == tab.toggle.name:
+ tab.pair(other_tab)
+ other_tab.pair(tab)
+
+ def assign_parents(self, tabs, parent_tab):
+ for tab in tabs:
+ if tab != parent_tab:
+ parent_tab.add_dependent_tab(tab)
+ tab.set_parent_tab(parent_tab)
+
def create_tabs(self, parent):
tabs = []
nodes_by_class = self.get_nodes_by_class()
@@ -744,49 +777,21 @@ class Params(InkstitchExtension):
parent_tab = None
new_tabs = []
for group, params in self.group_params(params):
- tab = ParamsTab(parent, id=wx.ID_ANY, name=group or cls.element_name, params=list(params), nodes=nodes)
+ tab_name = group or cls.element_name
+ tab = ParamsTab(parent, id=wx.ID_ANY, name=tab_name, params=list(params), nodes=nodes)
new_tabs.append(tab)
if group is None:
parent_tab = tab
- for tab in new_tabs:
- if tab != parent_tab:
- parent_tab.add_dependent_tab(tab)
- tab.set_parent_tab(parent_tab)
-
+ self.assign_parents(new_tabs, parent_tab)
tabs.extend(new_tabs)
- for tab in tabs:
- if tab.toggle and tab.toggle.inverse:
- for other_tab in tabs:
- if other_tab != tab and other_tab.toggle.name == tab.toggle.name:
- tab.pair(other_tab)
- other_tab.pair(tab)
-
- def tab_sort_key(tab):
- parent = tab.parent_tab or tab
-
- sort_key = (
- # For Stroke and SatinColumn, place the one that's
- # enabled first. Place dependent tabs first too.
- parent.toggle and parent.toggle_checkbox.IsChecked(),
-
- # If multiple tabs are enabled, make sure dependent
- # tabs are grouped with the parent.
- parent,
-
- # Within parent/dependents, put the parent first.
- tab == parent
- )
-
- return sort_key
-
- tabs.sort(key=tab_sort_key, reverse=True)
+ self.pair_tabs(tabs)
+ self.sort_tabs(tabs)
return tabs
-
def cancel(self):
self.cancelled = True
diff --git a/lib/stitch_plan/stitch.py b/lib/stitch_plan/stitch.py
index 5fe10fb8..462634cb 100644
--- a/lib/stitch_plan/stitch.py
+++ b/lib/stitch_plan/stitch.py
@@ -2,7 +2,7 @@ from ..utils.geometry import Point
class Stitch(Point):
- def __init__(self, x, y, color=None, jump=False, stop=False, trim=False, color_change=False, fake_color_change=False, no_ties=False):
+ def __init__(self, x, y=None, color=None, jump=False, stop=False, trim=False, color_change=False, no_ties=False):
self.x = x
self.y = y
self.color = color
@@ -10,20 +10,24 @@ class Stitch(Point):
self.trim = trim
self.stop = stop
self.color_change = color_change
- self.fake_color_change = fake_color_change
self.no_ties = no_ties
+ # Allow creating a Stitch from a Point
+ if isinstance(x, Point):
+ point = x
+ self.x = point.x
+ self.y = point.y
+
def __repr__(self):
- return "Stitch(%s, %s, %s, %s, %s, %s, %s, %s%s)" % (self.x,
- self.y,
- self.color,
- "JUMP" if self.jump else " ",
- "TRIM" if self.trim else " ",
- "STOP" if self.stop else " ",
- "NO TIES" if self.no_ties else " ",
- "FAKE " if self.fake_color_change else "",
- "COLOR CHANGE" if self.color_change else " "
- )
+ return "Stitch(%s, %s, %s, %s, %s, %s, %s, %s)" % (self.x,
+ self.y,
+ self.color,
+ "JUMP" if self.jump else " ",
+ "TRIM" if self.trim else " ",
+ "STOP" if self.stop else " ",
+ "NO TIES" if self.no_ties else " ",
+ "COLOR CHANGE" if self.color_change else " "
+ )
def copy(self):
return Stitch(self.x, self.y, self.color, self.jump, self.stop, self.trim, self.color_change, self.no_ties)
diff --git a/lib/stitch_plan/ties.py b/lib/stitch_plan/ties.py
index 573469f5..1d759c0e 100644
--- a/lib/stitch_plan/ties.py
+++ b/lib/stitch_plan/ties.py
@@ -1,26 +1,37 @@
from copy import deepcopy
from .stitch import Stitch
-from ..utils import cut_path
-from ..stitches import running_stitch
+from ..svg import PIXELS_PER_MM
def add_tie(stitches, tie_path):
- if stitches[-1].no_ties:
+ if len(tie_path) < 2 or stitches[0].no_ties:
# It's from a manual stitch block, so don't add tie stitches. The user
# will add them if they want them.
return
- tie_path = cut_path(tie_path, 0.6)
- tie_stitches = running_stitch(tie_path, 0.3)
- tie_stitches = [Stitch(stitch.x, stitch.y) for stitch in tie_stitches]
+ to_previous = tie_path[1] - tie_path[0]
+ length = to_previous.length()
+ if length > 0.5 * PIXELS_PER_MM:
+ # Travel back one stitch, stopping halfway there.
+ # Then go forward one stitch, stopping halfway between
+ # again.
- stitches.extend(deepcopy(tie_stitches[1:]))
- stitches.extend(deepcopy(list(reversed(tie_stitches))[1:]))
+ # but travel at most 1.5mm
+ length = min(length, 1.5 * PIXELS_PER_MM)
+
+ direction = to_previous.unit()
+ for delta in (0.5, 1.0, 0.5, 0):
+ stitches.append(Stitch(tie_path[0] + delta * length * direction))
+ else:
+ # Too short to travel part of the way to the previous stitch; ust go
+ # back and forth to it a couple times.
+ for i in (1, 0, 1, 0):
+ stitches.append(deepcopy(tie_path[i]))
def add_tie_off(stitches):
- add_tie(stitches, list(reversed(stitches)))
+ add_tie(stitches, stitches[-1:-3:-1])
def add_tie_in(stitches, upcoming_stitches):
@@ -36,10 +47,7 @@ def add_ties(stitch_plan):
for i, stitch in enumerate(color_block.stitches):
is_special = stitch.trim or stitch.jump or stitch.color_change or stitch.stop
- # see stop.py for an explanation of the fake color change
- is_fake = stitch.fake_color_change
-
- if is_special and not is_fake and not need_tie_in:
+ if is_special and not need_tie_in:
add_tie_off(new_stitches)
new_stitches.append(stitch)
need_tie_in = True
diff --git a/lib/svg/units.py b/lib/svg/units.py
index c4ec82a0..0de410ab 100644
--- a/lib/svg/units.py
+++ b/lib/svg/units.py
@@ -80,7 +80,10 @@ def convert_length(length):
@cache
def get_viewbox(svg):
- return svg.get('viewBox').strip().replace(',', ' ').split()
+ viewbox = svg.get('viewBox')
+ if viewbox is None:
+ viewbox = "0 0 0 0"
+ return viewbox.strip().replace(',', ' ').split()
@cache