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}}"/>
Related
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 have a function which generates a PDF and I have a Flask website. I would like to combine the two so that when you visit the site, a PDF is generated and downloaded. I am working on combining various bits of code that I don't fully understand. The PDF is generated and downloaded but fails to ever load when I try to open it. What am I missing?
import cStringIO
from reportlab.lib.enums import TA_JUSTIFY
from reportlab.lib.pagesizes import letter
from reportlab.platypus import SimpleDocTemplate, Paragraph
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from flask import make_response, Flask
from reportlab.pdfgen import canvas
app = Flask(__name__)
#app.route('/')
def pdf():
output = cStringIO.StringIO()
doc = SimpleDocTemplate("test.pdf",pagesize=letter)
Story=[]
styles=getSampleStyleSheet()
styles.add(ParagraphStyle(name='Justify', alignment=TA_JUSTIFY))
ptext = '<font size=12>test</font>'
Story.append(Paragraph(ptext, styles["Justify"]))
doc.build(Story)
pdf_out = output.getvalue()
output.close()
response = make_response(pdf_out)
response.headers['Content-Disposition'] = "attachment; filename='test.pdf"
response.mimetype = 'application/pdf'
return response
app.run()
You can use Flask's send_file for serving files:
from Flask import send_file
return send_file('test.pdf', as_attachment=True)
With as_attachment=True you can force the client to download the file instead of viewing it inside the browser.
Is it possible to generate a functional image tag in html from a BytesIO buffer? I'd like to do something along these lines:
import matplotlib
matplotlib.use('Agg')
import pylab
import Image
import io
temp_data = {'x':[1,2,3],'y':[2,4,5]}
pylab.plot(temp_data['x'], temp_data['y'])
img_buffer = io.BytesIO()
pylab.savefig(img_buffer, format = 'png')
img_buffer.seek(0)
img_tag = "<img src='data:image/png;base64,'" + img_buffer.getvalue() + "</img>"
May be necessary to re-format the value of the buffer in some way, or to change the content of the 'src' data. Thank you.
Python2
Towards the end of the code above, do this
import base64
img_tag = "<img src='data:image/png;base64," + base64.b64encode(img_buffer.getvalue()) + "'/>"
Python3
For this to work in python3 you will need to decode the bytes variable generated from base64.b64encode using str.decode method into a string as follows
import base64
str_equivalent_image = base64.b64encode(img_buffer.getvalue()).decode()
img_tag = "<img src='data:image/png;base64," + str_equivalent_image + "'/>"
If you are working with Flask, then you can return the UTF-8 format of the image and play with it.
figfile = BytesIO()
plt.savefig(figfile, format='png')
plt.clf() # this will clear the image
figfile.seek(0)
figdata_png = base64.b64encode(figfile.getvalue())
return figdata_png.decode('UTF-8')
Remember to mention it in <img/> tags. This is to implement in Flask.
I'm currently developing a small script to take screenshots and upload them to imgur using Python.
The code looks like this:
import time
import os
import ImageGrab
import urllib
import urllib2
time.sleep(1)
shot = ImageGrab.grab()
dir = os.path.join(r'C:\SAMPLE\PATH', 'Screen ' + time.strftime(r'%Y-%m-%d %H-%M-%S') + '.png')
shot.save(dir)
data = urllib.urlencode({"key":'MY_API_KEY', "image":urllib.quote(open(dir,'rb').read().encode("base64"))})
site = urllib2.Request("http://imgur.com/api/upload.json", data)
s = urllib2.urlopen(site)
print s.read()
I get a response from imgur but when I open the link I get a blank image (though its resolution is correct). I think the base64 encoding method may be off but I'm at a loss.
You should use b64encode from the base64 module. I don't know why, but it gives different results:
from base64 import b64encode
(...)
data = urllib.urlencode({"key":'MY_API_KEY', "image":urllib.quote(b64encode(open(dir,'rb').read()))})
The following code will of course create a PNG named test and save it on the server:
from matplotlib.figure import Figure
from matplotlib.backends.backend_agg import FigureCanvasAgg
fig = Figure(figsize=[4,4])
ax = fig.add_axes([.1,.1,.8,.8])
ax.scatter([1,2], [3,4])
canvas = FigureCanvasAgg(fig)
canvas.print_figure("test.png")
Then to view the image in the browser, we have to go to example.com/test.png. This means we have to call the page with the Python code first to create the test.png file, then go to the PNG file. Is there a way to draw the PNG and output from the Python page that creates the image? Thanks!
First you need a page to load a url from the webserver controller which generates the image:
<img src="/matplot/makegraph?arg1=foo" />
Then, embed the matplotlib code into the makegraph controller. You just need to capture the canvas rendered PNG in a memory buffer, then create an HTTP response and write the bytes back to the browser:
import cStringIO
from matplotlib.figure import Figure
from matplotlib.backends.backend_agg import FigureCanvasAgg
fig = Figure(figsize=[4,4])
ax = fig.add_axes([.1,.1,.8,.8])
ax.scatter([1,2], [3,4])
canvas = FigureCanvasAgg(fig)
# write image data to a string buffer and get the PNG image bytes
buf = cStringIO.StringIO()
canvas.print_png(buf)
data = buf.getvalue()
# pseudo-code for generating the http response from your
# webserver, and writing the bytes back to the browser.
# replace this with corresponding code for your web framework
headers = {
'Content-Type': 'image/png',
'Content-Length': len(data)
}
response.write(200, 'OK', headers, data)
Note: you may want to add caching for these if they're frequently generated with the same arguments, e.g. construct a key from the args and write the image data to memcache, then check memcache before regenerating the graph.
Just to update for python3
The StringIO and cStringIO modules are gone. Instead, import the io
module and use io.StringIO
https://docs.python.org/3.5/whatsnew/3.0.html?highlight=cstringio
So now would be something like:
import io
from matplotlib.figure import Figure
from matplotlib import pyplot as plt
fig = Figure(figsize=[4,4])
ax = fig.add_axes([.1,.1,.8,.8])
ax.scatter([1,2], [3,4])
buf = io.BytesIO()
fig.savefig(buf, format='png')
plt.close(fig)
data=buf.getvalue()
# In my case I would have used Django for the webpage
response = HttpResponse(data, content_type='image/png')
return response