I wish to create dynamic plots based on user input on a flask app. However I am getting the following error:
string argument expected, got 'bytes'
If I use io.BytesIO(), I am not getting this error, but I am not getting the plot on test.html
from flask import Flask
from flask import render_template
import matplotlib.pyplot as plt
import io
import base64
app = Flask(__name__)
#app.route('/plot')
def build_plot():
img = io.StringIO()
y = [1,2,3,4,5]
x = [0,2,1,3,4]
plt.plot(x,y)
plt.savefig(img, format='png')
img.seek(0)
plot_url = base64.b64encode(img.getvalue())
return render_template('test.html', plot_url=plot_url)
if __name__ == '__main__':
app.debug = True
app.run()
Test.html
<!DOCTYPE html>
<html>
<title> Plot</title>
<body>
<img src="data:image/png;base64, {{ plot_url }}">
</body>
</html>
Use BytesIO and later decode()
Working example
from flask import Flask
#from flask import render_template
import matplotlib.pyplot as plt
import io
import base64
app = Flask(__name__)
#app.route('/plot')
def build_plot():
img = io.BytesIO()
y = [1,2,3,4,5]
x = [0,2,1,3,4]
plt.plot(x,y)
plt.savefig(img, format='png')
img.seek(0)
plot_url = base64.b64encode(img.getvalue()).decode()
return '<img src="data:image/png;base64,{}">'.format(plot_url)
if __name__ == '__main__':
app.debug = True
app.run()
Related
I'd like to create a small app in Flask which:
1. Allows the user to upload data, in csv
2. Does stuff to the data (in this example, nothing will be done)
3. Plot results
4. Allow the user to download a plot of the results as pdf.
Code so far:
app.py
from flask import Flask, Response, request, render_template
import matplotlib.pyplot as plt
import matplotlib
from matplotlib.backends.backend_agg import FigureCanvasAgg
from matplotlib.backends.backend_svg import FigureCanvasSVG
import io
import csv
import pandas as pd
app = Flask(__name__)
#app.route("/", methods=['POST', 'GET'])
def index():
return render_template("index.html")
#app.route('/transform', methods=["GET", "POST"])
def transform_view():
""" process data and render plot on the fly.
"""
# GET input csv
f = request.files['input_file']
if not f:
return "Main input file not found!"
# Read uploaded data as a Stream into a dataframe.
stream = io.StringIO(f.stream.read().decode("UTF8"), newline=None)
csv_input = csv.reader(stream)
contents = []
for row in csv_input:
contents.append(row)
d0 = pd.DataFrame(data=contents[1:], columns=contents[0])
# Change to numeric columns
d0[["col1", "col2"]] = d0[["col1", "col2"]].apply(pd.to_numeric)
# Plot
fig, ax = plt.subplots()
bplot = ax.boxplot([d0["col1"], d0["col2"]])
# Print out plot as pdf?
#output = io.BytesIO()
return render_template("index.html")
if __name__ == "__main__":
app.run(debug=True)
index.html
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<form action="/transform" method="post" enctype="multipart/form-data">
<h3>Upload input file (csv)</h3>
<input type="file" name="input_file" id="input_file">
<br>
<input type="submit" value="Process & Download" name="submit">
</form>
</body>
</html>
sample_input.csv
col1,col2
1,5
2,6
3,7
9,10
6,11
So I can generate the pdf with matplotlib, but I'm not sure how to return this as a pdf to the user (the last step). I'm not married to matplotlib; if there are other plotting libraries which can return plots as pdf, I'm open to that as well.
Not tested (I don't have flask installed on this workstation) but you can save the figure as a pdf by giving the correct extension
fig.savefig('/tmp/some_unique_string.pdf')
and then serve the file with send_from_directory
from flask import send_from_directory
send_from_directory('/tmp', 'some_unique_string.pdf')
Some unsolicited advice:
You can read a csv with pandas.read_csv. This works with a file path or an IO stream and will change numeric columns to an appropriate numeric type automatically.
If you want to switch to a different backend, you need to do so before importing pyplot.
Specifically:
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
I am trying to render boxplot to html page using python flask. In the code i have imported excel sheet .xls to a data frame. On this dataframe have used pandas boxplot method to get box plot successfully. But when i am trying to render this box plot to html page using flask, i am able to see only box plot grid in html page and not actual box plot.
I have tried to save the box plot image first and then give its image source path in html page. but still unable to render it successfully to html page.
Python code:
from flask import Flask, render_template, send_file, make_response
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas
from mpl_toolkits.mplot3d import Axes3D
from io import BytesIO
import pandas as pd
plt.style.use('ggplot')
app = Flask(__name__)
#app.route('/do_box_plot/')
def do_box_plot():
df = pd.read_excel('Path\\sfile_name.xls', sheet_name='Sheet1')
df.boxplot(by = 'Programming_Language', column =['Entry_ProgrammingScore'], grid = True)
bytes_image = io.BytesIO()
plt.savefig(bytes_image, format='png')
bytes_image.seek(0)
return send_file(bytes_image, mimetype='image/png')
#app.route('/')
def index():
return render_template("index2.html")
if __name__ == '__main__':
app.run(debug = True)
HTML code:
<!doctype html>
<html>
<body>
<h1>BOX PLOT VISUALIZATIONS FOR TEST SCORES</h1>
<p>BOX PLOTS</p>
<img src='/do_box_plot/' alt="Box Plot" height="442" width="442">
</body>
</html>`enter code here`
Expect to render generated box plot to html page using flask. Could anyone help, thanks.
I am trying to render a image from my sqlite3 database, by using this library:
#!/usr/bin/python3
from flask import Flask, render_template, request, send_file
from flask_sqlalchemy import SQLAlchemy
from io import BytesIO
import base64
#app.route('/show/')
def show():
file_data = FileContents.query.filter_by(id=3).first()
image = b64encode(file_data.data)
return render_template('display.html', image = image)
and putting this variable in the html:
<html>
<body>
<img src="data:image;base64,{{image}}"/>
</body>
</html>
But I can't show the image (I am using python3 and google chorme) can someone help to get it?
You can add the extension. Example: <img src="data:image/png;base64,{{ image }}"\>
And decode image to utf-8 return render_template('display.html', image = image.decode('utf8'))
I am right now writing a program which grabs images from the internet and use them to start a server and write the html.
Here is my code:
import json
import requests
import urllib2
import webserver
import bottle
from bottle import run, route, request
#get images
r = requests.get("http://web.sfc.keio.ac.jp/~t14527jz/midterm/imagelist.json")
r.text
imageli = json.loads(r.text)
i = 1
for image in imageli:
if i <= 5:
fname = str(i)+".png"
urllib.urlretrieve(image,fname)
i += 1
#to start a server
#route('/')
#write a HTML, here is where the problem is,
#the content of html is <image src= "1.png"> but the picture is not on the server.
#and I don't know how to do it
def index():
return "<html><head><title>Final exam</title></head><body> <img src=\"1.png\"/>\n <img src=\"2.png\"/>\n <img src=\"3.png\"/>\n<img src=\"4.png\"/>\n<img src=\"5.png\"/>\n</body></html>"
if __name__ == '__main__':
bottle.debug(True)
run(host='localhost', port=8080, reloader=True)
And I am having a problem that the images cannot be shown on the website, the console says images cannot be found.
How can I solve this problem?
The problem here is that you haven't defined a route for serving your static files. You could set the root directory to serve static files by adding this route:
# To serve static png files from root.
#bottle.get('/<filename:re:.*\.png>')
def images(filename):
return bottle.static_file(filename, root='')
But you should really move these to a subdirectory, as in:
import json
import requests
import urllib
import bottle
from bottle import run, route, request
#get images
r = requests.get("http://web.sfc.keio.ac.jp/~t14527jz/midterm/imagelist.json")
r.text
imageli = json.loads(r.text)
i = 1
for image in imageli:
if i <= 5:
fname = "static/{}.png".format(i) # Note update here
urllib.urlretrieve(image, fname)
i += 1
#to start a server
#route('/')
def index():
return "<html><head><title>Final exam</title></head><body> <img src=\"1.png\"/>\n <img src=\"2.png\"/>\n <img src=\"3.png\"/>\n<img src=\"4.png\"/>\n<img src=\"5.png\"/>\n</body></html>"
# To serve static image files from static directory
#bottle.get('/<filename:re:.*\.(jpg|png|gif|ico)>')
def images(filename):
return bottle.static_file(filename, root='static')
if __name__ == '__main__':
bottle.debug(True)
run(host='localhost', port=8080, reloader=True)
I am working on a Flask app which generates a dynamic plot and displays it through a jinja template. I would like to use the template to call a function in the Flask app which returns png data, and then embed the response in a data uri.
This gist is very close to my goal, except I would like to avoid using url_for (and thus routes). Instead, I would like to just display the image data inline using a data uri (img src="data:image/png;base64,...)
Instead of sending the output back as an image with a response, take the output and encode it to base64:
try: # Python 3
from urllib.parse import quote
except ImportError: # Python 2
from urllib import quote
from base64 import b64encode
from io import BytesIO
png_output = BytesIO()
canvas.print_png(png_output)
data = b64encode(png_output.getvalue()).decode('ascii')
data_url = 'data:image/png;base64,{}'.format(quote(data))
This has the canvas write to an in-memory file, and the resulting PNG data is then encoded to base64 and interpolated in a data URL.
Complete solution based on your example, tested and working:
from flask import Flask, render_template
import urllib
app = Flask(__name__)
#app.route("/simple.png")
def simple():
import datetime
import StringIO
import random
from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas
from matplotlib.figure import Figure
from matplotlib.dates import DateFormatter
fig=Figure()
ax=fig.add_subplot(111)
x=[]
y=[]
now=datetime.datetime.now()
delta=datetime.timedelta(days=1)
for i in range(10):
x.append(now)
now+=delta
y.append(random.randint(0, 1000))
ax.plot_date(x, y, '-')
ax.xaxis.set_major_formatter(DateFormatter('%Y-%m-%d'))
fig.autofmt_xdate()
canvas=FigureCanvas(fig)
png_output = StringIO.StringIO()
canvas.print_png(png_output)
png_output = png_output.getvalue().encode("base64")
return render_template("test.html", img_data=urllib.quote(png_output.rstrip('\n')))
if __name__ == "__main__":
app.run()
Template:
<img src="data:image/png;base64,{{img_data}}"/>