2021-03-12 04:17:19 +00:00
|
|
|
# Authors: see git history
|
|
|
|
#
|
|
|
|
# Copyright (c) 2010 Authors
|
|
|
|
# Licensed under the GNU GPL version 3.0 or later. See the file LICENSE for details.
|
2023-12-17 22:03:39 +00:00
|
|
|
|
2018-10-24 00:08:46 +00:00
|
|
|
import os
|
2018-04-29 01:26:53 +00:00
|
|
|
import sys
|
2023-12-17 22:03:39 +00:00
|
|
|
import lib.debug_utils as debug_utils
|
|
|
|
from pathlib import Path
|
|
|
|
|
|
|
|
SCRIPTDIR = Path(__file__).parent.absolute()
|
|
|
|
|
|
|
|
if len(sys.argv) < 2:
|
|
|
|
exit(1) # no arguments - prevent uncidentally running this script
|
|
|
|
|
|
|
|
running_as_frozen = getattr(sys, 'frozen', None) is not None # check if running from pyinstaller bundle
|
|
|
|
running_from_inkscape = '.ink.svg' not in sys.argv # inkscape never starts extension with .ink.svg file in args
|
|
|
|
# running_from_inkscape = True # for testing
|
|
|
|
|
|
|
|
debug_active = bool((gettrace := getattr(sys, 'gettrace')) and gettrace()) # check if debugger is active on startup
|
|
|
|
debug_file = SCRIPTDIR / "DEBUG"
|
|
|
|
debug_type = 'none'
|
|
|
|
|
|
|
|
profile_file = SCRIPTDIR / "PROFILE"
|
|
|
|
profile_type = 'none'
|
|
|
|
|
|
|
|
# print(f"debug_type:'{debug_type}' profile_type:'{profile_type}'", file=sys.stderr) # for testing
|
|
|
|
|
|
|
|
# if script was already started from debugger then don't read debug file
|
|
|
|
if not running_as_frozen and not debug_active and os.path.exists(debug_file):
|
|
|
|
debug_type = debug_utils.parse_file(debug_file) # read type of debugger from debug_file DEBUG
|
|
|
|
if debug_type == 'none': # for better backward compatibility
|
|
|
|
print(f"Debug file exists but no debugger type found in '{debug_file.name}'", file=sys.stderr)
|
|
|
|
|
|
|
|
if os.path.exists(profile_file):
|
|
|
|
profile_type = debug_utils.parse_file(profile_file) # read type of profiler from profile_file PROFILE
|
|
|
|
if profile_type == 'none': # for better backward compatibility
|
|
|
|
print(f"Profile file exists but no profiler type found in '{profile_file.name}'", file=sys.stderr)
|
|
|
|
|
|
|
|
if running_from_inkscape:
|
|
|
|
if debug_type.endswith('-script'): # if offline debugging just create script for later debugging
|
|
|
|
debug_utils.write_offline_debug_script(SCRIPTDIR)
|
|
|
|
debug_type = 'none' # do not start debugger when running from inkscape
|
|
|
|
else: # not running from inkscape
|
|
|
|
if debug_type.endswith('-script'): # remove '-script' to propely initialize debugger packages for each editor
|
|
|
|
debug_type = debug_type.replace('-script', '')
|
|
|
|
|
|
|
|
if not running_as_frozen:
|
|
|
|
# When running in development mode, we prefer inkex installed by pip, not the one bundled with Inkscape.
|
|
|
|
# - move inkscape extensions path to the end of sys.path
|
|
|
|
# - we compare PYTHONPATH with sys.path and move PYTHONPATH to the end of sys.path
|
|
|
|
# - also user inkscape extensions path is moved to the end of sys.path - may cause problems?
|
|
|
|
# - path for deprecated-simple are removed from sys.path, will be added later by importing inkex
|
|
|
|
|
|
|
|
# PYTHONPATH to list
|
|
|
|
pythonpath = os.environ.get('PYTHONPATH', '').split(os.pathsep)
|
|
|
|
# remove pythonpath from sys.path
|
|
|
|
sys.path = [p for p in sys.path if p not in pythonpath]
|
|
|
|
# remove deprecated-simple, it will be added later by importing inkex
|
|
|
|
pythonpath = [p for p in pythonpath if not p.endswith('deprecated-simple')]
|
|
|
|
# add pythonpath to the end of sys.path
|
|
|
|
sys.path.extend(pythonpath)
|
|
|
|
|
|
|
|
# >> should be removed after previous code was tested <<
|
|
|
|
# if sys.platform == "darwin":
|
|
|
|
# extensions_path = "/Applications/Inkscape.app/Contents/Resources/share/inkscape/extensions" # Mac
|
|
|
|
# else:
|
|
|
|
# extensions_path = "/usr/share/inkscape/extensions" # Linux
|
|
|
|
# # windows not solved
|
|
|
|
# move inkscape extensions path to the end of sys.path
|
|
|
|
# sys.path.remove(extensions_path)
|
|
|
|
# sys.path.append(extensions_path)
|
|
|
|
# >> ------------------------------------------------- <<
|
|
|
|
|
|
|
|
import logging
|
2018-04-29 01:26:53 +00:00
|
|
|
from argparse import ArgumentParser
|
2021-03-04 17:40:53 +00:00
|
|
|
from io import StringIO
|
|
|
|
|
2023-09-07 17:25:47 +00:00
|
|
|
from lib.exceptions import InkstitchException, format_uncaught_exception
|
|
|
|
|
2021-03-04 17:40:53 +00:00
|
|
|
from inkex import errormsg
|
|
|
|
from lxml.etree import XMLSyntaxError
|
2018-10-24 00:08:46 +00:00
|
|
|
|
2019-03-28 18:47:05 +00:00
|
|
|
import lib.debug as debug
|
2020-04-20 18:52:50 +00:00
|
|
|
from lib import extensions
|
|
|
|
from lib.i18n import _
|
2023-09-07 17:25:47 +00:00
|
|
|
from lib.utils import restore_stderr, save_stderr
|
2018-04-29 01:26:53 +00:00
|
|
|
|
2023-12-17 22:03:39 +00:00
|
|
|
# file DEBUG exists next to inkstitch.py - enabling debug mode depends on value of debug_type in DEBUG file
|
|
|
|
if debug_type != 'none':
|
|
|
|
debug.enable(debug_type)
|
|
|
|
# check if debugger is really activated
|
|
|
|
debug_active = bool((gettrace := getattr(sys, 'gettrace')) and gettrace())
|
|
|
|
|
|
|
|
# ignore warnings in releases - see warnings.warn()
|
|
|
|
if running_as_frozen or not debug_active:
|
2022-01-11 22:11:57 +00:00
|
|
|
import warnings
|
|
|
|
warnings.filterwarnings('ignore')
|
|
|
|
|
2023-12-17 22:03:39 +00:00
|
|
|
# set logger for shapely
|
|
|
|
logger = logging.getLogger('shapely.geos') # attach logger of shapely, from ver 2.0.0 all logs are exceptions
|
2018-11-15 01:23:06 +00:00
|
|
|
logger.setLevel(logging.DEBUG)
|
2023-12-17 22:03:39 +00:00
|
|
|
shapely_errors = StringIO() # in memory file to store shapely errors
|
2018-11-15 01:23:06 +00:00
|
|
|
ch = logging.StreamHandler(shapely_errors)
|
|
|
|
ch.setLevel(logging.DEBUG)
|
|
|
|
formatter = logging.Formatter('%(name)s - %(levelname)s - %(message)s')
|
|
|
|
ch.setFormatter(formatter)
|
|
|
|
logger.addHandler(ch)
|
|
|
|
|
2023-12-17 22:03:39 +00:00
|
|
|
# pop '--extension' from arguments and generate extension class name from extension name
|
2018-04-29 02:14:23 +00:00
|
|
|
parser = ArgumentParser()
|
|
|
|
parser.add_argument("--extension")
|
|
|
|
my_args, remaining_args = parser.parse_known_args()
|
2018-04-29 01:26:53 +00:00
|
|
|
|
2018-04-29 02:14:23 +00:00
|
|
|
extension_name = my_args.extension
|
2018-07-29 00:40:14 +00:00
|
|
|
|
|
|
|
# example: foo_bar_baz -> FooBarBaz
|
|
|
|
extension_class_name = extension_name.title().replace("_", "")
|
|
|
|
|
|
|
|
extension_class = getattr(extensions, extension_class_name)
|
2023-12-17 22:03:39 +00:00
|
|
|
extension = extension_class() # create instance of extension class - call __init__ method
|
|
|
|
|
|
|
|
# extension run(), but we differentiate between debug and normal mode
|
|
|
|
# - in debug or profile mode we run extension or profile extension
|
|
|
|
# - in normal mode we run extension in try/except block to catch all exceptions
|
|
|
|
if debug_active or profile_type != "none": # if debug or profile mode
|
|
|
|
print(f"Extension:'{extension_name}' Debug active:{debug_active} type:'{debug_type}' "
|
|
|
|
f"Profile type:'{profile_type}'", file=sys.stderr)
|
|
|
|
profile_path = SCRIPTDIR / "profile_stats"
|
|
|
|
|
|
|
|
if profile_type == 'none':
|
|
|
|
extension.run(args=remaining_args)
|
|
|
|
elif profile_type == 'cprofile':
|
|
|
|
import cProfile
|
|
|
|
import pstats
|
|
|
|
profiler = cProfile.Profile()
|
2018-04-29 01:26:53 +00:00
|
|
|
|
2023-12-17 22:03:39 +00:00
|
|
|
profiler.enable()
|
|
|
|
extension.run(args=remaining_args)
|
2022-06-30 17:22:33 +00:00
|
|
|
profiler.disable()
|
|
|
|
|
2023-12-17 22:03:39 +00:00
|
|
|
profiler.dump_stats(profile_path.with_suffix(".prof")) # can be read by 'snakeviz -s' or 'pyprof2calltree'
|
|
|
|
with open(profile_path, 'w') as stats_file:
|
2022-06-30 17:22:33 +00:00
|
|
|
stats = pstats.Stats(profiler, stream=stats_file)
|
|
|
|
stats.sort_stats(pstats.SortKey.CUMULATIVE)
|
|
|
|
stats.print_stats()
|
2023-12-17 22:03:39 +00:00
|
|
|
print(f"profiling stats written to '{profile_path.name}' and '{profile_path.name}.prof'", file=sys.stderr)
|
|
|
|
|
|
|
|
elif profile_type == 'profile':
|
|
|
|
import profile
|
|
|
|
import pstats
|
|
|
|
profiler = profile.Profile()
|
|
|
|
|
|
|
|
profiler.run('extension.run(args=remaining_args)')
|
|
|
|
|
|
|
|
profiler.dump_stats(profile_path.with_suffix(".prof")) # can be read by 'snakeviz' or 'pyprof2calltree' - seems broken
|
|
|
|
with open(profile_path, 'w') as stats_file:
|
|
|
|
stats = pstats.Stats(profiler, stream=stats_file)
|
|
|
|
stats.sort_stats(pstats.SortKey.CUMULATIVE)
|
|
|
|
stats.print_stats()
|
|
|
|
print(f"profiling stats written to '{profile_path.name}'", file=sys.stderr)
|
|
|
|
|
|
|
|
elif profile_type == 'pyinstrument':
|
|
|
|
import pyinstrument
|
|
|
|
profiler = pyinstrument.Profiler()
|
|
|
|
|
|
|
|
profiler.start()
|
|
|
|
extension.run(args=remaining_args)
|
|
|
|
profiler.stop()
|
|
|
|
|
|
|
|
profile_path = SCRIPTDIR / "profile_stats.html"
|
|
|
|
with open(profile_path, 'w') as stats_file:
|
|
|
|
stats_file.write(profiler.output_html())
|
|
|
|
print(f"profiling stats written to '{profile_path.name}'", file=sys.stderr)
|
2022-06-30 17:22:33 +00:00
|
|
|
|
2023-12-17 22:03:39 +00:00
|
|
|
else: # if not debug nor profile mode
|
|
|
|
save_stderr() # hide GTK spam
|
new extension: Auto-Route Satin Columns (#330)
**video demo:** https://www.youtube.com/watch?v=tbghtqziB1g
This branch adds a new extension, Auto-Route Satin Columns, implementing #214! This is a huge new feature that opens the door wide for exciting stuff like lettering (#142).
To use it, select some satin columns and run the extension. After a few seconds, it will replace your satins with a new set with a logical stitching order. Under-pathing and jump-stitches will be added as necessary, and satins will be broken to facilitate jumps. The resulting satins will retain all of the parameters you had set on the original satins, including underlay, zig-zag spacing, etc.
By default, it will choose the left-most extreme as the starting point and the right-most extreme as the ending point (even if these occur partway through a satin such as the left edge of a letter "o"). You can override this by attaching the new "Auto-route satin stitch starting/ending position" commands.
There's also an option to add trims instead of jump stitches. Any jump stitch over 1mm is trimmed. I might make this configurable in the future but in my tests it seems to do a good job. Trim commands are added to the SVG, so it's easy enough to modify/delete as you see fit.
2018-10-30 23:43:21 +00:00
|
|
|
exception = None
|
|
|
|
try:
|
2021-03-04 17:40:53 +00:00
|
|
|
extension.run(args=remaining_args)
|
new extension: Auto-Route Satin Columns (#330)
**video demo:** https://www.youtube.com/watch?v=tbghtqziB1g
This branch adds a new extension, Auto-Route Satin Columns, implementing #214! This is a huge new feature that opens the door wide for exciting stuff like lettering (#142).
To use it, select some satin columns and run the extension. After a few seconds, it will replace your satins with a new set with a logical stitching order. Under-pathing and jump-stitches will be added as necessary, and satins will be broken to facilitate jumps. The resulting satins will retain all of the parameters you had set on the original satins, including underlay, zig-zag spacing, etc.
By default, it will choose the left-most extreme as the starting point and the right-most extreme as the ending point (even if these occur partway through a satin such as the left edge of a letter "o"). You can override this by attaching the new "Auto-route satin stitch starting/ending position" commands.
There's also an option to add trims instead of jump stitches. Any jump stitch over 1mm is trimmed. I might make this configurable in the future but in my tests it seems to do a good job. Trim commands are added to the SVG, so it's easy enough to modify/delete as you see fit.
2018-10-30 23:43:21 +00:00
|
|
|
except (SystemExit, KeyboardInterrupt):
|
|
|
|
raise
|
2021-03-04 17:40:53 +00:00
|
|
|
except XMLSyntaxError:
|
|
|
|
msg = _("Ink/Stitch cannot read your SVG file. "
|
|
|
|
"This is often the case when you use a file which has been created with Adobe Illustrator.")
|
|
|
|
msg += "\n\n"
|
|
|
|
msg += _("Try to import the file into Inkscape through 'File > Import...' (Ctrl+I)")
|
|
|
|
errormsg(msg)
|
2023-09-07 17:25:47 +00:00
|
|
|
except InkstitchException as exc:
|
|
|
|
errormsg(str(exc))
|
new extension: Auto-Route Satin Columns (#330)
**video demo:** https://www.youtube.com/watch?v=tbghtqziB1g
This branch adds a new extension, Auto-Route Satin Columns, implementing #214! This is a huge new feature that opens the door wide for exciting stuff like lettering (#142).
To use it, select some satin columns and run the extension. After a few seconds, it will replace your satins with a new set with a logical stitching order. Under-pathing and jump-stitches will be added as necessary, and satins will be broken to facilitate jumps. The resulting satins will retain all of the parameters you had set on the original satins, including underlay, zig-zag spacing, etc.
By default, it will choose the left-most extreme as the starting point and the right-most extreme as the ending point (even if these occur partway through a satin such as the left edge of a letter "o"). You can override this by attaching the new "Auto-route satin stitch starting/ending position" commands.
There's also an option to add trims instead of jump stitches. Any jump stitch over 1mm is trimmed. I might make this configurable in the future but in my tests it seems to do a good job. Trim commands are added to the SVG, so it's easy enough to modify/delete as you see fit.
2018-10-30 23:43:21 +00:00
|
|
|
except Exception:
|
2023-09-07 17:25:47 +00:00
|
|
|
errormsg(format_uncaught_exception())
|
|
|
|
sys.exit(1)
|
new extension: Auto-Route Satin Columns (#330)
**video demo:** https://www.youtube.com/watch?v=tbghtqziB1g
This branch adds a new extension, Auto-Route Satin Columns, implementing #214! This is a huge new feature that opens the door wide for exciting stuff like lettering (#142).
To use it, select some satin columns and run the extension. After a few seconds, it will replace your satins with a new set with a logical stitching order. Under-pathing and jump-stitches will be added as necessary, and satins will be broken to facilitate jumps. The resulting satins will retain all of the parameters you had set on the original satins, including underlay, zig-zag spacing, etc.
By default, it will choose the left-most extreme as the starting point and the right-most extreme as the ending point (even if these occur partway through a satin such as the left edge of a letter "o"). You can override this by attaching the new "Auto-route satin stitch starting/ending position" commands.
There's also an option to add trims instead of jump stitches. Any jump stitch over 1mm is trimmed. I might make this configurable in the future but in my tests it seems to do a good job. Trim commands are added to the SVG, so it's easy enough to modify/delete as you see fit.
2018-10-30 23:43:21 +00:00
|
|
|
finally:
|
|
|
|
restore_stderr()
|
|
|
|
|
|
|
|
if shapely_errors.tell():
|
2021-03-04 17:40:53 +00:00
|
|
|
errormsg(shapely_errors.getvalue())
|
new extension: Auto-Route Satin Columns (#330)
**video demo:** https://www.youtube.com/watch?v=tbghtqziB1g
This branch adds a new extension, Auto-Route Satin Columns, implementing #214! This is a huge new feature that opens the door wide for exciting stuff like lettering (#142).
To use it, select some satin columns and run the extension. After a few seconds, it will replace your satins with a new set with a logical stitching order. Under-pathing and jump-stitches will be added as necessary, and satins will be broken to facilitate jumps. The resulting satins will retain all of the parameters you had set on the original satins, including underlay, zig-zag spacing, etc.
By default, it will choose the left-most extreme as the starting point and the right-most extreme as the ending point (even if these occur partway through a satin such as the left edge of a letter "o"). You can override this by attaching the new "Auto-route satin stitch starting/ending position" commands.
There's also an option to add trims instead of jump stitches. Any jump stitch over 1mm is trimmed. I might make this configurable in the future but in my tests it seems to do a good job. Trim commands are added to the SVG, so it's easy enough to modify/delete as you see fit.
2018-10-30 23:43:21 +00:00
|
|
|
|
2023-09-07 17:25:47 +00:00
|
|
|
sys.exit(0)
|