From b3e9d12db4cfbbf8f1760d80fcd760d48a83e583 Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Sat, 21 Apr 2018 15:13:44 -0400 Subject: add inkstitch.threads.ThreadPalette class --- inkstitch/threads/__init__.py | 1 + inkstitch/threads/color.py | 8 +++-- inkstitch/threads/palette.py | 72 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 78 insertions(+), 3 deletions(-) create mode 100644 inkstitch/threads/palette.py diff --git a/inkstitch/threads/__init__.py b/inkstitch/threads/__init__.py index 3ba5ec15..fec82ced 100644 --- a/inkstitch/threads/__init__.py +++ b/inkstitch/threads/__init__.py @@ -1 +1,2 @@ from color import ThreadColor +from palette import ThreadPalette diff --git a/inkstitch/threads/color.py b/inkstitch/threads/color.py index ad3dc051..af474127 100644 --- a/inkstitch/threads/color.py +++ b/inkstitch/threads/color.py @@ -2,10 +2,11 @@ import simplestyle import re import colorsys + class ThreadColor(object): hex_str_re = re.compile('#([0-9a-z]{3}|[0-9a-z]{6})', re.I) - def __init__(self, color, name=None, description=None): + def __init__(self, color, name=None, number=None, manufacturer=None): if color is None: self.rgb = (0, 0, 0) elif isinstance(color, (list, tuple)): @@ -16,7 +17,8 @@ class ThreadColor(object): raise ValueError("Invalid color: " + repr(color)) self.name = name - self.description = description + self.number = number + self.manufacturer = manufacturer def __eq__(self, other): if isinstance(other, ThreadColor): @@ -77,4 +79,4 @@ class ThreadColor(object): # convert back to values in the range of 0-255 color = tuple(value * 255 for value in color) - return ThreadColor(color, name=self.name, description=self.description) + return ThreadColor(color, name=self.name, number=self.number, manufacturer=self.manufacturer) diff --git a/inkstitch/threads/palette.py b/inkstitch/threads/palette.py new file mode 100644 index 00000000..e1f47c7f --- /dev/null +++ b/inkstitch/threads/palette.py @@ -0,0 +1,72 @@ +from collections import Set +from .color import ThreadColor +from colormath.color_objects import sRGBColor, LabColor +from colormath.color_conversions import convert_color +from colormath.color_diff import delta_e_cie1994 + + +def compare_thread_colors(color1, color2): + # K_L=2 indicates textiles + return delta_e_cie1994(color1, color2, K_L=2) + + +class ThreadPalette(Set): + """Holds a set of ThreadColors all from the same manufacturer.""" + + def __init__(self, palette_file): + self.threads = dict() + self.parse_palette_file(palette_file) + + def parse_palette_file(self, palette_file): + """Read a GIMP palette file and load thread colors. + + Example file: + + GIMP Palette + Name: Ink/Stitch: Metro + Columns: 4 + # RGB Value Color Name Number + 240 186 212 Sugar Pink 1624 + 237 171 194 Carnatio 1636 + + """ + + with open(palette_file) as palette: + line = palette.readline().strip() + if line.lower() != "gimp palette": + raise ValueError("Invalid gimp palette header") + + self.name = palette.readline().strip() + if self.name.lower().startswith('name: ink/stitch: '): + self.name = self.name[18:] + + columns_line = palette.readline() + headers_line = palette.readline() + + for line in palette: + fields = line.split("\t", 3) + thread_color = [int(field) for field in fields[:3]] + thread_name, thread_number = fields[3].strip().rsplit(" ", 1) + thread_name = thread_name.strip() + + thread = ThreadColor(thread_color, thread_name, thread_number, manufacturer=self.name) + self.threads[thread] = convert_color(sRGBColor(*thread_color, is_upscaled=True), LabColor) + + def __contains__(self, thread): + return thread in self.threads + + def __iter__(self): + return iter(self.threads) + + def __len__(self): + return len(self.threads) + + def nearest_color(self, color): + """Find the thread in this palette that looks the most like the specified color.""" + + if isinstance(color, ThreadColor): + color = color.rgb + + color = convert_color(sRGBColor(*color, is_upscaled=True), LabColor) + + return min(self, key=lambda thread: compare_thread_colors(self.threads[thread], color)) -- cgit v1.2.3 From 378f079e6d15757ed6cb0b2e04f50642b5adaaac Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Sat, 21 Apr 2018 15:49:58 -0400 Subject: add ThreadCatalog and palette matching --- embroider_print.py | 2 ++ inkstitch/threads/__init__.py | 1 + inkstitch/threads/catalog.py | 72 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 75 insertions(+) create mode 100644 inkstitch/threads/catalog.py diff --git a/embroider_print.py b/embroider_print.py index cbdaeb09..7ab24876 100644 --- a/embroider_print.py +++ b/embroider_print.py @@ -21,6 +21,7 @@ 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 @@ -290,6 +291,7 @@ class Print(InkstitchExtension): patches = self.elements_to_patches(self.elements) stitch_plan = patches_to_stitch_plan(patches) + ThreadCatalog().match_and_apply_palette(stitch_plan) render_stitch_plan(self.document.getroot(), stitch_plan) self.strip_namespaces() diff --git a/inkstitch/threads/__init__.py b/inkstitch/threads/__init__.py index fec82ced..03cd777b 100644 --- a/inkstitch/threads/__init__.py +++ b/inkstitch/threads/__init__.py @@ -1,2 +1,3 @@ from color import ThreadColor from palette import ThreadPalette +from catalog import ThreadCatalog diff --git a/inkstitch/threads/catalog.py b/inkstitch/threads/catalog.py new file mode 100644 index 00000000..0f959a52 --- /dev/null +++ b/inkstitch/threads/catalog.py @@ -0,0 +1,72 @@ +import os +from os.path import dirname, realpath +import sys +from glob import glob +from collections import Sequence +from .palette import ThreadPalette + +class _ThreadCatalog(Sequence): + """Holds a set of ThreadPalettes.""" + + def __init__(self): + self.palettes = [] + self.load_palettes(self.get_palettes_path()) + + def get_palettes_path(self): + if getattr(sys, 'frozen', None) is not None: + path = sys._MEIPASS + else: + path = dirname(dirname(dirname(realpath(__file__)))) + + return os.path.join(path, 'palettes') + + def load_palettes(self, path): + for palette_file in glob(os.path.join(path, '*.gpl')): + self.palettes.append(ThreadPalette(palette_file)) + + def __getitem__(self, item): + return self.palettes[item] + + def __len__(self): + return len(self.palettes) + + def match_and_apply_palette(self, stitch_plan): + """Figure out which color palette was used and set thread names. + + Scans the catalog of color palettes and chooses one that seems most + likely to be the one that the user used. A palette will only be + chosen if more tha 80% of the thread colors in the stitch plan are + exact matches for threads in the palette. All other threads will be + matched to the closest thread in the palette. + """ + + threads = [color_block.color for color_block in stitch_plan] + + for palette in self: + num_matched = sum(1 for thread in threads if thread in palette) + if num_matched > (0.8 * len(threads)): + # Pick this one. + break + else: + # This block will only get run if we ran off the end of the loop + # without breaking. No palette had enough colors that exactly + # matched, so just do nothing. + return + + for thread in threads: + nearest = palette.find_nearest(thread) + + thread.name = nearest.name + thread.number = nearest.number + thread.manufacturer = nearest.manufacturer + +_catalog = None + +def ThreadCatalog(): + """Singleton _ThreadCatalog factory""" + + global _catalog + if _catalog is None: + _catalog = _ThreadCatalog() + + return _catalog -- cgit v1.2.3 From d4c95e080ffb80ceb6f10b663c7547b25689a840 Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Sat, 21 Apr 2018 16:23:13 -0400 Subject: assign thread color names in printout --- inkstitch/extensions.py | 7 ++++++- inkstitch/threads/catalog.py | 2 +- print/resources/inkstitch.js | 29 ++++++++++++++--------------- print/templates/color_swatch.html | 18 +++++++++--------- print/templates/operator_detailedview.html | 13 ++++++------- 5 files changed, 36 insertions(+), 33 deletions(-) diff --git a/inkstitch/extensions.py b/inkstitch/extensions.py index 8d8a5fbb..5befec9f 100644 --- a/inkstitch/extensions.py +++ b/inkstitch/extensions.py @@ -55,7 +55,12 @@ class InkStitchMetadata(MutableMapping): # implement these five methods and we get a full dict-like interface. def __setitem__(self, name, value): - self._find_item(name).text = json.dumps(value) + item = self._find_item(name) + + if value: + item.text = json.dumps(value) + else: + item.getparent().remove(item) def _find_item(self, name): tag = inkex.addNS(name, "inkstitch") diff --git a/inkstitch/threads/catalog.py b/inkstitch/threads/catalog.py index 0f959a52..5bf75f67 100644 --- a/inkstitch/threads/catalog.py +++ b/inkstitch/threads/catalog.py @@ -54,7 +54,7 @@ class _ThreadCatalog(Sequence): return for thread in threads: - nearest = palette.find_nearest(thread) + nearest = palette.nearest_color(thread) thread.name = nearest.name thread.number = nearest.number diff --git a/print/resources/inkstitch.js b/print/resources/inkstitch.js index 498b1211..4a4e4456 100644 --- a/print/resources/inkstitch.js +++ b/print/resources/inkstitch.js @@ -24,17 +24,17 @@ function setPageNumbers() { // Scale SVG (fit || full size) function scaleSVG(element, scale = 'fit') { - + // always center svg transform = "translate(-50%, -50%)"; - + if(scale == 'fit') { var scale = Math.min( - element.width() / element.find('svg').width(), + element.width() / element.find('svg').width(), element.height() / element.find('svg').height() ); } - + transform += " scale(" + scale + ")"; var label = parseInt(scale*100); @@ -71,17 +71,17 @@ function setSVGTransform(figure, transform) { $(function() { setTimeout(ping, 1000); setPageNumbers(); - + /* SCALING AND MOVING SVG */ - + /* Mousewheel scaling */ $('figure.inksimulation').on( 'DOMMouseScroll mousewheel', function (e) { if(e.ctrlKey == true) { - + var svg = $(this).find('svg'); var transform = svg.css('transform').match(/-?[\d\.]+/g); var scale = parseFloat(transform[0]); - + if (e.originalEvent.detail > 0 || e.originalEvent.wheelDelta < 0) { // scroll down = zoom out scale *= 0.97; @@ -91,7 +91,7 @@ $(function() { //scroll up scale *= 1.03; } - + // set modified scale transform[0] = scale; transform[3] = scale; @@ -102,19 +102,19 @@ $(function() { return false; } }); - + /* Fit SVG */ $('button.svg-fit').click(function() { var svgfigure = $(this).closest('figure'); scaleSVG(svgfigure, 'fit'); }); - + /* Full Size SVG */ $('button.svg-full').click(function() { var svgfigure = $(this).closest('figure'); scaleSVG(svgfigure, '1'); }); - + /* Drag SVG */ $('figure.inksimulation').on('mousedown', function(e) { var p0 = { x: e.pageX, y: e.pageY }; @@ -141,7 +141,7 @@ $(function() { // set it using setSVGTransform() to ensure that it's saved to the server setSVGTransform($(this), $(this).find('svg').css('transform')); }); - + /* Apply transforms to All */ $('button.svg-apply').click(function() { var transform = $(this).parent().siblings('svg').css('transform'); @@ -154,7 +154,7 @@ $(function() { $('[contenteditable="true"]').on('focusout', function() { /* change svg scale */ - var content = $(this).html(); + var content = $.trim($(this).text()); var field_name = $(this).attr('data-field-name'); if(field_name == 'svg-scale') { var scale = parseInt(content); @@ -295,4 +295,3 @@ $(function() { $.postJSON('/defaults', {'value': settings}); }); }); - diff --git a/print/templates/color_swatch.html b/print/templates/color_swatch.html index 21cc0c21..6785b080 100644 --- a/print/templates/color_swatch.html +++ b/print/templates/color_swatch.html @@ -1,18 +1,18 @@ - + {% if printview != 'detailedview' %}
- {{ _('Color') }}: {{ color_block.color.thread_name }} + {{ _('Color') }}: {{ color_block.color.name }} {# We don't want to see rgb if we have more than 7 colorSwatches #} {% if color_blocks|length < 7 %} {{ _('rgb') }}: {{ color_block.color.rgb }} {% endif %} {# We don't want to see thread_used if we have more than 7 colorSwatches to show #} - {% if color_blocks|length < 7 %} - {{ _('thread used') }}: {{ color_block.color.thread_description }} + {% if color_blocks|length < 7 %} + {{ _('thread') }}: {{ color_block.color.manufacturer }} {{ "#" + color_block.color.number if color_block.color.number }} {% endif %} {# We don't want to see num_stitch if we have more than 49 colorSwatches to show #} {% if color_blocks|length < 49 %} @@ -27,9 +27,9 @@
- + {% else %} - +
@@ -37,9 +37,9 @@
-

{{ _('Color') }}:{{ color_block.color.thread_name }}

+

{{ _('Color') }}:{{ color_block.color.name }}

{{ _('rgb') }}:{{ color_block.color.rgb }}

-

{{ _('thread used') }}:{{ color_block.color.thread_description }}

+

{{ _('thread') }}:{{ color_block.color.manufacturer }} {{ "#" + color_block.color.number if color_block.color.number }}

{{ _('# stitches') }}:{{ color_block.num_stitches }}

{{ _('# stops') }}:{{ color_block.num_stops }}

{{ _('# trims') }}:{{ color_block.num_trims }}

@@ -47,5 +47,5 @@
- + {%endif %} diff --git a/print/templates/operator_detailedview.html b/print/templates/operator_detailedview.html index f78028d7..0b948cb0 100644 --- a/print/templates/operator_detailedview.html +++ b/print/templates/operator_detailedview.html @@ -1,7 +1,7 @@
{% include 'headline.html' %}
- +
@@ -39,7 +39,7 @@
{% endif %} {% for color_block in color_block_part %} - +

@@ -53,11 +53,12 @@ {{ color_block.svg_preview|safe }}

- {{ color_block.color.thread_name }} + {{ color_block.color.name }} {{ color_block.color.rgb }} + {{ color_block.color.manufacturer }} {{ "#" + color_block.color.number if color_block.color.number }}

- {{ _('thread used') }}: {{ color_block.color.thread_description }} + {{ _('thread used') }}: {{ _('# stitches') }}: {{ color_block.num_stitches }}

@@ -71,7 +72,5 @@ {% endfor %}

- + {% include 'footer.html' %} - - -- cgit v1.2.3 From 0c60da4bb4887bfcd7a30c1a489391de5ded9cb8 Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Sat, 21 Apr 2018 21:57:40 -0400 Subject: add colormath to requirements.txt --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index 7771f0d1..450786cd 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,3 +10,4 @@ flask requests pywinutils; sys.platform == 'win32' pywin32; sys.platform == 'win32' +colormath -- cgit v1.2.3 From 0700817a3b7de96dad7ca51989416cddfb7e65f9 Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Sat, 21 Apr 2018 22:05:27 -0400 Subject: pick the palette with the most exact matches --- inkstitch/threads/catalog.py | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/inkstitch/threads/catalog.py b/inkstitch/threads/catalog.py index 5bf75f67..80eb9dfe 100644 --- a/inkstitch/threads/catalog.py +++ b/inkstitch/threads/catalog.py @@ -30,6 +30,11 @@ class _ThreadCatalog(Sequence): def __len__(self): return len(self.palettes) + def _num_exact_color_matches(self, palette, threads): + """Number of colors in stitch plan with an exact match in this palette.""" + + return sum(1 for thread in threads if thread in palette) + def match_and_apply_palette(self, stitch_plan): """Figure out which color palette was used and set thread names. @@ -41,16 +46,13 @@ class _ThreadCatalog(Sequence): """ threads = [color_block.color for color_block in stitch_plan] + palettes_and_matches = [(palette, self._num_exact_color_matches(palette, threads)) + for palette in self] + palette, matches = max(palettes_and_matches, key=lambda item: item[1]) - for palette in self: - num_matched = sum(1 for thread in threads if thread in palette) - if num_matched > (0.8 * len(threads)): - # Pick this one. - break - else: - # This block will only get run if we ran off the end of the loop - # without breaking. No palette had enough colors that exactly - # matched, so just do nothing. + if matches < 0.8 * len(stitch_plan): + # if less than 80% of the colors are an exact match, + # don't use this palette return for thread in threads: -- cgit v1.2.3 From c234d6ed2c7d8cd7b7991643dbec4383bed9e2dc Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Mon, 23 Apr 2018 23:12:48 -0400 Subject: UI to select different thread paette --- embroider_print.py | 4 ++- inkstitch/threads/catalog.py | 32 ++++++++++++++++------- messages.po | 30 +++++++++++++++++++--- print/resources/inkstitch.js | 22 +++++++++++++++- print/resources/style.css | 30 +++++++++++++++++++--- print/templates/ui.html | 61 ++++++++++++++++++++++++++++++++------------ 6 files changed, 145 insertions(+), 34 deletions(-) diff --git a/embroider_print.py b/embroider_print.py index 7ab24876..43efa758 100644 --- a/embroider_print.py +++ b/embroider_print.py @@ -291,7 +291,7 @@ class Print(InkstitchExtension): patches = self.elements_to_patches(self.elements) stitch_plan = patches_to_stitch_plan(patches) - ThreadCatalog().match_and_apply_palette(stitch_plan) + palette = ThreadCatalog().match_and_apply_palette(stitch_plan) render_stitch_plan(self.document.getroot(), stitch_plan) self.strip_namespaces() @@ -346,6 +346,8 @@ class Print(InkstitchExtension): }, 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 diff --git a/inkstitch/threads/catalog.py b/inkstitch/threads/catalog.py index 80eb9dfe..db50e678 100644 --- a/inkstitch/threads/catalog.py +++ b/inkstitch/threads/catalog.py @@ -24,6 +24,9 @@ class _ThreadCatalog(Sequence): for palette_file in glob(os.path.join(path, '*.gpl')): self.palettes.append(ThreadPalette(palette_file)) + def palette_names(self): + return list(sorted(palette.name for palette in self)) + def __getitem__(self, item): return self.palettes[item] @@ -36,13 +39,20 @@ class _ThreadCatalog(Sequence): return sum(1 for thread in threads if thread in palette) def match_and_apply_palette(self, stitch_plan): - """Figure out which color palette was used and set thread names. + palette = self.match_palette(stitch_plan) + + if palette is not None: + self.apply_palette(stitch_plan, palette) + + return palette + + def match_palette(self, stitch_plan): + """Figure out which color palette was used Scans the catalog of color palettes and chooses one that seems most likely to be the one that the user used. A palette will only be chosen if more tha 80% of the thread colors in the stitch plan are - exact matches for threads in the palette. All other threads will be - matched to the closest thread in the palette. + exact matches for threads in the palette. """ threads = [color_block.color for color_block in stitch_plan] @@ -53,14 +63,18 @@ class _ThreadCatalog(Sequence): if matches < 0.8 * len(stitch_plan): # if less than 80% of the colors are an exact match, # don't use this palette - return + return None + else: + return palette + + def apply_palette(self, stitch_plan, palette): + for color_block in stitch_plan: + nearest = palette.nearest_color(color_block.color) - for thread in threads: - nearest = palette.nearest_color(thread) + color_block.color.name = nearest.name + color_block.color.number = nearest.number + color_block.color.manufacturer = nearest.manufacturer - thread.name = nearest.name - thread.number = nearest.number - thread.manufacturer = nearest.manufacturer _catalog = None diff --git a/messages.po b/messages.po index 82c2dc2e..d1968764 100644 --- a/messages.po +++ b/messages.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2018-04-28 20:42-0400\n" +"POT-Creation-Date: 2018-04-29 21:29-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -339,7 +339,7 @@ msgstr "" msgid "rgb" msgstr "" -msgid "thread used" +msgid "thread" msgstr "" msgid "# stitches" @@ -406,6 +406,9 @@ msgstr "" msgid "Total nr trims" msgstr "" +msgid "thread used" +msgstr "" + msgid "Enter operator notes..." msgstr "" @@ -448,15 +451,36 @@ msgstr "" msgid "⚠ lost connection to Ink/Stitch" msgstr "" +msgid "Page Setup" +msgstr "" + msgid "Printing Size" msgstr "" msgid "Print Layouts" msgstr "" -msgid "Includes all settings visible here and also the icon." +msgid "Includes these Page Setup settings and also the icon." msgstr "" msgid "Save as defaults" msgstr "" +msgid "Design" +msgstr "" + +msgid "Thread Palette" +msgstr "" + +msgid "" +"Changing the thread palette will cause thread names and catalog numbers " +"to be recalculated based on the new palette. Any changes you have made " +"to color or thread names will be lost. Are you sure?" +msgstr "" + +msgid "Yes" +msgstr "" + +msgid "No" +msgstr "" + diff --git a/print/resources/inkstitch.js b/print/resources/inkstitch.js index 4a4e4456..60d17f61 100644 --- a/print/resources/inkstitch.js +++ b/print/resources/inkstitch.js @@ -154,7 +154,7 @@ $(function() { $('[contenteditable="true"]').on('focusout', function() { /* change svg scale */ - var content = $.trim($(this).text()); + var content = $(this).html(); var field_name = $(this).attr('data-field-name'); if(field_name == 'svg-scale') { var scale = parseInt(content); @@ -255,6 +255,26 @@ $(function() { $.postJSON('/settings/paper-size', {value: size}); }); + // Thread Palette + $('select#thread-palette').change(function(){ + $('.modal').show(); + }).on('update', function() { + $(this).data('current-value', $(this).find(':selected').val()); + console.log("selected: " + $(this).data('current-value')); + }).trigger('update'); + + $('#modal-yes').on('click', function(){ + // do shit with the newly-selected palette... + $("select#thread-palette").trigger("update"); + $('.modal').hide(); + }); + + $('#modal-no').on('click', function(){ + var select = $("select#thread-palette"); + select.find('[value="' + select.data('current-value') + '"]').prop('selected', true); + $('.modal').hide(); + }); + //Checkbox $(':checkbox').change(function() { var checked = $(this).prop('checked'); diff --git a/print/resources/style.css b/print/resources/style.css index 97dee6a8..58ec8714 100644 --- a/print/resources/style.css +++ b/print/resources/style.css @@ -214,9 +214,6 @@ body { border-bottom: 1px solid rgb(188, 188, 188); border-bottom: 1px solid rgba(129, 129, 129, 0.5); box-shadow: 0 1px 1px 1px rgba(194, 191, 191, 0.5); - } - - #settings-ui div { text-align: left; font-size: 12pt; } @@ -238,6 +235,33 @@ body { margin-bottom: 1em; } + #modal-background { + display: none; + z-index: 3; + position: fixed; + background: black; + opacity: 0.5; + width: 100%; + height: 100%; + top: 0; + left: 0; + } + + #modal-content { + display: none; + z-index: 4; + position: fixed; + width: 50%; + height: 25%%; + top: 200px; + left: 25%; + background: rgb(255, 255, 255); + border-bottom: 1px solid rgb(188, 188, 188); + box-shadow: 0 1px 1px 1px rgb(194, 191, 191); + text-align: center; + font-size: 16pt; + } + /* Header */ diff --git a/print/templates/ui.html b/print/templates/ui.html index f7246962..112a342e 100644 --- a/print/templates/ui.html +++ b/print/templates/ui.html @@ -9,26 +9,53 @@ {{ _('⚠ lost connection to Ink/Stitch') }}
- +

X

{{ _('Settings') }}

-
-

{{ _('Printing Size') }}: - + + + +

+
+
+
+ {{ _('Print Layouts') }} +

+

+

+

+
+ +
+ +
+ {{ _('Design') }} +

{{ _('Thread Palette') }}: +

-
-
-
- {{ _('Print Layouts') }}: -

-

-

-

-
- -
+ + + + + -- cgit v1.2.3 From 8608508e0237b06bea5aac7558c695fd7bc8492b Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Tue, 24 Apr 2018 00:22:45 -0400 Subject: allow selection of a different thread palette --- embroider_print.py | 34 ++++++++++++++++++++++++++++++++-- inkstitch/extensions.py | 4 ++-- inkstitch/threads/catalog.py | 11 +++++++++-- print/resources/inkstitch.js | 38 +++++++++++++++++++++++++------------- 4 files changed, 68 insertions(+), 19 deletions(-) diff --git a/embroider_print.py b/embroider_print.py index 43efa758..ee9193a8 100644 --- a/embroider_print.py +++ b/embroider_print.py @@ -99,6 +99,7 @@ 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 @@ -178,6 +179,35 @@ class PrintPreviewServer(Thread): 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 @@ -291,7 +321,7 @@ class Print(InkstitchExtension): patches = self.elements_to_patches(self.elements) stitch_plan = patches_to_stitch_plan(patches) - palette = ThreadCatalog().match_and_apply_palette(stitch_plan) + 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() @@ -354,7 +384,7 @@ class Print(InkstitchExtension): # metadata into it. self.document = deepcopy(self.original_document) - print_server = PrintPreviewServer(html=html, metadata=self.get_inkstitch_metadata()) + print_server = PrintPreviewServer(html=html, metadata=self.get_inkstitch_metadata(), stitch_plan=stitch_plan) print_server.start() time.sleep(1) diff --git a/inkstitch/extensions.py b/inkstitch/extensions.py index 5befec9f..c02cc579 100644 --- a/inkstitch/extensions.py +++ b/inkstitch/extensions.py @@ -75,11 +75,11 @@ class InkStitchMetadata(MutableMapping): try: return json.loads(item.text) - except ValueError: + except (ValueError, TypeError): return None def __delitem__(self, name): - item = self[name] + item = self._find_item(name) if item: self.metadata.remove(item) diff --git a/inkstitch/threads/catalog.py b/inkstitch/threads/catalog.py index db50e678..6ff09674 100644 --- a/inkstitch/threads/catalog.py +++ b/inkstitch/threads/catalog.py @@ -38,8 +38,11 @@ class _ThreadCatalog(Sequence): return sum(1 for thread in threads if thread in palette) - def match_and_apply_palette(self, stitch_plan): - palette = self.match_palette(stitch_plan) + def match_and_apply_palette(self, stitch_plan, palette=None): + if palette is None: + palette = self.match_palette(stitch_plan) + else: + palette = self.get_palette_by_name(palette) if palette is not None: self.apply_palette(stitch_plan, palette) @@ -75,6 +78,10 @@ class _ThreadCatalog(Sequence): color_block.color.number = nearest.number color_block.color.manufacturer = nearest.manufacturer + def get_palette_by_name(self, name): + for palette in self: + if palette.name == name: + return palette _catalog = None diff --git a/print/resources/inkstitch.js b/print/resources/inkstitch.js index 60d17f61..daa2cf4b 100644 --- a/print/resources/inkstitch.js +++ b/print/resources/inkstitch.js @@ -178,11 +178,11 @@ $(function() { $('[data-field-name="' + field_name + '"]').each(function(i, item) { var item = $(item); if (item.is(':checkbox')) { - item.prop('checked', value).trigger('change'); + item.prop('checked', value).trigger('initialize'); } else if (item.is('img')) { item.attr('src', value); } else if (item.is('select')) { - item.val(value).trigger('change'); + item.val(value).trigger('initialize'); } else if (item.is('figure.inksimulation')) { setSVGTransform(item, value); } else { @@ -249,10 +249,10 @@ $(function() { /* Settings */ // Paper Size - $('select#printing-size').change(function(){ - var size = $(this).find(':selected').val(); - $('.page').toggleClass('a4', size == 'a4'); - $.postJSON('/settings/paper-size', {value: size}); + $('select#printing-size').on('change initialize', function(){ + $('.page').toggleClass('a4', $(this).find(':selected').val() == 'a4'); + }).on('change', function() { + $.postJSON('/settings/paper-size', {value: $(this).find(':selected').val()}); }); // Thread Palette @@ -260,13 +260,26 @@ $(function() { $('.modal').show(); }).on('update', function() { $(this).data('current-value', $(this).find(':selected').val()); - console.log("selected: " + $(this).data('current-value')); }).trigger('update'); $('#modal-yes').on('click', function(){ - // do shit with the newly-selected palette... $("select#thread-palette").trigger("update"); $('.modal').hide(); + var body = {'name': $('select#thread-palette').find(':selected').val()}; + $.postJSON('/palette', body, function() { + $.getJSON('/threads', function(threads) { + console.log("threads: " + JSON.stringify(threads)); + $.each(threads, function(i, thread) { + console.log("doing: " + JSON.stringify(thread)); + $('[data-field-name="color-' + thread.hex + '"]').text(thread.name); + var thread_description = thread.manufacturer; + if (thread.number) { + thread_description += " #" + thread.number; + } + $('[data-field-name="thread-' + thread.hex + '"]').text(thread_description); + }); + }); + }); }); $('#modal-no').on('click', function(){ @@ -276,14 +289,13 @@ $(function() { }); //Checkbox - $(':checkbox').change(function() { - var checked = $(this).prop('checked'); + $(':checkbox').on('change initialize', function() { var field_name = $(this).attr('data-field-name'); - $('.' + field_name).toggle(checked); + $('.' + field_name).toggle($(this).prop('checked')); setPageNumbers(); - - $.postJSON('/settings/' + field_name, {value: checked}); + }).on('change', function() { + $.postJSON('/settings/' + field_name, {value: $(this).prop('checked')}); }); // Logo -- cgit v1.2.3 From 7a80e59d7751b6c228f00be4853b1443a6233840 Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Sat, 28 Apr 2018 21:29:58 -0400 Subject: fix palettes path --- inkstitch/threads/catalog.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inkstitch/threads/catalog.py b/inkstitch/threads/catalog.py index 6ff09674..cebae4ff 100644 --- a/inkstitch/threads/catalog.py +++ b/inkstitch/threads/catalog.py @@ -14,7 +14,7 @@ class _ThreadCatalog(Sequence): def get_palettes_path(self): if getattr(sys, 'frozen', None) is not None: - path = sys._MEIPASS + path = os.path.join(sys._MEIPASS, "..") else: path = dirname(dirname(dirname(realpath(__file__)))) -- cgit v1.2.3