本日は
Matplotlibで作成したグラフをFlaskで作ったWebアプリに埋め込みたい.
タイトルが長々しい.日本語難しい.
調べると多くのReferenceが存在する.
などがあります.
savefigを使って一度画像として保存してそれを読み込む例が多くみられます.
保存せずに直接行ける方法はないかと探してみたらGitHubで公開している例がありました.
元コードはPython2系のものですが, tacaswell 氏による Python3 パッチを当てていました.
問題は
サーバーサイドのコードだけあってもどう使うか,どうやってWeb側に渡すんだべ?と悶々としてました. どうやって調べたかは定かではないですが,
をなどを参考にしながら画像データを渡すことにしました.
実装例
一番感心があるところだと思うのでコードを貼り付けましょう.
Python側
#app.py
from flask import Flask, render_template
from io import BytesIO
import urllib
from matplotlib.backends.backend_agg import FigureCanvasAgg
from matplotlib.figure import Figure
import numpy as np
app = Flask(__name__)
@app.route("/plot/<func>")
def plot_graph(func='sin'):
fig = Figure()
ax = fig.add_subplot(111)
xs = np.linspace(-np.pi, np.pi, 100)
if func == 'sin':
ys = np.sin(xs)
elif func == 'cos':
ys = np.cos(xs)
elif func == 'tan':
ys = np.tan(xs)
else:
ys = xs
ax.plot(xs, ys)
canvas = FigureCanvasAgg(fig)
png_output = BytesIO()
canvas.print_png(png_output)
img_data = urllib.parse.quote(png_output.getvalue())
return img_data
@app.route("/")
def index():
return render_template("index.html", img_data=None)
if __name__ == "__main__":
app.run(debug=True, port=9999)
HTML側
<!--index.html-->
<!DOCTYPE html>
<html>
<head>
<title>Flask with matplotlib</title>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script type="text/javascript">
function drawGraph(obj) {
var idx = obj.selectedIndex;
var value = obj.options[idx].value;
var plotdata = document.getElementById('plotimg');
$.get("/plot/" + value, function(data) {
plotdata.src = "data:image/png:base64," + data;
});
};
$(document).ready(function() {
//initialize components
var target = document.getElementById('selector');
drawGraph(target);
});
</script>
</head>
<body>
<h1>Plot Graph</h1>
<div>
<p>select a function</p>
<select id=selector onchange="drawGraph(this)">
<option value="sin">sin</option>
<option value="cos" selected>cos</option>
<option value="tan">tan</option>
</select>
</div>
<br/>
<img id=plotimg></img>
</body>
</html>
HTML内で閉じるように書いています.もちろんJSとファイルを別に分離しても良いです.私はWeb屋ではないのでここら辺は雑になりがちです. 編集リクエストがありましたら順次対応してまいります.
Remark
Flaskをお使いの方はご存知だと思いますが,
ディレクトリの構造は次のようになります:
.
├── app.py
└── templates
└── index.html
すなわち, app.py
と同階層にtemplates
というフォルダを作成してその直下に index.html
を配置するというものになります.
振る舞いについて
このコードは外部ライブラリとしてjQueryを用いています.
ブラウザを立ち上げた時に
$(document).ready(function()){}
で定義した挙動が行われます.内部ではdrawGraph
が呼び出されます.これはさらに
$.get("/plot/" + value, function(data)
を呼び出し
サーバー側であるPythonのコードを呼び出します:
@app.route("/plot/<func>")
def plot_graph(func='sin'):
#do something
実行例
$ python app.py
この後ブラウザを立ち上げて 127.0.0.1:9999
にアクセス
selectタグ selector
の初期値はvalue=cos
となっているため, plot_graph
に渡される引数の値は cos
です.
画面左上のセレクタは sin
cos
tan
と選べます.選択するごとに drawGraph
がイベントハンドラとして呼び出されるので,対応する関数が描画されます.