From 1711747333c7666b644f6124de39c49cd0b26526 Mon Sep 17 00:00:00 2001 From: Date: Fri, 14 Sep 2007 15:22:57 -0400 Subject: [PATCH] first working version --- ruler_web.py | 163 --------- static/images/bl.png | Bin 0 -> 399 bytes static/images/br.png | Bin 0 -> 240 bytes static/images/r.png | Bin 0 -> 72 bytes static/images/tl.png | Bin 0 -> 3345 bytes static/images/tr.png | Bin 0 -> 247 bytes static/images/trash.png | Bin 655 -> 0 bytes static/images/yourule_banner.png | Bin 0 -> 21875 bytes static/images/yourule_banner.svg | 556 +++++++++++++++++++++++++++++++ static/style.css | 137 ++++++++ ruler.py => svgruler.py | 6 +- templates/_form_elements.tmpl | 6 +- templates/gallery.tmpl | 12 +- templates/index.tmpl | 18 +- templates/show_ruler.tmpl | 26 +- templates/site.tmpl | 24 ++ yourule.py | 220 ++++++++++++ 17 files changed, 978 insertions(+), 190 deletions(-) delete mode 100755 ruler_web.py create mode 100644 static/images/bl.png create mode 100644 static/images/br.png create mode 100644 static/images/r.png create mode 100644 static/images/tl.png create mode 100644 static/images/tr.png delete mode 100644 static/images/trash.png create mode 100644 static/images/yourule_banner.png create mode 100644 static/images/yourule_banner.svg create mode 100644 static/style.css rename ruler.py => svgruler.py (97%) create mode 100755 yourule.py diff --git a/ruler_web.py b/ruler_web.py deleted file mode 100755 index 2738f8f..0000000 --- a/ruler_web.py +++ /dev/null @@ -1,163 +0,0 @@ -#!/usr/bin/env python - -from __future__ import division -import web -import sys, os, re -from storm.locals import * -from ruler import RulerImage - -# the url map for the application -urls = ( '/', 'index', - '/ruler_([0-9\.]+)px_([0-9\.]+)([A-Za-z]+).(svg|png|jpg)', 'ruler_img', - '/show/(.*(svg|png|jpg))', 'ruler', - '/gallery(.*)', 'gallery', - '/delete/(\d+)', 'delete', - '/undelete/(\d+)', 'undelete') - -database = create_database("sqlite:ruler_web.db") -store = Store(database) - -class SavedRuler(object): - __storm_table__ = "gallery" - id = Int(primary=True) - pixel_width = Float() - unit_width = Float() - model = Unicode() - units = Unicode() - show = Int() - - def __init__(self, **kw): - self.pixel_width = kw['pixel_width'] - self.unit_width = kw['unit_width'] - self.units = kw['units'] - self.model = kw['model'] - - def cm_width(self): - if self.units == 'centimeters': - return self.unit_width - elif self.units == 'inches': - return(self.unit_width * 2.54) - - def in_width(self): - if self.units == 'inches': - return self.unit_width - elif self.units == 'centimeters': - return(self.unit_width * 0.3937) - - -class index: - def GET(self): - web.header("Content-Type","text/html; charset=utf-8") - web.render('index.tmpl') - - def POST(self): - input = web.input() - - pixel_width = input['pixel_width'] - unit_width = input['unit_width'] - units = input['units'] - ruler_url = 'ruler_%spx_%s%s.png' % (pixel_width, unit_width, units) - web.redirect('/show/%s' % ruler_url) - -class ruler: - def GET(self, ruler_url, ext): - web.debug('test test') - if web.input().has_key('fromgallery'): - fromgallery = True - else: - fromgallery = False - - other_unit, other_unit_url = get_other_units(ruler_url) - - web.header("Content-Type","text/html; charset=utf-8") - web.render('show_ruler.tmpl') - - -class ruler_img: - def GET(self, pixel_width=None, unit_width=None, units=None, ext=None): - - # TODO check to see if it's a format that we support - - # set ruler height to be 200 px always - pixel_width = float(pixel_width) - unit_width = float(unit_width) - - ruler_height = 200 - - scale = pixel_width / unit_width - ruler_length = int(unit_width) - - ruler = Ruler(scale, units, ruler_height, ruler_length) - - # print the header - if ext == 'svg': ext = 'svg+xml' - web.header("Content-Type", "image/%s" % ext) - - if ext == 'svg+xml': - sys.stdout.write(ruler.output()) - else: - pin, pout = os.popen2('convert -size %sx%s - %s:-' % \ - (pixel_width, ruler_height, ext)) - - pin.write(ruler.output()) - pin.close() - sys.stdout.write(pout.read()) - -class gallery: - def GET(self, ruler_url): - - if ruler_url: - pixel_width, unit_width, units = process_ruler_url(ruler_url)[0:3] - - rulers = store.find(SavedRuler, SavedRuler.show == 1) - rulers.order_by(SavedRuler.model) - - web.render('gallery.tmpl') - - def POST(self): - input = web.input() - - new_ruler = SavedRuler(pixel_width=float(input['pixel_width']), - unit_width=float(input['unit_width']), - units=unicode(input['units']), - model=unicode(input['model'])) - - store.add(new_ruler) - store.commit() - - web.redirect('gallery') - -class delete: - def GET(self, id): - ruler = store.get(SavedRuler, int(id)) - ruler.show = 0 - store.commit() - web.redirect('/gallery') - - -class undelete: - def GET(self, id): - ruler = store.get(SavedRuler, int(id)) - ruler.show = 1 - store.commit() - web.redirect('/gallery') - -def get_other_units(url): - pixel_width, unit_width, units = process_ruler_url(url)[0:3] - ruler = SavedRuler(pixel_width=float(pixel_width), - unit_width=float(unit_width), - units=unicode(units), - model=unicode(model)) - -def process_ruler_url(url): - url = re.sub(r'^/?(ruler.*)$', r'\1', url) - return(re.match( r'ruler_([\d\.]+)px_([\d\.]+)(\w+).(png|svg|jpg)', - url).groups()) - -# render the site template here so that i can use it later -web.render('site.tmpl', None, True, 'site') - -web.webapi.internalerror = web.debugerror -if __name__ == "__main__": - web.run(urls, globals(), web.reloader) - diff --git a/static/images/bl.png b/static/images/bl.png new file mode 100644 index 0000000000000000000000000000000000000000..f0970aea5e95cad7406b7e061aeb7494d133b9ea GIT binary patch literal 399 zcmeAS@N?(olHy`uVBq!ia0y~yU;#223 z;_m47i5sPEwaip#V%0TE)Z=IiOGwZ;<`#8)k3(ewYPf2Of6d?wN z1M6Q$^8?M>=ME$slt4xc2?B`;9YCVN1!SX&5|B_3WCW6(96-Xu1xS#t^yT!^sV*{n z?bp142EY1sF+)V~`|rJ9fo@o^tIlk;>ye)|d3%BOtyrhM)MxqS+i$nse}DaT>1$h{ zWv;a@IcBpvB-$2!l>n+=w{D7%VE55SC01vje?I;6Qfch#P@pQ&_Y4(w^Vi=$Aox&M zfPvx9s&a-sd)_g|J%3wfE!Q8uI&^U&P!;Rb%LY7?PcHeY0W_(rU%H`w_uY3@d+p@< iAAkJu*B7Wlwf@El|BjjFQo%rvGI+ZBxvX&2huAvyMk)W;{|7(M(+v@_y>JFDHwN%3Y@iP5Q!BK7YYNV!(g* z_rLX4UX${dKdz|xT*iO`pl*z>cE`=#rpyoVJsWsci*F8nQD z^}Lep!tHIkr#~$?zc=4%Ns!n7_vtUiH(r}nY_>Ub*1qlL8|G|@kTYD)0rUccr>mdK II;Vst0AvMjpa1{> literal 0 HcmV?d00001 diff --git a/static/images/r.png b/static/images/r.png new file mode 100644 index 0000000000000000000000000000000000000000..fab4ac01a9c370b04ed1eec072ac60cc311f8603 GIT binary patch literal 72 zcmeAS@N?(olHy`uVBq!ia0vp^OhC-Y0V1m%Ufcyz{GKk3Ar*6y6Tko{z|O!Ju9zeV PWHETU`njxgN@xNA%&!hW literal 0 HcmV?d00001 diff --git a/static/images/tl.png b/static/images/tl.png new file mode 100644 index 0000000000000000000000000000000000000000..04576f8f1e1065410f66a25c1f4a38e8fe2fee3c GIT binary patch literal 3345 zcmeAS@N?(olHy`uVBq!ia0y~yU;#3jA~=|URB;h=2?GPq4Nn)xkcv5P?{4%}4rFk> znBQ&GA*1Z1WYr*Yv9Y^=(LvZxP;g;(!<9uWyZQ1N4z9aVQnuwz@gnICWFv@yCya+i%Cl#qHa_|NLHdpx}j~o{xY2*c`i&V^-YH$iT2{>z%yq z^78WAw{PF~o1LLSK7Z%^_s1o!U%y_g50qBhZX?5&+;cU{^t%uPLqbu!_`g42>kj~( z!oPV=H85B{yypOt9D+bXKnX}FxBv-<4j|DW0MyT*2nstVP&l@XDjW@-(L^zt6-G;n v(eiP$Rv4``N1FmB3tM}H=;rOx2#>gTe~DWM4fr?GR0 literal 0 HcmV?d00001 diff --git a/static/images/tr.png b/static/images/tr.png new file mode 100644 index 0000000000000000000000000000000000000000..e4ac949ba7e4c2fd6cc45614a97ce1950d7e6d56 GIT binary patch literal 247 zcmeAS@N?(olHy`uVBq!ia0vp^azI?c!2~3;{$Je$r1p8bIEGZrd3$LiZ?l1b+r#@a zo4almc|PS75Y^tmoy5WB(DPW*hJ8BINw2OY4^Mq(`|+-1*8=vlpYN2$dSymVQ$M#q zsps{sTjiVB=RV(=8!c_nw^;hFRo`OX63`|33X!u=w00FU@}?_rFidT6^%!ov#8-D*x9;UmyzXuVS7M!RpKL;rKS7 PPZ&I1{an^LB{Ts5+lzb| literal 0 HcmV?d00001 diff --git a/static/images/trash.png b/static/images/trash.png deleted file mode 100644 index 0e0953c73c60adbbc762d0553f95a586f3054aa6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 655 zcmV;A0&x9_P)Cml&2>S!-Vj|(%DY|xv2rmiUs+--iLl+4{ z?G|A~=Ohg>vv9>N%M}DMW!>HPeV%zbm|I~4O9R8eF!SL%Uk3g&6hQCgwr&w;ys}lf zIX1F%*Xv2Q_jYug8SU>IsAzqBV(1P`&UnCRf8PMZ!;e^5{(|GUxUTch+I8+M7rU?V zW_t34vrC+PH%AZz1VKm`?q)<3MMP26&l(YiA-R=YS&0X*u<(|l`}Zn3VP^W}j)^_# z_6NY#uC7z$Rx^mTh**raXl*fCh+`p13_7uhfHnqW4A$CV1<;djx7Tl^S>Kr5@mDOH z8zq#g#&c^~-&kgAv%q#=W$W-maC!CHfe5XaxietSj|8I1&`<#3&|j$e*lNa`-i+0@!$Xe002ovPDHLkV1g_FA-@0s diff --git a/static/images/yourule_banner.png b/static/images/yourule_banner.png new file mode 100644 index 0000000000000000000000000000000000000000..877889cf5218f32d40f2ba0eef49654dc0b67eeb GIT binary patch literal 21875 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 literal 0 HcmV?d00001 diff --git a/static/images/yourule_banner.svg b/static/images/yourule_banner.svg new file mode 100644 index 0000000..3b46682 --- /dev/null +++ b/static/images/yourule_banner.svg @@ -0,0 +1,556 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + YouRule + Onscreen Ruler Generator + diff --git a/static/style.css b/static/style.css new file mode 100644 index 0000000..b589157 --- /dev/null +++ b/static/style.css @@ -0,0 +1,137 @@ +@charset "UTF-8"; + +html, body, div, span, applet, object, iframe, +h1, h2, h3, h4, h5, h6, p, blockquote, pre, +a, abbr, acronym, address, big, cite, code, +del, dfn, em, font, img, ins, kbd, q, s, samp, +small, strike, strong, sub, sup, tt, var, +dl, dt, dd, +fieldset, form, label, legend, +table, caption, tbody, tfoot, thead, tr, th, td { + margin: 0; + padding: 0; + border: 0; + outline: 0; + font-weight: inherit; + font-style: inherit; + font-family: inherit; + vertical-align: baseline; +} + +body { + font: Arial, Helvetica, sans-serif; +} + +label { + font-weight: bold; +} + +em { + font-style: italic; +} + +#wrapper { + padding: 1em 0 0 0; + margin: 0 auto 0 auto; + width: 885px; +} + +#header { + width: 885px; + height: 138px; + background: url(/static/images/yourule_banner.png); +} + +#header h1, #header h2 { + display: none; +} + +.mainbox { + margin-top: 1em; + background: url(/static/images/tl.png) no-repeat top left; +} + +.mainbox_top { + background: url(/static/images/tr.png) no-repeat top right; +} + +.mainbox_bottom { + background: url(/static/images/bl.png) no-repeat bottom left; +} + +.mainbox_bottom div { + background: url(/static/images/br.png) no-repeat bottom right; +} + +.mainbox_content { + background: url(/static/images/r.png) top right repeat-y; +} + + +.mainbox_top div, .mainbox_top, .mainbox_bottom div, .mainbox_bottom { + width: 100%; + height: 30px; + font-size: 1px; +} + +.mainbox_content, .mainbox_bottom { + margin-top: -19px; +} + +.mainbox_content { + padding: 0em 3em 2em 3em; + line-height: 1.5em; +} + +.mainbox_content h2 { + border-bottom: 3px #1e3d7b solid; + margin: 19px 0 0.8em 0; + font-weight: bold; +} + +.mainbox_content p { + margin-bottom: 0.5em; +} + +#gallery { + border: 1px solid #1e3d7b; + width: 100%; + border-collapse: collapse; +} + +#gallery th { + background: #1e3d7b; + color: white; +} + +#gallery td { + text-align: center; +} + +#menu { + text-align: right; + margin: 0.5em; +} + +#rulerimg { + margin: 0.5em; +} + +.errormsg { + background-color: #f7fb70; + color: #4c4c4c; + text-align: center; + border: 1px #fdff00 solid; + width: 20em; + margin: 1em; +} + +strong { + font-weight: bold; +} + +#footer { + margin-top: 3em; + font-size: 0.8em; + text-align: center; +} diff --git a/ruler.py b/svgruler.py similarity index 97% rename from ruler.py rename to svgruler.py index 39bcd3d..7c66050 100644 --- a/ruler.py +++ b/svgruler.py @@ -2,7 +2,7 @@ from __future__ import division import SVGdraw -class Ruler: +class SVGRuler: def __init__(self, scale=None, units=None, ruler_height=None, ruler_length=None): @@ -49,14 +49,14 @@ class Ruler: self.__drawline(0.3) self.__drawline(0.6) - self.svg.addElement(SVGdraw.text((self.point - font_height * 1.4), + self.svg.addElement(SVGdraw.text((self.point - font_height * 1.15), (self.ruler_height * 0.6 - 5), str(i + 1), font_height)) self.drawing.setSVG(self.svg) - def output(self): + def getxml(self): import cStringIO xml = cStringIO.StringIO() xml.write("\n") diff --git a/templates/_form_elements.tmpl b/templates/_form_elements.tmpl index 18c57c8..b365ce0 100644 --- a/templates/_form_elements.tmpl +++ b/templates/_form_elements.tmpl @@ -1,14 +1,14 @@ -

Units: +

-

+

-

+

+
+ + +
+
+
+ #filter Filter $body #end filter +
+
+
+ + + +
+ diff --git a/yourule.py b/yourule.py new file mode 100755 index 0000000..9010217 --- /dev/null +++ b/yourule.py @@ -0,0 +1,220 @@ +#!/usr/bin/env python + +from __future__ import division +import web +import sys, os, re +from storm.locals import * +from svgruler import SVGRuler + +# the url map for the application +urls = ( '/', 'index', + '/ruler_([0-9\.]+)px_([0-9\.]+)([A-Za-z]+).(svg|png|jpg)', 'ruler_img', + '/show/(.*(svg|png|jpg))', 'show_ruler', + '/gallery(.*)', 'gallery', + '/delete/(\d+)', 'delete', + '/undelete/(\d+)', 'undelete') + +database = create_database("sqlite:yourule.db") +store = Store(database) + +class Ruler(object): + __storm_table__ = "gallery" + id = Int(primary=True) + pixel_width = Float() + unit_width = Float() + model = Unicode() + units = Unicode() + show = Int() + cm_in_ratio = 0.3937 + + def __init__(self, **kw): + self.pixel_width = float(kw['pixel_width']) + self.unit_width = float(kw['unit_width']) + self.units = unicode(kw['units']) + if kw.has_key('model'): + self.model = unicode(kw['model']) + else: + self.model = u'' + + def cm_width(self): + if self.units == 'centimeters': + return self.unit_width + elif self.units == 'inches': + return(round(self.unit_width / self.cm_in_ratio, 2)) + + def in_width(self): + if self.units == 'inches': + return self.unit_width + elif self.units == 'centimeters': + return(round(self.unit_width * self.cm_in_ratio, 2)) + + def url(self): + return('ruler_%spx_%s%s.png' % (self.pixel_width, + self.unit_width, self.units)) + + +class index: + def GET(self): + web.header("Content-Type","text/html; charset=utf-8") + web.render('index.tmpl') + + def POST(self): + input = web.input() + + errormsg = validate_input(input) + + if errormsg: + pixel_width = input['pixel_width'] + unit_width = input['unit_width'] + units = input['units'] + web.render('index.tmpl') + else: + ruler = Ruler(pixel_width = input['pixel_width'], + unit_width = input['unit_width'], + units = input['units']) + + web.redirect('/show/%s' % ruler.url()) + +class show_ruler: + def GET(self, ruler_url, ext): + web.debug('test test') + if web.input().has_key('fromgallery'): + fromgallery = True + else: + fromgallery = False + + other_unit, other_unit_url = get_other_unit(ruler_url) + + web.header("Content-Type","text/html; charset=utf-8") + web.render('show_ruler.tmpl') + + +class ruler_img: + def GET(self, pixel_width=None, unit_width=None, units=None, ext=None): + + # TODO check to see if it's a format that we support + + # set ruler height to be 200 px always + pixel_width = float(pixel_width) + unit_width = float(unit_width) + + ruler_height = 200 + + scale = pixel_width / unit_width + ruler_length = int(unit_width) + + ruler = SVGRuler(scale, units, ruler_height, ruler_length) + + # print the header + if ext == 'svg': ext = 'svg+xml' + web.header("Content-Type", "image/%s" % ext) + + if ext == 'svg+xml': + sys.stdout.write(ruler.getxml()) + else: + pin, pout = os.popen2('convert -size %sx%s - %s:-' % \ + (pixel_width, ruler_height, ext)) + + pin.write(ruler.getxml()) + pin.close() + sys.stdout.write(pout.read()) + +class gallery: + def GET(self, ruler_url): + + if ruler_url: + pixel_width, unit_width, units = process_ruler_url(ruler_url)[0:3] + + rulers = store.find(Ruler, Ruler.show == 1) + rulers.order_by(Ruler.model) + web.render('gallery.tmpl') + + def POST(self, ruler_url): + input = web.input() + + errormsg = valid_input(input) + if not input.model: + errormsg = 'Please fill out all fields.' + + if errormsg: + pixel_width = input['pixel_width'] + unit_width = input['unit_width'] + units = input['units'] + model = input['model'] + else: + new_ruler = Ruler(pixel_width = input['pixel_width'], + unit_width = input['unit_width'], + units = input['units'], + model = input['model']) + + store.add(new_ruler) + store.commit() + + rulers = store.find(Ruler, Ruler.show == 1) + rulers.order_by(Ruler.model) + web.render('gallery.tmpl') + +class delete: + def GET(self, id): + ruler = store.get(Ruler, int(id)) + ruler.show = 0 + store.commit() + web.redirect('/gallery') + + +class undelete: + def GET(self, id): + ruler = store.get(Ruler, int(id)) + ruler.show = 1 + store.commit() + web.redirect('/gallery') + +def get_other_unit(url): + pixel_width, unit_width, units = process_ruler_url(url)[0:3] + + ruler = Ruler(pixel_width=pixel_width, unit_width=unit_width, units=units) + pixel_width, unit_width, units = process_ruler_url(url)[0:3] + + if units == 'centimeters': + units = 'inches' + unit_width = ruler.in_width() + elif units == 'inches': + units = 'centimeters' + unit_width = ruler.cm_width() + + new_ruler = Ruler(pixel_width=pixel_width, unit_width=unit_width, + units=units) + + web.debug(units) + return(units, new_ruler.url()) + + +def process_ruler_url(url): + url = re.sub(r'^/?(ruler.*)$', r'\1', url) + return(re.match( r'ruler_([\d\.]+)px_([\d\.]+)(\w+).(png|svg|jpg)', + url).groups()) + +def validate_input(input): + errormsg = False + + if not input.pixel_width \ + or not input.unit_width: + errormsg = 'Please fill out all fields.' + elif not re.match('^[\d\.]+$', input.pixel_width) \ + or not re.match('^[\d\.]+$', input.unit_width): + errormsg = "Widths must be numbers." + elif input['pixel_width'] < 0 \ + or input['unit_width'] < 0: + errormsg = 'Widths must be greater than postive.' + + return(errormsg) + +# render the site template here so that i can use it later +web.render('site.tmpl', None, True, 'site') + +web.webapi.internalerror = web.debugerror +if __name__ == "__main__": + web.run(urls, globals(), web.reloader) + +application = web.wsgifunc(web.webpyfunc(urls, globals())) + -- 2.30.2