kopia lustrzana https://github.com/brendabell/knittingtools
commit
07b182bfb3
|
|
@ -0,0 +1 @@
|
|||
# intentionally empty
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
# chkconfig: 2345 75 35
|
||||
# description: knittingtools
|
||||
|
||||
SERVICE=knittingtools
|
||||
|
||||
# Source function library.
|
||||
. /etc/init.d/functions
|
||||
|
||||
lockfile=/var/lock/${SERVICE}.lock
|
||||
procname=/path/to/knittingtools/venv/bin/python
|
||||
scriptname=/path/to/knittingtools/server.py
|
||||
pidfile=/var/run/${SERVICE}.pid
|
||||
|
||||
RETVAL=0
|
||||
prog="knittingtools"
|
||||
|
||||
start () {
|
||||
if [ -f "$pidfile" ]; then
|
||||
checkpid `cat $pidfile` && return 0
|
||||
fi
|
||||
echo -n $"Starting $prog: "
|
||||
$procname $scriptname 1>/dev/null 2>&1 &
|
||||
RETVAL=$?
|
||||
PID=$!
|
||||
echo $PID
|
||||
echo $PID >$pidfile
|
||||
echo
|
||||
[ $RETVAL -eq 0 ] && touch $lockfile
|
||||
return $RETVAL
|
||||
}
|
||||
|
||||
stop () {
|
||||
RETVAL=0
|
||||
if [ -f "${pidfile}" ]; then
|
||||
echo -n $"Stopping $prog: "
|
||||
killproc -p ${pidfile} $procname
|
||||
RETVAL=$?
|
||||
echo
|
||||
fi
|
||||
[ $RETVAL -eq 0 ] && rm -f $lockfile
|
||||
return $RETVAL
|
||||
}
|
||||
|
||||
restart () {
|
||||
stop
|
||||
start
|
||||
}
|
||||
|
||||
# See how we were called.
|
||||
case "$1" in
|
||||
start|stop|restart)
|
||||
$1 ;;
|
||||
status)
|
||||
status -p ${pidfile} $procname ;;
|
||||
condrestart)
|
||||
test ! -f $lockfile || restart ;;
|
||||
*)
|
||||
echo $"Usage: $0 {start|stop|status|restart|condrestart}"
|
||||
exit 2
|
||||
esac
|
||||
|
|
@ -1,14 +1,16 @@
|
|||
import cairosvg
|
||||
import cgi
|
||||
from os import curdir
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import traceback
|
||||
|
||||
from modules.pcgenerator import PCGenerator
|
||||
|
||||
def pcgenerator_get(handler):
|
||||
|
||||
f = open("{}/templates/{}".format(
|
||||
curdir,
|
||||
f = open("{}/../templates/{}".format(
|
||||
os.path.dirname(os.path.realpath(__file__)),
|
||||
"pcgenerator.html"))
|
||||
|
||||
try:
|
||||
|
|
@ -21,6 +23,8 @@ def pcgenerator_get(handler):
|
|||
|
||||
except Exception:
|
||||
exc_type, exc_value, exc_traceback = sys.exc_info()
|
||||
handler.log_error("%s", traceback.format_exception(exc_type, exc_value,exc_traceback))
|
||||
|
||||
handler.wfile.write(
|
||||
"<h1>Aw, snap! We seem to have a problem.</h1><p><b>")
|
||||
handler.wfile.write(
|
||||
|
|
@ -30,7 +34,6 @@ def pcgenerator_get(handler):
|
|||
"<a href='http://www.ravelry.com/people/beebell'>beebell on Ravelry</a>. "
|
||||
"It will be helpful if you include the pattern you uploaded to help me "
|
||||
"diagnose the issue.")
|
||||
handler.log_error("%s", traceback.format_exception(exc_type, exc_value,exc_traceback))
|
||||
|
||||
finally:
|
||||
f.close()
|
||||
|
|
@ -38,47 +41,66 @@ def pcgenerator_get(handler):
|
|||
def pcgenerator_post(handler):
|
||||
|
||||
try:
|
||||
ctype, pdict = cgi.parse_header(handler.headers.getheader('content-type'))
|
||||
ctype, pdict = cgi.parse_header(handler.headers.getheader('Content-Type'))
|
||||
if ctype == 'multipart/form-data':
|
||||
query=cgi.parse_multipart(handler.rfile, pdict)
|
||||
|
||||
upfilecontent = query.get('upfile')
|
||||
if len(upfilecontent[0]) > 2500:
|
||||
if len(upfilecontent[0]) > 4000:
|
||||
handler.send_response(302)
|
||||
handler.send_header('Content-type', 'text/html')
|
||||
handler.end_headers()
|
||||
handler.wfile.write("Sorry. Your file cannot exceed 2500 bytes!")
|
||||
else:
|
||||
horz_repeat = query.get('horz')
|
||||
machine_type = query.get('machine')
|
||||
vert_repeat = query.get('vert')
|
||||
cell_height = query.get('rowheight')
|
||||
cell_width = query.get('colwidth')
|
||||
convert_to_png = query.get('png', [''])[0] == 'png'
|
||||
|
||||
generator = PCGenerator(
|
||||
handler,
|
||||
upfilecontent[0],
|
||||
float(cell_height[0]),
|
||||
float(cell_width[0]),
|
||||
int(horz_repeat[0]),
|
||||
machine_type[0],
|
||||
int(vert_repeat[0]))
|
||||
result = generator.generate()
|
||||
|
||||
handler.send_response(200)
|
||||
handler.send_header('Content-type', 'image/svg+xml')
|
||||
handler.send_header("Content-Disposition", "attachment; filename=punchcard.svg")
|
||||
filename_template = 'attachment; filename="punchcard-{}.{}"'
|
||||
|
||||
if convert_to_png:
|
||||
result = cairosvg.svg2png(bytestring=result)
|
||||
handler.send_header('Content-type', 'image/png')
|
||||
handler.send_header('Content-Disposition', filename_template.format(int(time.time()), "png"))
|
||||
else:
|
||||
handler.send_header('Content-type', 'image/svg+xml')
|
||||
handler.send_header('Content-Disposition', filename_template.format(int(time.time()), "svg"))
|
||||
|
||||
handler.end_headers()
|
||||
handler.wfile.write(result)
|
||||
|
||||
return
|
||||
|
||||
except ValueError as e:
|
||||
exc_type, exc_value, exc_traceback = sys.exc_info()
|
||||
handler.log_error("%s", traceback.format_exception(exc_type, exc_value,exc_traceback))
|
||||
|
||||
handler.send_response(302)
|
||||
handler.send_header('Content-type', 'text/html')
|
||||
handler.end_headers()
|
||||
handler.wfile.write(
|
||||
"<h1>Aw, snap!</h1><p>")
|
||||
"<h1>Aw, snap! We seem to have a problem.</h1><p><b>")
|
||||
handler.wfile.write(
|
||||
repr(traceback.format_exception(exc_type, exc_value,exc_traceback)))
|
||||
handler.wfile.write(e)
|
||||
handler.wfile.write(
|
||||
"</b><p>Please report this error via private message to "
|
||||
"<a href='http://www.ravelry.com/people/beebell'>beebell on Ravelry</a>. "
|
||||
"It will be helpful if you include the pattern you uploaded to help me "
|
||||
"diagnose the issue.")
|
||||
|
||||
except Exception:
|
||||
exc_type, exc_value, exc_traceback = sys.exc_info()
|
||||
handler.log_error("%s", traceback.format_exception(exc_type, exc_value,exc_traceback))
|
||||
|
||||
handler.send_response(302)
|
||||
handler.send_header('Content-type', 'text/html')
|
||||
handler.end_headers()
|
||||
|
|
@ -91,12 +113,11 @@ def pcgenerator_post(handler):
|
|||
"<a href='http://www.ravelry.com/people/beebell'>beebell on Ravelry</a>. "
|
||||
"It will be helpful if you include the pattern you uploaded to help me "
|
||||
"diagnose the issue.")
|
||||
handler.log_error("%s", traceback.format_exception(exc_type, exc_value,exc_traceback))
|
||||
|
||||
def calculator_get(handler):
|
||||
|
||||
f = open("{}/templates/{}".format(
|
||||
curdir,
|
||||
f = open("{}/../templates/{}".format(
|
||||
os.path.dirname(os.path.realpath(__file__)),
|
||||
"calculator.html"))
|
||||
|
||||
try:
|
||||
|
|
@ -109,6 +130,8 @@ def calculator_get(handler):
|
|||
|
||||
except Exception:
|
||||
exc_type, exc_value, exc_traceback = sys.exc_info()
|
||||
handler.log_error("%s", traceback.format_exception(exc_type, exc_value,exc_traceback))
|
||||
|
||||
handler.wfile.write(
|
||||
"<h1>Aw, snap! We seem to have a problem.</h1><p><b>")
|
||||
handler.wfile.write(
|
||||
|
|
@ -118,15 +141,14 @@ def calculator_get(handler):
|
|||
"<a href='http://www.ravelry.com/people/beebell'>beebell on Ravelry</a>. "
|
||||
"It will be helpful if you include the pattern you uploaded to help me "
|
||||
"diagnose the issue.")
|
||||
handler.log_error("%s", traceback.format_exception(exc_type, exc_value,exc_traceback))
|
||||
|
||||
finally:
|
||||
f.close()
|
||||
|
||||
def index_get(handler):
|
||||
f = open("{}/templates/{}".format(
|
||||
curdir,
|
||||
"index.html"))
|
||||
f = open("{}/../templates/{}".format(
|
||||
os.path.dirname(os.path.realpath(__file__)),
|
||||
"index.html"))
|
||||
|
||||
try:
|
||||
handler.send_response(200)
|
||||
|
|
@ -138,6 +160,8 @@ def index_get(handler):
|
|||
|
||||
except Exception:
|
||||
exc_type, exc_value, exc_traceback = sys.exc_info()
|
||||
handler.log_error("%s", traceback.format_exception(exc_type, exc_value,exc_traceback))
|
||||
|
||||
handler.wfile.write(
|
||||
"<h1>Aw, snap! We seem to have a problem.</h1><p><b>")
|
||||
handler.wfile.write(
|
||||
|
|
@ -147,7 +171,6 @@ def index_get(handler):
|
|||
"<a href='http://www.ravelry.com/people/beebell'>beebell on Ravelry</a>. "
|
||||
"It will be helpful if you include the pattern you uploaded to help me "
|
||||
"diagnose the issue.")
|
||||
handler.log_error("%s", traceback.format_exception(exc_type, exc_value,exc_traceback))
|
||||
|
||||
finally:
|
||||
f.close()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,23 @@
|
|||
[loggers]
|
||||
keys=root
|
||||
|
||||
[handlers]
|
||||
keys=fileHandler
|
||||
|
||||
[formatters]
|
||||
keys=fileFormatter
|
||||
|
||||
[logger_root]
|
||||
level=DEBUG
|
||||
handlers=fileHandler
|
||||
|
||||
[handler_fileHandler]
|
||||
args=('/var/log/knittingtools.log', 'a')
|
||||
backupCount: 7
|
||||
class=logging.handlers.RotatingFileHandler
|
||||
formatter=fileFormatter
|
||||
level=DEBUG
|
||||
|
||||
[formatter_fileFormatter]
|
||||
format=%(asctime)s - %(name)s - %(levelname)s - %(message)s
|
||||
datefmt=
|
||||
|
|
@ -1,70 +1,130 @@
|
|||
import svgwrite
|
||||
|
||||
# default values
|
||||
|
||||
# number of overlapping blank rows at the top of the card
|
||||
blank_rows = 2
|
||||
# machine specs
|
||||
specs = {
|
||||
'12-stitch-br-sr': {
|
||||
'card_width': 142,
|
||||
'blank_rows': 2,
|
||||
'row_height': 5.0,
|
||||
'stitch_width': 9.0,
|
||||
'pattern_hole_radius': 3.5,
|
||||
'pattern_hole_xoffset': 22.5,
|
||||
'clip_hole_radius': 3.5,
|
||||
'clip_hole_xoffset': 5.0,
|
||||
'clip_hole_yoffset': 5.0,
|
||||
'tractor_hole_radius': 3.0,
|
||||
'tractor_hole_xoffset': 12.5,
|
||||
'tractor_hole_yoffset': 2.5,
|
||||
'stitches': 12,
|
||||
'corner_offset': 2,
|
||||
},
|
||||
'24-stitch-br-sr': {
|
||||
'card_width': 142,
|
||||
'blank_rows': 2,
|
||||
'row_height': 5.0,
|
||||
'stitch_width': 4.5,
|
||||
'pattern_hole_radius': 3.5,
|
||||
'pattern_hole_xoffset': 17.5,
|
||||
'clip_hole_radius': 3.5,
|
||||
'clip_hole_xoffset': 5.0,
|
||||
'clip_hole_yoffset': 5.0,
|
||||
'tractor_hole_radius': 3.0,
|
||||
'tractor_hole_xoffset': 12.0,
|
||||
'tractor_hole_yoffset': 2.5,
|
||||
'stitches': 24,
|
||||
'corner_offset': 2,
|
||||
},
|
||||
'40-stitch-deco': {
|
||||
'card_width': 242,
|
||||
'blank_rows': 3,
|
||||
'row_height': (305.0 / 58.0),
|
||||
'stitch_width': 5.0,
|
||||
'pattern_hole_radius': 3.0,
|
||||
'pattern_hole_xoffset': 22.0,
|
||||
'clip_hole_radius': 3.0,
|
||||
'clip_hole_xoffset': 2.0,
|
||||
'clip_hole_yoffset': 5.5,
|
||||
'tractor_hole_radius': 2.5,
|
||||
'tractor_hole_xoffset': 12.5,
|
||||
'tractor_hole_yoffset': 5.5,
|
||||
'stitches': 40,
|
||||
'corner_offset': 0,
|
||||
},
|
||||
}
|
||||
|
||||
# width of the side margin in mm
|
||||
side_margin = 17.0
|
||||
|
||||
# height of one row on the card in mm
|
||||
row_height = 5.0
|
||||
class Layout:
|
||||
|
||||
# width of one stitch on the card in mm
|
||||
stitch_width = 4.5
|
||||
#stitch_width = 9.0
|
||||
def __init__(self, machine_id, stitches, rows, horz_repeat, vert_repeat):
|
||||
|
||||
# radius of a pattern hole in mm
|
||||
pattern_hole_radius = 3.5
|
||||
# total width of the cut card
|
||||
self.card_width = specs[machine_id]['card_width']
|
||||
|
||||
# radius of a clip hole in mm
|
||||
clip_hole_radius = 3.0
|
||||
# number of overlapping blank rows at the top of the card
|
||||
self.blank_rows = specs[machine_id]['blank_rows']
|
||||
|
||||
# radius of a sprocket hole in mm
|
||||
sprocket_hole_radius = 3.5
|
||||
# height of one row on the card in mm
|
||||
self.row_height = specs[machine_id]['row_height']
|
||||
|
||||
# drawing stroke width
|
||||
stroke_width='.1'
|
||||
# width of one stitch on the card in mm
|
||||
self.stitch_width = specs[machine_id]['stitch_width']
|
||||
|
||||
# fill color
|
||||
fill_color = 'white'
|
||||
# radius of a pattern hole in mm
|
||||
self.pattern_hole_radius = specs[machine_id]['pattern_hole_radius']
|
||||
|
||||
# stroke_color
|
||||
stroke_color = 'black'
|
||||
# offset of the first pattern hole from the left edge of the card in mm
|
||||
self.pattern_hole_xoffset = specs[machine_id]['pattern_hole_xoffset']
|
||||
|
||||
card_width = 0
|
||||
card_height = 0
|
||||
card_rows = 0
|
||||
card_stitches = 0
|
||||
# radius of a clip hole in mm
|
||||
self.clip_hole_radius = specs[machine_id]['clip_hole_radius']
|
||||
|
||||
class PCGenerator:
|
||||
# offset of a clip hole from the left/right edge of the card in mm
|
||||
self.clip_hole_xoffset = specs[machine_id]['clip_hole_xoffset']
|
||||
|
||||
def __init__(self, data, cell_height, cell_width, horz_repeat, vert_repeat):
|
||||
global row_height
|
||||
global stitch_width
|
||||
# offset of a clip hole from the top/bottom edges of the card in mm
|
||||
self.clip_hole_yoffset = specs[machine_id]['clip_hole_yoffset']
|
||||
|
||||
self.data = data.split()
|
||||
self.horz_repeat = horz_repeat
|
||||
self.vert_repeat = vert_repeat
|
||||
row_height = cell_height
|
||||
stitch_width = cell_width
|
||||
# radius of a tractor hole in mm
|
||||
self.tractor_hole_radius = specs[machine_id]['tractor_hole_radius']
|
||||
|
||||
def generate(self):
|
||||
global card_rows
|
||||
global card_stitches
|
||||
global card_width
|
||||
global card_height
|
||||
# offset of a tractor hole from the left/right edge of the card in mm
|
||||
self.tractor_hole_xoffset = specs[machine_id]['tractor_hole_xoffset']
|
||||
|
||||
card_rows = len(self.data)
|
||||
card_stitches = len(self.data[0])
|
||||
if card_rows > 200 or card_stitches > 30:
|
||||
# offset of a tractor hole from the top/bottom edge of the card in mm
|
||||
self.tractor_hole_yoffset = specs[machine_id]['tractor_hole_yoffset']
|
||||
|
||||
self.corner_offset = specs[machine_id]['corner_offset']
|
||||
|
||||
self.card_stitches = stitches
|
||||
self.card_rows = rows
|
||||
|
||||
if self.card_rows > 200 or self.card_stitches > 30:
|
||||
raise ValueError(
|
||||
"Your pattern seems to exceed 200 rows and/or 30 stitches. "
|
||||
"Are you sure you uploaded the right text file?")
|
||||
|
||||
card_width = (side_margin * 2) + (card_stitches * self.horz_repeat * stitch_width)
|
||||
card_height = ((blank_rows * 2) + (card_rows * self.vert_repeat)) * row_height
|
||||
self.horz_repeat = horz_repeat
|
||||
self.vert_repeat = vert_repeat
|
||||
|
||||
self.card_height = ((self.blank_rows * 2) + (self.card_rows * self.vert_repeat)) * self.row_height
|
||||
|
||||
|
||||
class PCGenerator:
|
||||
|
||||
def __init__(self, handler, data, machine_id, vert_repeat):
|
||||
|
||||
self.handler = handler
|
||||
self.data = data.split()
|
||||
self.layout = Layout(
|
||||
machine_id,
|
||||
len(self.data[0]),
|
||||
len(self.data),
|
||||
specs[machine_id]['stitches'] / len(self.data[0]),
|
||||
vert_repeat
|
||||
)
|
||||
|
||||
def generate(self):
|
||||
|
||||
diagram = self.create_card()
|
||||
|
||||
|
|
@ -72,173 +132,143 @@ class PCGenerator:
|
|||
self.draw_pattern(diagram, self.data, objects)
|
||||
self.draw_blank_lines(diagram, objects)
|
||||
self.draw_clip_holes(diagram, objects)
|
||||
self.draw_sprocket_holes(diagram, objects)
|
||||
self.draw_tractor_holes(diagram, objects)
|
||||
|
||||
# sort the list to optimize cutting
|
||||
sorted_objects = sorted(objects, key=lambda x: (float(x.attribs['cy']), float(x.attribs['cx'])))
|
||||
for i in sorted_objects:
|
||||
diagram.add(i)
|
||||
|
||||
return diagram.tostring()
|
||||
return '<?xml version="1.0" encoding="UTF-8" standalone="no"?>{}'.format(diagram.tostring())
|
||||
|
||||
def create_card(self):
|
||||
global card_width
|
||||
global card_height
|
||||
|
||||
|
||||
diagram = svgwrite.Drawing(
|
||||
"punchcard.svg",
|
||||
size=(
|
||||
'{0}mm'.format(card_width),
|
||||
'{0}mm'.format(card_height)),
|
||||
'{0}mm'.format(self.layout.card_width),
|
||||
'{0}mm'.format(self.layout.card_height)),
|
||||
viewBox=(
|
||||
'0 0 {0} {1}'.format(card_width, card_height)),
|
||||
'0 0 {0} {1}'.format(self.layout.card_width, self.layout.card_height)),
|
||||
preserveAspectRatio='none')
|
||||
|
||||
shape_points = [
|
||||
(2, 0),
|
||||
(card_width-2, 0),
|
||||
(card_width-1, 1),
|
||||
(card_width-1, 20),
|
||||
(card_width, 22),
|
||||
(card_width, card_height-22),
|
||||
(card_width-1, card_height-20),
|
||||
(card_width-1, card_height-1),
|
||||
(card_width-2, card_height),
|
||||
(2, card_height),
|
||||
(1, card_height-1),
|
||||
(1, card_height-20),
|
||||
(0, card_height-22),
|
||||
(0, 22),
|
||||
(1, 20),
|
||||
(1, 1)]
|
||||
diagram.add(diagram.polygon(
|
||||
points=shape_points,
|
||||
fill=fill_color,
|
||||
stroke=stroke_color,
|
||||
stroke_width=stroke_width))
|
||||
points=self.get_card_shape(),
|
||||
fill='white',
|
||||
stroke='black',
|
||||
stroke_width=.1))
|
||||
|
||||
return diagram
|
||||
|
||||
def draw_pattern(self, diagram, lines, objects):
|
||||
global card_rows
|
||||
global card_stitches
|
||||
global fill_color
|
||||
global pattern_hole_radius
|
||||
global row_color
|
||||
global row_height
|
||||
global side_margin
|
||||
global stitch_width
|
||||
global stroke_color
|
||||
global stroke_width
|
||||
|
||||
|
||||
# main body of card
|
||||
yoffset = 10.0 + (row_height / 2)
|
||||
for row_repeat in range(self.vert_repeat):
|
||||
for rows in range(card_rows):
|
||||
xoffset = side_margin + (stitch_width / 2)
|
||||
for stitch_repeat in range(self.horz_repeat):
|
||||
for stitches in range(card_stitches):
|
||||
yoffset = (self.layout.blank_rows * self.layout.row_height) + (self.layout.row_height / 2)
|
||||
for row_repeat in range(self.layout.vert_repeat):
|
||||
for rows in range(self.layout.card_rows):
|
||||
xoffset = self.layout.pattern_hole_xoffset + (self.layout.pattern_hole_radius / 2)
|
||||
for stitch_repeat in range(self.layout.horz_repeat):
|
||||
for stitches in range(self.layout.card_stitches):
|
||||
if lines[rows][stitches].upper() == 'X':
|
||||
objects.append(diagram.circle(
|
||||
center=(xoffset, yoffset),
|
||||
fill=fill_color,
|
||||
r = (pattern_hole_radius / 2),
|
||||
stroke=stroke_color,
|
||||
stroke_width=stroke_width))
|
||||
xoffset += stitch_width
|
||||
yoffset += row_height
|
||||
fill='white',
|
||||
r = (self.layout.pattern_hole_radius / 2),
|
||||
stroke='black',
|
||||
stroke_width=.1))
|
||||
xoffset += self.layout.stitch_width
|
||||
yoffset += self.layout.row_height
|
||||
|
||||
def draw_blank_lines(self, diagram, objects):
|
||||
global blank_rows
|
||||
global card_stitches
|
||||
global fill_color
|
||||
global pattern_hole_radius
|
||||
global row_height
|
||||
global side_margin
|
||||
global stitch_width
|
||||
global stroke_color
|
||||
global stroke_width
|
||||
|
||||
|
||||
# blank rows at top
|
||||
yoffset = row_height / 2
|
||||
for rows in range(blank_rows):
|
||||
xoffset = side_margin + (stitch_width / 2)
|
||||
for stitch_repeat in range(self.horz_repeat):
|
||||
for stitches in range(card_stitches):
|
||||
yoffset = self.layout.row_height / 2
|
||||
for rows in range(self.layout.blank_rows):
|
||||
xoffset = self.layout.pattern_hole_xoffset + (self.layout.pattern_hole_radius / 2)
|
||||
for stitch_repeat in range(self.layout.horz_repeat):
|
||||
for stitches in range(self.layout.card_stitches):
|
||||
objects.append(diagram.circle(
|
||||
center=(xoffset, yoffset),
|
||||
fill=fill_color,
|
||||
r = (pattern_hole_radius / 2),
|
||||
stroke=stroke_color,
|
||||
stroke_width=stroke_width))
|
||||
xoffset += stitch_width
|
||||
yoffset += row_height
|
||||
fill='white',
|
||||
r = (self.layout.pattern_hole_radius / 2),
|
||||
stroke='black',
|
||||
stroke_width=.1))
|
||||
xoffset += self.layout.stitch_width
|
||||
yoffset += self.layout.row_height
|
||||
|
||||
# blank rows at bottom
|
||||
yoffset = (card_height - (row_height * blank_rows)) + (row_height / 2)
|
||||
for rows in range(blank_rows):
|
||||
xoffset = side_margin + (stitch_width / 2)
|
||||
for stitch_repeat in range(self.horz_repeat):
|
||||
for stitches in range(card_stitches):
|
||||
yoffset = (self.layout.card_height - (self.layout.row_height * self.layout.blank_rows)) + (self.layout.row_height / 2)
|
||||
for rows in range(self.layout.blank_rows):
|
||||
xoffset = self.layout.pattern_hole_xoffset + (self.layout.pattern_hole_radius / 2)
|
||||
for stitch_repeat in range(self.layout.horz_repeat):
|
||||
for stitches in range(self.layout.card_stitches):
|
||||
objects.append(diagram.circle(
|
||||
center=(xoffset, yoffset),
|
||||
fill=fill_color,
|
||||
r = (pattern_hole_radius / 2),
|
||||
stroke=stroke_color,
|
||||
stroke_width=stroke_width))
|
||||
xoffset += stitch_width
|
||||
yoffset += row_height
|
||||
fill='white',
|
||||
r = (self.layout.pattern_hole_radius / 2),
|
||||
stroke='black',
|
||||
stroke_width=.1))
|
||||
xoffset += self.layout.stitch_width
|
||||
yoffset += self.layout.row_height
|
||||
|
||||
def draw_clip_holes(self, diagram, objects):
|
||||
global card_height
|
||||
global clip_hole_radius
|
||||
global fill_color
|
||||
global row_height
|
||||
global side_margin
|
||||
global stitch_width
|
||||
global stroke_color
|
||||
global stroke_width
|
||||
|
||||
left_xoffset = side_margin + (stitch_width / 2) - 6.0
|
||||
right_xoffset = (card_width - side_margin - (stitch_width / 2)) + 6.0
|
||||
yoffset = row_height / 2
|
||||
|
||||
while yoffset < card_height:
|
||||
# clip holes on left
|
||||
self.draw_side_holes(
|
||||
diagram,
|
||||
objects,
|
||||
self.layout.clip_hole_xoffset,
|
||||
self.layout.clip_hole_yoffset,
|
||||
self.layout.clip_hole_radius)
|
||||
|
||||
def draw_tractor_holes(self, diagram, objects):
|
||||
|
||||
self.draw_side_holes(
|
||||
diagram,
|
||||
objects,
|
||||
self.layout.tractor_hole_xoffset,
|
||||
self.layout.tractor_hole_yoffset,
|
||||
self.layout.tractor_hole_radius)
|
||||
|
||||
def draw_side_holes(self, diagram, objects, xoffset, yoffset, radius):
|
||||
|
||||
left_xoffset = xoffset + (radius / 2)
|
||||
right_xoffset = self.layout.card_width - left_xoffset
|
||||
|
||||
while yoffset < self.layout.card_height:
|
||||
# holes on left
|
||||
objects.append(diagram.circle(
|
||||
center=(left_xoffset, yoffset),
|
||||
fill=fill_color,
|
||||
r = (clip_hole_radius / 2),
|
||||
stroke=stroke_color,
|
||||
stroke_width=stroke_width))
|
||||
# clip holes on right
|
||||
fill='white',
|
||||
r = (radius / 2),
|
||||
stroke='black',
|
||||
stroke_width=.1))
|
||||
# holes on right
|
||||
objects.append(diagram.circle(
|
||||
center=(right_xoffset, yoffset),
|
||||
fill=fill_color,
|
||||
r = (clip_hole_radius / 2),
|
||||
stroke=stroke_color,
|
||||
stroke_width=stroke_width))
|
||||
yoffset += row_height
|
||||
fill='white',
|
||||
r = (radius / 2),
|
||||
stroke='black',
|
||||
stroke_width=.1))
|
||||
yoffset += self.layout.row_height
|
||||
|
||||
def draw_sprocket_holes(self, diagram, objects):
|
||||
|
||||
left_xoffset = 6.5
|
||||
right_xoffset = card_width - 6.5
|
||||
yoffset = row_height
|
||||
for row_repeat in range(self.vert_repeat):
|
||||
for rows in range(((card_rows * self.vert_repeat) + (blank_rows * 2)) / 2):
|
||||
# sprocket holes on left
|
||||
objects.append(diagram.circle(
|
||||
center=(left_xoffset, yoffset),
|
||||
fill=fill_color,
|
||||
r = (sprocket_hole_radius / 2),
|
||||
stroke=stroke_color,
|
||||
stroke_width=stroke_width))
|
||||
# sprocket holes on left
|
||||
objects.append(diagram.circle(
|
||||
center=(right_xoffset, yoffset),
|
||||
fill=fill_color,
|
||||
r = (sprocket_hole_radius / 2),
|
||||
stroke=stroke_color,
|
||||
stroke_width=stroke_width))
|
||||
yoffset += (row_height * 2)
|
||||
def get_card_shape(self):
|
||||
|
||||
corner_radius = self.layout.corner_offset + 1
|
||||
|
||||
return [
|
||||
(corner_radius, 0),
|
||||
(self.layout.card_width - corner_radius, 0),
|
||||
(self.layout.card_width - self.layout.corner_offset, self.layout.corner_offset),
|
||||
(self.layout.card_width - self.layout.corner_offset, 20),
|
||||
(self.layout.card_width, 22),
|
||||
(self.layout.card_width, self.layout.card_height - 22),
|
||||
(self.layout.card_width - self.layout.corner_offset, self.layout.card_height - 20),
|
||||
(self.layout.card_width - self.layout.corner_offset, self.layout.card_height - 1),
|
||||
(self.layout.card_width - corner_radius, self.layout.card_height),
|
||||
(corner_radius, self.layout.card_height),
|
||||
( self.layout.corner_offset, self.layout.card_height - self.layout.corner_offset),
|
||||
( self.layout.corner_offset, self.layout.card_height - 20),
|
||||
(0, self.layout.card_height - 22),
|
||||
(0, 22),
|
||||
( self.layout.corner_offset, 20),
|
||||
( self.layout.corner_offset, 1)]
|
||||
|
|
|
|||
95
server.py
95
server.py
|
|
@ -1,9 +1,14 @@
|
|||
import logging
|
||||
import logging.config
|
||||
import os
|
||||
import sys
|
||||
import traceback
|
||||
|
||||
from BaseHTTPServer import BaseHTTPRequestHandler
|
||||
from BaseHTTPServer import HTTPServer
|
||||
|
||||
from handlers import actions
|
||||
|
||||
|
||||
pcgenerator_actions = {
|
||||
'get': actions.pcgenerator_get,
|
||||
'post': actions.pcgenerator_post }
|
||||
|
|
@ -23,44 +28,86 @@ template_map = {
|
|||
'/pcgenerator/': pcgenerator_actions,
|
||||
'/calculator': calculator_actions,
|
||||
'/calculator/': calculator_actions,
|
||||
'/': index_actions,
|
||||
'/index': index_actions }
|
||||
'/knittingtools': index_actions,
|
||||
'/knittingtools/': index_actions,
|
||||
'/index': index_actions,
|
||||
'/index/': index_actions,
|
||||
'/': index_actions}
|
||||
|
||||
logging.config.fileConfig("{0}/{1}".format(os.path.dirname(os.path.realpath(__file__)), 'logging.conf'))
|
||||
|
||||
logger = logging.getLogger('root')
|
||||
|
||||
class MyHandler(BaseHTTPRequestHandler):
|
||||
|
||||
def do_GET(self):
|
||||
print self.path
|
||||
actions = template_map.get(self.path, None)
|
||||
if actions is None:
|
||||
self.send_response(404)
|
||||
self.send_header('Content-type', 'text/html')
|
||||
self.end_headers()
|
||||
self.wfile.write('Not found!')
|
||||
def handle_not_found(self):
|
||||
self.send_response(404)
|
||||
self.send_header('Content-type', 'text/html')
|
||||
self.end_headers()
|
||||
self.wfile.write(
|
||||
"<h1>Aw, snap! We seem to have a problem.</h1><p><b>")
|
||||
self.wfile.write('The request resource was not found on this server.')
|
||||
|
||||
actions['get'](self)
|
||||
def do_GET(self):
|
||||
try:
|
||||
actions = template_map.get(self.path, None)
|
||||
if actions is None:
|
||||
self.handle_not_found()
|
||||
return
|
||||
|
||||
actions['get'](self)
|
||||
except Exception:
|
||||
exc_type, exc_value, exc_traceback = sys.exc_info()
|
||||
self.log_error("%s %s\n" % (
|
||||
exc_type,
|
||||
exc_value))
|
||||
logger.debug("path=%s %s",
|
||||
self.path,
|
||||
repr(traceback.format_exception(exc_type, exc_value,exc_traceback)))
|
||||
|
||||
def do_POST(self):
|
||||
print self.path
|
||||
actions = template_map.get(self.path, None)
|
||||
if actions is None:
|
||||
self.send_response(404)
|
||||
self.send_header('Content-type', 'text/html')
|
||||
self.end_headers()
|
||||
self.wfile.write('Not found!')
|
||||
try:
|
||||
actions = template_map.get(self.path, None)
|
||||
if actions is None:
|
||||
self.handle_not_found()
|
||||
return
|
||||
|
||||
actions['post'](self)
|
||||
actions['post'](self)
|
||||
except Exception:
|
||||
exc_type, exc_value, exc_traceback = sys.exc_info()
|
||||
self.log_error("%s %s\n" % (
|
||||
exc_type,
|
||||
exc_value))
|
||||
logger.debug("path=%s %s",
|
||||
self.path,
|
||||
repr(traceback.format_exception(exc_type, exc_value, exc_traceback)))
|
||||
|
||||
def log_request(self, code=None, size=None):
|
||||
logger.info("%s - - [%s] %s %s %s" % (
|
||||
self.headers.get('X-Forwarded-For', None) or self.address_string(),
|
||||
self.log_date_time_string(),
|
||||
self.requestline,
|
||||
code or '-',
|
||||
size or '-'))
|
||||
|
||||
def log_error(self, *args):
|
||||
logger.error(args)
|
||||
|
||||
def log_message(self, *args):
|
||||
logger.info("%s - - [%s] %s" % (
|
||||
self.headers.get('X-Forwarded-For', None) or self.address_string(),
|
||||
self.log_date_time_string(),
|
||||
args))
|
||||
|
||||
def main():
|
||||
try:
|
||||
server = HTTPServer(('', 8088), MyHandler)
|
||||
print 'started httpserver...'
|
||||
logger.info("Starting server...")
|
||||
server = HTTPServer(('', 8080), MyHandler)
|
||||
server.serve_forever()
|
||||
except KeyboardInterrupt:
|
||||
print '^C received, shutting down server'
|
||||
logger.info("Stopping server...")
|
||||
except Exception:
|
||||
print sys.exc_info()[0]
|
||||
logger.error(sys.exc_info()[0])
|
||||
finally:
|
||||
server.socket.close()
|
||||
|
||||
|
|
|
|||
|
|
@ -28,11 +28,12 @@
|
|||
|
||||
<body>
|
||||
<h1>Knitting Gauge Calculator</h1>
|
||||
<a href="/"><Back></a>
|
||||
<a href="/knittingtools">[Back To Tools Home]</a> <a href="http://www.brendaabell.com">[Back To My Blog]</a>
|
||||
<hr>
|
||||
|
||||
<p>This app will help you calculate stitches and rows when your gauge differs from the gauge specified on the pattern.
|
||||
<p>The forms are reactive. I.e., when you enter a value in any field, the other fields will automatically be adjusted for the value you entered. For example, if you enter inches in the "Imperial" column, the equivalent value in centimeters will automatically displayed in the "Metric" column.
|
||||
<p>IMPORTANT!!! This app is currently considered Beta. As such, there may be bugs. If you see something that doesn't look right, please report the issue using the instructions at the bottom of this page.
|
||||
|
||||
<hr>
|
||||
|
||||
|
|
@ -198,6 +199,7 @@
|
|||
<p>After you compute the number of stitches or rows required for your gauge, consult the pattern to see if knitting a different size will produce the desired results.
|
||||
<p style="margin-left: 40px">For example, if the pattern instructs you to cast on 80 stitches for a size medium but your gauge requires you to cast on 90 stitches to achieve the same width, look at the pattern to see if the next larger size specifies a cast on that's closer to 90 stitches. Knitting a larger size at a smaller gauge (or smaller size at a larger gauge) may produce the same result.
|
||||
|
||||
<h2>Bugs & Disclaimers</h2>
|
||||
<p>If you have questions or feedback, please contact <a href="http://www.ravelry.com/people/beebell">beebell on Ravelry</a> via private message or log at issue on <a href="https://github.com/brendabell/knittingtools/issues">GitHub</a>.</p>
|
||||
|
||||
</body>
|
||||
|
|
|
|||
|
|
@ -24,16 +24,18 @@
|
|||
|
||||
<body>
|
||||
<h1>Punchcard Generator</h1>
|
||||
<a href="/"><Back></a>
|
||||
<a href="/knittingtools">[Back To Tools Home]</a> <a href="http://www.brendaabell.com">[Back To My Blog]</a>
|
||||
<hr>
|
||||
|
||||
<p>This app will allow you to upload a small text file that contains your pattern represented as a sequence of rows with X's for punched holes and any other character except space for unpunched holes. The program will generate an SVG file that can be imported into the software you use to cut material with your die cutter.
|
||||
<p>IMPORTANT!!! This app is currently considered Beta. As such, there may be bugs. If you see something that doesn't look right, please report the issue using the instructions at the bottom of this page.
|
||||
|
||||
<h2>TL;DR</h2>
|
||||
<ul>
|
||||
<li>Select the file to upload.</li>
|
||||
<li>Enter your punchcard row height and column width in millimeters.</li>
|
||||
<li>Enter horizontal and vertical repeats if you need them.</li>
|
||||
<li>Select your machine type.</li>
|
||||
<li>Enter the # vertical repeats if you need them. The # horizontal repeats will be automatically calculated based on the machine type.</li>
|
||||
<li>Check "Convert to PNG" if you'd like to download a PNG image instead of SVG.</li>
|
||||
<li>Click the Upload button.</li>
|
||||
</ul>In a few seconds, you should see a dialog prompting you for where you want to save your file. If you need more detailed instructions, keep reading below.
|
||||
|
||||
|
|
@ -43,34 +45,35 @@
|
|||
method="POST"
|
||||
enctype="multipart/form-data"
|
||||
action="/pcgenerator"
|
||||
onfocus="x.value=horz.value; y.value=vert.value"
|
||||
oninput="x.value=horz.value; y.value=vert.value">
|
||||
onfocus="y.value=vert.value"
|
||||
oninput="y.value=vert.value">
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td style="text-align:right">Select file to upload:<br> </td>
|
||||
<td colspan="2"><input type="file" name="upfile"><br><small>(maximum file size 2500 characters)<small></td>
|
||||
<td colspan="2"><input type="file" name="upfile"><br><small>(maximum file size 4000 characters)<small></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="text-align:right">Height of 1 punchcard row (mm):</td>
|
||||
<td colspan="2"><input type="number" name="rowheight" min="1.0" max="10.0" step="0.1" value="5.0"></td>
|
||||
<td style="text-align:right">Select machine type:<br> </td>
|
||||
<td colspan="2">
|
||||
<select name="machine">
|
||||
<option value="12-stitch-br-sr">12-stitch Brother/Silver Reed/Studio</option>
|
||||
<option value="24-stitch-br-sr">24-stitch Brother/Silver Reed/Studio</option>
|
||||
<option value="40-stitch-deco">40-stitch Passap Deco</option>
|
||||
</select>
|
||||
<p><b>NOTICE: Passap Deco support is currently experimental and has not been fully tested.</b>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="text-align:right">Width of 1 punchcard column (mm):</td>
|
||||
<td colspan="2"><input type="number" name="colwidth" min="1.0" max="10.0" step="0.1" value="4.5"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="text-align:right"># Horizontal repeats (up to 12):</td>
|
||||
<td colspan="2"><input type="range" id="horz" name="horz" min="1" max="12" step="1" value="1">
|
||||
<output id="x" name="x" value="1"></output>
|
||||
<script>x.value=horz.value;</script></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="text-align:right"># Vertical repeats (up to 12):</td>
|
||||
<td><input type="range" id="vert" name="vert" min="1" max="12" step="1" value="1">
|
||||
<td style="text-align:right"># Vertical repeats (up to 24):</td>
|
||||
<td><input type="range" id="vert" name="vert" min="1" max="24" step="1" value="1">
|
||||
<output id="y" name="y" value="1"></output>
|
||||
<script>y.value=vert.value;</script></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="test-align:right">Convert to PNG image:</td>
|
||||
<td><input type="checkbox" name="png" value="png"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td colspan="2"><input type="submit" value="Upload"></td>
|
||||
|
|
@ -95,9 +98,11 @@
|
|||
---x-----x--
|
||||
</pre>
|
||||
|
||||
<p>By default, the generator will do 1 horizontal repeat and 1 vertical repeat. I.e., if your file contains 36 rows with 24 characters per row, the generated file will produce a 24 row x 36 stitch card. You can increase the width and/or length by adjusting the horizontal and/or vertical repeat before clicking the upload button.
|
||||
<p>The generator will determine the number of horizontal repeats based on the number of characters in your first row and the type of machine. I.e., if row 1 contains 4 characters and you select a 12-stitch machine, the generator will automatically do 3 horizontal repeats: 4 * 3 = 12.
|
||||
|
||||
<p>What this means is that you can design a simple 4x4 card and use repeats to make the card as wide or as long as it needs to be in order to work properly. For example, the following text file will generate a 24 row x 48 stitch card if you specify 6 for the horizontal repeat and 12 for the vertical repeat:
|
||||
<p>By default, the generator will do 1 vertical repeat. I.e., if your file contains 36 rows, the generated file will produce a 36 row card. You can increase the length by adjusting the vertical repeat before clicking the upload button.
|
||||
|
||||
<p>What this means is that you can design a simple 4x4 card and use repeats to make the card as wide or as long as it needs to be in order to work properly. For example, the following text file will generate a 24 row x 48 stitch card if you select a 24-stitch machine and specify 12 for the vertical repeat:
|
||||
|
||||
<pre>
|
||||
x-x-
|
||||
|
|
@ -106,29 +111,11 @@
|
|||
-x-x
|
||||
</pre>
|
||||
|
||||
<p>You'll need to specify the height and width of 1 row and 1 column on the punchcard. You can determine the correct values by measuring the size of the squares on a card that works with your knitting machine. If your card doesn't have squares, you can measure the distance between the centers of two adjacent holes. For reference:
|
||||
|
||||
<p>
|
||||
<table>
|
||||
<tr>
|
||||
<th>Knitting Machine</th>
|
||||
<th style="text-align:center">Row Height<br>(mm)</th>
|
||||
<th style="text-align:center">Column Width<br>(mm)</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Brother 24-Stitch Standard</td>
|
||||
<td style="text-align:center">5</td>
|
||||
<td style="text-align:center">4.5</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Brother 12-Stitch Bulky</td>
|
||||
<td style="text-align:center">5</td>
|
||||
<td style="text-align:center">9</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p>The resulting SVG file can be imported into any application that understands SVG. You can also display the SVG in some browsers. Neither the text file or resulting SVG will be saved on this site. Be sure to keep them in a safe location so they don't get lost.
|
||||
|
||||
<p>Silhouette users have reported issues importing SVG files or having to resize the SVG in Inkscape before importing. If you have trouble with the SVG file, try checking the Convert To PNG button to see if that helps.
|
||||
|
||||
<h2>Bugs & Disclaimers</h2>
|
||||
<p>This program has only been tested using Sure Cuts A Lot on a Pazzles Creative Mighty. YMMV!
|
||||
|
||||
<p>If you have questions or feedback, please contact <a href="http://www.ravelry.com/people/beebell">beebell on Ravelry</a> via private message or log at issue on <a href="https://github.com/brendabell/knittingtools/issues">GitHub</a>.</p>
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
# intentionally empty
|
||||
Plik diff jest za duży
Load Diff
|
Po Szerokość: | Wysokość: | Rozmiar: 753 KiB |
|
|
@ -0,0 +1,4 @@
|
|||
-x--
|
||||
-xxx
|
||||
xxx-
|
||||
--x-
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
# invocation: python -m test.test
|
||||
|
||||
from modules.pcgenerator import PCGenerator
|
||||
import cairosvg
|
||||
|
||||
pattern='-x--\n-xxx\nxxx-\n--x-\n'
|
||||
#machine = '12-stitch-br-sr'
|
||||
#machine = '24-stitch-br-sr'
|
||||
machine = '40-stitch-deco'
|
||||
generator = PCGenerator(None, pattern, machine, 10)
|
||||
result = generator.generate()
|
||||
text_file = open("{}.svg".format(machine), "w")
|
||||
text_file.write(result)
|
||||
text_file.close()
|
||||
png_file = open("{}.png".format(machine), "w")
|
||||
cairosvg.svg2png(bytestring=result,write_to=png_file)
|
||||
png_file.close()
|
||||
Ładowanie…
Reference in New Issue