diff options
| author | Lex Neva <github.com@lexneva.name> | 2018-04-28 21:56:35 -0400 |
|---|---|---|
| committer | Lex Neva <github.com@lexneva.name> | 2018-04-29 21:45:55 -0400 |
| commit | d332e36d16ff3325427a0f2129af503c6189dcdc (patch) | |
| tree | c3aeafbd0691b0d9baa514e2cc35d59ddf362491 | |
| parent | dc61707aa459ebfb9d3a083dcc1181d34af61c5f (diff) | |
remove old extension scripts
| -rw-r--r-- | embroider.py | 112 | ||||
| -rw-r--r-- | embroider_palettes.py | 121 | ||||
| -rw-r--r-- | embroider_params.py | 772 | ||||
| -rw-r--r-- | embroider_print.py | 414 | ||||
| -rw-r--r-- | embroider_simulate.py | 285 | ||||
| -rw-r--r-- | messages.po | 254 |
6 files changed, 127 insertions, 1831 deletions
diff --git a/embroider.py b/embroider.py deleted file mode 100644 index 8c5d135b..00000000 --- a/embroider.py +++ /dev/null @@ -1,112 +0,0 @@ -#!/usr/bin/python -# -# Important resources: -# lxml interface for walking SVG tree: -# http://codespeak.net/lxml/tutorial.html#elementpath -# Inkscape library for extracting paths from SVG: -# http://wiki.inkscape.org/wiki/index.php/Python_modules_for_extensions#simplepath.py -# Shapely computational geometry library: -# http://gispython.org/shapely/manual.html#multipolygons -# Embroidery file format documentation: -# http://www.achatina.de/sewing/main/TECHNICL.HTM - -import sys -import traceback -sys.path.append("/usr/share/inkscape/extensions") -import os - -import inkex -import inkstitch -from inkstitch import _, PIXELS_PER_MM -from inkstitch.extensions import InkstitchExtension -from inkstitch.stitch_plan import patches_to_stitch_plan -from inkstitch.svg import render_stitch_plan - - -class Embroider(InkstitchExtension): - def __init__(self, *args, **kwargs): - InkstitchExtension.__init__(self) - self.OptionParser.add_option("-c", "--collapse_len_mm", - action="store", type="float", - dest="collapse_length_mm", default=3.0, - help="max collapse length (mm)") - self.OptionParser.add_option("--hide_layers", - action="store", type="choice", - choices=["true", "false"], - dest="hide_layers", default="true", - help="Hide all other layers when the embroidery layer is generated") - self.OptionParser.add_option("-O", "--output_format", - action="store", type="string", - dest="output_format", default="csv", - help="Output file extenstion (default: csv)") - self.OptionParser.add_option("-P", "--path", - action="store", type="string", - dest="path", default=".", - help="Directory in which to store output file") - self.OptionParser.add_option("-F", "--output-file", - action="store", type="string", - dest="output_file", - help="Output filename.") - self.OptionParser.add_option("-b", "--max-backups", - action="store", type="int", - dest="max_backups", default=5, - help="Max number of backups of output files to keep.") - self.OptionParser.usage += _("\n\nSeeing a 'no such option' message? Please restart Inkscape to fix.") - - def get_output_path(self): - if self.options.output_file: - output_path = os.path.join(self.options.path, self.options.output_file) - else: - svg_filename = self.document.getroot().get(inkex.addNS('docname', 'sodipodi'), "embroidery.svg") - csv_filename = svg_filename.replace('.svg', '.%s' % self.options.output_format) - output_path = os.path.join(self.options.path, csv_filename) - - def add_suffix(path, suffix): - if suffix > 0: - path = "%s.%s" % (path, suffix) - - return path - - def move_if_exists(path, suffix=0): - source = add_suffix(path, suffix) - - if suffix >= self.options.max_backups: - return - - dest = add_suffix(path, suffix + 1) - - if os.path.exists(source): - move_if_exists(path, suffix + 1) - - if os.path.exists(dest): - os.remove(dest) - - os.rename(source, dest) - - move_if_exists(output_path) - - return output_path - - def effect(self): - if not self.get_elements(): - return - - if self.options.hide_layers: - self.hide_all_layers() - - patches = self.elements_to_patches(self.elements) - stitch_plan = patches_to_stitch_plan(patches, self.options.collapse_length_mm * PIXELS_PER_MM) - inkstitch.write_embroidery_file(self.get_output_path(), stitch_plan, self.document.getroot()) - render_stitch_plan(self.document.getroot(), stitch_plan) - - -if __name__ == '__main__': - sys.setrecursionlimit(100000) - e = Embroider() - - try: - e.affect() - except KeyboardInterrupt: - # for use at the command prompt for debugging - print >> sys.stderr, "interrupted!" - print >> sys.stderr, traceback.format_exc() diff --git a/embroider_palettes.py b/embroider_palettes.py deleted file mode 100644 index a3b59834..00000000 --- a/embroider_palettes.py +++ /dev/null @@ -1,121 +0,0 @@ -#!/usr/bin/python -# - -import sys -import traceback -import os -from os.path import realpath, dirname -from glob import glob -from threading import Thread -import socket -import errno -import time -import logging -import wx -import inkex -from inkstitch.utils import guess_inkscape_config_path - - -class InstallPalettesFrame(wx.Frame): - def __init__(self, *args, **kwargs): - wx.Frame.__init__(self, *args, **kwargs) - - default_path = os.path.join(guess_inkscape_config_path(), "palettes") - - panel = wx.Panel(self) - sizer = wx.BoxSizer(wx.VERTICAL) - - text = wx.StaticText(panel, label=_("Directory in which to install palettes:")) - font = wx.Font(12, wx.DEFAULT, wx.NORMAL, wx.NORMAL) - text.SetFont(font) - sizer.Add(text, proportion=0, flag=wx.ALL|wx.EXPAND, border=10) - - path_sizer = wx.BoxSizer(wx.HORIZONTAL) - self.path_input = wx.TextCtrl(panel, wx.ID_ANY, value=default_path) - path_sizer.Add(self.path_input, proportion=3, flag=wx.RIGHT|wx.EXPAND, border=20) - chooser_button = wx.Button(panel, wx.ID_OPEN, _('Choose another directory...')) - path_sizer.Add(chooser_button, proportion=1, flag=wx.EXPAND) - sizer.Add(path_sizer, proportion=0, flag=wx.ALL|wx.EXPAND, border=10) - - buttons_sizer = wx.BoxSizer(wx.HORIZONTAL) - install_button = wx.Button(panel, wx.ID_ANY, _("Install")) - install_button.SetBitmap(wx.ArtProvider.GetBitmap(wx.ART_TICK_MARK)) - buttons_sizer.Add(install_button, proportion=0, flag=wx.ALIGN_RIGHT|wx.ALL, border=5) - cancel_button = wx.Button(panel, wx.ID_CANCEL, _("Cancel")) - buttons_sizer.Add(cancel_button, proportion=0, flag=wx.ALIGN_RIGHT|wx.ALL, border=5) - sizer.Add(buttons_sizer, proportion=0, flag=wx.ALIGN_RIGHT) - - outer_sizer = wx.BoxSizer(wx.HORIZONTAL) - outer_sizer.Add(sizer, proportion=0, flag=wx.ALIGN_CENTER_VERTICAL) - - panel.SetSizer(outer_sizer) - panel.Layout() - - chooser_button.Bind(wx.EVT_BUTTON, self.chooser_button_clicked) - cancel_button.Bind(wx.EVT_BUTTON, self.cancel_button_clicked) - install_button.Bind(wx.EVT_BUTTON, self.install_button_clicked) - - def cancel_button_clicked(self, event): - self.Destroy() - - def chooser_button_clicked(self, event): - dialog = wx.DirDialog(self, _("Choose Inkscape palettes directory")) - if dialog.ShowModal() != wx.ID_CANCEL: - self.path_input.SetValue(dialog.GetPath()) - - def install_button_clicked(self, event): - try: - self.install_palettes() - except Exception, e: - wx.MessageDialog(self, - _('Thread palette installation failed') + ': \n' + traceback.format_exc(), - _('Installation Failed'), - wx.OK).ShowModal() - else: - wx.MessageDialog(self, - _('Thread palette files have been installed. Please restart Inkscape to load the new palettes.'), - _('Installation Completed'), - wx.OK).ShowModal() - - self.Destroy() - - def install_palettes(self): - path = self.path_input.GetValue() - palettes_dir = self.get_bundled_palettes_dir() - self.copy_files(glob(os.path.join(palettes_dir, "*")), path) - - def get_bundled_palettes_dir(self): - if getattr(sys, 'frozen', None) is not None: - return realpath(os.path.join(sys._MEIPASS, '..', 'palettes')) - else: - return os.path.join(dirname(realpath(__file__)), 'palettes') - - if (sys.platform == "win32"): - # If we try to just use shutil.copy it says the operation requires elevation. - def copy_files(self, files, dest): - import winutils - - winutils.copy(files, dest) - else: - def copy_files(self, files, dest): - import shutil - - if not os.path.exists(dest): - os.makedirs(dest) - - for palette_file in files: - shutil.copy(palette_file, dest) - -class InstallPalettes(inkex.Effect): - def effect(self): - app = wx.App() - installer_frame = InstallPalettesFrame(None, title=_("Ink/Stitch Thread Palette Installer"), size=(450, 200)) - installer_frame.Show() - app.MainLoop() - - -if __name__ == '__main__': - #save_stderr() - effect = InstallPalettes() - effect.affect() - #restore_stderr() diff --git a/embroider_params.py b/embroider_params.py deleted file mode 100644 index 607dbb2a..00000000 --- a/embroider_params.py +++ /dev/null @@ -1,772 +0,0 @@ -#!/usr/bin/env python -# -*- coding: UTF-8 -*- - -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 -import inkstitch -from functools import partial -from itertools import groupby -from inkstitch import _ -from inkstitch.extensions import InkstitchExtension -from inkstitch.stitch_plan import patches_to_stitch_plan -from inkstitch.elements import EmbroideryElement, Fill, AutoFill, Stroke, SatinColumn -from inkstitch.utils import save_stderr, restore_stderr -from embroider_simulate import EmbroiderySimulator - - -def presets_path(): - try: - import appdirs - config_path = appdirs.user_config_dir('inkstitch') - except ImportError: - config_path = os.path.expanduser('~/.inkstitch') - - if not os.path.exists(config_path): - os.makedirs(config_path) - return os.path.join(config_path, 'presets.json') - - -def load_presets(): - try: - with open(presets_path(), 'r') as presets: - presets = json.load(presets) - return presets - except: - return {} - - -def save_presets(presets): - with open(presets_path(), 'w') as presets_file: - json.dump(presets, presets_file) - - -def load_preset(name): - return load_presets().get(name) - - -def save_preset(name, data): - presets = load_presets() - presets[name] = data - save_presets(presets) - - -def delete_preset(name): - presets = load_presets() - presets.pop(name, None) - save_presets(presets) - - -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'): - dlg = wx.MessageDialog(parent, message, caption, wx.OK | wx.ICON_INFORMATION) - dlg.ShowModal() - dlg.Destroy() - - -class ParamsTab(ScrolledPanel): - def __init__(self, *args, **kwargs): - self.params = kwargs.pop('params', []) - self.name = kwargs.pop('name', None) - self.nodes = kwargs.pop('nodes') - kwargs["style"] = wx.TAB_TRAVERSAL - ScrolledPanel.__init__(self, *args, **kwargs) - self.SetupScrolling() - - self.changed_inputs = set() - self.dependent_tabs = [] - self.parent_tab = None - self.param_inputs = {} - self.paired_tab = None - self.disable_notify_pair = False - - toggles = [param for param in self.params if param.type == 'toggle'] - - if toggles: - self.toggle = toggles[0] - self.params.remove(self.toggle) - self.toggle_checkbox = wx.CheckBox(self, label=self.toggle.description) - - value = any(self.toggle.values) - if self.toggle.inverse: - value = not value - self.toggle_checkbox.SetValue(value) - - self.toggle_checkbox.Bind(wx.EVT_CHECKBOX, self.update_toggle_state) - self.toggle_checkbox.Bind(wx.EVT_CHECKBOX, self.changed) - - self.param_inputs[self.toggle.name] = self.toggle_checkbox - else: - self.toggle = None - - self.settings_grid = wx.FlexGridSizer(rows=0, cols=3, hgap=10, vgap=10) - self.settings_grid.AddGrowableCol(0, 1) - self.settings_grid.SetFlexibleDirection(wx.HORIZONTAL) - - self.__set_properties() - self.__do_layout() - - if self.toggle: - self.update_toggle_state() - # end wxGlade - - def pair(self, tab): - # print self.name, "paired with", tab.name - self.paired_tab = tab - self.update_description() - - def add_dependent_tab(self, tab): - self.dependent_tabs.append(tab) - self.update_description() - - def set_parent_tab(self, tab): - self.parent_tab = tab - - def is_dependent_tab(self): - return self.parent_tab is not None - - def enabled(self): - if self.toggle_checkbox: - return self.toggle_checkbox.IsChecked() - else: - return True - - def update_toggle_state(self, event=None, notify_pair=True): - enable = self.enabled() - # print self.name, "update_toggle_state", enable - for child in self.settings_grid.GetChildren(): - widget = child.GetWindow() - if widget: - child.GetWindow().Enable(enable) - - if notify_pair and self.paired_tab: - self.paired_tab.pair_changed(enable) - - for tab in self.dependent_tabs: - tab.dependent_enable(enable) - - if event: - event.Skip() - - def pair_changed(self, value): - # print self.name, "pair_changed", value - new_value = not value - - if self.enabled() != new_value: - self.set_toggle_state(not value) - self.update_toggle_state(notify_pair=False) - - if self.on_change_hook: - self.on_change_hook(self) - - def dependent_enable(self, enable): - if enable: - self.toggle_checkbox.Enable() - else: - self.set_toggle_state(False) - self.toggle_checkbox.Disable() - self.update_toggle_state() - - if self.on_change_hook: - self.on_change_hook(self) - - def set_toggle_state(self, value): - if self.toggle_checkbox: - self.toggle_checkbox.SetValue(value) - self.changed_inputs.add(self.toggle_checkbox) - - def get_values(self): - values = {} - - if self.toggle: - checked = self.enabled() - if self.toggle_checkbox in self.changed_inputs and not self.toggle.inverse: - values[self.toggle.name] = checked - - if not checked: - # Ignore params on this tab if the toggle is unchecked, - # because they're grayed out anyway. - return values - - for name, input in self.param_inputs.iteritems(): - if input in self.changed_inputs and input != self.toggle_checkbox: - values[name] = input.GetValue() - - return values - - def apply(self): - values = self.get_values() - for node in self.nodes: - # print >> sys.stderr, "apply: ", self.name, node.id, values - for name, value in values.iteritems(): - node.set_param(name, value) - - def on_change(self, callable): - self.on_change_hook = callable - - def changed(self, event): - self.changed_inputs.add(event.GetEventObject()) - event.Skip() - - if self.on_change_hook: - self.on_change_hook(self) - - def load_preset(self, preset): - preset_data = preset.get(self.name, {}) - - for name, value in preset_data.iteritems(): - if name in self.param_inputs: - self.param_inputs[name].SetValue(value) - self.changed_inputs.add(self.param_inputs[name]) - - 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() - - def update_description(self): - if len(self.nodes) == 1: - description = _("These settings will be applied to 1 object.") - else: - description = _("These settings will be applied to %d objects.") % len(self.nodes) - - if any(len(param.values) > 1 for param in self.params): - description += "\n • " + _("Some settings had different values across objects. Select a value from the dropdown or enter a new one.") - - if self.dependent_tabs: - if len(self.dependent_tabs) == 1: - description += "\n • " + _("Disabling this tab will disable the following %d tabs.") % len(self.dependent_tabs) - else: - description += "\n • " + _("Disabling this tab will disable the following tab.") - - if self.paired_tab: - description += "\n • " + _("Enabling this tab will disable %s and vice-versa.") % self.paired_tab.name - - self.description_text = description - - def resized(self, event): - if not hasattr(self, 'rewrap_timer'): - self.rewrap_timer = wx.Timer() - self.rewrap_timer.Bind(wx.EVT_TIMER, self.rewrap) - - # If we try to rewrap every time we get EVT_SIZE then a resize is - # extremely slow. - self.rewrap_timer.Start(50, oneShot=True) - event.Skip() - - def rewrap(self, event=None): - self.description.SetLabel(self.description_text) - self.description.Wrap(self.GetSize().x - 20) - self.description_container.Layout() - if event: - event.Skip() - - def __set_properties(self): - # begin wxGlade: SatinPane.__set_properties - # end wxGlade - pass - - def __do_layout(self): - # just to add space around the settings - box = wx.BoxSizer(wx.VERTICAL) - - summary_box = wx.StaticBox(self, wx.ID_ANY, label=_("Inkscape objects")) - sizer = wx.StaticBoxSizer(summary_box, wx.HORIZONTAL) -# sizer = wx.BoxSizer(wx.HORIZONTAL) - self.description = wx.StaticText(self) - self.update_description() - 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) - 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) - - for param in self.params: - 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) - - if param.type == 'boolean': - - if len(param.values) > 1: - input = wx.CheckBox(self, style=wx.CHK_3STATE) - input.Set3StateValue(wx.CHK_UNDETERMINED) - else: - input = wx.CheckBox(self) - if param.values: - input.SetValue(param.values[0]) - - 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.Bind(wx.EVT_COMBOBOX, self.changed) - input.Bind(wx.EVT_TEXT, self.changed) - else: - value = param.values[0] if param.values else "" - input = wx.TextCtrl(self, wx.ID_ANY, value=str(value)) - input.Bind(wx.EVT_TEXT, self.changed) - - self.param_inputs[param.name] = input - - self.settings_grid.Add(input, proportion=1, flag=wx.ALIGN_CENTER_VERTICAL) - self.settings_grid.Add(wx.StaticText(self, label=param.unit or ""), proportion=1, flag=wx.ALIGN_CENTER_VERTICAL) - - box.Add(self.settings_grid, proportion=1, flag=wx.ALL, border=10) - self.SetSizer(box) - - self.Layout() - -# end of class SatinPane - -class SettingsFrame(wx.Frame): - def __init__(self, *args, **kwargs): - # begin wxGlade: MyFrame.__init__ - self.tabs_factory = kwargs.pop('tabs_factory', []) - self.cancel_hook = kwargs.pop('on_cancel', None) - wx.Frame.__init__(self, None, wx.ID_ANY, - _("Embroidery Params") - ) - self.notebook = wx.Notebook(self, wx.ID_ANY) - self.tabs = self.tabs_factory(self.notebook) - - for tab in self.tabs: - tab.on_change(self.update_simulator) - - self.simulate_window = None - self.simulate_thread = None - self.simulate_refresh_needed = Event() - - wx.CallLater(1000, self.update_simulator) - - self.presets_box = wx.StaticBox(self, wx.ID_ANY, label=_("Presets")) - - self.preset_chooser = wx.ComboBox(self, wx.ID_ANY) - self.update_preset_list() - - self.load_preset_button = wx.Button(self, wx.ID_ANY, _("Load")) - self.load_preset_button.Bind(wx.EVT_BUTTON, self.load_preset) - - self.add_preset_button = wx.Button(self, wx.ID_ANY, _("Add")) - self.add_preset_button.Bind(wx.EVT_BUTTON, self.add_preset) - - self.overwrite_preset_button = wx.Button(self, wx.ID_ANY, _("Overwrite")) - self.overwrite_preset_button.Bind(wx.EVT_BUTTON, self.overwrite_preset) - - self.delete_preset_button = wx.Button(self, wx.ID_ANY, _("Delete")) - self.delete_preset_button.Bind(wx.EVT_BUTTON, self.delete_preset) - - self.cancel_button = wx.Button(self, wx.ID_ANY, _("Cancel")) - self.cancel_button.Bind(wx.EVT_BUTTON, self.cancel) - self.Bind(wx.EVT_CLOSE, self.cancel) - - self.use_last_button = wx.Button(self, wx.ID_ANY, _("Use Last Settings")) - self.use_last_button.Bind(wx.EVT_BUTTON, self.use_last) - - self.apply_button = wx.Button(self, wx.ID_ANY, _("Apply and Quit")) - self.apply_button.Bind(wx.EVT_BUTTON, self.apply) - - self.__set_properties() - self.__do_layout() - # end wxGlade - - def update_simulator(self, tab=None): - if self.simulate_window: - self.simulate_window.stop() - self.simulate_window.clear() - - if not self.simulate_thread or not self.simulate_thread.is_alive(): - self.simulate_thread = Thread(target=self.simulate_worker) - self.simulate_thread.daemon = True - self.simulate_thread.start() - - self.simulate_refresh_needed.set() - - def simulate_worker(self): - while True: - self.simulate_refresh_needed.wait() - self.simulate_refresh_needed.clear() - self.update_patches() - - def update_patches(self): - patches = self.generate_patches() - - if patches and not self.simulate_refresh_needed.is_set(): - wx.CallAfter(self.refresh_simulator, patches) - - def refresh_simulator(self, patches): - stitch_plan = patches_to_stitch_plan(patches) - if self.simulate_window: - self.simulate_window.stop() - self.simulate_window.load(stitch_plan=stitch_plan) - else: - my_rect = self.GetRect() - simulator_pos = my_rect.GetTopRight() - simulator_pos.x += 5 - - screen_rect = wx.Display(0).ClientArea - max_width = screen_rect.GetWidth() - my_rect.GetWidth() - max_height = screen_rect.GetHeight() - - try: - self.simulate_window = EmbroiderySimulator(None, -1, _("Preview"), - simulator_pos, - size=(300, 300), - stitch_plan=stitch_plan, - on_close=self.simulate_window_closed, - target_duration=5, - max_width=max_width, - max_height=max_height) - except: - 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: - pass - - info_dialog(self, error, _("Internal Error")) - - self.simulate_window.Show() - wx.CallLater(10, self.Raise) - - wx.CallAfter(self.simulate_window.go) - - def simulate_window_closed(self): - self.simulate_window = None - - def generate_patches(self): - patches = [] - nodes = [] - - for tab in self.tabs: - tab.apply() - - if tab.enabled() and not tab.is_dependent_tab(): - nodes.extend(tab.nodes) - - # sort nodes into the proper stacking order - nodes.sort(key=lambda node: node.order) - - try: - for node in nodes: - if self.simulate_refresh_needed.is_set(): - # cancel; params were updated and we need to start over - return [] - - # Making a copy of the embroidery element is an easy - # way to drop the cache in the @cache decorators used - # for many params in embroider.py. - - patches.extend(copy(node).embroider(None)) - except SystemExit: - raise - except: - # Ignore errors. This can be things like incorrect paths for - # satins or division by zero caused by incorrect param values. - pass - - return patches - - def update_preset_list(self): - preset_names = load_presets().keys() - preset_names = [preset for preset in preset_names if preset != "__LAST__"] - self.preset_chooser.SetItems(sorted(preset_names)) - - def get_preset_name(self): - preset_name = self.preset_chooser.GetValue().strip() - if preset_name: - return preset_name - else: - info_dialog(self, _("Please enter or select a preset name first."), caption=_('Preset')) - return - - def check_and_load_preset(self, preset_name): - preset = load_preset(preset_name) - if not preset: - info_dialog(self, _('Preset "%s" not found.') % preset_name, caption=_('Preset')) - - return preset - - def get_preset_data(self): - preset = {} - - current_tab = self.tabs[self.notebook.GetSelection()] - while current_tab.parent_tab: - current_tab = current_tab.parent_tab - - tabs = [current_tab] - if current_tab.paired_tab: - tabs.append(current_tab.paired_tab) - tabs.extend(current_tab.paired_tab.dependent_tabs) - tabs.extend(current_tab.dependent_tabs) - - for tab in tabs: - tab.save_preset(preset) - - return preset - - def add_preset(self, event, overwrite=False): - preset_name = self.get_preset_name() - if not preset_name: - return - - if not overwrite and load_preset(preset_name): - info_dialog(self, _('Preset "%s" already exists. Please use another name or press "Overwrite"') % preset_name, caption=_('Preset')) - - save_preset(preset_name, self.get_preset_data()) - self.update_preset_list() - - event.Skip() - - 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: - return - - for tab in self.tabs: - tab.load_preset(preset) - - - def load_preset(self, event): - preset_name = self.get_preset_name() - if not preset_name: - return - - self._load_preset(preset_name) - - event.Skip() - - - def delete_preset(self, event): - preset_name = self.get_preset_name() - if not preset_name: - return - - preset = self.check_and_load_preset(preset_name) - if not preset: - return - - delete_preset(preset_name) - self.update_preset_list() - self.preset_chooser.SetValue("") - - event.Skip() - - def _apply(self): - for tab in self.tabs: - tab.apply() - - def apply(self, event): - self._apply() - save_preset("__LAST__", self.get_preset_data()) - self.close() - - def use_last(self, event): - self._load_preset("__LAST__") - self.apply(event) - - def close(self): - if self.simulate_window: - self.simulate_window.stop() - self.simulate_window.Close() - - self.Destroy() - - def cancel(self, event): - if self.cancel_hook: - self.cancel_hook() - - self.close() - - def __set_properties(self): - # begin wxGlade: MyFrame.__set_properties - self.notebook.SetMinSize((800, 600)) - self.preset_chooser.SetSelection(-1) - # end wxGlade - - def __do_layout(self): - # begin wxGlade: MyFrame.__do_layout - sizer_1 = wx.BoxSizer(wx.VERTICAL) - #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(sizer_3, 0, wx.ALIGN_RIGHT, 0) - self.SetSizer(sizer_1) - sizer_1.Fit(self) - self.Layout() - # end wxGlade - -class EmbroiderParams(InkstitchExtension): - def __init__(self, *args, **kwargs): - self.cancelled = False - InkstitchExtension.__init__(self, *args, **kwargs) - - def embroidery_classes(self, node): - element = EmbroideryElement(node) - classes = [] - - if element.get_style("fill"): - classes.append(AutoFill) - classes.append(Fill) - - if element.get_style("stroke"): - classes.append(Stroke) - - if element.get_style("stroke-dasharray") is None: - classes.append(SatinColumn) - - return classes - - def get_nodes_by_class(self): - nodes = self.get_nodes() - nodes_by_class = defaultdict(list) - - for z, node in enumerate(nodes): - for cls in self.embroidery_classes(node): - element = cls(node) - element.order = z - nodes_by_class[cls].append(element) - - return sorted(nodes_by_class.items(), key=lambda (cls, nodes): cls.__name__) - - def get_values(self, param, nodes): - getter = 'get_param' - - if param.type in ('toggle', 'boolean'): - getter = 'get_boolean_param' - else: - getter = 'get_param' - - values = filter(lambda item: item is not None, - (getattr(node, getter)(param.name, str(param.default)) for node in nodes)) - - return values - - def group_params(self, params): - def by_group_and_sort_index(param): - return param.group, param.sort_index - - def by_group(param): - return param.group - - return groupby(sorted(params, key=by_group_and_sort_index), by_group) - - def create_tabs(self, parent): - tabs = [] - for cls, nodes in self.get_nodes_by_class(): - params = cls.get_params() - - for param in params: - param.values = list(set(self.get_values(param, nodes))) - - 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) - 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) - - 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) - - return tabs - - - def cancel(self): - self.cancelled = True - - def effect(self): - app = wx.App() - frame = SettingsFrame(tabs_factory=self.create_tabs, on_cancel=self.cancel) - frame.Show() - app.MainLoop() - - if self.cancelled: - # This prevents the superclass from outputting the SVG, because we - # may have modified the DOM. - sys.exit(0) - - -# end of class MyFrame -if __name__ == "__main__": - save_stderr() - - try: - e = EmbroiderParams() - e.affect() - except SystemExit: - raise - except: - traceback.print_exc() - - restore_stderr() diff --git a/embroider_print.py b/embroider_print.py deleted file mode 100644 index ee9193a8..00000000 --- a/embroider_print.py +++ /dev/null @@ -1,414 +0,0 @@ -#!/usr/bin/python -# - -import sys -import traceback -import os -from threading import Thread -import socket -import errno -import time -import logging -from copy import deepcopy -import wx -import appdirs -import json - -import inkex -import inkstitch -from inkstitch import _, PIXELS_PER_MM, SVG_GROUP_TAG -from inkstitch.extensions import InkstitchExtension -from inkstitch.stitch_plan import patches_to_stitch_plan -from inkstitch.svg import render_stitch_plan -from inkstitch.utils import save_stderr, restore_stderr -from inkstitch.threads import ThreadCatalog - -from jinja2 import Environment, FileSystemLoader, select_autoescape -from datetime import date -import base64 - -from flask import Flask, request, Response, send_from_directory, jsonify -import webbrowser -import requests - - -def datetimeformat(value, format='%Y/%m/%d'): - return value.strftime(format) - - -def defaults_path(): - defaults_dir = appdirs.user_config_dir('inkstitch') - - if not os.path.exists(defaults_dir): - os.makedirs(defaults_dir) - - return os.path.join(defaults_dir, 'print_settings.json') - - -def load_defaults(): - try: - with open(defaults_path(), 'r') as defaults_file: - defaults = json.load(defaults_file) - return defaults - except: - return {} - - -def save_defaults(defaults): - with open(defaults_path(), 'w') as defaults_file: - json.dump(defaults, defaults_file) - - -def open_url(url): - # Avoid spurious output from xdg-open. Any output on stdout will crash - # inkscape. - null = open(os.devnull, 'w') - old_stdout = os.dup(sys.stdout.fileno()) - os.dup2(null.fileno(), sys.stdout.fileno()) - - if getattr(sys, 'frozen', False): - - # PyInstaller sets LD_LIBRARY_PATH. We need to temporarily clear it - # to avoid confusing xdg-open, which webbrowser will run. - - # The following code is adapted from PyInstaller's documentation - # http://pyinstaller.readthedocs.io/en/stable/runtime-information.html - - old_environ = dict(os.environ) # make a copy of the environment - lp_key = 'LD_LIBRARY_PATH' # for Linux and *BSD. - lp_orig = os.environ.get(lp_key + '_ORIG') # pyinstaller >= 20160820 has this - if lp_orig is not None: - os.environ[lp_key] = lp_orig # restore the original, unmodified value - else: - os.environ.pop(lp_key, None) # last resort: remove the env var - - webbrowser.open(url) - - # restore the old environ - os.environ.clear() - os.environ.update(old_environ) - else: - webbrowser.open(url) - - # restore file descriptors - os.dup2(old_stdout, sys.stdout.fileno()) - os.close(old_stdout) - - -class PrintPreviewServer(Thread): - def __init__(self, *args, **kwargs): - self.html = kwargs.pop('html') - self.metadata = kwargs.pop('metadata') - self.stitch_plan = kwargs.pop('stitch_plan') - Thread.__init__(self, *args, **kwargs) - self.daemon = True - self.last_request_time = None - self.shutting_down = False - - self.__setup_app() - - def __set_resources_path(self): - if getattr(sys, 'frozen', False): - self.resources_path = os.path.join(sys._MEIPASS, 'print', 'resources') - else: - self.resources_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'print', 'resources') - - def __setup_app(self): - self.__set_resources_path() - self.app = Flask(__name__) - - @self.app.before_request - def request_started(): - self.last_request_time = time.time() - - @self.app.before_first_request - def start_watcher(): - self.watcher_thread = Thread(target=self.watch) - self.watcher_thread.daemon = True - self.watcher_thread.start() - - @self.app.route('/') - def index(): - return self.html - - @self.app.route('/shutdown', methods=['POST']) - def shutdown(): - self.shutting_down = True - request.environ.get('werkzeug.server.shutdown')() - return _('Closing...') + '<br/><br/>' + _('It is safe to close this window now.') - - @self.app.route('/resources/<path:resource>', methods=['GET']) - def resources(resource): - return send_from_directory(self.resources_path, resource, cache_timeout=1) - - @self.app.route('/ping') - def ping(): - # Javascript is letting us know it's still there. This resets self.last_request_time. - return "pong" - - @self.app.route('/printing/start') - def printing_start(): - # temporarily turn off the watcher while the print dialog is up, - # because javascript will be frozen - self.last_request_time = None - return "OK" - - @self.app.route('/printing/end') - def printing_end(): - # nothing to do here -- request_started() will restart the watcher - return "OK" - - @self.app.route('/settings/<field_name>', methods=['POST']) - def set_field(field_name): - self.metadata[field_name] = request.json['value'] - return "OK" - - @self.app.route('/settings/<field_mame>', methods=['GET']) - def get_field(field_name): - return jsonify(self.metadata[field_name]) - - @self.app.route('/settings', methods=['GET']) - def get_settings(): - settings = {} - settings.update(load_defaults()) - settings.update(self.metadata) - return jsonify(settings) - - @self.app.route('/defaults', methods=['POST']) - def set_defaults(): - save_defaults(request.json['value']) - return "OK" - - @self.app.route('/palette', methods=['POST']) - def set_palette(): - name = request.json['name'] - catalog = ThreadCatalog() - palette = catalog.get_palette_by_name(name) - catalog.apply_palette(self.stitch_plan, palette) - - # clear any saved color or thread names - for field in self.metadata: - if field.startswith('color-') or field.startswith('thread-'): - del self.metadata[field] - - self.metadata['thread-palette'] = name - - return "OK" - - @self.app.route('/threads', methods=['GET']) - def get_threads(): - threads = [] - for color_block in self.stitch_plan: - threads.append({ - 'hex': color_block.color.hex_digits, - 'name': color_block.color.name, - 'manufacturer': color_block.color.manufacturer, - 'number': color_block.color.number, - }) - - return jsonify(threads) - - def stop(self): - # for whatever reason, shutting down only seems possible in - # the context of a flask request, so we'll just make one - requests.post("http://%s:%s/shutdown" % (self.host, self.port)) - - def watch(self): - try: - while True: - time.sleep(1) - if self.shutting_down: - break - - if self.last_request_time is not None and \ - (time.time() - self.last_request_time) > 3: - self.stop() - break - except: - # seems like sometimes this thread blows up during shutdown - pass - - def disable_logging(self): - logging.getLogger('werkzeug').setLevel(logging.ERROR) - - def run(self): - self.disable_logging() - - self.host = "127.0.0.1" - self.port = 5000 - - while True: - try: - self.app.run(self.host, self.port, threaded=True) - except socket.error, e: - if e.errno == errno.EADDRINUSE: - self.port += 1 - continue - else: - raise - else: - break - - -class PrintInfoFrame(wx.Frame): - def __init__(self, *args, **kwargs): - self.print_server = kwargs.pop("print_server") - wx.Frame.__init__(self, *args, **kwargs) - - panel = wx.Panel(self) - sizer = wx.BoxSizer(wx.VERTICAL) - - text = wx.StaticText(panel, label=_("A print preview has been opened in your web browser. This window will stay open in order to communicate with the JavaScript code running in your browser.\n\nThis window will close after you close the print preview in your browser, or you can close it manually if necessary.")) - font = wx.Font(14, wx.DEFAULT, wx.NORMAL, wx.NORMAL) - text.SetFont(font) - sizer.Add(text, proportion=1, flag=wx.ALL|wx.EXPAND, border=20) - - stop_button = wx.Button(panel, id=wx.ID_CLOSE) - stop_button.Bind(wx.EVT_BUTTON, self.close_button_clicked) - sizer.Add(stop_button, proportion=0, flag=wx.ALIGN_CENTER|wx.ALL, border=10) - - panel.SetSizer(sizer) - panel.Layout() - - self.timer = wx.PyTimer(self.__watcher) - self.timer.Start(250) - - def close_button_clicked(self, event): - self.print_server.stop() - - def __watcher(self): - if not self.print_server.is_alive(): - self.timer.Stop() - self.timer = None - self.Destroy() - - -class Print(InkstitchExtension): - def build_environment(self): - if getattr( sys, 'frozen', False ) : - template_dir = os.path.join(sys._MEIPASS, "print", "templates") - else: - template_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)), "print", "templates") - - env = Environment( - loader = FileSystemLoader(template_dir), - autoescape=select_autoescape(['html', 'xml']), - extensions=['jinja2.ext.i18n'] - ) - - env.filters['datetimeformat'] = datetimeformat - env.install_gettext_translations(inkstitch.translation) - - return env - - def strip_namespaces(self): - # namespace prefixes seem to trip up HTML, so get rid of them - for element in self.document.iter(): - if element.tag[0]=='{': - element.tag = element.tag[element.tag.index('}',1) + 1:] - - def effect(self): - # It doesn't really make sense to print just a couple of selected - # objects. It's almost certain they meant to print the whole design. - # If they really wanted to print just a few objects, they could set - # the rest invisible temporarily. - self.selected = {} - - if not self.get_elements(): - return - - self.hide_all_layers() - - patches = self.elements_to_patches(self.elements) - stitch_plan = patches_to_stitch_plan(patches) - palette = ThreadCatalog().match_and_apply_palette(stitch_plan, self.get_inkstitch_metadata()['thread-palette']) - render_stitch_plan(self.document.getroot(), stitch_plan) - - self.strip_namespaces() - - # Now the stitch plan layer will contain a set of groups, each - # corresponding to a color block. We'll create a set of SVG files - # corresponding to each individual color block and a final one - # for all color blocks together. - - svg = self.document.getroot() - layers = svg.findall("./g[@{http://www.inkscape.org/namespaces/inkscape}groupmode='layer']") - stitch_plan_layer = svg.find(".//*[@id='__inkstitch_stitch_plan__']") - - # First, delete all of the other layers. We don't need them and they'll - # just bulk up the SVG. - for layer in layers: - if layer is not stitch_plan_layer: - svg.remove(layer) - - overview_svg = inkex.etree.tostring(self.document) - - color_block_groups = stitch_plan_layer.getchildren() - - for i, group in enumerate(color_block_groups): - # clear the stitch plan layer - del stitch_plan_layer[:] - - # add in just this group - stitch_plan_layer.append(group) - - # save an SVG preview - stitch_plan.color_blocks[i].svg_preview = inkex.etree.tostring(self.document) - - env = self.build_environment() - template = env.get_template('index.html') - - html = template.render( - view = {'client_overview': False, 'client_detailedview': False, 'operator_overview': True, 'operator_detailedview': True}, - logo = {'src' : '', 'title' : 'LOGO'}, - date = date.today(), - client = "", - job = { - 'title': '', - 'num_colors': stitch_plan.num_colors, - 'num_color_blocks': len(stitch_plan), - 'num_stops': stitch_plan.num_stops, - 'num_trims': stitch_plan.num_trims, - 'dimensions': stitch_plan.dimensions_mm, - 'num_stitches': stitch_plan.num_stitches, - 'estimated_time': '', # TODO - 'estimated_thread': '', # TODO - }, - svg_overview = overview_svg, - color_blocks = stitch_plan.color_blocks, - palettes = ThreadCatalog().palette_names(), - selected_palette = palette.name, - ) - - # We've totally mucked with the SVG. Restore it so that we can save - # metadata into it. - self.document = deepcopy(self.original_document) - - print_server = PrintPreviewServer(html=html, metadata=self.get_inkstitch_metadata(), stitch_plan=stitch_plan) - print_server.start() - - time.sleep(1) - open_url("http://%s:%s/" % (print_server.host, print_server.port)) - - app = wx.App() - info_frame = PrintInfoFrame(None, title=_("Ink/Stitch Print"), size=(450, 350), print_server=print_server) - info_frame.Show() - app.MainLoop() - - -if __name__ == '__main__': - exception = None - - save_stderr() - try: - effect = Print() - effect.affect() - except: - exception = traceback.format_exc() - restore_stderr() - - if exception: - print >> sys.stderr, exception - sys.exit(1) - else: - sys.exit(0) diff --git a/embroider_simulate.py b/embroider_simulate.py deleted file mode 100644 index c7c3f6bd..00000000 --- a/embroider_simulate.py +++ /dev/null @@ -1,285 +0,0 @@ -import sys -import os -import numpy -import wx -import inkex -import simplestyle -import colorsys -from itertools import izip - -import inkstitch -from inkstitch.extensions import InkstitchExtension -from inkstitch import PIXELS_PER_MM -from inkstitch.stitch_plan import patches_to_stitch_plan -from inkstitch.svg import color_block_to_point_lists - - -class EmbroiderySimulator(wx.Frame): - def __init__(self, *args, **kwargs): - stitch_plan = kwargs.pop('stitch_plan', None) - self.on_close_hook = kwargs.pop('on_close', None) - self.frame_period = kwargs.pop('frame_period', 80) - self.stitches_per_frame = kwargs.pop('stitches_per_frame', 1) - self.target_duration = kwargs.pop('target_duration', None) - - self.margin = 10 - - screen_rect = wx.Display(0).ClientArea - self.max_width = kwargs.pop('max_width', screen_rect.GetWidth()) - self.max_height = kwargs.pop('max_height', screen_rect.GetHeight()) - self.scale = 1 - - wx.Frame.__init__(self, *args, **kwargs) - - self.panel = wx.Panel(self, wx.ID_ANY) - self.panel.SetFocus() - - self.load(stitch_plan) - - if self.target_duration: - self.adjust_speed(self.target_duration) - - self.buffer = wx.Bitmap(self.width * self.scale + self.margin * 2, self.height * self.scale + self.margin * 2) - self.dc = wx.MemoryDC() - self.dc.SelectObject(self.buffer) - self.canvas = wx.GraphicsContext.Create(self.dc) - - self.clear() - - self.Bind(wx.EVT_SIZE, self.on_size) - self.panel.Bind(wx.EVT_PAINT, self.on_paint) - self.panel.Bind(wx.EVT_KEY_DOWN, self.on_key_down) - - self.timer = None - - self.last_pos = None - - self.Bind(wx.EVT_CLOSE, self.on_close) - - def load(self, stitch_plan=None): - if stitch_plan: - self.mirror = False - self.segments = self._stitch_plan_to_segments(stitch_plan) - else: - return - - self.trim_margins() - self.calculate_dimensions() - - def adjust_speed(self, duration): - self.frame_period = 1000 * float(duration) / len(self.segments) - self.stitches_per_frame = 1 - - while self.frame_period < 1.0: - self.frame_period *= 2 - self.stitches_per_frame *= 2 - - def on_key_down(self, event): - keycode = event.GetKeyCode() - - if keycode == ord("+") or keycode == ord("=") or keycode == wx.WXK_UP: - if self.frame_period == 1: - self.stitches_per_frame *= 2 - else: - self.frame_period = self.frame_period / 2 - elif keycode == ord("-") or keycode == ord("_") or keycode == wx.WXK_DOWN: - if self.stitches_per_frame == 1: - self.frame_period *= 2 - else: - self.stitches_per_frame /= 2 - elif keycode == ord("Q"): - self.Close() - elif keycode == ord('P'): - if self.timer.IsRunning(): - self.timer.Stop() - else: - self.timer.Start(self.frame_period) - elif keycode == ord("R"): - self.stop() - self.clear() - self.go() - - self.frame_period = max(1, self.frame_period) - self.stitches_per_frame = max(self.stitches_per_frame, 1) - - if self.timer.IsRunning(): - self.timer.Stop() - self.timer.Start(self.frame_period) - - def _strip_quotes(self, string): - if string.startswith('"') and string.endswith('"'): - string = string[1:-1] - - return string - - def color_to_pen(self, color): - return wx.Pen(color.visible_on_white.rgb) - - def _stitch_plan_to_segments(self, stitch_plan): - segments = [] - - for color_block in stitch_plan: - pen = self.color_to_pen(color_block.color) - - for point_list in color_block_to_point_lists(color_block): - # if there's only one point, there's nothing to do, so skip - if len(point_list) < 2: - continue - - for start, end in izip(point_list[:-1], point_list[1:]): - segments.append(((start, end), pen)) - - return segments - - def all_coordinates(self): - for segment in self.segments: - start, end = segment[0] - - yield start - yield end - - def trim_margins(self): - """remove any unnecessary whitespace around the design""" - - min_x = sys.maxint - min_y = sys.maxint - - for x, y in self.all_coordinates(): - min_x = min(min_x, x) - min_y = min(min_y, y) - - new_segments = [] - - for segment in self.segments: - (start, end), color = segment - - new_segment = ( - ( - (start[0] - min_x, start[1] - min_y), - (end[0] - min_x, end[1] - min_y), - ), - color - ) - - new_segments.append(new_segment) - - self.segments = new_segments - - def calculate_dimensions(self): - # 0.01 avoids a division by zero below for designs with no width or - # height (e.g. a straight vertical or horizontal line) - width = 0.01 - height = 0.01 - - for x, y in self.all_coordinates(): - width = max(width, x) - height = max(height, y) - - self.width = width - self.height = height - self.scale = min(float(self.max_width) / width, float(self.max_height) / height) - - # make room for decorations and the margin - self.scale *= 0.95 - - def go(self): - self.clear() - - self.current_stitch = 0 - - if not self.timer: - self.timer = wx.PyTimer(self.draw_one_frame) - - self.timer.Start(self.frame_period) - - def on_close(self, event): - self.stop() - - if self.on_close_hook: - self.on_close_hook() - - # If we keep a reference here, wx crashes when the process exits. - self.canvas = None - - self.Destroy() - - def stop(self): - if self.timer: - self.timer.Stop() - - def clear(self): - self.dc.SetBackground(wx.Brush('white')) - self.dc.Clear() - self.last_pos = None - self.Refresh() - - def on_size(self, e): - # ensure that the whole canvas is visible - window_width, window_height = self.GetSize() - client_width, client_height = self.GetClientSize() - - decorations_width = window_width - client_width - decorations_height = window_height - client_height - - self.SetSize((self.width * self.scale + decorations_width + self.margin * 2, - self.height * self.scale + decorations_height + self.margin * 2)) - - e.Skip() - - def on_paint(self, e): - dc = wx.PaintDC(self.panel) - dc.Blit(0, 0, self.buffer.GetWidth(), self.buffer.GetHeight(), self.dc, 0, 0) - - if self.last_pos: - dc.DrawLine(self.last_pos[0] - 10, self.last_pos[1], self.last_pos[0] + 10, self.last_pos[1]) - dc.DrawLine(self.last_pos[0], self.last_pos[1] - 10, self.last_pos[0], self.last_pos[1] + 10) - - def draw_one_frame(self): - for i in xrange(self.stitches_per_frame): - try: - ((x1, y1), (x2, y2)), color = self.segments[self.current_stitch] - - if self.mirror: - y1 = self.height - y1 - y2 = self.height - y2 - - x1 = x1 * self.scale + self.margin - y1 = y1 * self.scale + self.margin - x2 = x2 * self.scale + self.margin - y2 = y2 * self.scale + self.margin - - self.canvas.SetPen(color) - self.canvas.DrawLines(((x1, y1), (x2, y2))) - self.Refresh() - - self.current_stitch += 1 - self.last_pos = (x2, y2) - except IndexError: - self.timer.Stop() - -class SimulateEffect(InkstitchExtension): - def __init__(self): - InkstitchExtension.__init__(self) - self.OptionParser.add_option("-P", "--path", - action="store", type="string", - dest="path", default=".", - help="Directory in which to store output file") - - def effect(self): - if not self.get_elements(): - return - - patches = self.elements_to_patches(self.elements) - stitch_plan = patches_to_stitch_plan(patches) - app = wx.App() - frame = EmbroiderySimulator(None, -1, _("Embroidery Simulation"), wx.DefaultPosition, size=(1000, 1000), stitch_plan=stitch_plan) - app.SetTopWindow(frame) - frame.Show() - wx.CallAfter(frame.go) - app.MainLoop() - - -if __name__ == "__main__": - effect = SimulateEffect() - effect.affect() - sys.exit(0) diff --git a/messages.po b/messages.po index c8c14147..a9fae6ff 100644 --- a/messages.po +++ b/messages.po @@ -17,310 +17,310 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 2.5.3\n" -msgid "" -"\n" -"\n" -"Seeing a 'no such option' message? Please restart Inkscape to fix." +#, python-format +msgid "parseLengthWithUnits: unknown unit %s" msgstr "" -msgid "Directory in which to install palettes:" +#, python-format +msgid "Unknown unit: %s" msgstr "" -msgid "Choose another directory..." +msgid "Stitch Plan" msgstr "" -msgid "Install" +msgid "Auto-Fill" msgstr "" -msgid "Cancel" +msgid "Automatically routed fill stitching" msgstr "" -msgid "Choose Inkscape palettes directory" +msgid "Running stitch length (traversal between sections)" msgstr "" -msgid "Thread palette installation failed" +msgid "Underlay" msgstr "" -msgid "Installation Failed" +msgid "AutoFill Underlay" msgstr "" -msgid "" -"Thread palette files have been installed. Please restart Inkscape to " -"load the new palettes." +msgid "Fill angle (default: fill angle + 90 deg)" msgstr "" -msgid "Installation Completed" +msgid "Row spacing (default: 3x fill row spacing)" msgstr "" -msgid "Ink/Stitch Thread Palette Installer" +msgid "Max stitch length" msgstr "" -msgid "These settings will be applied to 1 object." +msgid "Inset" msgstr "" -#, python-format -msgid "These settings will be applied to %d objects." +msgid "TRIM after" msgstr "" -msgid "" -"Some settings had different values across objects. Select a value from " -"the dropdown or enter a new one." +msgid "Trim thread after this object (for supported machines and file formats)" msgstr "" -#, python-format -msgid "Disabling this tab will disable the following %d tabs." +msgid "STOP after" msgstr "" -msgid "Disabling this tab will disable the following tab." +msgid "" +"Add STOP instruction after this object (for supported machines and file " +"formats)" msgstr "" -#, python-format -msgid "Enabling this tab will disable %s and vice-versa." +msgid "Fill" msgstr "" -msgid "Inkscape objects" +msgid "Manually routed fill stitching" msgstr "" -msgid "Embroidery Params" +msgid "Angle of lines of stitches" msgstr "" -msgid "Presets" +msgid "Flip fill (start right-to-left)" msgstr "" -msgid "Load" +msgid "Spacing between rows" msgstr "" -msgid "Add" +msgid "Maximum fill stitch length" msgstr "" -msgid "Overwrite" +msgid "Stagger rows this many times before repeating" msgstr "" -msgid "Delete" +msgid "Satin Column" msgstr "" -msgid "Use Last Settings" +msgid "Custom satin column" msgstr "" -msgid "Apply and Quit" +msgid "Zig-zag spacing (peak-to-peak)" msgstr "" -msgid "Preview" +msgid "Pull compensation" msgstr "" -msgid "Internal Error" +msgid "Contour underlay" msgstr "" -msgid "Please enter or select a preset name first." +msgid "Contour Underlay" msgstr "" -msgid "Preset" +msgid "Stitch length" msgstr "" -#, python-format -msgid "Preset \"%s\" not found." +msgid "Contour underlay inset amount" msgstr "" -#, python-format -msgid "" -"Preset \"%s\" already exists. Please use another name or press " -"\"Overwrite\"" +msgid "Center-walk underlay" msgstr "" -msgid "Closing..." +msgid "Center-Walk Underlay" msgstr "" -msgid "It is safe to close this window now." +msgid "Zig-zag underlay" msgstr "" -msgid "" -"A print preview has been opened in your web browser. This window will " -"stay open in order to communicate with the JavaScript code running in " -"your browser.\n" -"\n" -"This window will close after you close the print preview in your browser," -" or you can close it manually if necessary." +msgid "Zig-zag Underlay" msgstr "" -msgid "Ink/Stitch Print" +msgid "Zig-Zag spacing (peak-to-peak)" msgstr "" -msgid "Embroidery Simulation" +msgid "Inset amount (default: half of contour underlay inset)" msgstr "" -#, python-format -msgid "parseLengthWithUnits: unknown unit %s" +msgid "" +"One or more rails crosses itself, and this is not allowed. Please split " +"into multiple satin columns." msgstr "" -#, python-format -msgid "Unknown unit: %s" +msgid "satin column: One or more of the rungs doesn't intersect both rails." msgstr "" -msgid "Stitch Plan" +msgid "Each rail should intersect both rungs once." msgstr "" -msgid "Auto-Fill" +msgid "" +"satin column: One or more of the rungs intersects the rails more than " +"once." msgstr "" -msgid "Automatically routed fill stitching" +#, python-format +msgid "satin column: object %s has a fill (but should not)" msgstr "" -msgid "Running stitch length (traversal between sections)" +#, python-format +msgid "" +"satin column: object %(id)s has two paths with an unequal number of " +"points (%(length1)d and %(length2)d)" msgstr "" -msgid "Underlay" +msgid "Satin stitch along paths" msgstr "" -msgid "AutoFill Underlay" +msgid "Running stitch length" msgstr "" -msgid "Fill angle (default: fill angle + 90 deg)" +msgid "Repeats" msgstr "" -msgid "Row spacing (default: 3x fill row spacing)" +msgid "Manual stitch placement" msgstr "" -msgid "Max stitch length" +msgid "" +"Stitch every node in the path. Stitch length and zig-zag spacing are " +"ignored." msgstr "" -msgid "Inset" +msgid "" +"Legacy running stitch setting detected!\n" +"\n" +"It looks like you're using a stroke smaller than 0.5 units to indicate a " +"running stitch, which is deprecated. Instead, please set your stroke to " +"be dashed to indicate running stitch. Any kind of dash will work." msgstr "" -msgid "TRIM after" +msgid "No embroiderable paths selected." msgstr "" -msgid "Trim thread after this object (for supported machines and file formats)" +msgid "No embroiderable paths found in document." msgstr "" -msgid "STOP after" +msgid "Tip: use Path -> Object to Path to convert non-paths before embroidering." msgstr "" msgid "" -"Add STOP instruction after this object (for supported machines and file " -"formats)" +"\n" +"\n" +"Seeing a 'no such option' message? Please restart Inkscape to fix." msgstr "" -msgid "Fill" +msgid "Directory in which to install palettes:" msgstr "" -msgid "Manually routed fill stitching" +msgid "Choose another directory..." msgstr "" -msgid "Angle of lines of stitches" +msgid "Install" msgstr "" -msgid "Flip fill (start right-to-left)" +msgid "Cancel" msgstr "" -msgid "Spacing between rows" +msgid "Choose Inkscape palettes directory" msgstr "" -msgid "Maximum fill stitch length" +msgid "Thread palette installation failed" msgstr "" -msgid "Stagger rows this many times before repeating" +msgid "Installation Failed" msgstr "" -msgid "Satin Column" +msgid "" +"Thread palette files have been installed. Please restart Inkscape to " +"load the new palettes." msgstr "" -msgid "Custom satin column" +msgid "Installation Completed" msgstr "" -msgid "Zig-zag spacing (peak-to-peak)" +msgid "Ink/Stitch Thread Palette Installer" msgstr "" -msgid "Pull compensation" +msgid "These settings will be applied to 1 object." msgstr "" -msgid "Contour underlay" +#, python-format +msgid "These settings will be applied to %d objects." msgstr "" -msgid "Contour Underlay" +msgid "" +"Some settings had different values across objects. Select a value from " +"the dropdown or enter a new one." msgstr "" -msgid "Stitch length" +#, python-format +msgid "Disabling this tab will disable the following %d tabs." msgstr "" -msgid "Contour underlay inset amount" +msgid "Disabling this tab will disable the following tab." msgstr "" -msgid "Center-walk underlay" +#, python-format +msgid "Enabling this tab will disable %s and vice-versa." msgstr "" -msgid "Center-Walk Underlay" +msgid "Inkscape objects" msgstr "" -msgid "Zig-zag underlay" +msgid "Embroidery Params" msgstr "" -msgid "Zig-zag Underlay" +msgid "Presets" msgstr "" -msgid "Zig-Zag spacing (peak-to-peak)" +msgid "Load" msgstr "" -msgid "Inset amount (default: half of contour underlay inset)" +msgid "Add" msgstr "" -msgid "" -"One or more rails crosses itself, and this is not allowed. Please split " -"into multiple satin columns." +msgid "Overwrite" msgstr "" -msgid "satin column: One or more of the rungs doesn't intersect both rails." +msgid "Delete" msgstr "" -msgid "Each rail should intersect both rungs once." +msgid "Use Last Settings" msgstr "" -msgid "" -"satin column: One or more of the rungs intersects the rails more than " -"once." +msgid "Apply and Quit" msgstr "" -#, python-format -msgid "satin column: object %s has a fill (but should not)" +msgid "Preview" msgstr "" -#, python-format -msgid "" -"satin column: object %(id)s has two paths with an unequal number of " -"points (%(length1)d and %(length2)d)" +msgid "Internal Error" msgstr "" -msgid "Satin stitch along paths" +msgid "Please enter or select a preset name first." msgstr "" -msgid "Running stitch length" +msgid "Preset" msgstr "" -msgid "Repeats" +#, python-format +msgid "Preset \"%s\" not found." msgstr "" -msgid "Manual stitch placement" +#, python-format +msgid "" +"Preset \"%s\" already exists. Please use another name or press " +"\"Overwrite\"" msgstr "" -msgid "" -"Stitch every node in the path. Stitch length and zig-zag spacing are " -"ignored." +msgid "Closing..." msgstr "" -msgid "" -"Legacy running stitch setting detected!\n" -"\n" -"It looks like you're using a stroke smaller than 0.5 units to indicate a " -"running stitch, which is deprecated. Instead, please set your stroke to " -"be dashed to indicate running stitch. Any kind of dash will work." +msgid "It is safe to close this window now." msgstr "" -msgid "No embroiderable paths selected." +msgid "" +"A print preview has been opened in your web browser. This window will " +"stay open in order to communicate with the JavaScript code running in " +"your browser.\n" +"\n" +"This window will close after you close the print preview in your browser," +" or you can close it manually if necessary." msgstr "" -msgid "No embroiderable paths found in document." +msgid "Ink/Stitch Print" msgstr "" -msgid "Tip: use Path -> Object to Path to convert non-paths before embroidering." +msgid "Embroidery Simulation" msgstr "" msgid "" |
