• ptyhon flask SSE 浏览器和服务器实时通信-例子实时推送随机数到前端画echart曲线图


    ptyhon flask SSE 浏览器和服务器实时通信-例子实时推送随机数到前端画echart曲线图

    注意 SSE 是单向传输通道,只能服务器向浏览器发送。如果浏览器向服务器发送信息,就变成了另一次 HTTP 请求。

    SSE连接只能由客户端浏览器关闭,后端停止发送数据会触发sse的error 事件。可以在前端设置sse的error事件触发时停止sse连接。

    适用场景:向服务器请求一些连续数据,而且不用前端给出反馈,而且服务器只负责传输数据。

    例子:实时推送随机数到前端画echart曲线图

    例子实现图:

    询问按钮是开启sse请求,停止按钮是停止sse请求。(目前停止后没有清除图表,可以自行添加)

    在这里插入图片描述
    app.py

    import json
    import random
    import time
    from datetime import datetime
    
    from flask import Flask, Response, render_template, stream_with_context, jsonify
    
    app = Flask(__name__)
    random.seed()  # Initialize the random number generator
    
    @app.route('/')
    def index():
        return render_template('index2.html')
    
    @app.route('/chart-data')
    def chart_data():
        print('chart_data-' * 5)
        def generate_random_data():
            print('generate_random_data-' * 5)
            a = 0  # 设置如果前端不中止SSE连接,就在a=1000时完成数据传输
            while True:
                a += 1
                if a ==1000:
                    break
                json_data = json.dumps(
                    {'time': a, 'value': random.random() * 100})
                # 1 SSE 返回格式是json字符串,要使用yield返回,字符串后面一定要跟随 \n\n
                yield f"data:{json_data}\n\n"
                time.sleep(1)  # 1s 发送一次
    	# 2 stream_with_context 设置SSE连接函数,mimetype="text/event-stream" 是设置SSE返回格式
        response = Response(stream_with_context(generate_random_data()), mimetype="text/event-stream")
        response.headers["Cache-Control"] = "no-cache"
        response.headers["X-Accel-Buffering"] = "no"
        return response
    
    if __name__ == '__main__':
        # 需要开启多线程模式
        app.run(debug=True, port=5143)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    前端文件
    templates/index2.html

    DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Creating Real-Time Charts with Flasktitle>
        <link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.3.1/css/bootstrap.min.css" rel="stylesheet">
        
        <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.0/jquery.min.js">script>
    
        <script type="text/javascript" src="https://fastly.jsdelivr.net/npm/echarts@5.3.3/dist/echarts.min.js">script>
    head>
    
    <body>
        <div class="container">
            <div class="row">
                <div class="col-5">div>
                <div class="col-12">
                    <div class="card">
    
                        <div class="card-body" id="mychart" style="width: 900px;height: 400px">
                        div>
                    div>
                    <br>
                    <div>
                        <button id="btn1" type="button" class="btn btn-primary">询问button>
                        <button id="btn2" type="button" class="btn btn-primary">停止button>
                    div>
                div>
                <div class="col-5">div>
            div>
        div>
        <script>
            $(document).ready(function () {
                var dom = document.getElementById('mychart');
                var myChart = echarts.init(dom, null, {
                    renderer: 'canvas',
                    useDirtyRect: false
                });
                var option;  // 图表参数
    
                option = {
                    title: {
                        text: 'Stacked Line',
                        zlevel: 0,  //默认值
                        z: 6,  //默认值
                    },
                    // 滚动条配置
                    dataZoom: [
                        {
                            type: 'slider',
                            show: true,
                            xAxisIndex: [0],
                            start: 0,    //初始化时,滑动条宽度开始标度
                            end: 100,
                            bottom: '3%',
                            height: 10, //组件高度
                            fillerColor: '#3E86FF',
                            borderColor: "transparent",
                            backgroundColor: 'white',//两边未选中的滑动条区域的颜色
                            showDataShadow: false,//是否显示数据阴影 默认auto
                            showDetail: false,//即拖拽时候是否显示详细数值信息 默认true
                        },
                        //下面这个属性是里面拖动配置
                        {
                            type: 'inside',
                            xAxisIndex: 0,
                            start: 0,//默认为1
                            end: 100,//默认为100
                        }
                    ],
                    // 显示鼠标接触位置曲线数据
                    tooltip: {
                        trigger: 'axis'
                    },
                    // 曲线图例
                    legend: {
                        data: ['Random Dataset', 'Random Dataset2']   //1
                    },
                    // 曲线框距离图表框的距离
                    grid: {
                        top: '15%',
                        left: '5%',
                        right: '140',
                        bottom: '8%',
                        containLabel: true,
                    },
                    toolbox: {
                        feature: {
                            saveAsImage: {},
                            dataView: {}, // 是否显示出原始数据
                        }
                    },
                    xAxis: {
                        // x轴配置,两条曲线共用一个x轴
                        type: 'category',
                        boundaryGap: false,
                        data: []  // 2
                    },
                    yAxis: {
                        type: 'value'
                    },
                    series: [
                        // 两条曲线y轴配置
                        {
                            name: 'Random Dataset',
                            data: [],
                            type: 'line',
                            showSymbol: false,
                            // 配置曲线尾端显示数据格式
                            endLabel: {
                                show: true,
                                formatter: function (params) {
                                    return params.seriesName + ': ' + params.data;
                                }
                            },
                            labelLayout: {
                                moveOverlap: 'shiftY'
                            },
                            emphasis: {
                                focus: 'series'
                            },
                        },
                        {
                            name: 'Random Dataset2',
                            data: [],
                            type: 'line',
                            showSymbol: false,
                            endLabel: {
                                show: true,
                                formatter: function (params) {
                                    return params.seriesName + ': ' + params.data;
                                }
                            },
                            labelLayout: {
                                moveOverlap: 'shiftY'
                            },
                            emphasis: {
                                focus: 'series'
                            },
                        }
                    ] //3
    
                }
    			// 初始化图表
                if (option && typeof option === 'object') {
                    // 设置为true的话,就是notMerge,不合并,false的话,就Merge,之前的东西还保留~
                    myChart.setOption(option, true);
                }
                //实现了图表跟随窗口大小自适应的需求
                window.addEventListener('resize', myChart.resize);
    
                // SSE接收
                var source = null;
                $("#btn1").click(function () {
                    source = new EventSource("/chart-data");
                    // readyState一个 unsigned short 值,代表连接状态。可能值是 CONNECTING (0), OPEN (1), 或者 CLOSED (2)。
                    console.log('EventSource.readyState ' + source.readyState);
    
                    // sse 连接开启时回调函数
                    source.onopen = function (event) {
                        console.log("onopen");
                        console.log('EventSource.readyState ' + source.readyState);
                    }
                    // 消息监听,event 是后端返回的数据,相当于python字典
                    source.onmessage = function (event) {
                        update_data(event);
                    }
                    // 监听 error 事件,后端超时没有传输数据时触发
                    source.onerror = function (event) {
                        console.log("error happened");
                        source.close();
                        console.log('EventSource.readyState ' + source.readyState);
                    }
                })
    
                // 绑定停止按钮事件
                $("#btn2").click(function () {
                    if (source !== null) {
                        source.close();
                        // delete_data();
                    }
                })
    
                // 更新图表数据
                function update_data(event) {
                    const data = JSON.parse(event.data);
                    console.log(data)
                    option.xAxis.data.push(data.time);
                    option.series[0].data.push(data.value);
                    option.series[1].data.push(data.value + 20);
                    myChart.setOption(option, true)
                }
    
                // 删除图表数据
                function delete_data() {
                    option.xAxis.data = [];
                    option.series[0].data = [];
                    option.series[1].data = [];
                    myChart.setOption(option, true);
                }
            });
    
        script>
    body>
    
    html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206

    参考

    使用服务器发送事件 - Web API 接口参考 | MDN (mozilla.org)
    Creating Real-Time Charts with Flask (ron.sh)
    后端主动向前端推送消息–SSE_故友dd的博客-CSDN博客_后端主动推送数据给前端

  • 相关阅读:
    2022.8.8考试游记总结
    前端页面JS事务学习Day02
    软考-软件项目活动图详解
    LuatOS-SOC接口文档(air780E)--keyboard - 键盘矩阵
    【知识总结】金九银十offer拿到手软的前端面试题——Javascript篇(一)
    Python与ArcGIS系列(六)查找和修复数据源
    jupyter使用conda虚拟环境操作步骤
    智能变电站网络报文记录及故障录波分析装置
    centos服务器命令行安装虚拟机并ssh连接
    神经网络异常检测模型,神经网络异常检测方法
  • 原文地址:https://blog.csdn.net/qq_38463737/article/details/126163498