diff --git a/lib/elements/auto_fill.py b/lib/elements/auto_fill.py index 65b11fb16..b8d8d15fb 100644 --- a/lib/elements/auto_fill.py +++ b/lib/elements/auto_fill.py @@ -1,8 +1,12 @@ import math +import traceback + from shapely import geometry as shgeo + +from ..exceptions import InkstitchException from ..i18n import _ -from ..utils import cache from ..stitches import auto_fill +from ..utils import cache from .element import param, Patch from .fill import Fill @@ -92,6 +96,18 @@ class AutoFill(Fill): def fill_underlay_inset(self): return self.get_float_param('fill_underlay_inset_mm', 0) + @property + @param( + 'fill_underlay_skip_last', + _('Skip last stitch in each row'), + tooltip=_('The last stitch in each row is quite close to the first stitch in the next row. ' + 'Skipping it decreases stitch count and density.'), + group=_('AutoFill Underlay'), + type='boolean', + default=False) + def fill_underlay_skip_last(self): + return self.get_boolean_param("fill_underlay_skip_last", False) + @property @param('expand_mm', _('Expand'), @@ -142,25 +158,42 @@ class AutoFill(Fill): starting_point = self.get_starting_point(last_patch) ending_point = self.get_ending_point() - if self.fill_underlay: - stitches.extend(auto_fill(self.underlay_shape, - self.fill_underlay_angle, - self.fill_underlay_row_spacing, - self.fill_underlay_row_spacing, - self.fill_underlay_max_stitch_length, + try: + if self.fill_underlay: + stitches.extend(auto_fill(self.underlay_shape, + self.fill_underlay_angle, + self.fill_underlay_row_spacing, + self.fill_underlay_row_spacing, + self.fill_underlay_max_stitch_length, + self.running_stitch_length, + self.staggers, + self.fill_underlay_skip_last, + starting_point)) + starting_point = stitches[-1] + + stitches.extend(auto_fill(self.fill_shape, + self.angle, + self.row_spacing, + self.end_row_spacing, + self.max_stitch_length, self.running_stitch_length, self.staggers, - starting_point)) - starting_point = stitches[-1] + self.skip_last, + starting_point, + ending_point)) + except InkstitchException, exc: + # for one of our exceptions, just print the message + self.fatal(_("Unable to autofill: ") + str(exc)) + except Exception, exc: + # for an uncaught exception, give a little more info so that they can create a bug report + message = "" + message += _("Error during autofill! This means that there is a problem with Ink/Stitch.") + message += "\n\n" + # L10N this message is followed by a URL: https://github.com/inkstitch/inkstitch/issues/new + message += _("If you'd like to help us make Ink/Stitch better, please paste this whole message into a new issue at: ") + message += "https://github.com/inkstitch/inkstitch/issues/new\n\n" + message += traceback.format_exc() - stitches.extend(auto_fill(self.fill_shape, - self.angle, - self.row_spacing, - self.end_row_spacing, - self.max_stitch_length, - self.running_stitch_length, - self.staggers, - starting_point, - ending_point)) + self.fatal(message) return [Patch(stitches=stitches, color=self.color)] diff --git a/lib/elements/element.py b/lib/elements/element.py index d98673513..10b1852ad 100644 --- a/lib/elements/element.py +++ b/lib/elements/element.py @@ -1,15 +1,15 @@ -import sys from copy import deepcopy +import sys -from ..i18n import _ -from ..utils import cache -from ..svg import PIXELS_PER_MM, convert_length, get_doc_size, apply_transforms -from ..commands import find_commands - -# inkscape-provided utilities -import simplestyle -import cubicsuperpath from cspsubdiv import cspsubdiv +import cubicsuperpath +import simplestyle + +from ..commands import find_commands +from ..i18n import _ +from ..svg import PIXELS_PER_MM, convert_length, get_doc_size, apply_transforms +from ..svg.tags import INKSCAPE_LABEL +from ..utils import cache class Patch: @@ -274,7 +274,14 @@ class EmbroideryElement(object): return patches 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, self.node.get("id") + ":", _("error:"), message.encode("UTF-8") + label = self.node.get(INKSCAPE_LABEL) + id = self.node.get("id") + if label: + name = "%s (%s)" % (label, id) + else: + name = id + + # L10N used when showing an error message to the user such as + # "Some Path (path1234): error: satin column: One or more of the rungs doesn't intersect both rails." + print >> sys.stderr, "%s: %s %s" % (name, _("error:"), message.encode("UTF-8")) sys.exit(1) diff --git a/lib/elements/fill.py b/lib/elements/fill.py index 4156a24b0..357adf4b7 100644 --- a/lib/elements/fill.py +++ b/lib/elements/fill.py @@ -1,11 +1,12 @@ -from shapely import geometry as shgeo import math -from .element import param, EmbroideryElement, Patch +from shapely import geometry as shgeo + from ..i18n import _ +from ..stitches import legacy_fill from ..svg import PIXELS_PER_MM from ..utils import cache -from ..stitches import legacy_fill +from .element import param, EmbroideryElement, Patch class Fill(EmbroideryElement): @@ -40,6 +41,17 @@ class Fill(EmbroideryElement): # SVG spec says the default fill is black return self.get_style("fill", "#000000") + @property + @param( + 'skip_last', + _('Skip last stitch in each row'), + tooltip=_('The last stitch in each row is quite close to the first stitch in the next row. ' + 'Skipping it decreases stitch count and density.'), + type='boolean', + default=False) + def skip_last(self): + return self.get_boolean_param("skip_last", False) + @property @param( 'flip', @@ -133,5 +145,6 @@ class Fill(EmbroideryElement): self.end_row_spacing, self.max_stitch_length, self.flip, - self.staggers) + self.staggers, + self.skip_last) return [Patch(stitches=stitch_list, color=self.color) for stitch_list in stitch_lists] diff --git a/lib/elements/satin_column.py b/lib/elements/satin_column.py index 1f9854edb..f891e049b 100644 --- a/lib/elements/satin_column.py +++ b/lib/elements/satin_column.py @@ -1,12 +1,13 @@ -from itertools import chain, izip from copy import deepcopy -from shapely import geometry as shgeo, affinity as shaffinity -import cubicsuperpath +from itertools import chain, izip + +import cubicsuperpath +from shapely import geometry as shgeo, affinity as shaffinity -from .element import param, EmbroideryElement, Patch from ..i18n import _ -from ..utils import cache, Point, cut, collapse_duplicate_point from ..svg import line_strings_to_csp, point_lists_to_csp +from ..utils import cache, Point, cut, collapse_duplicate_point +from .element import param, EmbroideryElement, Patch class SatinColumn(EmbroideryElement): @@ -255,7 +256,7 @@ class SatinColumn(EmbroideryElement): intersections += len(intersection) break elif not isinstance(intersection, shgeo.Point): - self.fatal("intersection is a: %s %s" % (intersection, intersection.geoms)) + self.fatal("INTERNAL ERROR: intersection is: %s %s" % (intersection, getattr(intersection, 'geoms', None))) else: intersections += 1 @@ -716,22 +717,22 @@ class SatinColumn(EmbroideryElement): # First, verify that we have valid paths. self.validate_satin_column() - patches = [] + patch = Patch(color=self.color) if self.center_walk_underlay: - patches.append(self.do_center_walk()) + patch += self.do_center_walk() if self.contour_underlay: - patches.append(self.do_contour_underlay()) + patch += self.do_contour_underlay() if self.zigzag_underlay: # zigzag underlay comes after contour walk underlay, so that the # zigzags sit on the contour walk underlay like rail ties on rails. - patches.append(self.do_zigzag_underlay()) + patch += self.do_zigzag_underlay() if self.e_stitch: - patches.append(self.do_e_stitch()) + patch += self.do_e_stitch() else: - patches.append(self.do_satin()) + patch += self.do_satin() - return patches + return [patch] diff --git a/lib/exceptions.py b/lib/exceptions.py new file mode 100644 index 000000000..c1ff36f0b --- /dev/null +++ b/lib/exceptions.py @@ -0,0 +1,2 @@ +class InkstitchException(Exception): + pass diff --git a/lib/extensions/convert_to_satin.py b/lib/extensions/convert_to_satin.py index ef4ac557a..2b586e360 100644 --- a/lib/extensions/convert_to_satin.py +++ b/lib/extensions/convert_to_satin.py @@ -1,17 +1,18 @@ -import inkex -from shapely import geometry as shgeo -from itertools import chain, groupby -import numpy -from numpy import diff, sign, setdiff1d -import math from copy import deepcopy +from itertools import chain, groupby +import math + +import inkex +from numpy import diff, sign, setdiff1d +import numpy +from shapely import geometry as shgeo -from .base import InkstitchExtension -from ..svg.tags import SVG_PATH_TAG -from ..svg import get_correction_transform, PIXELS_PER_MM from ..elements import Stroke -from ..utils import Point from ..i18n import _ +from ..svg import get_correction_transform, PIXELS_PER_MM +from ..svg.tags import SVG_PATH_TAG +from ..utils import Point +from .base import InkstitchExtension class SelfIntersectionError(Exception): @@ -112,10 +113,10 @@ class ConvertToSatin(InkstitchExtension): if not isinstance(left_rail, shgeo.LineString) or \ not isinstance(right_rail, shgeo.LineString): - # If the parallel offsets come out as anything but a LineString, that means the - # path intersects itself, when taking its stroke width into consideration. See - # the last example for parallel_offset() in the Shapely documentation: - # https://shapely.readthedocs.io/en/latest/manual.html#object.parallel_offset + # If the parallel offsets come out as anything but a LineString, that means the + # path intersects itself, when taking its stroke width into consideration. See + # the last example for parallel_offset() in the Shapely documentation: + # https://shapely.readthedocs.io/en/latest/manual.html#object.parallel_offset raise SelfIntersectionError() # for whatever reason, shapely returns a right-side offset's coordinates in reverse diff --git a/lib/extensions/params.py b/lib/extensions/params.py index d3cb154a9..4d04ba230 100644 --- a/lib/extensions/params.py +++ b/lib/extensions/params.py @@ -6,6 +6,7 @@ from itertools import groupby import os import sys + import wx from wx.lib.scrolledpanel import ScrolledPanel @@ -460,15 +461,16 @@ class Params(InkstitchExtension): element = EmbroideryElement(node) classes = [] - if element.get_style("fill"): - classes.append(AutoFill) - classes.append(Fill) + if not is_command(node): + if element.get_style("fill", "black") is not None: + classes.append(AutoFill) + classes.append(Fill) - if element.get_style("stroke") and not is_command(node): - classes.append(Stroke) + if element.get_style("stroke") is not None: + classes.append(Stroke) - if element.get_style("stroke-dasharray") is None: - classes.append(SatinColumn) + if element.get_style("stroke-dasharray") is None: + classes.append(SatinColumn) return classes diff --git a/lib/gui/simulator.py b/lib/gui/simulator.py index 0eed18c92..e0d78983c 100644 --- a/lib/gui/simulator.py +++ b/lib/gui/simulator.py @@ -385,8 +385,8 @@ class DrawingPanel(wx.Panel): self.go() def choose_zoom_and_pan(self, event=None): - # ignore if called before we load the stitch plan - if not self.width and not self.height: + # ignore if EVT_SIZE fired before we load the stitch plan + if not self.width and not self.height and event is not None: return panel_width, panel_height = self.GetClientSize() diff --git a/lib/output.py b/lib/output.py index 5fa7791fd..216227657 100644 --- a/lib/output.py +++ b/lib/output.py @@ -1,11 +1,13 @@ -import sys import pyembroidery +import sys + 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 +from .i18n import _ +from .stitch_plan import Stitch +from .svg import PIXELS_PER_MM, get_doc_size, get_viewbox_transform +from .utils import Point def get_command(stitch): @@ -57,6 +59,7 @@ def write_embroidery_file(file_path, stitch_plan, svg, settings={}): origin = get_origin(svg) pattern = pyembroidery.EmbPattern() + stitch = Stitch(0, 0) for color_block in stitch_plan: pattern.add_thread(color_block.color.pyembroidery_thread) @@ -99,5 +102,5 @@ def write_embroidery_file(file_path, stitch_plan, svg, 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) + print >> sys.stderr, _("Error writing to %(path)s: %(error)s") % dict(path=file_path, error=e.strerror) sys.exit(1) diff --git a/lib/stitches/auto_fill.py b/lib/stitches/auto_fill.py index 28c79effa..0f07b7952 100644 --- a/lib/stitches/auto_fill.py +++ b/lib/stitches/auto_fill.py @@ -1,16 +1,22 @@ -import sys -import shapely -import networkx -from itertools import groupby, izip from collections import deque +from itertools import groupby, izip +import sys -from .fill import intersect_region_with_grating, row_num, stitch_row -from .running_stitch import running_stitch +import networkx +import shapely + +from ..exceptions import InkstitchException from ..i18n import _ from ..utils.geometry import Point as InkstitchPoint, cut +from .fill import intersect_region_with_grating, row_num, stitch_row +from .running_stitch import running_stitch -class MaxQueueLengthExceeded(Exception): +class MaxQueueLengthExceeded(InkstitchException): + pass + + +class InvalidPath(InkstitchException): pass @@ -39,16 +45,25 @@ class PathEdge(object): return self.key == self.SEGMENT_KEY -def auto_fill(shape, angle, row_spacing, end_row_spacing, max_stitch_length, running_stitch_length, staggers, starting_point, ending_point=None): +def auto_fill(shape, + angle, + row_spacing, + end_row_spacing, + max_stitch_length, + running_stitch_length, + staggers, + skip_last, + starting_point, + ending_point=None): stitches = [] rows_of_segments = intersect_region_with_grating(shape, angle, row_spacing, end_row_spacing) segments = [segment for row in rows_of_segments for segment in row] - graph = build_graph(shape, segments, angle, row_spacing) + graph = build_graph(shape, segments, angle, row_spacing, max_stitch_length) path = find_stitch_path(graph, segments, starting_point, ending_point) - stitches.extend(path_to_stitches(graph, path, shape, angle, row_spacing, max_stitch_length, running_stitch_length, staggers)) + stitches.extend(path_to_stitches(graph, path, shape, angle, row_spacing, max_stitch_length, running_stitch_length, staggers, skip_last)) return stitches @@ -80,7 +95,7 @@ def project(shape, coords, outline_index): return outline.project(shapely.geometry.Point(*coords)) -def build_graph(shape, segments, angle, row_spacing): +def build_graph(shape, segments, angle, row_spacing, max_stitch_length): """build a graph representation of the grating segments This function builds a specialized graph (as in graph theory) that will @@ -163,12 +178,21 @@ def build_graph(shape, segments, angle, row_spacing): if i % 2 == edge_set: graph.add_edge(node1, node2, key="extra") - if not networkx.is_eulerian(graph): - raise Exception(_("Unable to autofill. This most often happens because your shape is made up of multiple sections that aren't connected.")) + check_graph(graph, shape, max_stitch_length) return graph +def check_graph(graph, shape, max_stitch_length): + if networkx.is_empty(graph) or not networkx.is_eulerian(graph): + if shape.area < max_stitch_length ** 2: + raise InvalidPath(_("This shape is so small that it cannot be filled with rows of stitches. " + "It would probably look best as a satin column or running stitch.")) + else: + raise InvalidPath(_("Cannot parse shape. " + "This most often happens because your shape is made up of multiple sections that aren't connected.")) + + def node_list_to_edge_list(node_list): return zip(node_list[:-1], node_list[1:]) @@ -317,14 +341,16 @@ def get_outline_nodes(graph, outline_index=0): def find_initial_path(graph, starting_point, ending_point=None): starting_node = nearest_node_on_outline(graph, starting_point) - if ending_point is None: + if ending_point is not None: + ending_node = nearest_node_on_outline(graph, ending_point) + + if ending_point is None or starting_node is ending_node: # If they didn't give an ending point, pick either neighboring node # along the outline -- doesn't matter which. We do this because # the algorithm requires we start with _some_ path. neighbors = [n for n, keys in graph.adj[starting_node].iteritems() if 'outline' in keys] return [PathEdge((starting_node, neighbors[0]), "initial")] else: - ending_node = nearest_node_on_outline(graph, ending_point) outline_nodes = get_outline_nodes(graph) # Multiply the outline_nodes list by 2 (duplicate it) because @@ -513,7 +539,10 @@ def connect_points(shape, start, end, running_stitch_length, row_spacing): # Now do running stitch along the path we've found. running_stitch() will # avoid cutting sharp corners. path = [InkstitchPoint(*p) for p in points] - return running_stitch(path, running_stitch_length) + stitches = running_stitch(path, running_stitch_length) + + # The row of stitches already stitched the first point, so skip it. + return stitches[1:] def trim_end(path): @@ -521,14 +550,14 @@ def trim_end(path): path.pop() -def path_to_stitches(graph, path, shape, angle, row_spacing, max_stitch_length, running_stitch_length, staggers): +def path_to_stitches(graph, path, shape, angle, row_spacing, max_stitch_length, running_stitch_length, staggers, skip_last): path = collapse_sequential_outline_edges(graph, path) stitches = [] for edge in path: if edge.is_segment(): - stitch_row(stitches, edge[0], edge[1], angle, row_spacing, max_stitch_length, staggers) + stitch_row(stitches, edge[0], edge[1], angle, row_spacing, max_stitch_length, staggers, skip_last) else: stitches.extend(connect_points(shape, edge[0], edge[1], running_stitch_length, row_spacing)) diff --git a/lib/stitches/auto_satin.py b/lib/stitches/auto_satin.py index 7bc3e67ce..e204a4456 100644 --- a/lib/stitches/auto_satin.py +++ b/lib/stitches/auto_satin.py @@ -242,7 +242,7 @@ class RunningStitch(object): @cache def reversed(self): - return RunningStitch(shgeo.LineString(reversed(self.path.coords)), self.style) + return RunningStitch(shgeo.LineString(reversed(self.path.coords)), self.original_element) def is_sequential(self, other): if not isinstance(other, RunningStitch): diff --git a/lib/stitches/fill.py b/lib/stitches/fill.py index af0a84036..e00d66dec 100644 --- a/lib/stitches/fill.py +++ b/lib/stitches/fill.py @@ -1,15 +1,16 @@ -import shapely import math +import shapely + from ..svg import PIXELS_PER_MM from ..utils import cache, Point as InkstitchPoint -def legacy_fill(shape, angle, row_spacing, end_row_spacing, max_stitch_length, flip, staggers): +def legacy_fill(shape, angle, row_spacing, end_row_spacing, max_stitch_length, flip, staggers, skip_last): rows_of_segments = intersect_region_with_grating(shape, angle, row_spacing, end_row_spacing, flip) groups_of_segments = pull_runs(rows_of_segments, shape, row_spacing) - return [section_to_stitches(group, angle, row_spacing, max_stitch_length, staggers) + return [section_to_stitches(group, angle, row_spacing, max_stitch_length, staggers, skip_last) for group in groups_of_segments] @@ -37,7 +38,7 @@ def adjust_stagger(stitch, angle, row_spacing, max_stitch_length, staggers): return stitch - offset * east(angle) -def stitch_row(stitches, beg, end, angle, row_spacing, max_stitch_length, staggers): +def stitch_row(stitches, beg, end, angle, row_spacing, max_stitch_length, staggers, skip_last=False): # We want our stitches to look like this: # # ---*-----------*----------- @@ -81,7 +82,7 @@ def stitch_row(stitches, beg, end, angle, row_spacing, max_stitch_length, stagge stitches.append(beg + offset * row_direction) offset += max_stitch_length - if (end - stitches[-1]).length() > 0.1 * PIXELS_PER_MM: + if (end - stitches[-1]).length() > 0.1 * PIXELS_PER_MM and not skip_last: stitches.append(end) @@ -164,7 +165,7 @@ def intersect_region_with_grating(shape, angle, row_spacing, end_row_spacing=Non return rows -def section_to_stitches(group_of_segments, angle, row_spacing, max_stitch_length, staggers): +def section_to_stitches(group_of_segments, angle, row_spacing, max_stitch_length, staggers, skip_last): stitches = [] swap = False @@ -174,7 +175,7 @@ def section_to_stitches(group_of_segments, angle, row_spacing, max_stitch_length if (swap): (beg, end) = (end, beg) - stitch_row(stitches, beg, end, angle, row_spacing, max_stitch_length, staggers) + stitch_row(stitches, beg, end, angle, row_spacing, max_stitch_length, staggers, skip_last) swap = not swap diff --git a/lib/stitches/running_stitch.py b/lib/stitches/running_stitch.py index 5f8ed21e6..fa8c50ba2 100644 --- a/lib/stitches/running_stitch.py +++ b/lib/stitches/running_stitch.py @@ -23,7 +23,7 @@ def running_stitch(points, stitch_length): segment_start = points[0] last_segment_direction = None - # This tracks the distance we've travelled along the current segment so + # This tracks the distance we've traveled along the current segment so # far. Each time we make a stitch, we add the stitch_length to this # value. If we fall off the end of the current segment, we carry over # the remainder to the next segment. @@ -62,8 +62,12 @@ def running_stitch(points, stitch_length): last_segment_direction = segment_direction distance -= segment_length + # stitch a single point if the path has a length of zero + if not output: + output.append(segment_start) + # stitch the last point unless we're already almost there - if (segment_start - output[-1]).length() > 0.1: + if (segment_start - output[-1]).length() > 0.1 or len(output) == 0: output.append(segment_start) return output diff --git a/lib/svg/svg.py b/lib/svg/svg.py index b1cc91d91..3fceebfba 100644 --- a/lib/svg/svg.py +++ b/lib/svg/svg.py @@ -1,12 +1,12 @@ -import simpletransform -import simplestyle import inkex +import simplestyle +import simpletransform -from .units import get_viewbox_transform -from .tags import SVG_GROUP_TAG, INKSCAPE_LABEL, INKSCAPE_GROUPMODE, SVG_PATH_TAG, SVG_DEFS_TAG -from .realistic_rendering import realistic_stitch, realistic_filter from ..i18n import _ from ..utils import cache +from .realistic_rendering import realistic_stitch, realistic_filter +from .tags import SVG_GROUP_TAG, INKSCAPE_LABEL, INKSCAPE_GROUPMODE, SVG_PATH_TAG, SVG_DEFS_TAG +from .units import get_viewbox_transform def color_block_to_point_lists(color_block): @@ -21,6 +21,9 @@ def color_block_to_point_lists(color_block): if not stitch.jump and not stitch.color_change: point_lists[-1].append(stitch.as_tuple()) + # filter out empty point lists + point_lists = [p for p in point_lists if p] + return point_lists diff --git a/lib/utils/geometry.py b/lib/utils/geometry.py index ab7f24c11..05cfc4b28 100644 --- a/lib/utils/geometry.py +++ b/lib/utils/geometry.py @@ -1,6 +1,7 @@ -from shapely.geometry import LineString, Point as ShapelyPoint import math +from shapely.geometry import LineString, Point as ShapelyPoint + def cut(line, distance, normalized=False): """ Cuts a LineString in two at a distance from its starting point. @@ -51,9 +52,8 @@ def cut_path(points, length): def collapse_duplicate_point(geometry): - if hasattr(geometry, 'geoms'): - if geometry.area < 0.01: - return geometry.representative_point() + if geometry.area < 0.01: + return geometry.representative_point() return geometry diff --git a/pyembroidery b/pyembroidery index d26e28002..47b795a08 160000 --- a/pyembroidery +++ b/pyembroidery @@ -1 +1 @@ -Subproject commit d26e280020bbe1d2b5af977b9cd74f60db8be71b +Subproject commit 47b795a084bdc3281fbf944b940609bf86193fd8 diff --git a/requirements.txt b/requirements.txt index e1ab6e084..e81b32e3f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,7 +5,7 @@ networkx==2.2 shapely lxml appdirs -numpy +numpy<1.16.0 jinja2 requests colormath diff --git a/stub.py b/stub.py index 3d8731962..7c9975244 100644 --- a/stub.py +++ b/stub.py @@ -1,10 +1,11 @@ #!/usr/bin/env python -import sys import os import subprocess +import sys import traceback + # ink/stitch # # stub.py: pyinstaller execution stub @@ -15,7 +16,6 @@ import traceback # This Python script exists only to execute the actual extension binary. It # can be copied to, e.g., "embroider_params.py", in which case it will look # for a binary at inkstitch/bin/embroider_params. - script_name = os.path.basename(__file__) if script_name.endswith('.py'): @@ -48,12 +48,12 @@ if sys.platform == "win32": import msvcrt msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY) -stdout = stdout.strip() -if stdout: - print stdout.strip(), +sys.stdout.write(stdout) +sys.stdout.flush() stderr = stderr.strip() if stderr: - print >> sys.stderr, stderr.strip(), + sys.stderr.write(stderr.strip()) + sys.stderr.flush() sys.exit(extension.returncode)