summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--embroider_print.py36
-rw-r--r--inkstitch/extensions.py11
-rw-r--r--inkstitch/threads/__init__.py2
-rw-r--r--inkstitch/threads/catalog.py95
-rw-r--r--inkstitch/threads/color.py8
-rw-r--r--inkstitch/threads/palette.py72
-rw-r--r--messages.po30
-rw-r--r--print/resources/inkstitch.js81
-rw-r--r--print/resources/style.css30
-rw-r--r--print/templates/color_swatch.html18
-rw-r--r--print/templates/operator_detailedview.html13
-rw-r--r--print/templates/ui.html61
-rw-r--r--requirements.txt1
13 files changed, 387 insertions, 71 deletions
diff --git a/embroider_print.py b/embroider_print.py
index cbdaeb09..ee9193a8 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
@@ -98,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
@@ -177,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
@@ -290,6 +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, self.get_inkstitch_metadata()['thread-palette'])
render_stitch_plan(self.document.getroot(), stitch_plan)
self.strip_namespaces()
@@ -344,13 +376,15 @@ 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
# 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 8d8a5fbb..c02cc579 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")
@@ -70,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/__init__.py b/inkstitch/threads/__init__.py
index 3ba5ec15..03cd777b 100644
--- a/inkstitch/threads/__init__.py
+++ b/inkstitch/threads/__init__.py
@@ -1 +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..cebae4ff
--- /dev/null
+++ b/inkstitch/threads/catalog.py
@@ -0,0 +1,95 @@
+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 = os.path.join(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 palette_names(self):
+ return list(sorted(palette.name for palette in self))
+
+ def __getitem__(self, item):
+ return self.palettes[item]
+
+ 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, 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)
+
+ 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.
+ """
+
+ 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])
+
+ if matches < 0.8 * len(stitch_plan):
+ # if less than 80% of the colors are an exact match,
+ # don't use this palette
+ 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)
+
+ color_block.color.name = nearest.name
+ 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
+
+def ThreadCatalog():
+ """Singleton _ThreadCatalog factory"""
+
+ global _catalog
+ if _catalog is None:
+ _catalog = _ThreadCatalog()
+
+ return _catalog
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))
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 <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\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 498b1211..daa2cf4b 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');
@@ -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,21 +249,53 @@ $(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
+ $('select#thread-palette').change(function(){
+ $('.modal').show();
+ }).on('update', function() {
+ $(this).data('current-value', $(this).find(':selected').val());
+ }).trigger('update');
+
+ $('#modal-yes').on('click', function(){
+ $("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(){
+ 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');
+ $(':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
@@ -295,4 +327,3 @@ $(function() {
$.postJSON('/defaults', {'value': settings});
});
});
-
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/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' %}
<div class="color-swatch">
<div class="swatch-info">
<svg width="100%" height="100%" class="color-swatch-svg" xmlns="http://www.w3.org/2000/svg">
<rect fill="rgb{{ color_block.color.rgb }}" stroke="rgb(192, 192, 192)" stroke-width="1px" width="100%" height="100%" />
<text fill="rgb{{ color_block.color.font_color }}">
- <tspan dy="1.2em" x="2mm" y="2mm" class="color-name">{{ _('Color') }}: </tspan><tspan data-field-name="color-{{ color_block.color.hex_digits }}">{{ color_block.color.thread_name }}</tspan>
+ <tspan dy="1.2em" x="2mm" y="2mm" class="color-name">{{ _('Color') }}: </tspan><tspan data-field-name="color-{{ color_block.color.hex_digits }}">{{ color_block.color.name }}</tspan>
{# We don't want to see rgb if we have more than 7 colorSwatches #}
{% if color_blocks|length < 7 %}
<tspan dy="1.2em" x="2mm" class="color-rgb">{{ _('rgb') }}: {{ color_block.color.rgb }}</tspan>
{% endif %}
{# We don't want to see thread_used if we have more than 7 colorSwatches to show #}
- {% if color_blocks|length < 7 %}
- <tspan dy="1.2em" x="2mm" class="swatch-thread">{{ _('thread used') }}: {{ color_block.color.thread_description }}</tspan>
+ {% if color_blocks|length < 7 %}
+ <tspan dy="1.2em" x="2mm" class="swatch-thread" data-field-name="thread-{{ color_block.color.hex_digits }}">{{ _('thread') }}: {{ color_block.color.manufacturer }} {{ "#" + color_block.color.number if color_block.color.number }}</tspan>
{% 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 @@
</svg>
</div>
</div>
-
+
{% else %}
-
+
<div class="color-swatch">
<div class="swatch-info">
<svg width="100%" height="100%" class="colorSwatchSVG" xmlns="http://www.w3.org/2000/svg">
@@ -37,9 +37,9 @@
</svg>
<div class="color-info">
<div>
- <p><span class="color-name">{{ _('Color') }}:</span><span data-field-name="color-{{ color_block.color.hex_digits }}" contenteditable="true" data-placeholder="Enter thread name...">{{ color_block.color.thread_name }}</span></p>
+ <p><span class="color-name">{{ _('Color') }}:</span><span data-field-name="color-{{ color_block.color.hex_digits }}" contenteditable="true" data-placeholder="Enter thread name...">{{ color_block.color.name }}</span></p>
<p><span class="color-rgb">{{ _('rgb') }}:</span><span>{{ color_block.color.rgb }}</span></p>
- <p><span class="swatch-thread">{{ _('thread used') }}:</span><span>{{ color_block.color.thread_description }}</span></p>
+ <p><span class="swatch-thread">{{ _('thread') }}:</span><span data-field-name="thread-{{ color_block.color.hex_digits }}" contenteditable="true">{{ color_block.color.manufacturer }} {{ "#" + color_block.color.number if color_block.color.number }}</span></p>
<p><span class="swatch-stitches">{{ _('# stitches') }}:</span><span>{{ color_block.num_stitches }}</span></p>
<p><span class="swatch-stops">{{ _('# stops') }}:</span><span>{{ color_block.num_stops }}</span></p>
<p><span class="swatch-trims">{{ _('# trims') }}:</span><span>{{ color_block.num_trims }}</span></p>
@@ -47,5 +47,5 @@
</div>
</div>
</div>
-
+
{%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 @@
<header>
{% include 'headline.html' %}
</header>
-
+
<main>
<div class="operator-job-info">
<div class="job-headline">
@@ -39,7 +39,7 @@
</div>
{% endif %}
{% for color_block in color_block_part %}
-
+
<div style="display: table-row;">
<p class="operator-svg operator-colorswatch">
<svg xmlns="http://www.w3.org/2000/svg">
@@ -53,11 +53,12 @@
{{ color_block.svg_preview|safe }}
</p>
<p>
- <span data-field-name="color-{{ color_block.color.hex_digits }}" contenteditable="true" data-placeholder="Enter thread name...">{{ color_block.color.thread_name }}</span>
+ <span data-field-name="color-{{ color_block.color.hex_digits }}" contenteditable="true" data-placeholder="Enter thread name...">{{ color_block.color.name }}</span>
<span>{{ color_block.color.rgb }}</span>
+ <span data-field-name="thread-{{ color_block.color.hex_digits }}" contenteditable="true">{{ color_block.color.manufacturer }} {{ "#" + color_block.color.number if color_block.color.number }}</span>
</p>
<p>
- <span>{{ _('thread used') }}: {{ color_block.color.thread_description }}</span>
+ <span>{{ _('thread used') }}:</span>
<span>{{ _('# stitches') }}: {{ color_block.num_stitches }}</span>
</p>
<p>
@@ -71,7 +72,5 @@
{% endfor %}
</div>
</main>
-
+
{% include 'footer.html' %}
-
-
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') }}
</div>
</div>
-
+
<div id="settings-ui">
<p id="close-settings">X</p>
<h1>{{ _('Settings') }}</h1>
- <div>
- <p>{{ _('Printing Size') }}:
- <select id="printing-size" data-field-name="paper-size">
- <option value="letter" selected="selected">Letter</option>
- <option value="a4">A4</option>
+ <fieldset>
+ <legend>{{ _('Page Setup') }}</legend>
+ <div>
+ <p>{{ _('Printing Size') }}:
+ <select id="printing-size" data-field-name="paper-size">
+ <option value="letter" selected="selected">Letter</option>
+ <option value="a4">A4</option>
+ </select>
+ </p>
+ </div>
+ <div>
+ <fieldset>
+ <legend>{{ _('Print Layouts') }}</legend>
+ <p><input type="checkbox" id="client-overview" data-field-name="client-overview" /><label for="client-overview">Client Overview</label></p>
+ <p><input type="checkbox" id="client-detailedview" data-field-name="client-detailedview" /><label for="client-detailedview">Client Detailed View</label></p>
+ <p><input type="checkbox" id="operator-overview" data-field-name="operator-overview" CHECKED /><label for="operator-overview">Operator Overview</label></p>
+ <p><input type="checkbox" id="operator-detailedview" data-field-name="operator-detailedview" CHECKED /><label for="operator-detailedview">Operator Detailed View</label></p>
+ </fieldset>
+ <button id="save-settings" title="{{ _("Includes these Page Setup settings and also the icon.") }}">{{ _("Save as defaults") }}</button>
+ </div>
+ </fieldset>
+ <fieldset>
+ <legend>{{ _('Design') }}</legend>
+ <p>{{ _('Thread Palette') }}:
+ <select id="thread-palette" data-field-name="thread-palette">
+ {% if selected_palette is none %}
+ <option value="" selected>None</option>
+ {% endif %}
+ {% for palette in palettes %}
+ <option value="{{ palette }}" {{ "selected" if palette == selected_palette else "" }}>{{ palette }}</option>
+ {% endfor %}
</select>
</p>
- </div>
- <div>
- <fieldset>
- <legend>{{ _('Print Layouts') }}:</legend>
- <p><input type="checkbox" id="client-overview" data-field-name="client-overview" /><label for="client-overview">Client Overview</label></p>
- <p><input type="checkbox" id="client-detailedview" data-field-name="client-detailedview" /><label for="client-detailedview">Client Detailed View</label></p>
- <p><input type="checkbox" id="operator-overview" data-field-name="operator-overview" CHECKED /><label for="operator-overview">Operator Overview</label></p>
- <p><input type="checkbox" id="operator-detailedview" data-field-name="operator-detailedview" CHECKED /><label for="operator-detailedview">Operator Detailed View</label></p>
- </fieldset>
- <button id="save-settings" title="{{ _("Includes all settings visible here and also the icon.") }}">{{ _("Save as defaults") }}</button>
- </div>
+ </fieldset>
+ </div>
+
+ <div id="modal-background" class="modal"></div>
+ <div id="modal-content" class="modal">
+ <p>
+ {{ _("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?") }}
+ </p>
+ <p>
+ <button id="modal-yes">{{ _("Yes") }}</button>
+ <button id="modal-no">{{ _("No") }}</button>
+ </p>
</div>
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