到上一篇文章已经完成了博客所需的基础框架,包括博客展示、分页及数据库内容读写等,这里还想要添加自动画图的功能
先大致准备一下前端代码,因为后期还要添加其他画图工具,这里就先传入一个draw_type来标识:
<section>
<header class="main">
<h1>{{draw_type.upper()}}</h1>
</header>
{% if draw_type=="heatmap" %}
<span class="image main"><img src="{{ static_url("images/heatmap.png") }}" alt="" /></span>
{% end %}
</section>
<section>
<form id="upload-form" action="/tool/{{draw_type}}" method="post" enctype="multipart/form-data" >
<div class="main">
<input type="file" name="file" id="file" class="inputfile" />
</div>
<div class="row gtr-uniform">
<div class="col-6 col-12-xsmall">
<select name="save-type" id="save-type">
<option value="png">选择保存的图片格式 默认png</option>
<option value="png">png</option>
<option value="tiff">tiff</option>
<option value="jpg">jpg</option>
</select>
<div class="col-6 col-12-xsmall">
{% if draw_type=="heatmap" %}
<input type="text" name="color" id="color" placeholder="请输入颜色 默认 RdYlBu_r" />
{% end %}
</div>
</div>
</div>
<div class="main">
<input type="submit" value="submit" name="submit" id="submit" class="primary" />
</div>
</form>
</section>
然后写python代码
要完成画图,首先是需要用户输入参数,然后后台画图后提供下载,所以需要一个get,一个post功能。
class ToolHandler(tornado.web.RequestHandler):
def get(self, draw_type):
self.render('tool.html', draw_type=draw_type)
def post(self, draw_type):
save_type = self.get_argument('save-type')
submit = self.get_argument('submit')
if draw_type == "heatmap":
color = self.get_argument('color')
if not color:
color = "RdYlBu_r"
if not save_type:
save_type = "png"
if submit:
# 读取上传的文件,并保存到本地
upload_path = os.path.join(os.path.dirname(__file__), 'static', 'files') # 文件的暂存路径
file_metas = self.request.files.get('file', None) # 提取表单中‘name’为‘file’的文件元数据
if not file_metas:
raise tornado.web.HTTPError(404)
for meta in file_metas:
filename = meta['filename']
current_time = time.strftime('%Y%m%d', time.localtime(time.time()))
filename = current_time+str(random.randint(0, 10000000))+"."+filename.split(".")[-1]
file_path = os.path.join(upload_path, filename)
with open(file_path, 'wb') as up:
up.write(meta['body'])
if os.path.exists(file_path):
# 如果文件上传好了就开始画图
if draw_type == "heatmap":
drawing_cls = config.DrawingConfig(file_path, save_type, color)
drawing_cls.heatmap
# 准备把画图配置都写到config中,都通过config配置及调用
if os.path.exists(file_path+"."+save_type):
# 如果画图好了,就开始进行文件下载
self.set_header('Content-Type', 'application/octet-stream')
self.set_header('Content-Disposition', 'attachment; filename=' + filename+"."+save_type)
with open(file_path+"."+save_type, 'rb') as f:
while True:
data = f.read()
if not data:
break
self.write(data)
self.finish()
好了,tornado相关的代码完成,接下来写一个画heatmap的脚本。
# -*- coding: utf-8 -*-
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.cm
import re
plt.switch_backend('agg')
# agg让画图后台进行,不然后面部署到服务器上会出错
class HeatMap:
def __init__(self, data_path, save_type="png", color="RdYlBu_r"):
self.data_path = data_path
self.color = color
self.save_type = save_type
def read_data(self):
# 支持三种格式,都通过pandas读取数据
if re.findall('.xlsx$', self.data_path):
datas = pd.read_excel(self.data_path)
elif re.findall('.csv$', self.data_path):
datas = pd.read_excel(self.data_path)
elif re.findall('.txt$', self.data_path):
datas = pd.read_table(self.data_path)
else:
raise IOError("only xlsx/txt/csv supported!!")
# 提取数据中的最大值,最小值,以及header
header = []
data_max = 0
data_min = 0
for x in datas:
header.append(str(x))
if datas[x][np.argmax(datas[x])] > data_max:
data_max = datas[x][np.argmax(datas[x])]
if datas[x][np.argmin(datas[x])] < data_min:
data_min = datas[x][np.argmin(datas[x])]
return datas, header, data_max, data_min
def map_color(self):
# 获取heatmap图的颜色
cmap = matplotlib.cm.get_cmap(self.color, 1000)
return cmap
def main(self):
datas, header, data_max, data_min = self.read_data()
plt.figure()
plt_tmp = plt.imshow(datas, interpolation="nearest", cmap=self.map_color(), aspect='auto', vmin=data_min, vmax=data_max)
plt.xticks(range(len(header)), header, rotation=60)
plt.yticks((),())
cb = plt.colorbar(mappable=plt_tmp, cax=None, ax=None, shrink=1)
cb.set_label('(%)')
plt.savefig(self.data_path+"."+self.save_type)
然后再config中加入heatmap绘图的配置:
class DrawingConfig:
def __init__(self, file_path, save_type="png", color="YdYlBu_r"):
self.file_path = file_path
self.save_type = save_type
self.color = color
@property # 这里想直接 DrawingConfig.heatmap 调用
def heatmap(self):
return heatmap.HeatMap(self.file_path, save_type=self.save_type, color=self.color).main()
其他类型的绘图也通过同种方式添加,这样网站的开发就几乎完成了,后面再美化美化就可以往服务器上部署了。