From 7bf8c2d871eb012ad2fbbf34433bd6ea0affb9e5 Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Fri, 24 Aug 2018 21:20:59 -0400 Subject: helpful error message for empty path "d" attribute (fixes #220) also internationalizes "error:" string --- lib/elements/element.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/elements/element.py b/lib/elements/element.py index 4edb00c0..bca743c2 100644 --- a/lib/elements/element.py +++ b/lib/elements/element.py @@ -197,7 +197,11 @@ class EmbroideryElement(object): # In a path, each element in the 3-tuple is itself a tuple of (x, y). # Tuples all the way down. Hasn't anyone heard of using classes? - return cubicsuperpath.parsePath(self.node.get("d")) + d = self.node.get("d", "") + if not d: + self.fatal(_("Object %(id)s has an empty 'd' attribute. Please delete this object from your document.") % dict(id=self.node.get("id"))) + + return cubicsuperpath.parsePath(d) @cache def parse_path(self): @@ -264,5 +268,7 @@ class EmbroideryElement(object): return patches def fatal(self, message): - print >> sys.stderr, "error:", message + # L10N used when showing an error message to the user such as "satin column: One or more of the rungs doesn't + # intersect both rails." + print >> sys.stderr, _("error:"), message sys.exit(1) -- cgit v1.2.3 From 5c0e2b8e9fcba9f06b288e6dbf8d5183fb93d5d4 Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Fri, 24 Aug 2018 21:25:30 -0400 Subject: don't show an icon on windows --- lib/extensions/install.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/extensions/install.py b/lib/extensions/install.py index 6c179beb..d8e91a8d 100644 --- a/lib/extensions/install.py +++ b/lib/extensions/install.py @@ -56,6 +56,13 @@ class InstallerFrame(wx.Frame): self.path_input.SetValue(dialog.GetPath()) def install_button_clicked(self, event): + if sys.platform == "win32": + # On windows, the default icon shows as a broken image. No idea + # why. Workaround: don't show an icon. + style = wx.ICON_NONE + else: + style = 0 + try: self.install_addons('palettes') self.install_addons('symbols') @@ -63,12 +70,12 @@ class InstallerFrame(wx.Frame): wx.MessageDialog(self, _('Inkscape add-on installation failed') + ': \n' + traceback.format_exc(), _('Installation Failed'), - wx.OK).ShowModal() + wx.OK | style).ShowModal() else: wx.MessageDialog(self, _('Inkscape add-on files have been installed. Please restart Inkscape to load the new add-ons.'), _('Installation Completed'), - wx.OK).ShowModal() + wx.OK | style).ShowModal() self.Destroy() -- cgit v1.2.3 From 29a8bd37d5f0d10ee3b306caeee9a9d46247b02e Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Fri, 24 Aug 2018 21:44:42 -0400 Subject: useful error message when writing embroidery file fails (fixes #279) --- lib/output.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/output.py b/lib/output.py index d5c513e2..1278c5b1 100644 --- a/lib/output.py +++ b/lib/output.py @@ -84,4 +84,10 @@ def write_embroidery_file(file_path, stitch_plan, svg): "full_jump": True, } - pyembroidery.write(pattern, file_path, settings) + try: + pyembroidery.write(pattern, file_path, settings) + except IOError as e: + # L10N low-level file error. %(error)s is (hopefully?) translated by + # the user's system automatically. + print >> sys.stderr, _("Error writing to %(path)s: %(error)s") % dict(path=file_path, error=e.message) + sys.exit(1) -- cgit v1.2.3 From 1531e8f52014c871646db770e0066d56d757a880 Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Fri, 24 Aug 2018 21:47:23 -0400 Subject: support ~, and %vars% in Embroider output directory (#279) --- lib/extensions/embroider.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/extensions/embroider.py b/lib/extensions/embroider.py index 921201d6..7c8adfc9 100644 --- a/lib/extensions/embroider.py +++ b/lib/extensions/embroider.py @@ -39,7 +39,7 @@ class Embroider(InkstitchExtension): def get_output_path(self): if self.options.output_file: - output_path = os.path.join(self.options.path, self.options.output_file) + output_path = os.path.join(os.path.expanduser(os.path.expandvars(self.options.path)), self.options.output_file) else: csv_filename = '%s.%s' % (self.get_base_file_name(), self.options.output_format) output_path = os.path.join(self.options.path, csv_filename) -- cgit v1.2.3 From e2b5e968340aeab55c4b7c280946640fb3131107 Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Sat, 1 Sep 2018 16:26:47 -0400 Subject: add change indicator to Params (#217) --- lib/extensions/params.py | 47 +++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 39 insertions(+), 8 deletions(-) (limited to 'lib') diff --git a/lib/extensions/params.py b/lib/extensions/params.py index c464e044..c301ed1a 100644 --- a/lib/extensions/params.py +++ b/lib/extensions/params.py @@ -17,7 +17,7 @@ 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 +from ..utils import save_stderr, restore_stderr, get_bundled_dir from ..simulator import EmbroiderySimulator from ..commands import is_command @@ -112,10 +112,14 @@ class ParamsTab(ScrolledPanel): else: self.toggle = None - self.settings_grid = wx.FlexGridSizer(rows=0, cols=3, hgap=10, vgap=10) - self.settings_grid.AddGrowableCol(0, 1) + self.param_change_indicators = {} + + self.settings_grid = wx.FlexGridSizer(rows=0, cols=4, hgap=10, vgap=15) + self.settings_grid.AddGrowableCol(1, 2) self.settings_grid.SetFlexibleDirection(wx.HORIZONTAL) + self.pencil_icon = wx.Image(os.path.join(get_bundled_dir("icons"), "pencil_20x20.png")).ConvertToBitmap() + self.__set_properties() self.__do_layout() @@ -218,7 +222,11 @@ class ParamsTab(ScrolledPanel): self.on_change_hook = callable def changed(self, event): - self.changed_inputs.add(event.GetEventObject()) + input = event.GetEventObject() + self.changed_inputs.add(input) + + param = self.inputs_to_params[input] + self.enable_change_indicator(param) event.Skip() if self.on_change_hook: @@ -297,13 +305,17 @@ class ParamsTab(ScrolledPanel): box.Add(sizer, proportion=0, flag=wx.ALL, border=5) if self.toggle: - box.Add(self.toggle_checkbox, proportion=0, flag=wx.BOTTOM, border=10) + 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.toggle_checkbox, proportion=0, flag=wx.ALIGN_CENTER_VERTICAL) + box.Add(toggle_sizer, proportion=0, flag=wx.BOTTOM, border=10) for param in self.params: + self.settings_grid.Add(self.create_change_indicator(param.name), proportion=0, flag=wx.ALIGN_CENTER_VERTICAL) + description = wx.StaticText(self, label=param.description) description.SetToolTip(param.tooltip) - - self.settings_grid.Add(description, proportion=1, flag=wx.EXPAND|wx.RIGHT, border=40) + self.settings_grid.Add(description, proportion=1, flag=wx.EXPAND|wx.RIGHT|wx.ALIGN_CENTER_VERTICAL|wx.TOP, border=5) if param.type == 'boolean': @@ -327,14 +339,33 @@ class ParamsTab(ScrolledPanel): self.param_inputs[param.name] = input - self.settings_grid.Add(input, proportion=1, flag=wx.ALIGN_CENTER_VERTICAL) + 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()} + box.Add(self.settings_grid, proportion=1, flag=wx.ALL, border=10) self.SetSizer(box) self.Layout() + def create_change_indicator(self, param): + indicator = wx.Button(self, style=wx.BORDER_NONE | wx.BU_NOTEXT, size=(28, 28)) + indicator.SetToolTip(_('Click to force this parameter to be saved when you click "Apply and Quit"')) + indicator.Bind(wx.EVT_BUTTON, lambda event: self.enable_change_indicator(param)) + + self.param_change_indicators[param] = indicator + return indicator + + def enable_change_indicator(self, param): + self.param_change_indicators[param].SetBitmapLabel(self.pencil_icon) + self.param_change_indicators[param].SetToolTip(_('This parameter will be saved when you click "Apply and Quit"')) + + self.changed_inputs.add(self.param_inputs[param]) + + if self.on_change_hook(): + self.on_change_hook(self) + # end of class SatinPane class SettingsFrame(wx.Frame): -- cgit v1.2.3 From 05327d56dbdcc9392c447fc72ef8dbd5c16f0ddc Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Sat, 1 Sep 2018 16:38:53 -0400 Subject: stop showing 'None' in Params --- lib/elements/auto_fill.py | 2 +- lib/extensions/params.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/elements/auto_fill.py b/lib/elements/auto_fill.py index cfee6d7d..9801b610 100644 --- a/lib/elements/auto_fill.py +++ b/lib/elements/auto_fill.py @@ -55,7 +55,7 @@ class AutoFill(Fill): def fill_underlay_angle(self): underlay_angle = self.get_float_param("fill_underlay_angle") - if underlay_angle: + if underlay_angle is not None: return math.radians(underlay_angle) else: return self.angle + math.pi / 2.0 diff --git a/lib/extensions/params.py b/lib/extensions/params.py index c301ed1a..f5da7e8b 100644 --- a/lib/extensions/params.py +++ b/lib/extensions/params.py @@ -329,7 +329,7 @@ class ParamsTab(ScrolledPanel): input.Bind(wx.EVT_CHECKBOX, self.changed) elif len(param.values) > 1: - input = wx.ComboBox(self, wx.ID_ANY, choices=sorted(param.values), style=wx.CB_DROPDOWN) + input = wx.ComboBox(self, wx.ID_ANY, choices=sorted(str(value) for value in param.values), style=wx.CB_DROPDOWN) input.Bind(wx.EVT_COMBOBOX, self.changed) input.Bind(wx.EVT_TEXT, self.changed) else: @@ -720,7 +720,7 @@ class Params(InkstitchExtension): getter = 'get_param' values = filter(lambda item: item is not None, - (getattr(node, getter)(param.name, str(param.default)) for node in nodes)) + (getattr(node, getter)(param.name, param.default) for node in nodes)) return values -- cgit v1.2.3 From 29f0e31ccdce084abf5f49d366733558cd288cfa Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Sat, 1 Sep 2018 19:54:13 -0400 Subject: name AutoFill tabs consistently --- lib/elements/auto_fill.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/elements/auto_fill.py b/lib/elements/auto_fill.py index 9801b610..65b11fb1 100644 --- a/lib/elements/auto_fill.py +++ b/lib/elements/auto_fill.py @@ -8,7 +8,7 @@ from .fill import Fill class AutoFill(Fill): - element_name = _("Auto-Fill") + element_name = _("AutoFill") @property @param('auto_fill', _('Automatically routed fill stitching'), type='toggle', default=True) -- cgit v1.2.3 From c3d6780bf172cc8fc1a5c35a9fde73e101d37e78 Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Sat, 1 Sep 2018 19:54:34 -0400 Subject: only save modified values in presets --- lib/extensions/params.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/extensions/params.py b/lib/extensions/params.py index f5da7e8b..2e304c7b 100644 --- a/lib/extensions/params.py +++ b/lib/extensions/params.py @@ -243,9 +243,7 @@ class ParamsTab(ScrolledPanel): self.update_toggle_state() def save_preset(self, storage): - preset = storage[self.name] = {} - for name, input in self.param_inputs.iteritems(): - preset[name] = input.GetValue() + storage[self.name] = self.get_values() def update_description(self): if len(self.nodes) == 1: -- cgit v1.2.3 From 1c2dc2d95442b3663eebf1e2b633f3bb388982c0 Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Sat, 1 Sep 2018 20:06:39 -0400 Subject: add error message for invalid geometries (#216) --- lib/elements/fill.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/elements/fill.py b/lib/elements/fill.py index 626573e6..4156a24b 100644 --- a/lib/elements/fill.py +++ b/lib/elements/fill.py @@ -120,7 +120,10 @@ class Fill(EmbroideryElement): poly_ary.sort(key=lambda point_list: shgeo.Polygon(point_list).area, reverse=True) polygon = shgeo.MultiPolygon([(poly_ary[0], poly_ary[1:])]) - # print >> sys.stderr, "polygon valid:", polygon.is_valid + + if not polygon.is_valid: + self.fatal(_("shape is not valid. This can happen if the border crosses over itself.")) + return polygon def to_patches(self, last_patch): -- cgit v1.2.3 From 71643ba280d251b6bf7727d5f6841d8cda1a5bca Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Sat, 1 Sep 2018 20:09:18 -0400 Subject: make error messages more useful --- lib/elements/element.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/elements/element.py b/lib/elements/element.py index bca743c2..ec50ce22 100644 --- a/lib/elements/element.py +++ b/lib/elements/element.py @@ -270,5 +270,5 @@ class EmbroideryElement(object): def fatal(self, message): # L10N used when showing an error message to the user such as "satin column: One or more of the rungs doesn't # intersect both rails." - print >> sys.stderr, _("error:"), message + print >> sys.stderr, self.node.get("id") + ":", _("error:"), message sys.exit(1) -- cgit v1.2.3 From c980279ae681561a5066f929998d437eb6f256f3 Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Sat, 1 Sep 2018 20:11:44 -0400 Subject: fix missing imports --- lib/output.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'lib') diff --git a/lib/output.py b/lib/output.py index 1278c5b1..cf7895e8 100644 --- a/lib/output.py +++ b/lib/output.py @@ -1,6 +1,8 @@ +import sys import pyembroidery import simpletransform +from .i18n import _ from .utils import Point from .svg import PIXELS_PER_MM, get_doc_size, get_viewbox_transform from .commands import global_command -- cgit v1.2.3 From b437b8403c4758813c780b16cebe8357e95a8746 Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Sun, 9 Sep 2018 00:06:47 -0400 Subject: fix pencil icon path --- lib/extensions/params.py | 4 ++-- lib/utils/paths.py | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/extensions/params.py b/lib/extensions/params.py index 2e304c7b..6012263a 100644 --- a/lib/extensions/params.py +++ b/lib/extensions/params.py @@ -17,7 +17,7 @@ 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_bundled_dir +from ..utils import save_stderr, restore_stderr, get_resource_dir from ..simulator import EmbroiderySimulator from ..commands import is_command @@ -118,7 +118,7 @@ class ParamsTab(ScrolledPanel): self.settings_grid.AddGrowableCol(1, 2) self.settings_grid.SetFlexibleDirection(wx.HORIZONTAL) - self.pencil_icon = wx.Image(os.path.join(get_bundled_dir("icons"), "pencil_20x20.png")).ConvertToBitmap() + self.pencil_icon = wx.Image(os.path.join(get_resource_dir("icons"), "pencil_20x20.png")).ConvertToBitmap() self.__set_properties() self.__do_layout() diff --git a/lib/utils/paths.py b/lib/utils/paths.py index 863e8e69..0da8a154 100644 --- a/lib/utils/paths.py +++ b/lib/utils/paths.py @@ -8,3 +8,9 @@ def get_bundled_dir(name): return realpath(os.path.join(sys._MEIPASS, "..", name)) else: return realpath(os.path.join(dirname(realpath(__file__)), '..', '..', name)) + +def get_resource_dir(name): + if getattr(sys, 'frozen', None) is not None: + return realpath(os.path.join(sys._MEIPASS, name)) + else: + return realpath(os.path.join(dirname(realpath(__file__)), '..', '..', name)) -- cgit v1.2.3 From 944022ee53d97af451e810f3b98ff077f77e13dc Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Mon, 10 Sep 2018 23:04:08 -0400 Subject: proper running stitch for big row spacing --- lib/stitches/auto_fill.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/stitches/auto_fill.py b/lib/stitches/auto_fill.py index e732c940..1660cd4e 100644 --- a/lib/stitches/auto_fill.py +++ b/lib/stitches/auto_fill.py @@ -472,7 +472,8 @@ def connect_points(shape, start, end, running_stitch_length, row_spacing): # most cases. 1.4 is chosen as approximately the length of the # stitch connecting two rows if the side of the shape is at a 45 # degree angle to the rows of stitches (sqrt(2)). - if abs(end_projection - start_projection) < row_spacing * 1.4: + direct_distance = abs(end_projection - start_projection) + if direct_distance < row_spacing * 1.4 and direct_distance < running_stitch_length: return [InkstitchPoint(end.x, end.y)] # The outline path has a "natural" starting point. Think of this as -- cgit v1.2.3 From ba1c2ea78ffce7ac2c08e8b54f834024747d816f Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Wed, 12 Sep 2018 20:25:15 -0400 Subject: fix lint --- lib/utils/paths.py | 1 + 1 file changed, 1 insertion(+) (limited to 'lib') diff --git a/lib/utils/paths.py b/lib/utils/paths.py index 0da8a154..6dbaf13d 100644 --- a/lib/utils/paths.py +++ b/lib/utils/paths.py @@ -9,6 +9,7 @@ def get_bundled_dir(name): else: return realpath(os.path.join(dirname(realpath(__file__)), '..', '..', name)) + def get_resource_dir(name): if getattr(sys, 'frozen', None) is not None: return realpath(os.path.join(sys._MEIPASS, name)) -- cgit v1.2.3