Simple Interactive SVG Application with Flask and Jinja

In this post I’ll provide a simple example of serving a page with Flask and feeding SVG instructions into a Jinja template through an HTML form.

Structure

Our application will have the following structure:

app/
    app.py
    templates/
        layout.html
        submit.html
    static/
        style.css
        style.sass

Jinja Forms

First we’ll look at the HTML forms through the lens of Jinja templates. We’ll be drawing an SVG circle, so the user will need to input the x and y coordinates of the center of the circle and the radius. The following is the submit.html document.

{% extends "layout.html" %}
{% block body %}
<h1>ouroboros</h1>
{{ svg|safe }}
<form method="post" action="{{url_for('form')}}" enctype="multipart/form-data">
    <p>cx:</p>
    <input type="text" name="cx">
    <p>cy:</p>
    <input type="text" name="cy">
    <p>r:</p>
    <input type="text" name="r">
    <input type="submit">
</form>
{% endblock %}
  • Lines 1 and 2 say that we’re grabbing boilerplate from “layout.html”
  • Line 4 injects the SVG code into the template
  • Line 5 begins the form
  • Line 6 is a label
  • Line 7 is an input field
  • Line 12 is a submit button

The HTML boilerplate is encapsulated in the layout.html document, also in the templates directory.

<!DOCTYPE html>
<html>
    <head>
        <title>Python Function Finder</title>
        <link rel="stylesheet" type="text/css" href="{{url_for('static',filename='style.css')}}">
    </head>
    <div class=page>
        {% block body %}{% endblock %}
    </div>
</html>

Flask App

This is the server for the application. It’s actually not too bad. There’s a function circle() that takes the radius and the coordinates of the center of the circle, and returns an HTML string describing an SVG element. We render the submit.html page with both 'GET' and 'POST' requests.

#!/usr/bin/env python

from flask import Flask, render_template, request, url_for
from werkzeug import secure_filename

app = Flask(__name__)

def circle( cx, cy, r ):
    svg = '<svg width="100" height="100">{0}</svg>'
    circ = '<circle cx="{0}" cy="{1}" r="{2}" stroke="red"/>'
    circ = circ.format( cx, cy, r )
    svg = svg.format( circ )
    return svg

@app.route('/',methods=['GET','POST'])
def form():
    if request.method == 'POST':
        cx = request.form['cx']
        cy = request.form['cy']
        r  = request.form['r']
        svg = circle( cx, cy, r )
        context = { 'cx':cx, 'cy':cy, 'r':r, 'svg':svg }
        return render_template( 'submit.html', **context )
    elif request.method == 'GET':
        return render_template( 'submit.html' )

if __name__=='__main__':
    app.debug = True
    app.run( host='localhost', port=8080 )

Styling

This was my sass code. I’m not very interested in CSS, so it’s embarrassingly minimal.

*
  font-family: Helvetica, sans-serif
  
h1
  color: #555555

input[type="text"]
  padding: 2px
  border: solid 1px #dcdcdc
  transition: box-shadow 0.3s, border 0.3s

input[type="text"]:focus, input[type="text"].focus
  border: solid 1px #707070
  box-shadow: 0 0 5px 1px #969696

So that’s it. You’ll need to have Flask and all of its dependencies, and you’ll need to install Sass, which uses Ruby, but that should provide you with a page that can take user input and render SVG graphics using Flask, Jinja, and Sass.