67

I'm very new to Flask and Matplotlib. I'd like to be able to show a simple chart I generated in some html, but I'm having a very hard time figuring out how. Here is my Python code:

from flask import Flask, render_template
import numpy as np
import pandas
import matplotlib.pyplot as plt

app = Flask(__name__)
variables = pandas.read_csv('C:\\path\\to\\variable.csv')
price =variables['price']


@app.route('/test')
def chartTest():
    lnprice=np.log(price)
    plt.plot(lnprice)
    return render_template('untitled1.html', name = plt.show())

if __name__ == '__main__':
   app.run(debug = True)

And here is my HTML:

<!doctype html>
<html>
   <body>

      <h1>Price Chart</h1>

      <p>{{ name }}</p>

      <img src={{ name }} alt="Chart" height="42" width="42">

   </body>
</html>
3
  • 3
    Unfortunately it does not simply work that way, you will need to save the plot as an image/vector first (either to disk or store in memory) so it can be loaded in the browser. This involves a few more things, but you can get started looking at how to save it first here stackoverflow.com/a/29931148/9802392 and then check how to serve it from Flask here stackoverflow.com/questions/20646822/… Commented Jun 6, 2018 at 19:48
  • Thank you, I'll get right to reading through those. Commented Jun 6, 2018 at 19:49
  • 2
    Comment here if you get stuck, ideally showing some progress of what you have tried in the question, serving a file from Flask has several solutions and is prerequisite that your html can show/reach that file. Your html is not necessarily wrong completely, you cannot just pass a Python object to the view, so what you pass as {{ name }} should be a URL. And you want that to be in "" too. Hope you succeed. Commented Jun 6, 2018 at 19:56

2 Answers 2

104

You can generate the image on-the-fly in Flask URL route handler:

import io
import random
from flask import Response
from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas
from matplotlib.figure import Figure

@app.route('/plot.png')
def plot_png():
    fig = create_figure()
    output = io.BytesIO()
    FigureCanvas(fig).print_png(output)
    return Response(output.getvalue(), mimetype='image/png')

def create_figure():
    fig = Figure()
    axis = fig.add_subplot(1, 1, 1)
    xs = range(100)
    ys = [random.randint(1, 50) for x in xs]
    axis.plot(xs, ys)
    return fig

Then you need to include the image in your HTML template:

<img src="/plot.png" alt="my plot">
Sign up to request clarification or add additional context in comments.

7 Comments

This won't show changing dynamic plot content reliably due to browser caching. For static plots that won't change on reload, this is fine
You can (and should, if the data are dynamic) add appropriate http headers to disable caching. Ultimately, if you need the data to be “realtime”, you should choose different approach – feed the data to the browser via websocket and plot them using JavaScript (d3.js or similar).
I am trying to adapt plot_png() to receive arguments xs, ys but there is a TypeError: plot_png() missing 2 required positional arguments: 'xs' and 'ys'. I just pasted the same random generated samples in the main() instead of inside the create_figure(). What's wrong?
A flask app might be called from uwsgi using multiple threads. Is the above usage of matplotlib thread-safe?
@KrisStern This didn't work for me initially either, using flask 2.0.1. But I changed line 5 from from matplotlib.figure import Figure to from matplotlib import pyplot as plt. After that I removed the first line of plot_png and changed the line before the return to FigureCanvas(plt.gcf()).print_png(output). Worked like a dream.
|
12

As @d parolin pointed out, the figure generated by matplotlib will need to be saved before being rendered by the HTML. In order to serve images in flask by HTML, you will need to store the image in your flask file directory:

static/
  images/
    plot.png --> store plots here
templates/

Therefore, in your application, use plt.savefig:

@app.route('/test')
def chartTest():
  lnprice=np.log(price)
  plt.plot(lnprice)   
  plt.savefig('/static/images/new_plot.png')
  return render_template('untitled1.html', name = 'new_plot', url ='/static/images/new_plot.png')

Then in untitled1.html:

  <p>{{ name }}</p>

  <img src={{ url}} alt="Chart" height="42" width="42">

4 Comments

There is no need to save the image. In order to display it in HTML you can put the data directly into src argument of img tag.
@MaciekS This sounds like a good idea. Please show a code example.
@mac13k It is difficult to put the code here, so I shared it as a GitLab snippet.
If anyone reading this gets NSInternalInconsistencyException, avoid using Matplotlib GUI. This is done by importing the Agg - see this answer: stackoverflow.com/a/15713545/13055097

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.