From 23d392c2f7a744d38a369f8bc91cfd0f215f82b0 Mon Sep 17 00:00:00 2001 From: jaseg Date: Fri, 29 Mar 2019 22:09:16 +0900 Subject: Working on the design --- gerboweb/deploy/nginx.conf | 2 +- gerboweb/deploy/playbook.yml | 6 - gerboweb/deploy/uwsgi-gerboweb.ini | 1 - gerboweb/gerboweb.py | 10 +- gerboweb/static/style.css | 241 +++++++++++++++++++++++++++++++++++++ gerboweb/templates/index.html | 217 +++++++++++++++++++++------------ 6 files changed, 388 insertions(+), 89 deletions(-) create mode 100644 gerboweb/static/style.css diff --git a/gerboweb/deploy/nginx.conf b/gerboweb/deploy/nginx.conf index c76a3db..6344904 100644 --- a/gerboweb/deploy/nginx.conf +++ b/gerboweb/deploy/nginx.conf @@ -65,7 +65,7 @@ http { include /etc/nginx/default.d/*.conf; location ^~ /static/ { - root /var/lib/gerboweb/static; + root /var/lib/gerboweb; } location / { diff --git a/gerboweb/deploy/playbook.yml b/gerboweb/deploy/playbook.yml index 3789c21..9753df6 100644 --- a/gerboweb/deploy/playbook.yml +++ b/gerboweb/deploy/playbook.yml @@ -152,12 +152,6 @@ name: uwsgi-app@gerboweb.socket enabled: yes - - name: Enable and launch uwsgi systemd service - systemd: - name: uwsgi-app@gerboweb.service - enabled: yes - state: restarted - - name: Enable and launch job processor systemd: name: gerboweb-job-processor.service diff --git a/gerboweb/deploy/uwsgi-gerboweb.ini b/gerboweb/deploy/uwsgi-gerboweb.ini index 748af71..3c8addd 100644 --- a/gerboweb/deploy/uwsgi-gerboweb.ini +++ b/gerboweb/deploy/uwsgi-gerboweb.ini @@ -1,5 +1,4 @@ [uwsgi] -chmod-socket = 660 master = True cheap = True idle = 600 diff --git a/gerboweb/gerboweb.py b/gerboweb/gerboweb.py index 17e03e2..1f8d884 100644 --- a/gerboweb/gerboweb.py +++ b/gerboweb/gerboweb.py @@ -9,7 +9,7 @@ from os import path import os import sqlite3 -from flask import Flask, url_for, redirect, session, make_response, render_template, request, send_file, abort +from flask import Flask, url_for, redirect, session, make_response, render_template, request, send_file, abort, flash from flask_wtf import FlaskForm from flask_wtf.file import FileField, FileRequired from wtforms.fields import RadioField @@ -26,7 +26,7 @@ class UploadForm(FlaskForm): class OverlayForm(UploadForm): upload_file = FileField(validators=[FileRequired()]) - side = RadioField('Side', choices=[('top', 'Top'), ('bottom', 'Bottom')]) + side = RadioField('Side', choices=[('top', 'Top'), ('bottom', 'Bottom')], default=lambda: session.get('last_download')) class ResetForm(FlaskForm): pass @@ -56,6 +56,7 @@ def require_session_id(fun): @app.route('/') @require_session_id def index(): + flash(f'Gerber file successfully uploaded.', 'success') forms = { 'gerber_form': UploadForm(), 'overlay_form': OverlayForm(), @@ -108,6 +109,7 @@ def upload(namespace): session_id=session['session_id'], side=upload_form.side.data) + flash(f'{"Gerber" if namespace == "gerber" else "Overlay"} file successfully uploaded.', 'success') return redirect(url_for('index')) @app.route('/render/preview/') @@ -120,10 +122,12 @@ def render_preview(side): def render_download(side): if not side in ('top', 'bottom'): return abort(400, 'side must be either "top" or "bottom"') + + session['last_download'] = side return send_file(tempfile_path(f'render_{side}.png'), mimetype='image/png', as_attachment=True, - attachment_filename=f'{path.splitext(session["filename"])[0]}_render.png') + attachment_filename=f'{path.splitext(session["filename"])[0]}_render_{side}.png') @app.route('/output/download') def output_download(): diff --git a/gerboweb/static/style.css b/gerboweb/static/style.css new file mode 100644 index 0000000..975c7f2 --- /dev/null +++ b/gerboweb/static/style.css @@ -0,0 +1,241 @@ + +:root { + --c-blue1: #19aeff; + --c-blue2: #0084c8; + --c-blue3: #005c94; + --c-red1: #ff4141; + --c-red2: #dc0000; + --c-red3: #b50000; + --c-orange1: #ffff3e; + --c-orange2: #ff9900; + --c-orange3: #ff6600; + --c-brown1: #ffc022; + --c-brown2: #b88100; + --c-brown3: #804d00; + --c-green1: #ccff42; + --c-green2: #9ade00; + --c-green3: #009100; + --c-purple1: #f1caff; + --c-purple2: #d76cff; + --c-purple3: #ba00ff; + --c-metallic1: #bdcdd4; + --c-metallic2: #9eabb0; + --c-metallic3: #364e59; + --c-metallic4: #0e232e; + --c-grey1: #ffffff; + --c-grey2: #cccccc; + --c-grey3: #999999; + --c-grey4: #666666; + --c-grey5: #2d2d2d; + + --cg1: #003018; + --cg2: #006130; + --cg3: #00964a; + --cg4: #00d167; + --cg5: #4cffa4; + --cg6: #b7ffda; + --cg7: #e1fff0; +} + +body { + font-family: 'Helvetica', 'Arial', sans-serif; + color: var(--c-metallic4); + display: flex; + flex-direction: row; + justify-content: center; + margin: 0; + background-color: hsl(10 10% 97%); +} + +.layout-container { + flex-basis: 55em; + flex-shrink: 1; + flex-grow: 0; + padding: 3em; + background-color: white; +} + +div.flash-success { + background-color: var(--c-green1); + color: hsl(80 20% 20%); + text-shadow: 0 0 2px var(--c-green1); + border-radius: 5px; + margin: 1em; + padding-left: 3em; + padding-right: 3em; + padding-top: 2em; + padding-bottom: 2em; +} + +div.flash-success::before { + content: "Success!"; + display: block; + font-weight: bold; + font-size: 16pt; + margin-right: 1em; + margin-bottom: 0.5em; +} + +div.desc { + margin-top: 5em; + margin-bottom: 7em; +} + +div.loading-message { + text-align: center; + margin-top: 2em; +} + +.steps { + display: flex; + flex-direction: column; +} + +.step { + display: flex; + flex-direction: row; + align-items: flex-start; + justify-content: center; + flex-wrap: wrap; + width: 100%; + padding-top: 20px; +} + +.step > .description { + flex-basis: 20em; + flex-shrink: 0; + flex-grow: 0; + margin-left: 20px; + + overflow-wrap: break-word; + word-wrap: break-word; + hyphens: auto; + text-align: justify; +} + +.step > .description > h2 { + text-align: right; + margin-top: 0 +} + +.step > .controls { + flex-grow: 1; + flex-shrink: 1; + display: flex; + flex-direction: column; + align-items: stretch; + margin-right: 20px; + margin-left: 20px; + + padding: 1em; + + background-color: hsl(210 40% 97%); + border-radius: 5px; +} + +input.reset-button { + background-color: var(--c-red1); + color: var(--c-grey1); + text-shadow: 0 0 2px var(--c-red3); + border: 0; + border-radius: 5px; + padding: 0.5em 1em 0.5em 1em; +} + +input.submit-button { + background-color: var(--c-green2); + color: hsl(80 20% 20%); + text-shadow: 0 0 2px var(--c-green1); + font-weight: bold; + margin-left: 1em; + border: 0; + border-radius: 5px; + padding: 0.5em 1em 0.5em 1em; +} + +.controls > .form-controls { + margin-bottom: 1em; +} + +.controls > .submit-buttons { + margin-top: 1em; + text-align: right; +} + +.controls > .download-controls { + padding: 1em; + margin-bottom: 1em; + display: flex; + flex-direction: column; + align-items: center; +} + +a.output-download:link, a.output-download:hover, a.output-download:visited, a.output-download:active { + font-size: 30pt; + font-weight: bold; + color: var(--c-metallic4); + text-shadow: 0.5px 0.5px 0.5px var(--c-metallic2); +} + +.preview-images { + display: flex; + flex-direction: row; + flex-wrap: wrap; + align-items: flex-start; + justify-content: space-around; +} + +.preview { + width: 200px; + height: 200px; + border-radius: 5px; + display: flex; + justify-content: center; + align-items: center; +} + +a.overlay:link, a.overlay:hover, a.overlay:visited, a.overlay:active { + text-align: center; + font-size: 30pt; + font-weight: bold; + color: var(--c-metallic4); + text-shadow: 0.5px 0.5px 0.5px var(--c-metallic2); +} + +/* Spinner from https://loading.io/css/ */ +.lds-ring { + display: inline-block; + position: relative; + width: 64px; + height: 64px; +} +.lds-ring div { + box-sizing: border-box; + display: block; + position: absolute; + width: 51px; + height: 51px; + margin: 6px; + border: 6px solid var(--c-metallic4); + border-radius: 50%; + animation: lds-ring 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite; + border-color: var(--c-metallic4) transparent transparent transparent; +} +.lds-ring div:nth-child(1) { + animation-delay: -0.45s; +} +.lds-ring div:nth-child(2) { + animation-delay: -0.3s; +} +.lds-ring div:nth-child(3) { + animation-delay: -0.15s; +} +@keyframes lds-ring { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(360deg); + } +} + diff --git a/gerboweb/templates/index.html b/gerboweb/templates/index.html index c5c8503..3e4c255 100644 --- a/gerboweb/templates/index.html +++ b/gerboweb/templates/index.html @@ -1,86 +1,147 @@ - - - Gerbolyze Raster image to PCB renderer - - -
-

Raster image to PCB converter

-

- Gerbolyze is a tool for rendering black and white raster (PNG) images directly onto gerber layers. You can - use this to put art on a PCB's silkscreen, solder mask or copper layers. The input is a black-and-white PNG - image that is vectorized and rendered into an existing gerber file. Gerbolyze works with gerber files - produced with any EDA toolchain and has been tested to work with both Altium and KiCAD. -

-
+ + + Gerbolyze Raster image to PCB renderer + + + +
+
+

Raster image to PCB converter

+

+ Gerbolyze is a tool for rendering black and white raster (PNG) images directly onto gerber layers. You can + use this to put art on a PCB's silkscreen, solder mask or copper layers. The input is a black-and-white PNG + image that is vectorized and rendered into an existing gerber file. Gerbolyze works with gerber files + produced with any EDA toolchain and has been tested to work with both Altium and KiCAD. +

+
-
-

Step 1: Upload zipped gerber files

-

- First, upload a zip file containing all your gerber files. The default file names used by KiCAD, Eagle - and Altium are supported. -

+ {% with messages = get_flashed_messages(with_categories=True) %} + {% if messages %} +
+ {% for category, message in messages %} +
{{ message }}
+ {% endfor %} +
+ {% endif %} + {% endwith %} -
- {{gerber_form.csrf_token}} - {{gerber_form.upload_file.label}} {{gerber_form.upload_file(size=20)}} - -
-
+
{{reset_form.csrf_token}}
- {% if 'render_job' in session or has_renders %} -
-

Step 2: Download the target side's preview image

-

- Second, download either the top or bottom preview image and use it to align and scale your own artwork - in an image editing program such as Gimp. Then upload your overlay image below. +

+
+
+

Step 1: Upload zipped gerber files

+

+ First, upload a zip file containing all your gerber files. The default file names used by KiCAD, Eagle + and Altium are supported. +

+
- Note that you will have to convert grayscale images into binary images yourself. Gerbolyze can't do this - for you since there are lots of variables involved. Our Guideline on image processing gives an overview on - one way to produce agreeable binary images from grayscale source material. -

- {% if 'render_job' in session %} - Processing... (this may take several minutes!) - {% else %} - Download - Download - {% endif %} -
- {{reset_form.csrf_token}} - -
-
+
+
+ {{gerber_form.csrf_token}} +
+
+
Upload Gerber file:
+ +
+
+ + +
+
+
-
-

Step 3: Upload overlay image

-

- Now, upload your binary overlay image as a PNG and let gerbolyze render it onto the target layer. The PNG - file should be a black and white binary file with details generally above about 10px size. Antialiased - edges are supported. -

-
- {{overlay_form.csrf_token}} - {{overlay_form.upload_file.label}} {{overlay_form.upload_file(size=20)}} - {{overlay_form.side.label}} {{overlay_form.side()}} - -
-
+ {% if 'render_job' in session or has_renders %} +
+
+

Step 2: Download the target side's preview image

+

+ Second, download either the top or bottom preview image and use it to align and scale your own artwork + in an image editing program such as Gimp. Then upload your overlay image below. - {% if 'vector_job' in session or has_output %} -

-

Step 4: Download the processed gerber files

- {% if 'vector_job' in session %} - Processing... (this may take several minutes!) - {% else %} - Download - {% endif %} -
- {{reset_form.csrf_token}} - -
-
- {% endif %} {# vector job #} - {% endif %} {# render job #} - + Note that you will have to convert grayscale images into binary images yourself. Gerbolyze can't do this + for you since there are lots of variables involved. Our Guideline on image processing gives an overview on + one way to produce agreeable binary images from grayscale source material. +

+
+
+ {% if 'render_job' in session %} + Processing... (this may take several minutes!) + {% else %} + + {% endif %} +
+ +
+
+
+ +
+
+

Step 3: Upload overlay image

+

+ Now, upload your binary overlay image as a PNG and let gerbolyze render it onto the target layer. The PNG + file should be a black and white binary file with details generally above about 10px size. Antialiased + edges are supported. +

+
+
+
+ {{overlay_form.csrf_token}} +
+
+
Upload Overlay PNG file:
+ +
+
+
Target layer:
+ + + + +
+
+ + +
+
+
+ + {% if 'vector_job' in session or has_output %} +
+
+

Step 4: Download the processed gerber files

+
+
+ {# if 'vector_job' in session FIXME #} + {% if True %} +
+
+
Processing...
+
(this may take several minutes!)
+
+ {% else %} + + {% endif %} +
+ +
+
+
+ {% endif %} {# vector job #} + {% endif %} {# render job #} +
+
+ -- cgit