from flask, how to send the matplotlib plot image to html? - python

In flask, how can I make the image saved from plt in matplotlib show up on a webpage? The below python code creates a plot and saves it as a .png image. The HTML file also follows. When I ran it I ended up with the error saying:
TypeError: expected str, bytes or os.PathLike object, not NoneType.
Anyone who can help me please show me what to do.
#app.route('/images/<air_quality>')
def images(air_quality):
return render_template("displaygraph.html", title=air_quality)
#app.route('/fig/<air_quality>')
def fig(air_quality):
import matplotlib.pyplot as plt
import numpy as np
from io import BytesIO
img = BytesIO()
pm10 = np.array(['23', '45', '56', '12'])
pm25 = np.array(['34', '56', '59', '34'])
dates = np.array(['2017-12-20', '2017-12-21', '2017-12-22', '2017-12-23'])
plt.figure(figsize=(12, 8), dpi=100, facecolor='1.0')
plt.title("GangNam", fontsize=20)
plt.plot_date(dates, pm10, 'rs--', label='pm10')
plt.plot_date(dates, pm25, 'gs--', label='pm25')
plt.legend()
img = plt.savefig('img.png')
return send_file(img, mimetype='image/png')
I do not how to display HTML file here. But the file has this inside the img tag:
src="{{url_for('fig', air_quality = title)}}

You can save pylab figure into in-memory file like BytesIO(py3), StringIO(py2).
plt.savefig(img, format='png')
img.seek(0)
response=make_response(img.getvalue())
response.headers['Content-Type'] = 'image/png'
img.close()
return response

Related

Return Figure in FastAPI

Im trying to return a matplotlib.figure.Figure in FastAPI.
If I save it like an image it works (code here):
#router.get("/graph/{id_file}", name="Return the graph obtained")
async def create_graph(id_file: str):
data = HAR.createGraph(id_file)
graph = HAR.scatterplot(data['dateTimes'], data['label'], "Time", "Activity")
graph.savefig('saved_figure.jpg')
return FileResponse('saved_figure.jpg')
Where graph is my Figure.
But I would like to show it without saving in mi computer.
savefig can accept binary file-like object. It can be used to achieve what you want.
The code could be:
from io import BytesIO
from starlette.responses import StreamingResponse
...
#router.get("/graph/{id_file}", name="Return the graph obtained")
async def create_graph(id_file: str):
data = HAR.createGraph(id_file)
graph = HAR.scatterplot(data['dateTimes'], data['label'], "Time", "Activity")
# create a buffer to store image data
buf = BytesIO()
graph.savefig(buf, format="png")
buf.seek(0)
return StreamingResponse(buf, media_type="image/png")

give a bytes to reportlab.lib.utils.ImageReader

I have read that you can use a bytes like object to reportlab.lib.utils.ImageReader(). If I read in a file path it works fine, but I want to use a byte like object instead that way I can save the plot I want in memory, and not have to constantly be saving updated plots on the drive.
This is where I found the code to convert the image into a string
https://www.programcreek.com/2013/09/convert-image-to-string-in-python/
This is an example of how to use BytesIO as input for ImageReader()
How to draw image from raw bytes using ReportLab?
This class is used to make a plot and pass in a save it to memory with BytesIO(). string is the value I'm going to pass later
#imports
import PyPDF2
from io import BytesIO
from reportlab.lib import utils
from reportlab.lib.pagesizes import landscape, letter
from reportlab.platypus import (Image, SimpleDocTemplate,
Paragraph, Spacer)
from reportlab.lib.styles import getSampleStyleSheet
from reportlab.lib.units import inch, mm
import datetime
import os
import csv
import io
import base64
import urllib
from django.contrib import admin
from django.forms import model_to_dict
from django.http import HttpResponse
from django.urls import path
from django.views.decorators.csrf import csrf_protect
from django.utils.decorators import method_decorator
from reporting import models, functions, functions2
import matplotlib
matplotlib.use('agg')
from matplotlib import pyplot as plt
import numpy as np
def make_plot(data):
items = [tuple(item) for item in data.items()]
keys = [item[0] for item in items]
vals = [item[1] for item in items]
fig, ax = plt.subplots()
ind = np.arange(len(keys)) # the x locations for the groups
width = 0.35 # the width of the bars
rects1 = ax.bar(ind - width/2, vals, width)
ax.set_ylabel('Count')
ax.set_xticks(ind)
ax.set_xticklabels(keys)
buf = io.BytesIO()
fig.savefig(buf, format='png')
buf.seek(0)
string = base64.b64encode(buf.read())
return 'data:image/png;base64,' + urllib.parse.quote(string), string
This is the minimum code to show how the information is moved to where the error occurs.
class ProgressReportAdmin(ReadOnlyAdmin):
current_extra_context = None
#csrf_protect_m
def changelist_view(self, request, extra_context=None):
plot = make_plot(data)
self.current_extra_context = plot[1]
def export(self, request):
image = self.current_extra_context
pdf = functions.LandscapeMaker(image, fname, rotate=True)
pdf.save()
This is where the error occurs, in the scaleImage function
class LandscapeMaker(object):
def __init__(self, image_path, filename, rotate=False):
self.pdf_file = os.path.join('.', 'media', filename)
self.logo_path = image_path
self.story = [Spacer(0, 1*inch)]
def save(self):
fileObj = BytesIO()
self.doc = SimpleDocTemplate(fileObj, pagesize=letter,
leftMargin=1*inch)
self.doc.build(self.story,
onFirstPage=self.create_pdf)
def create_pdf(self, canvas, doc):
logo = self.scaleImage(self.logo_path)
def scaleImage(self, img_path, maxSize=None):
#Error1 occurs on
img = utils.ImageReader(img_path)
img.fp.close()
#Error2
#image = BytesIO(img_path)
#img = utils.ImageReader(image)
#img.fp.close()
For Error1 I receive:
raise IOError('Cannot open resource "%s"' % name)
img = utils.ImageReader(img_path)
"OSError: Cannot open resource "b'iVBORw0KGgoAAA' etc.,
For Error2 I receive
OSError: cannot identify image file <_io.BytesIO object at 0x7f8e4057bc50>
cannot identify image file <_io.BytesIO object at 0x7f8e4057bc50>
fileName=<_io.BytesIO object at 0x7f8e4057bc50> identity=[ImageReader#0x7f8e43fd15c0]
I think you have to pass buff to ImageReader somehow.
I'm using this function to save and draw the figures I generate with matplotlib and it works perfectly for me.
seek(offset, whence=SEEK_SET) Change the stream position to the given offset. Behaviour depends on the whence parameter. The default value for whence is SEEK_SET.
getvalue() doesn't work except the seek(0)
def save_and_draw(fig, x_img, y_img, width_img=width_img, height_img=height_img):
imgdata = BytesIO()
fig.savefig(imgdata, format='png')
imgdata.seek(0)
imgdata = ImageReader(imgdata)
self.c.drawImage(imgdata, x_img, y_img, width_img, height_img)
plt.close(fig)

How to fix size problems using flask and matplotlib to generate plots?

enter image description hereI am setting up a web application and I want to be able to make plots using matplotlib and flask. For one of the plots, I had to use "plt.tight_layout()" to be able to read the labels. My problem now is that I would to adjust the size of the plots to have the same dimensions in the y axis in both of them.
I am using a Python 2.7.16 anaconda enviroment on macOS Mojave (Version 10.14.4).
I have tried to use the following functions:
plt.rcParams["figure.figsize"] = (5,1)
plt.figure(figsize=(5,1))
The only function that worked was:
plt.gcf().set_size_inches(9.0, 1.78)
but I am not able to set it up manually
from flask import Flask, request, redirect, g, render_template
import requests
import base64
from cStringIO import StringIO
import matplotlib.pyplot as plt
from matplotlib.figure import Figure
app = Flask(__name__)
#app.route("/callback/")
def song_analysis():
# Making a plot
img = StringIO()
plt.scatter(names,valence, color=[ 'deeppink','lightpink','lime','orange','blue','green','red','cyan','magenta','yellow','black','white','wheat', 'black', 'indigo', 'snow', 'orchid', 'steelblue', 'grey','aqua'])
plt.xticks([])
plt.tight_layout()
plt.savefig(img,format='png')
plt.close()
img.seek(0)
plot_url = base64.b64encode(img.getvalue())
img2= StringIO()
plt.scatter(names,speechiness, color=[ 'deeppink','lightpink','lime','orange','blue','green','red','cyan','magenta','yellow','black','white','wheat', 'black', 'indigo', 'snow', 'orchid', 'steelblue', 'grey','aqua'])
plt.xticks(rotation=90)
plt.tight_layout()
plt.savefig(img2,format='png')
plt.close()
img2.seek(0)
plot_url2 = base64.b64encode(img2.getvalue())
return render_template("index.html", info_name = [display_name], plot_url=plot_url, plot_url2=plot_url2)
if __name__ == "__main__":
app.run(debug=True,port=PORT)
Including these lines in the html file:
<img src="data:image/png;base64, {{ plot_url }}" width="800">
<img src="data:image/png;base64, {{ plot_url2 }}" width="800">

Matplotlib into a Django Template

Im using python 3.4 and Django 1.8. I want to "print" a matplotlib result in a Django template. I reach this a few days ago, so I continue in other things of my Django App. Now, I dont know why, I was going to show the result to a friend, and my template with a matplotlib graph, now shows a big code! I dont know why this happen, because my view doesnt change in anything from when it was showing the right graph! Please help me!
This is my view!
from django.shortcuts import render
from matplotlib import pylab
from pylab import *
import PIL
import PIL.Image
import io
from io import *
def graphic(request):
pos = arange(10)+ 2
barh(pos,(1,2,3,4,5,6,7,8,9,10),align = 'center')
yticks(pos,('#hcsm','#ukmedlibs','#ImmunoChat','#HCLDR','#ICTD2015','#hpmglobal','#BRCA','#BCSM','#BTSM','#OTalk'))
xlabel('Popularity')
ylabel('Hashtags')
title('Hashtags')
subplots_adjust(left=0.21)
buffer = io.BytesIO()
canvas = pylab.get_current_fig_manager().canvas
canvas.draw()
graphIMG = PIL.Image.fromstring('RGB', canvas.get_width_height(), canvas.tostring_rgb())
graphIMG.save(buffer, "PNG")
content_type="Image/png"
buffercontent=buffer.getvalue()
graphic = (buffercontent ,content_type)
pylab.close()
return render(request, 'graphic.html',{'graphic':graphic})
Of course in my graphic.html is a variable called {{graphic}} inside a blockcontent!
This was showing the right result in my template! What happen?
Now sometimes when i run my template it shows a big code, or just show me this django error:
Exception Value:
main thread is not in main loop
Exception Location: C:\Python34\lib\site-packages\matplotlib\backends\tkagg.py in blit, line 17
Help!
from io import BytesIO
import base64
import matplotlib.pyplot as plt
import numpy as np
def graphic(request):
pos = np.arange(10)+ 2
fig = plt.figure(figsize=(8, 3))
ax = fig.add_subplot(111)
ax.barh(pos, np.arange(1, 11), align='center')
ax.set_yticks(pos)
ax.set_yticklabels(('#hcsm',
'#ukmedlibs',
'#ImmunoChat',
'#HCLDR',
'#ICTD2015',
'#hpmglobal',
'#BRCA',
'#BCSM',
'#BTSM',
'#OTalk',),
fontsize=15)
ax.set_xticks([])
ax.invert_yaxis()
ax.set_xlabel('Popularity')
ax.set_ylabel('Hashtags')
ax.set_title('Hashtags')
plt.tight_layout()
buffer = BytesIO()
plt.savefig(buffer, format='png')
buffer.seek(0)
image_png = buffer.getvalue()
buffer.close()
graphic = base64.b64encode(image_png)
graphic = graphic.decode('utf-8')
return render(request, 'graphic.html',{'graphic':graphic})
and in the template:
<img src="data:image/png;base64,{{ graphic|safe }}">
I have:
matplotlib==3.0.2 and Django==2.1.4
Edit:
try with
graphic = cStringIO.StringIO()
canvas.print_png(graphic)
return render(request, 'graphic.html',{'graphic':graphic})
You have to specify that your image is a binary string:
<img src="data:image/png;base64,{{graphic|safe}}">
Or actually save it to the filesystem and provide the path.
Alternatively you could use Bokeh which can give you the html + javascript to embed the plot directly in the template, then it is dynamically generated and brings nice features.
The final solution was to create a special view that returns the matplotlib plot in an empty template, like this:
def grafico (rquest):
pos = arange(10)+ 2
barh(pos,(1,2,3,4,5,6,7,8,9,10),align = 'center')
yticks(pos,('#hcsm','#ukmedlibs','#ImmunoChat','#HCLDR','#ICTD2015','#hpmglobal','#BRCA','#BCSM','#BTSM','#OTalk'))
xlabel('Popularidad')
ylabel('Hashtags')
title('Gráfico de Hashtags')
subplots_adjust(left=0.21)
buffer = io.BytesIO()
canvas = pylab.get_current_fig_manager().canvas
canvas.draw()
graphIMG = PIL.Image.fromstring('RGB', canvas.get_width_height(), canvas.tostring_rgb())
graphIMG.save(buffer, "PNG")
pylab.close()
return HttpResponse (buffer.getvalue(), content_type="Image/png")
The next step is to put in your template this:
<img src="url_of_the_graphic_view">
And thats all!
def show(request):
x = np.arange(10)
y = x
fig = plt.figure()
plt.plot(x, y)
canvas = fig.canvas
buf, size = canvas.print_to_buffer()
image = Image.frombuffer('RGBA', size, buf, 'raw', 'RGBA', 0, 1)
buffer=io.BytesIO()
image.save(buffer,'PNG')
graphic = buffer.getvalue()
graphic = base64.b64encode(graphic)
buffer.close()
return render(request, 'graphic.html',{'graphic':graphic})
I had a broken icon image as well, after using the answers above, and I fixed it by removing the b' and ' from the graphic base64 binary representation :
return render(request, 'graphic.html', {'graphic': str(graphic)[2:-1]})

Create a graph from a CSV file and render to browser with Django and the Pandas Python library

I'm learning how to use the Django framework for a work project that will allow users to load files in various formats (at the moment I am only dealing with CSV files), graph that data using Pandas, and display that data back to the user via a Django template. I haven't had any problems creating the graph in iPython, but have been struggling with getting it to an HTML Django template.
I've followed the following example from matplotlib:
# graph input file
from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas
from matplotlib.figure import Figure
from matplotlib.dates import DateFormatter
def graph(request):
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)
response = HttpResponse( content_type = 'image/png')
canvas.print_png(response)
return response
The above example works great and I can see it in a template, but that's just a graph with hard-coded values.
I've attempted to use Pandas because of its seemingly simplistic syntax and my attempts in Django are as follows:
# graph input file
import pandas as pd
from pandas import DataFrame
def graph(request):
data_df = pd.read_csv("C:/Users/vut46744/Desktop/graphite_project/sampleCSV.csv")
data_df = pd.DataFrame(dataArray)
data_df.plot()
response = HttpResponse( content_type = 'image/png')
return response
In Django calling the .plot() displays the graph fine, but displays a blank page to the HTML template. I've also tried using Numpy's genfromtxt() and loadtxt(), but to no avail. Also, my Google searches have not been fruitful either.
Any help or suggestion would be great. If you know of a better alternative to Pandas then I am willing to try other options.
Haven't tried this yet, but I would attack it something like:
def graph(request):
fig = Figure()
ax = fig.add_subplot(111)
data_df = pd.read_csv("C:/Users/vut46744/Desktop/graphite_project/sampleCSV.csv")
data_df = pd.DataFrame(data_df)
data_df.plot(ax=ax)
canvas = FigureCanvas(fig)
response = HttpResponse(content_type='image/png')
canvas.print_png(response)
return response

Categories

Resources