flask+Pyecharts+ajax实现分tab页展示多图

需求是做一个简单的网页前后台分离服务,实现获得页面输入的起始时间段,后台计算多个量化指标,完成图形后,在前台实现分页签的可视化展示。 作为演示,选定"前30名涨跌幅、换手率(正排倒排)、成交量(正排倒排)"

技术基础参考利用 Flask 动态展示 Pyecharts 图表数据的几种方法一文中"Flask 前后端分离"部分,不再赘述。 主要思路是一次查询,一次计算形成结果集(dataform),并根据dataform的对应标的代码和不同指标,形成分别涨跌幅、换手率和成交图表,在前端页对应三个页签显示。

主要难点是:

pyecharts的Tab对象没有dump_options_with_quotes()方法,所以只能利用html的tab对象,后台需要把多个图形按适当方式传递到前台,前台解析后再匹配到对应的组件。

目录

复制代码
-  templates(页面目录)-mdStat2.html
                                   -404.html
- util(后台目录)-dataProcess.py (数据处理)
                   			-drawChart.py(画图)
                   			-Utility.py(函数工具)
-firstServer.py(flask启动程序)

入口页面:

复制代码
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>查询股票指标报告</title>
        <!-- 引入 echarts.js -->
        <script src="https://cdn.bootcss.com/jquery/3.0.0/jquery.min.js"></script>   // jquery引入
        <script src="https://cdn.staticfile.org/echarts/4.3.0/echarts.min.js"></script>  //Echarts引入
        <style>
            .btns input{
                width:100px;
                height: 40px;
                background-color: #ddd;
                border: 0;
            }
            .btns .current{
                background-color: gold;
            }
            .cons .active{
                display: block;
            }
            .tab1{
                width: 1000px;
                height: 300px;
            }
    
            .none {
                display: none;
            }
        </style>
        <script>  //触发tab切换
            $(function () {
                var $btn = $('.btns input');
                var $div = $('.cons div');
                $btn.click(function () {
                    $(this).addClass('current').siblings().removeClass('current');
                    $('.cons .item').eq($(this).index()).addClass('active').siblings('.item').removeClass('active');
                })
            })
        </script>
    </head>
    <body>
       // 查询form
        <form id="form1" onsubmit="return false" action="#" method="post">
            <p id="p1">起始日期:
                <input name="startDate" type="text" id="startDate" tabindex="1" size="16" value="" placeholder="起始日期"/>
            </p>
            <p id="p2">结束日期:
                <input name="endDate" type="text" id="endDate" tabindex="2" size="16" value="" placeholder="结束日期"/>
            </p>
            <p><input type="submit" value="查询" onClick="getData()"></p>
        </form>
        <div class="btns">  //tab页签对象
            <input type="button" name="" value="01" class="current">
            <input type="button" name="" value="02">
            <input type="button" name="" value="03">
        </div>
        <div class="cons">
            <div  class="clearfloat item none active">
                <div id="tab1" class="tab1"></div>
            </div>
            <div class="clearfloat item none">
                <div id="tab2" class="tab1"></div>
            </div>
            <div class="clearfloat item none">
                <div id="tab3" class="tab1"></div>
            </div>
        </div>
    <script type="text/javascript">  
        function getData() {   //查询触发的ajax提交和返回处理
            $.ajax({
                type: "POST",
                dataType: "json",
                url: "/DataStat1" ,
                data: $('#form1').serialize(),
                success: function (result) {
                   
                    // console.log(result["chart1"],result["chart2"])
                    var myChart1 = echarts.init(document.getElementById('tab1'));
                    var ch1=$.parseJSON(result["chart1"])
                    myChart1.setOption(ch1);
                    
                    var myChart2 = echarts.init(document.getElementById('tab2'));
                    var ch2=$.parseJSON(result["chart2"])
                    myChart2.setOption(ch2);
                },
                error: function() {
                    alert("错误的日期!");
                }
            });
          // alert("query!");
        }
    </script>
    </body>
</html>

dataProcess.py和Utility.py 略过(一查一大把)最后形成,结果列表

ts_code name industry incease_rate turn_over volumn

600036 招商银行 银行 ...

...

排序后即可绘图

drawChart.py

复制代码
''
Created on 2023-9-3

@author: 13795
'''
from pyecharts.charts import Bar, Grid
from pyecharts import options as opts
from pyecharts.globals import ThemeType
#import os

def draw_report(df_result):
    grid_increase = Grid()
   
    #涨幅排序,第一个图
    df_stock_increase=df_result.sort_values(by=['increaseCloseRate'], ascending=False)
    print('df_stock_increase',df_stock_increase) 
    df_stock_increase=df_stock_increase[0:30]
    
     
    bar1=Bar(init_opts=opts.InitOpts(theme=ThemeType.WHITE))
    x1=df_stock_increase['ts_code'].tolist()
    y1=df_stock_increase["increaseCloseRate"].tolist()
    bar1.add_xaxis(x1)
    bar1.add_yaxis('总涨幅',y1)
    bar1.set_global_opts(title_opts=opts.TitleOpts(title="涨幅", subtitle="按涨幅排序")
                        ,xaxis_opts=opts.AxisOpts(name='股票'
                                                  ,name_textstyle_opts=opts.TextStyleOpts(font_size=13)
                                                  ,axislabel_opts=opts.LabelOpts(font_size=10,rotate=15)                                    
                                             )##坐标轴标签的格式配置
                        ,yaxis_opts=opts.AxisOpts(name = '涨幅',position='right'))
    bar1.set_series_opts(label_opts=opts.LabelOpts(position='right',color='red',font_size=8))
    bar1.reversal_axis()
    
    
    
    grid_increase.add(bar1,grid_opts=opts.GridOpts(pos_left="50%",height="100%"))
    
    
    #换手率,第二个图标 
    #降幅排序
    df_stock_decrease=df_result.sort_values(by=['increaseCloseRate'], ascending=True)
    print('df_stock_decrease',df_stock_increase) 
    df_stock_decrease=df_stock_decrease[0:30]
    
    bar2=Bar(init_opts=opts.InitOpts(theme=ThemeType.WHITE))
    x=df_stock_decrease['ts_code'].tolist()
    y1=df_stock_decrease["increaseCloseRate"].tolist()
    bar2.add_xaxis(x)
    bar2.add_yaxis('总跌幅',y1)
    bar2.set_global_opts(title_opts=opts.TitleOpts(title="跌幅", subtitle="按跌幅排序")
                        ,xaxis_opts=opts.AxisOpts(name_textstyle_opts=opts.TextStyleOpts(font_size=13)
                                                  ,axislabel_opts=opts.LabelOpts(font_size=10,rotate=15)                                    
                                             )##坐标轴标签的格式配置
                        ,yaxis_opts=opts.AxisOpts(name = '跌幅'))
    bar2.set_series_opts(label_opts=opts.LabelOpts(position='left',color='blue',font_size=8))
    bar2.reversal_axis()
    
    grid_increase.add(bar2,grid_opts=opts.GridOpts(pos_right="50%",height="100%"))
    
  #第二个图
    grid_turnover = Grid()     
    #换手率排序
    df_turnover_increase=df_result.sort_values(by=['turnover_mean'], ascending=False)
    print('df_turnover_increase',df_turnover_increase) 
    df_turnover_increase=df_turnover_increase[0:30]
    
    
    bar3=Bar(init_opts=opts.InitOpts(theme=ThemeType.WHITE))
    x=df_turnover_increase['ts_code'].tolist()
    y1=df_turnover_increase["turnover_mean"].tolist()
    bar3.add_xaxis(x)
    bar3.add_yaxis('换手率最高',y1)
    bar3.set_global_opts(title_opts=opts.TitleOpts(title="换手率", subtitle="按换手最多")
                        ,xaxis_opts=opts.AxisOpts(name_textstyle_opts=opts.TextStyleOpts(font_size=13)
                                                  ,axislabel_opts=opts.LabelOpts(font_size=10,rotate=15)                                    
                                             )##坐标轴标签的格式配置
                        ,yaxis_opts=opts.AxisOpts(name = '换手率最多',position='right'))
    bar3.set_series_opts(label_opts=opts.LabelOpts(position='right',color='red',font_size=8))
    bar3.reversal_axis()
    
    grid_turnover.add(bar3,grid_opts=opts.GridOpts(pos_top="50%",pos_left="50%",height="100%"))
    

    df_turnover_decrease=df_result.sort_values(by=['turnover_mean'], ascending=True)
    
    
    print('df_turnover_decrease',df_turnover_decrease) 
    df_turnover_decrease=df_turnover_decrease[0:30]
    
    bar4=Bar(init_opts=opts.InitOpts(theme=ThemeType.WHITE))
    x=df_turnover_decrease['ts_code'].tolist()
    y1=df_turnover_decrease["turnover_mean"].tolist()
    bar4.add_xaxis(x)
    bar4.add_yaxis('换手率最低',y1)
    bar4.set_global_opts(title_opts=opts.TitleOpts(title="换手率", subtitle="按换手率最低")
                        ,xaxis_opts=opts.AxisOpts(name_textstyle_opts=opts.TextStyleOpts(font_size=13)
                                                  ,axislabel_opts=opts.LabelOpts(font_size=10,rotate=15)                                    
                                             )##坐标轴标签的格式配置
                        ,yaxis_opts=opts.AxisOpts(name = '换手率最低'))
    bar4.set_series_opts(label_opts=opts.LabelOpts(position='right',color='blue',font_size=8))
    bar4.reversal_axis()
    
    grid_turnover.add(bar4,grid_opts=opts.GridOpts(pos_top="50%",pos_right="50%",height="100%"))
    return grid_increase,grid_turnover  #返回

grid_increase对应涨跌幅页面,grid_turnover对应换手率排序页面

对应的页面控制跳转及flask启动程序 firstserver.sh

复制代码
#coding=gbk
'''
Created on 2023-7-2
@author: 13795
'''
from flask import Flask,render_template, request
#from pyecharts.charts import Bar
from pyecharts import options as opts
import util.Uitility as ut
import util.dataProcess as dp
import util.drawChart1 as dw
#from jinja2.utils import markupsafe
import json

app = Flask(__name__)
#def first():
#    return "<p>这是我的第一个flask程序!</p>"

@app.route('/mdStat2')
def mdStat1():
    #计算个股和板块在一段时间内基本统计信息
    data = request.args.to_dict()
    return render_template("mdStat2.html", result_json=data)

@app.route("/index2")
def index2():
    c = bar_base()
    return markupsafe.Markup(c.render_embed())
    #return render_template("index.html")

@app.route("/DataStat1", methods=['GET', 'POST'])
def get_dataStat1():
    #统计信息
    startDate=request.form.get('startDate')
    endDate=request.form.get('endDate')
    
    dp1=dp.dataProcess()

    if ut.checkDate(startDate,endDate):
        df_result=dp1.cal_report(startDate,endDate)
        #print('result',df_result)
        chart1,chart2=dw.draw_report(df_result)
        resultChart={"chart1":chart1.dump_options_with_quotes(),"chart2":chart2.dump_options_with_quotes()}
        result=json.dumps(resultChart)
        #return chart1.dump_options_with_quotes(),chart2.dump_options_with_quotes()
        return result
    
    else:
        return 'error date input' 


if __name__ == '__main__':
    app.run(host='0.0.0.0')

注意返回页面需要ajax提交跳转"/DataStat1"对应的处理函数get_dataStat1()中按json方式拼接 resultChart={"chart1":chart1.dump_options_with_quotes(),"chart2":chart2.dump_options_with_quotes()}

而在页面mdStat中 ,需要把获得JSON对象转换为javascript对象,即ch1=$.parseJSON(result["chart1"])...,否则会报错 ,说明参考
jquery each报 Uncaught TypeError: Cannot use 'in' operator to search for错误

复制代码
 <script type="text/javascript">  
        function getData() {   //查询触发的ajax提交和返回处理
            $.ajax({
              ...
                success: function (result) {
                   
                    // console.log(result["chart1"],result["chart2"])
                    var myChart1 = echarts.init(document.getElementById('tab1'));
                    var ch1=$.parseJSON(result["chart1"])
                    myChart1.setOption(ch1);
                    
                    var myChart2 = echarts.init(document.getElementById('tab2'));
                    var ch2=$.parseJSON(result["chart2"])
                    myChart2.setOption(ch2);
                },
                error: function() {
                    alert("错误的日期!");
                }
            });

然后启动访问 localhost:5000/mdStat2

结果

相关推荐
databook6 小时前
Manim实现闪光轨迹特效
后端·python·动效
Juchecar8 小时前
解惑:NumPy 中 ndarray.ndim 到底是什么?
python
用户8356290780518 小时前
Python 删除 Excel 工作表中的空白行列
后端·python
Json_8 小时前
使用python-fastApi框架开发一个学校宿舍管理系统-前后端分离项目
后端·python·fastapi
数据智能老司机15 小时前
精通 Python 设计模式——分布式系统模式
python·设计模式·架构
数据智能老司机16 小时前
精通 Python 设计模式——并发与异步模式
python·设计模式·编程语言
数据智能老司机16 小时前
精通 Python 设计模式——测试模式
python·设计模式·架构
数据智能老司机16 小时前
精通 Python 设计模式——性能模式
python·设计模式·架构
c8i16 小时前
drf初步梳理
python·django
每日AI新事件16 小时前
python的异步函数
python