├── .gitattributes ├── .gitignore ├── DockerFile ├── LICENSE ├── README.md ├── config.ini ├── config_handler.py ├── data_stream.py ├── data_streams └── samples.py ├── examples ├── VisClient.py ├── send_and_setup_rtsp_server_address_using_FFMPEG_or_GSTREAMER.py ├── send_geomarkers.py ├── send_heatmap_data.py ├── send_heatmap_data_as_image.py ├── send_post_request_to_HTTP_server.py ├── send_random_number_stream.py ├── send_stock_price_stream.py ├── send_text_stream.py └── send_webcam_images.py ├── flask_handler.py ├── http_client.py ├── http_server.py ├── image_server.py ├── images ├── chartjs.png ├── demo1.PNG ├── demo1.gif ├── demo2.gif ├── flask_logo.png ├── jquery.jpg ├── plotly.png ├── python.png └── structure.png ├── main.py ├── requirements.txt ├── scheduler.py ├── socket_client.py ├── socket_server.py ├── static └── css │ └── style.css └── templates ├── camera_sample.html ├── index.html └── map_sample.html /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | 53 | # Translations 54 | *.mo 55 | *.pot 56 | 57 | # Django stuff: 58 | *.log 59 | local_settings.py 60 | db.sqlite3 61 | 62 | # Flask stuff: 63 | instance/ 64 | .webassets-cache 65 | 66 | # Scrapy stuff: 67 | .scrapy 68 | 69 | # Sphinx documentation 70 | docs/_build/ 71 | 72 | # PyBuilder 73 | target/ 74 | 75 | # Jupyter Notebook 76 | .ipynb_checkpoints 77 | 78 | # IPython 79 | profile_default/ 80 | ipython_config.py 81 | 82 | # pyenv 83 | .python-version 84 | 85 | # pipenv 86 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 87 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 88 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 89 | # install all needed dependencies. 90 | #Pipfile.lock 91 | 92 | # celery beat schedule file 93 | celerybeat-schedule 94 | 95 | # SageMath parsed files 96 | *.sage.py 97 | 98 | # Environments 99 | .env 100 | .venv 101 | env/ 102 | venv/ 103 | ENV/ 104 | env.bak/ 105 | venv.bak/ 106 | 107 | # Spyder project settings 108 | .spyderproject 109 | .spyproject 110 | 111 | # Rope project settings 112 | .ropeproject 113 | 114 | # mkdocs documentation 115 | /site 116 | 117 | # mypy 118 | .mypy_cache/ 119 | .dmypy.json 120 | dmypy.json 121 | 122 | # Pyre type checker 123 | .pyre/ 124 | -------------------------------------------------------------------------------- /DockerFile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:18.04 2 | 3 | RUN apt-get update 4 | RUN apt-get install -y python3 python3-dev python3-pip 5 | RUN apt-get install -y libsm6 libxext6 libxrender-dev 6 | 7 | COPY ./ ./app 8 | WORKDIR /app 9 | 10 | # Make sure to expose all ports 11 | EXPOSE 80 8001 8002 8003 12 | 13 | RUN pip3 install -r requirements.txt 14 | 15 | CMD python3 main.py 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2019 Grebtsew 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Automatic Visualization of Realtime Data Stream Charts in Flask 2 | Visualize arbitrary realtime data streams with just a few lines of code! 3 | 4 | ![demo2](images/demo1.gif) 5 | 6 |
7 | Table of Contents (click to expand) 8 | 9 | 10 | 11 | - [About](#About) 12 | - [Solution](#Solution) 13 | - [Getting-Started](#Getting-Started) 14 | - [How-to-Send-data-to-your-Server](#How-to-Send-data-to-your-Server) 15 | - [Docker](#Docker) 16 | - [Demo](#demo) 17 | - [Examples](#examples) 18 | - [What-is-flask?](#What-is-flask?) 19 | - [What-is-JSChart?](#What-is-Chart.JS?) 20 | - [License](#license) 21 | - [Sources](#sources) 22 | 23 | 24 |
25 | 26 | # About 27 | Visualizing data flows are important in a project where maintaining data streams are of priority. 28 | The abiliy to visualize data in realtime can contribute with huge advantages while debugging code or demonstrating. In this implementation we are visualizing data in charts using Chart.JS, a simple powerful library for creating charts in Javascript. The server can now also visualize image streams and heatmaps using Plotly, a modern analytics app for enterprises. This implementation utilizes Flask and is developed mainly in python3 and Javascript. 29 | 30 |

31 | 32 | 33 | 34 |

35 |

36 | 37 | 38 |

39 | 40 | ## Solution 41 | This implementation is started by running the `main.py` file. The `Starter` will then set a timer for triggering the webbrowser after one second and then start the `Flask Handler`. The `Flask Handler` will start the website containing a `Flask-SocketIO` server. The `Flask-Server` will receive json objects and update the `GUI` listview. When the `Flask-Server` is started the `Scheduler` will be triggered. The `Scheduler` will start the middle-man servers consisting of a `TCP Socket Server`, a `TCP Socket Server for large files` and a `HTTP Server` which has the purpose of receiving messages and proxy them to the flask server. In the `demo` we also start some data streams with the scheduler. You basicly have two alternatives on sending data to this implementation. Either create a data stream in the `scheduler` or create a seperate `tcp socket client` or `http client` and send data to the tcp socket server while running. See examples. 42 | 43 | See program structure image below: 44 | ![structure](images/structure.png) 45 | 46 | # Getting Started 47 | 1. Install program by firstly installing all the required packages in python3: 48 | **Note**: If you want to use docker see [here](#Docker). 49 | ``` 50 | pip install -r requirements.txt 51 | ``` 52 | 53 | 2. Start the implementation by running: 54 | ``` 55 | python3 main.py 56 | ``` 57 | 58 | 3. If the website doesn't open automatically, open a webbrowser of your choice and go to: `https://127.0.0.1:5000/` 59 | 60 | 4. Edit the `Scheduler` file and Comment the `demo()` line, to make sure the demo data streams won't start. 61 | 62 | 5. Create your stream by looking at the [heading below](#How-to-Send-data-to-your-Server). 63 | 64 | Example data json: 65 | ``` 66 | data = { 67 | "id":1337, 68 | "value": [1,1], 69 | "type":"line", 70 | "active_points": 20, 71 | "label":"Label", 72 | "legend": ["one", "two"], 73 | "name":"Example", 74 | "borderColor":["#3e95cd", "#e8c3b9"], 75 | "backgroundColor":["#3e95cd","#e8c3b9"], 76 | "api_crypt":"password-1" 77 | } 78 | ``` 79 | 80 | # Change Config 81 | Ports and addresses can be changed in the `config.ini` file. 82 | 83 | # How to Send data to your Server 84 | As mentioned in the [solution heading above](#Solution) there are two ways of sending data streams to this implementation, creating a socket client or creating data stream in the implementation. 85 | 86 | ## Create a seperate TCP Socket Client 87 | I created two simple example of tcp socket clients in `examples/`. One that creates a data stream from live stock share prices and one that create a data stream from random numbers. 88 | 89 | ## Using the scheduler 90 | Take a closer look at the `scheduler.py` file, where more functions can be added to server start. In the current implementation there are two stream examples using scheduler in the `demo()` function. 91 | 92 | The first one is a seperate tcp socket client started from the scheduler. Check out the scheduler and `socket_client.py`. The second one is a stream using the `DataStream` class. Check out `data_streams/samples.py`. 93 | 94 | ## Showing RTSP Streams 95 | To create a video feed send a json with rtsp address as value and a unique id. Example: 96 | ``` 97 | video_data= { 98 | 'id': 12, 99 | 'value': rtsp://192.168.0.25:554/live.sdp, 100 | 'type': 'video_stream', 101 | 'name': 'Video Stream HTTP Example', 102 | # Crypt password from config.ini 103 | 'api_crypt':CRYPT 104 | } 105 | ``` 106 | 107 | # Docker 108 | 1. Build the docker image for this project by running: 109 | ``` 110 | docker build . --tag="JSChart-flask:1.0" 111 | ``` 112 | 2. Run the image in background by running: 113 | ``` 114 | docker run -d -p 80:80 JSChart-flask:1.0 115 | ``` 116 | 117 | 2. Or Run image in interactive mode by running: 118 | ``` 119 | docker run -it -p 80:80 JSChart-flask:1.0 120 | ``` 121 | 122 | 3. Open your webbrowser of choice and go to: 123 | ``` 124 | http://127.0.0.1/ 125 | or 126 | http://localhost/ 127 | ``` 128 | **Note**: all ports and addresses can be changed in `config.ini` 129 | # Demo 130 | 131 | This is the output on the console during execution. 132 | ![demo1](images/demo1.PNG) 133 | 134 | This is how the implementation looks like during execution of the `demo()`. 135 | ![demo3](images/demo1.gif) 136 | 137 | Examples of how each chart look and how the data should be represented in json see: 138 | * https://www.chartjs.org/samples/latest/ 139 | * https://tobiasahlin.com/blog/chartjs-charts-to-get-you-started 140 | 141 | ## What is flask? 142 | [Flask](https://en.wikipedia.org/wiki/Flask_(web_framework)) is a micro web framework, enabling websites to be hosted in python. 143 | 144 | ## What is Chart.JS? 145 | [Chart.JS](https://www.chartjs.org/) is an opensource project with the main purpose to provide awesome charts for html5 and javascript. 146 | 147 | ## What is Plotly? 148 | [Plotly](https://plot.ly/) is a collection of open source Graphing Libraries for visualizing data in a vast amount of formats. 149 | 150 | # Licenses 151 | See ![license](LICENSE) 152 | 153 | # Known Issues 154 | List of known issues: 155 | * Ignore the `WebSocket transport not available. Install eventlet or gevent and gevent-websocket for improved performance.` warning as eventlet doesn't support Flask-socketio! Make sure eventlet is not installed in your python environment! 156 | 157 | # Sources 158 | The main inspiration and solutions comes from the following sources: 159 | * https://gitlab.com/patkennedy79/flask_chartjs_example 160 | * https://github.com/roniemartinez/real-time-charts-with-flask 161 | 162 | # Deprecated Demo 163 | A deprecated demo can be intresting to see how the application has developed from earlier versions! 164 | ![demo2](images/demo2.gif) 165 | 166 | 167 | # TODO 168 | These are functions that I will add 169 | * geo tagging free api map 170 | * 3d graphs 171 | * 3d algoritm 172 | * Bubble matrix 173 | * Tree 174 | -------------------------------------------------------------------------------- /config.ini: -------------------------------------------------------------------------------- 1 | [Flask] 2 | # HOST will give error if not this 3 | HOST=0.0.0.0 4 | # PORT must be same as website port 5 | PORT=80 6 | 7 | [Website] 8 | HOST=127.0.0.1 9 | # PORT must be same as Flask Port 10 | PORT=80 11 | 12 | [SocketServer] 13 | HOST=127.0.0.1 14 | PORT=8010 15 | 16 | [ImageServer] 17 | HOST=127.0.0.1 18 | PORT=8020 19 | 20 | [HTTPServer] 21 | HOST=127.0.0.1 22 | PORT=8030 23 | CRYPT-PASSWORD=password-1 24 | -------------------------------------------------------------------------------- /config_handler.py: -------------------------------------------------------------------------------- 1 | import configparser 2 | 3 | class ConfigHandler(): 4 | 5 | def __init__(self, path="config.ini"): 6 | self.path = path 7 | self.config = self.readconfig_file() 8 | 9 | def readconfig_file(self): 10 | config = configparser.ConfigParser() 11 | config.read("config.ini") 12 | return config 13 | 14 | def __str__(self): 15 | print("List all contents") 16 | for section in self.config.sections(): 17 | print("Section: %s" % section) 18 | for options in self.config.options(section): 19 | print("x %s:::%s:::%s" % (options, 20 | self.config.get(section, options), 21 | str(type(options)))) 22 | def get_all(self, section): 23 | res = [] 24 | for options in self.config.options(section): 25 | res.append(self.config.get(section, options)) 26 | 27 | return res 28 | 29 | def get(self, section, value): 30 | return self.config.get(section, value) 31 | def getboolean(self, section, value): 32 | return self.config.getboolean(section,value) 33 | -------------------------------------------------------------------------------- /data_stream.py: -------------------------------------------------------------------------------- 1 | from threading import Thread 2 | import flask_handler 3 | from datetime import datetime 4 | import time 5 | import threading 6 | """ 7 | This file contains some data structures for better implementation structure. 8 | """ 9 | 10 | class Config(): 11 | # Describes the visuals of graphs 12 | def __init__(self, _id= 0, _type = 'line', _active_points = 20, 13 | _delay = 1, _name = "RealtimeGraph", _label=["Value"], _legend=["data"], 14 | _width = 200, _height = 100, backgroundColor = ["rgb(255, 99, 132)"], 15 | borderColor = ["rgb(255, 99, 132)"], fill = "false"): 16 | self.type = _type 17 | self.active_points = _active_points 18 | self.delay = _delay = 1 19 | self.id = _id 20 | self.name = _name 21 | self.label = _label 22 | self.legend = _legend 23 | self.width = _width 24 | self.height = _height 25 | self.backgroundColor = backgroundColor 26 | self.borderColor = borderColor 27 | self.fill = fill 28 | 29 | class DataStream(Thread): 30 | def __init__(self, _config, _data_func): 31 | super(DataStream, self).__init__() 32 | self.data_func = _data_func 33 | self.config = _config 34 | 35 | def run(self): 36 | while not flask_handler.thread_stop_event.isSet(): 37 | flask_handler.socketio.emit('server', 38 | {'id':self.config.id, 39 | 'time': datetime.now().strftime('%Y-%m-%d %H:%M:%S'), 40 | 'value': self.data_func(), 41 | 'type': self.config.type, 42 | 'active_points': self.config.active_points, 43 | 'label': self.config.label, 44 | 'legend': self.config.legend, 45 | 'name': self.config.name, 46 | 'width': self.config.width, 47 | 'height': self.config.height, 48 | "backgroundColor": self.config.backgroundColor, 49 | "borderColor" : self.config.borderColor, 50 | "fill" : self.config.fill}, 51 | namespace='/test') 52 | time.sleep(self.config.delay) 53 | 54 | 55 | def def_param(vari, deff): 56 | if vari is None: 57 | return deff 58 | else: 59 | return vari 60 | 61 | def send_request(id, data, type = 'line', active_points = 20, _label="Value", _legend="data", _width = 200, _height = 100, _name = "Graph", backgroundColor = "rgb(255, 99, 132)", borderColor = "rgb(255, 99, 132)", fill = "false"): 62 | type = def_param(type, 'line') 63 | active_points = def_param(active_points, 20) 64 | _label = def_param(_label, ['Value']) 65 | _legend = def_param(_legend, ['data']) 66 | _height = def_param(_height, 200) 67 | _width = def_param(_width, 100) 68 | _name = def_param(_name, 'Graph') 69 | fill = def_param(fill, "False") 70 | backgroundColor = def_param(backgroundColor, ["rgb(255, 99, 132)"]) 71 | borderColor = def_param(borderColor, ["rgb(255, 99, 132)"]) 72 | 73 | flask_handler.socketio.emit('server', 74 | {'id': id, 75 | 'time': datetime.now().strftime('%Y-%m-%d %H:%M:%S'), 76 | 'value': data, 77 | 'type': type, 78 | 'active_points': active_points, 79 | 'label': _label, 80 | 'legend': _legend, 81 | 'name': _name, 82 | 'width': _width, 83 | 'height': _height, 84 | "backgroundColor": backgroundColor, 85 | "borderColor" : borderColor, 86 | "fill" : fill}, 87 | namespace='/test') 88 | -------------------------------------------------------------------------------- /data_streams/samples.py: -------------------------------------------------------------------------------- 1 | # Example random_nr data stream 2 | from data_stream import Config 3 | from random import random 4 | from random import randrange 5 | 6 | """ 7 | Random generator working on this pc 8 | """ 9 | def random_nr(): 10 | return [round(random()*10, 3)] 11 | 12 | random_nr_config = Config(_name = "Random Number Stream") 13 | 14 | """ 15 | These are sample requests send via tcp sockets 16 | """ 17 | 18 | def random_color(): 19 | color = [randrange(255),randrange(255),randrange(255)] 20 | return color 21 | 22 | samplelist = [] 23 | 24 | samplelist.append('{"id":10, "value": ['+str(int(round(random()*10, 3)))+'], "type":"line","active_points": 20, "width":300, "height":150, "label":["value"], "legend":["legend"], "name":"line chart" }') 25 | samplelist.append('{"id":20, "value": ['+str(round(random()*10, 3))+'], "type":"line","active_points": 20, "width":300, "height":150, "label":["value"], "legend":["legend"], "name":"line chart with full json", "fill": true, "backgroundColor":["#3e95cd"], "borderColor":["#3e95cd"]}') 26 | samplelist.append('{"id":40, "value": ['+str(round(random()*10, 3))+'], "type":"pie","active_points": 20, "width":300, "height":150, "label":["value"], "legend":["legend"], "name":"Pie chart", "fill": false, "backgroundColor":["#e8c3b9"], "borderColor":["#c45850"]}') 27 | samplelist.append('{"id":30, "value": ['+str(round(random()*10, 3))+'], "type":"bar","active_points": 20, "width":300, "height":150, "label":["value"], "legend":["legend"], "name":"Bar chart", "fill": false, "backgroundColor":["#8e5ea2"], "borderColor":["#e8c3b9"]}') 28 | samplelist.append('{"id":50, "value": ['+str(round(random()*10, 3))+'], "type":"radar","active_points": 20, "width":300, "height":150, "label":["value"], "legend":["legend"], "name":"Radar chart", "fill": false, "backgroundColor":["#8e5ea2"], "borderColor":["#c45850"]}') 29 | samplelist.append('{"id":70, "value": ['+str(round(random()*10, 3))+'], "type":"doughnut","active_points": 20, "width":300, "height":150, "label":["value"], "legend":["legend"], "name":"Doughnut chart", "fill": false, "backgroundColor":["#3e95cd"], "borderColor":["#c45850"]}') 30 | samplelist.append('{"id":80, "value": ['+str(round(random()*10, 3))+'], "type":"horizontalBar","active_points": 20, "width":300, "height":150, "label":["value"], "legend":["legend"], "name":"Horizontal Bar chart", "fill": false, "backgroundColor":["#3cba9f"], "borderColor":["#c45850"]}') 31 | #samplelist.append('{"id":60, "value": ['+str(int(round(random()*10, 3)))+'], "type":"polarArea","active_points": 20, "width":300, "height":150, "label":["value"], "legend":["legend"], "name":"Polar Area chart", "fill": false, "backgroundColor":["#3cba9f"], "borderColor":["#c45850"]}') 32 | -------------------------------------------------------------------------------- /examples/VisClient.py: -------------------------------------------------------------------------------- 1 | """ 2 | This Class uses the flask visualization repo to show data 3 | """ 4 | import threading 5 | import socket 6 | import struct 7 | import pickle 8 | import cv2 9 | 10 | class VisClient(threading.Thread): 11 | """ 12 | This client sends images for detection server 13 | """ 14 | 15 | def __init__(self, address,port): 16 | super(VisClient,self).__init__() 17 | self.address = address 18 | self.port = port 19 | self.s = socket.socket() 20 | self.s.connect((self.address,self.port)) 21 | self.encode_param = [int(cv2.IMWRITE_JPEG_QUALITY), 90] 22 | 23 | 24 | def send_large(self,frame): 25 | 26 | data = pickle.dumps(frame, 0) 27 | size = len(data) 28 | #print("Sending Image of size ", size) 29 | self.s.sendall(struct.pack(">L", size) + data) 30 | 31 | def send(self, data): 32 | """ 33 | Send data to vis 34 | """ 35 | self.s.sendall(str.encode(data)) 36 | 37 | def run(self): 38 | self.send() 39 | -------------------------------------------------------------------------------- /examples/send_and_setup_rtsp_server_address_using_FFMPEG_or_GSTREAMER.py: -------------------------------------------------------------------------------- 1 | """ 2 | This Demo demonstrates how to start a FFMPEG & GSTREAMER rtsp server of the webcam stream and send the address to 3 | our Visualizer for visualization! 4 | """ 5 | 6 | address = "127.0.0.1" 7 | port = 1337 8 | webcam_name= "USB2.0 UVC 1M WebCam" 9 | Use_FFMPEG= False 10 | 11 | import os 12 | 13 | print ("Starting server") 14 | 15 | if Use_FFMPEG: 16 | # Create rtsp server for FFMPEG, require FFMPEG command installed on pc and a webcam! 17 | stream = os.popen('ffmpeg -f dshow -i video="'+webcam_name+'" -acodec libmp3lame -ar 11025 -f mpegts udp://'+address+":"+str(port)) 18 | else: 19 | # Create rtsp server for GSTREAMER, require GSTREAMER command installed on pc and a webcam! 20 | stream = os.popen('gst-launch-1.0 autovideosrc device=/dev/videoX ! video/x-raw,width=640,height=480,encoding-name=H264 ! videoconvert ! jpegenc ! udpsink host='+address+' port='+str(port)) 21 | 22 | # Use top or activity manager to shutdown the streams if you do not use debugmode! Pycharm loses childprocesses! 23 | 24 | print("Stream active at :" +'udp://'+address+":"+str(port)) 25 | 26 | print("Sending stream address to Visulization Stream...") 27 | video_data= { 28 | 'id': 144124, 29 | 'value': 'udp://'+address+":"+str(port), 30 | 'type': 'video_stream', 31 | 'name': 'Video Stream HTTP Example', 32 | 'api_crypt':"password-1"} 33 | 34 | headers = {'Content-type': 'application/json', 'Accept': 'text/plain'} 35 | API_ENDPOINT = "http://localhost:8030" # set in config 36 | 37 | import requests 38 | import json 39 | import time 40 | while True: 41 | # sending post request and saving response as response object 42 | requests.post(url=API_ENDPOINT, data=json.dumps(video_data), headers=headers) 43 | time.sleep(2) 44 | 45 | 46 | 47 | """ 48 | #Uncomment this if you want to test the rtsp server streams in this program! 49 | import cv2 50 | print(" Test Capture video ") 51 | cap = cv2.VideoCapture("udp://"+address+":"+str(port)) 52 | # get list 53 | while True: 54 | ret, frame = cap.read() 55 | 56 | cv2.imshow("test", frame) 57 | cv2.waitKey(1) 58 | """ 59 | 60 | while True: 61 | time.sleep(10) # keep thread alive 62 | 63 | -------------------------------------------------------------------------------- /examples/send_geomarkers.py: -------------------------------------------------------------------------------- 1 | 2 | # importing the requests library 3 | import requests 4 | import time 5 | from random import random 6 | import json 7 | 8 | # defining the api-endpoint 9 | API_ENDPOINT = "http://127.0.0.1:8030" 10 | 11 | CRYPT = "password-1" 12 | 13 | # data to be sent to api 14 | 15 | data = { 16 | 'id':151515, 17 | 'value':'{ center: {lat: 41.40338, lng: 2.17403}, zoom: 4, data: [{lat: 41.40334, lng: 2.17404},{lat: 41.40335, lng: 2.17407} ] }' , 18 | 'type':'map', 19 | 'name':'Geo Marker Map Example', 20 | 'api_crypt':CRYPT 21 | } 22 | headers = {'Content-type': 'application/json', 'Accept': 'text/plain'} 23 | 24 | while True: 25 | print("Sending our POST request to server ...") 26 | print(API_ENDPOINT, data) 27 | # sending post request and saving response as response object 28 | r = requests.post(url = API_ENDPOINT, data = json.dumps(data), headers=headers) 29 | 30 | 31 | time.sleep(15) 32 | -------------------------------------------------------------------------------- /examples/send_heatmap_data.py: -------------------------------------------------------------------------------- 1 | import socket 2 | import time 3 | from random import random 4 | 5 | """ 6 | Stock price stream example for this program 7 | """ 8 | 9 | def main(): 10 | HOST = '127.0.0.1' # Standard loopback interface address (localhost) 11 | PORT = 65432 # can change this if you want 12 | 13 | # Create a TCP/IP socket 14 | sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 15 | 16 | # Bind the socket to the port 17 | server_address = (HOST, PORT) 18 | 19 | sock.connect(server_address) 20 | print("start Sending data") 21 | while True: 22 | point = (int(round(random()*192, 3)),int(round(random()*108, 3))) 23 | send_str = '{"id":80085, "value":['+str(point[0])+","+str(point[1])+'], "type":"heatmap","active_points": 20, "width":192, "height":108, "label":["Random Number"], "legend": ["random"], "name":"Random Pos Heatmap", "borderColor":["#3e95cd"], "backgroundColor" :["#3e95cd"]}' 24 | sock.sendall(str.encode(send_str)) 25 | 26 | time.sleep(1) 27 | 28 | if __name__ == '__main__': 29 | main() 30 | -------------------------------------------------------------------------------- /examples/send_heatmap_data_as_image.py: -------------------------------------------------------------------------------- 1 | from VisClient import VisClient 2 | import time 3 | import base64 4 | import random 5 | 6 | 7 | """ 8 | This Function is under development and is not currently working! 9 | """ 10 | from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas 11 | from matplotlib.figure import Figure 12 | 13 | import numpy as np 14 | import matplotlib 15 | import matplotlib.pyplot as plt 16 | import cv2 17 | 18 | pixels = np.zeros((1080, 1920)) 19 | 20 | data_heat_value = 1 21 | 22 | 23 | conn = VisClient("127.0.0.1",12345) 24 | print("Creating and sending heatmap to server!!!") 25 | while True: 26 | # Add 100 points 27 | for i in range(0,100): 28 | # Add random point in heatmap 29 | pixels[random.randint(0,1080-1)][random.randint(0,1920-1)] += data_heat_value 30 | fig, ax = plt.subplots() 31 | 32 | im = ax.imshow(pixels) 33 | 34 | 35 | ax.set_title("Heatmap Example") 36 | fig.tight_layout() 37 | 38 | fig.canvas.draw() 39 | 40 | #plt.show() 41 | 42 | image = np.array(fig.canvas.renderer._renderer) 43 | 44 | #cv2.imshow("test", image) 45 | #cv2.waitKey(1) 46 | 47 | retval, buffer = cv2.imencode('.png', image) 48 | encoded_string = "data:image/png;base64,"+base64.b64encode(buffer).decode() 49 | 50 | send_string = '{"id":696969969, "value":"'+encoded_string+'", "type":"image","name":""}' 51 | 52 | plt.close(fig) 53 | 54 | conn.send_large(send_string) 55 | 56 | time.sleep(2) 57 | -------------------------------------------------------------------------------- /examples/send_post_request_to_HTTP_server.py: -------------------------------------------------------------------------------- 1 | 2 | # importing the requests library 3 | import requests 4 | import time 5 | from random import random 6 | import json 7 | 8 | # defining the api-endpoint 9 | API_ENDPOINT = "http://127.0.0.1:8030" 10 | 11 | CRYPT = "password-1" 12 | 13 | # data to be sent to api 14 | data = { 15 | 'id':223322, 16 | 'value':[round(random()*100, 3)], 17 | 'type':'line', 18 | 'active_points': 20, 19 | 'label':['Random Number'], 20 | 'legend': ['random'], 21 | 'name':'Random Number Example', 22 | 'borderColor':['#3e95cd'], 23 | 'backgroundColor':['#3e95cd'], 24 | 'api_crypt':CRYPT 25 | } 26 | headers = {'Content-type': 'application/json', 'Accept': 'text/plain'} 27 | 28 | while True: 29 | print("Sending our POST request to server ...") 30 | print(API_ENDPOINT, data) 31 | # sending post request and saving response as response object 32 | r = requests.post(url = API_ENDPOINT, data = json.dumps(data), headers=headers) 33 | 34 | 35 | time.sleep(5) 36 | -------------------------------------------------------------------------------- /examples/send_random_number_stream.py: -------------------------------------------------------------------------------- 1 | import socket 2 | import time 3 | from random import random 4 | 5 | """ 6 | Stock price stream example for this program 7 | """ 8 | 9 | def main(): 10 | HOST = '127.0.0.1' # Standard loopback interface address (localhost) 11 | PORT = 65432 # can change this if you want 12 | 13 | # Create a TCP/IP socket 14 | sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 15 | 16 | # Bind the socket to the port 17 | server_address = (HOST, PORT) 18 | 19 | sock.connect(server_address) 20 | 21 | while True: 22 | 23 | send_str = '{"id":80085, "value":['+str(round(random()*100, 3))+'], "type":"line","active_points": 20, "label":["Random Number"], "legend": ["random"], "name":"Random Number Example", "borderColor":["#3e95cd"], "backgroundColor" :["#3e95cd"]}' 24 | sock.sendall(str.encode(send_str)) 25 | 26 | time.sleep(0.5) 27 | 28 | if __name__ == '__main__': 29 | main() 30 | -------------------------------------------------------------------------------- /examples/send_stock_price_stream.py: -------------------------------------------------------------------------------- 1 | import socket 2 | import time 3 | 4 | from yahoo_fin import stock_info as si 5 | 6 | """ 7 | Stock price stream example for this program 8 | """ 9 | 10 | def main(): 11 | HOST = '127.0.0.1' # Standard loopback interface address (localhost) 12 | PORT = 65432 # can change this if you want 13 | 14 | # Create a TCP/IP socket 15 | sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 16 | 17 | # Bind the socket to the port 18 | server_address = (HOST, PORT) 19 | 20 | sock.connect(server_address) 21 | 22 | while True: 23 | # get live price of Apple 24 | apple = si.get_live_price("aapl") 25 | 26 | # or Amazon 27 | amazon = si.get_live_price("amzn") 28 | 29 | print("apple: " ,apple,"amazon: ", amazon) 30 | 31 | send_str = '{"id":1337, "value": ['+str(apple)+', '+ str(amazon)+'], "type":"line","active_points": 20, "label":"Share Price ($)", "legend": ["apple", "amazon"], "name":"Stock Share Prices Example", "borderColor":["#3e95cd", "#e8c3b9"], "backgroundColor" :["#3e95cd","#e8c3b9"]}' 32 | sock.sendall(str.encode(send_str)) 33 | 34 | time.sleep(2) 35 | 36 | if __name__ == '__main__': 37 | main() 38 | -------------------------------------------------------------------------------- /examples/send_text_stream.py: -------------------------------------------------------------------------------- 1 | import socket 2 | import time 3 | 4 | """ 5 | Stock price stream example for this program 6 | """ 7 | import random 8 | import string 9 | 10 | def randomString(stringLength=10): 11 | """Generate a random string of fixed length """ 12 | letters = string.ascii_lowercase 13 | return ''.join(random.choice(letters) for i in range(stringLength)) 14 | 15 | def main(): 16 | HOST = '127.0.0.1' # Standard loopback interface address (localhost) 17 | PORT = 65432 # can change this if you want 18 | 19 | # Create a TCP/IP socket 20 | sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 21 | 22 | # Bind the socket to the port 23 | server_address = (HOST, PORT) 24 | 25 | sock.connect(server_address) 26 | print("Start sending random text") 27 | while True: 28 | 29 | send_str = '{"id":80085, "value":"'+randomString(random.randint(1,50))+'", "type":"text","active_points": 20, "name":"Random Text Example", "borderColor":["#ffffff"], "backgroundColor" :["#000000"]}' 30 | sock.sendall(str.encode(send_str)) 31 | 32 | time.sleep(0.5) 33 | 34 | if __name__ == '__main__': 35 | main() 36 | -------------------------------------------------------------------------------- /examples/send_webcam_images.py: -------------------------------------------------------------------------------- 1 | 2 | from VisClient import VisClient 3 | import time 4 | import cv2 5 | import base64 6 | import threading 7 | 8 | 9 | class sender(threading.Thread): 10 | def __init__(self, conn, image): 11 | super(sender,self).__init__() 12 | self.done = True 13 | self.conn = conn 14 | self.image = image 15 | def is_done(self): 16 | return self.done 17 | 18 | def run(self): 19 | self.done = False 20 | retval, buffer = cv2.imencode('.png', self.image) 21 | encoded_string = "data:image/png;base64,"+base64.b64encode(buffer).decode() 22 | send_string = '{"id":69696969, "value":"'+encoded_string+'", "type":"image","name":"Webcam Stream"}' 23 | self.conn.send_large(send_string) 24 | self.done = True 25 | 26 | 27 | 28 | cap = cv2.VideoCapture(0) 29 | conn = VisClient("127.0.0.1",12345) 30 | extra_thread = None 31 | print("Sending Images with one sec delay") 32 | while True: 33 | time.sleep(2) 34 | ret, image = cap.read() 35 | 36 | if extra_thread is None: 37 | extra_thread = sender(conn, image) 38 | extra_thread.start() 39 | else: 40 | 41 | if extra_thread.is_done() : 42 | extra_thread = sender(conn, image) 43 | extra_thread.start() 44 | 45 | #time.sleep(1) 46 | 47 | #cv2.imshow("test", image) 48 | #cv2.waitKey(1) 49 | -------------------------------------------------------------------------------- /flask_handler.py: -------------------------------------------------------------------------------- 1 | #from gevent import monkey 2 | #monkey.patch_all() 3 | 4 | #import eventlet 5 | #eventlet.monkey_patch() 6 | 7 | 8 | from flask_socketio import SocketIO, emit 9 | from flask import Flask,Response, render_template, url_for, copy_current_request_context 10 | 11 | from threading import Thread, Event 12 | from scheduler import scheduler 13 | from socket_server import SocketServer 14 | 15 | """ 16 | Flask handler manages the start and connection to Flask website/server. 17 | """ 18 | 19 | app = Flask(__name__, static_url_path='/static') 20 | app.config['DEBUG'] = False # let this be false to only start one webbrowser 21 | app.config['THREADED'] = True 22 | 23 | #turn the flask app into a socketio app 24 | socketio = SocketIO(app, async_mode="threading") 25 | 26 | thread = Thread() # scheduler thread 27 | thread_stop_event = Event() 28 | 29 | #lock = threading.Lock() 30 | 31 | def start_flask_application(): 32 | from config_handler import ConfigHandler 33 | [HOST,PORT] = ConfigHandler().get_all("Flask") # pylint: disable=unbalanced-tuple-unpacking 34 | socketio.run(app, host=HOST, port=PORT) # SocketIOServer 35 | app.run(host=HOST, port=PORT) # Other Server 36 | 37 | @app.route('/') 38 | def index(): 39 | #only by sending this page first will the client be connected to the socketio instance 40 | return render_template('index.html') 41 | 42 | # For camear 43 | vcapture_list = [] 44 | def gen(device): 45 | import cv2 46 | try: 47 | if(device in vcapture_list): 48 | print("Device stream already streaming " + str(device)) 49 | vcap = cv2.VideoCapture(device) 50 | vcapture_list.append(device) 51 | while True: 52 | ret, frame = vcap.read() 53 | if frame is None: 54 | continue 55 | (flag, encodedImage) = cv2.imencode(".jpg", frame) 56 | if not flag: 57 | continue 58 | yield(b'--frame\r\n' b'Content-Type: image/jpeg\r\n\r\n' + 59 | bytearray(encodedImage) + b'\r\n') 60 | except Exception: 61 | print("Capture failed " + str(device)) 62 | 63 | @app.route('/video_feed/') 64 | def video_feed(device): 65 | # return the response generated along with the specific media 66 | # type (mime type) 67 | print(device) 68 | device = device.replace("skipableslash","/") 69 | print(device) 70 | try: 71 | return Response(gen(int(device)),mimetype = "multipart/x-mixed-replace; boundary=frame") 72 | except Exception: 73 | return Response(gen(device),mimetype = "multipart/x-mixed-replace; boundary=frame") 74 | 75 | @socketio.on('connect', namespace='/test') 76 | def test_connect(): 77 | # need visibility of the global thread object 78 | global thread 79 | print('Flask Client connected') 80 | 81 | #Start the generator threads only if the thread has not been started before. 82 | if not thread.isAlive(): 83 | scheduler() 84 | 85 | @socketio.on('disconnect', namespace='/test') 86 | def test_disconnect(): 87 | print('Flask Client disconnected') 88 | -------------------------------------------------------------------------------- /http_client.py: -------------------------------------------------------------------------------- 1 | import requests 2 | from threading import Thread 3 | import json 4 | import time 5 | """ 6 | This simple socket client 7 | connects to our socket server and sends live stream numbers 8 | to be displayed in flask. 9 | """ 10 | 11 | class HTTPClient(Thread): 12 | 13 | def __init__(self): 14 | super(HTTPClient, self).__init__() 15 | 16 | def run(self): 17 | from config_handler import ConfigHandler 18 | (HOST, PORT, CRYPT) = ConfigHandler().get_all("HTTPServer") # pylint: disable=unbalanced-tuple-unpacking 19 | 20 | data = { 21 | 'id': 12, 22 | 'value': [10], 23 | 'type': 'line', 24 | 'active_points': 20, 25 | 'label': ['Random HTTP Number'], 26 | 'legend': ['random'], 27 | 'name': 'HTTP Example', 28 | 'borderColor': ['#3e95cd'], 29 | 'backgroundColor': ['#3e95cd'], 30 | 'api_crypt':CRYPT 31 | } 32 | 33 | """ 34 | video_data= { 35 | 'id': 12, 36 | 'value': 0, 37 | 'type': 'video_stream', 38 | 'name': 'Video Stream HTTP Example', 39 | 'api_crypt':CRYPT 40 | } 41 | """ 42 | 43 | headers = {'Content-type': 'application/json', 'Accept': 'text/plain'} 44 | API_ENDPOINT = "http://"+HOST+":"+PORT 45 | 46 | while True: 47 | # sending post request and saving response as response object 48 | requests.post(url=API_ENDPOINT, data=json.dumps(data), headers=headers) 49 | time.sleep(2) 50 | 51 | -------------------------------------------------------------------------------- /http_server.py: -------------------------------------------------------------------------------- 1 | """ 2 | This server will receive HTTP post requests and send the data to flask 3 | """ 4 | from threading import Thread 5 | from data_stream import send_request 6 | import http.server 7 | import json 8 | from functools import partial 9 | from http.server import BaseHTTPRequestHandler, HTTPServer 10 | 11 | class S(BaseHTTPRequestHandler): 12 | 13 | def __init__(self, CRYPT="password-1", *args, **kwargs): 14 | self.CRYPT = CRYPT 15 | super().__init__(*args, **kwargs) 16 | 17 | 18 | 19 | def _set_response(self): 20 | self.send_response(200, "ok") 21 | self.send_header('Access-Control-Allow-Origin', '*') 22 | self.send_header('Access-Control-Allow-Methods', 'POST, OPTIONS, HEAD, GET') 23 | self.send_header("Access-Control-Allow-Headers", "X-Requested-With") 24 | self.send_header("Access-Control-Allow-Headers", "Content-Type") 25 | self.send_header('Content-type', 'application/json') 26 | self.end_headers() 27 | 28 | def do_HEAD(self): 29 | self._set_response() 30 | 31 | def do_OPTIONS(self): 32 | self._set_response() 33 | 34 | def do_POST(self): 35 | #print(self.client_address,self.headers) 36 | 37 | if self.headers['Content-Length']: 38 | 39 | content_length = int(self.headers['Content-Length']) # <--- Gets the size of data 40 | post_data = self.rfile.read(content_length) # <--- Gets the data itself 41 | # decode incoming data // see if password is correct here! 42 | try: 43 | #print("data",post_data) 44 | data = json.loads(post_data) 45 | #print("json", data) 46 | if data['api_crypt'] : 47 | if data['api_crypt'] == self.CRYPT: 48 | 49 | print("Sending request " + str(data["value"])) 50 | send_request(id = data["id"], data=data["value"], type =safe(data, "type"), active_points =safe(data, "active_points"), 51 | _label=safe(data, "label"), _legend=safe(data, "legend"), _width = safe(data, "width"), _height = safe(data, "height"), 52 | _name = safe(data, "name"), fill = safe(data, "fill"), backgroundColor = safe(data, "backgroundColor"), 53 | borderColor = safe(data, "borderColor")) 54 | except Exception as e: 55 | print("ERROR: "+str(e)) 56 | self._set_response() 57 | 58 | class HTTPserver(Thread): 59 | 60 | def __init__(self): 61 | super().__init__() 62 | 63 | def run(self): 64 | from config_handler import ConfigHandler 65 | (HOST, PORT, CRYPT) = ConfigHandler().get_all("HTTPServer") # pylint: disable=unbalanced-tuple-unpacking 66 | server_address = (str(HOST),int(PORT)) 67 | httpd = HTTPServer(server_address, partial(S, CRYPT)) 68 | try: 69 | httpd.serve_forever() 70 | except KeyboardInterrupt: 71 | pass 72 | httpd.server_close() 73 | 74 | def safe(json, value): 75 | try: 76 | return json[value] 77 | except Exception: 78 | return 79 | -------------------------------------------------------------------------------- /image_server.py: -------------------------------------------------------------------------------- 1 | import time 2 | import json 3 | import cv2 4 | import pickle 5 | import struct 6 | from threading import Thread, Event 7 | from data_stream import send_request 8 | import socket 9 | 10 | """ 11 | ImageServer is a multithreaded socket server 12 | This server is used to send larger packages to Flask 13 | receiving n amount of connections and proxy the messages 14 | to flask 15 | """ 16 | 17 | class ImageServer(Thread): 18 | def __init__(self): 19 | super(ImageServer, self).__init__() 20 | 21 | def handle_connection(self, conn): 22 | with conn: 23 | while True: 24 | data = b"" 25 | payload_size = struct.calcsize(">L") 26 | try: 27 | # Recieve image package size 28 | while len(data) < payload_size: 29 | data += conn.recv(4096) 30 | 31 | packed_msg_size = data[:payload_size] 32 | data = data[payload_size:] 33 | msg_size = struct.unpack(">L", packed_msg_size)[0] 34 | 35 | # Recieve image 36 | while len(data) < msg_size: 37 | data += conn.recv(4096) 38 | frame_data = data[:msg_size] 39 | data = data[msg_size:] 40 | 41 | data=pickle.loads(frame_data, fix_imports=True, encoding="bytes") 42 | data = json.loads(data) 43 | 44 | send_request(id = data["id"], data=data["value"], type =safe(data, "type"), active_points =safe(data, "active_points"), 45 | _label=safe(data, "label"), _legend=safe(data, "legend"), _width = safe(data, "width"), _height = safe(data, "height"), 46 | _name = safe(data, "name"), fill = safe(data, "fill"), backgroundColor = safe(data, "backgroundColor"), borderColor = safe(data, "borderColor")) 47 | except Exception : 48 | pass 49 | # Got corrupt image data1 50 | #print(" WARNING: an error occured in image_server: ", e) 51 | 52 | 53 | def run(self): 54 | from config_handler import ConfigHandler 55 | (HOST, PORT) = ConfigHandler().get_all("ImageServer") # pylint: disable=unbalanced-tuple-unpacking 56 | 57 | #HOST = '127.0.0.1' # Standard loopback interface address (localhost) 58 | #PORT = 12345 # Port to listen on (non-privileged ports are > 1023) 59 | 60 | with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: 61 | s.bind((str(HOST), int(PORT))) 62 | s.listen() 63 | try: 64 | while True: 65 | conn, addr = s.accept() 66 | print('Connected by', addr) 67 | Thread(target=self.handle_connection, args=(conn,)).start() 68 | except Exception as e: 69 | print(e) 70 | 71 | def safe(json, value): 72 | try: 73 | return json[value] 74 | except Exception: 75 | return 76 | -------------------------------------------------------------------------------- /images/chartjs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grebtsew/Visualize-Realtime-Data-Stream-Chart-in-Flask/c9c5cee4d8f231d28744d1ae7de231e13981f4b1/images/chartjs.png -------------------------------------------------------------------------------- /images/demo1.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grebtsew/Visualize-Realtime-Data-Stream-Chart-in-Flask/c9c5cee4d8f231d28744d1ae7de231e13981f4b1/images/demo1.PNG -------------------------------------------------------------------------------- /images/demo1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grebtsew/Visualize-Realtime-Data-Stream-Chart-in-Flask/c9c5cee4d8f231d28744d1ae7de231e13981f4b1/images/demo1.gif -------------------------------------------------------------------------------- /images/demo2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grebtsew/Visualize-Realtime-Data-Stream-Chart-in-Flask/c9c5cee4d8f231d28744d1ae7de231e13981f4b1/images/demo2.gif -------------------------------------------------------------------------------- /images/flask_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grebtsew/Visualize-Realtime-Data-Stream-Chart-in-Flask/c9c5cee4d8f231d28744d1ae7de231e13981f4b1/images/flask_logo.png -------------------------------------------------------------------------------- /images/jquery.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grebtsew/Visualize-Realtime-Data-Stream-Chart-in-Flask/c9c5cee4d8f231d28744d1ae7de231e13981f4b1/images/jquery.jpg -------------------------------------------------------------------------------- /images/plotly.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grebtsew/Visualize-Realtime-Data-Stream-Chart-in-Flask/c9c5cee4d8f231d28744d1ae7de231e13981f4b1/images/plotly.png -------------------------------------------------------------------------------- /images/python.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grebtsew/Visualize-Realtime-Data-Stream-Chart-in-Flask/c9c5cee4d8f231d28744d1ae7de231e13981f4b1/images/python.png -------------------------------------------------------------------------------- /images/structure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grebtsew/Visualize-Realtime-Data-Stream-Chart-in-Flask/c9c5cee4d8f231d28744d1ae7de231e13981f4b1/images/structure.png -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | from flask_handler import start_flask_application, app 2 | import webbrowser 3 | import threading 4 | import functools 5 | from config_handler import ConfigHandler 6 | 7 | """ 8 | Start Program with this file by running "python3 start.py" 9 | """ 10 | 11 | [HOST, PORT] = ConfigHandler().get_all("Website") # pylint: disable=unbalanced-tuple-unpacking 12 | 13 | url = "http://"+HOST+":{0}".format(PORT) 14 | 15 | if __name__ == '__main__': 16 | threading.Timer(1, functools.partial( webbrowser.open, url )).start() 17 | start_flask_application() 18 | 19 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | Flask-SocketIO==4.2.1 2 | Flask==1.1.1 3 | yahoo-fin==0.8.2 4 | opencv-python>=3.4.1.15 5 | requests>=2.23.0 6 | ConfigParser>=4.0.2 -------------------------------------------------------------------------------- /scheduler.py: -------------------------------------------------------------------------------- 1 | # include new streams to start in this file 2 | 3 | from data_streams.samples import samplelist, random_nr_config, random_nr 4 | 5 | from socket_client import SocketClient 6 | from socket_server import SocketServer 7 | from image_server import ImageServer 8 | from data_stream import DataStream 9 | from http_client import HTTPClient 10 | from http_server import HTTPserver 11 | 12 | import threading 13 | 14 | """ 15 | Scheduler starts the SocketServer and local data streams when 16 | flask server is up and running. 17 | """ 18 | 19 | def scheduler(): 20 | """ 21 | Start all streams 22 | """ 23 | print("Flask up and running, now starting data streams...") 24 | 25 | # Start TCP socket Server 26 | SocketServer().start() 27 | print("SocketServer Started") 28 | ImageServer().start() 29 | print("ImageServer Started") 30 | # Start HTTP server 31 | HTTPserver().start() 32 | print("HTTPServer Started") 33 | # Can be a smart idea to start streams here! 34 | # Start some demo flows 35 | #demo() 36 | 37 | def demo(): 38 | 39 | # Start Example TCP socket client 40 | for message in samplelist: # see samplelist in /data_streams/samples.py 41 | SocketClient(message=message).start() 42 | 43 | # Start HTTP example client 44 | HTTPClient().start() 45 | 46 | # Start Example Random Number Stream 47 | DataStream(random_nr_config, random_nr).start() 48 | -------------------------------------------------------------------------------- /socket_client.py: -------------------------------------------------------------------------------- 1 | import socket 2 | import time 3 | from threading import Thread 4 | 5 | """ 6 | This simple socket client 7 | connects to our socket server and sends live stream numbers 8 | to be displayed in flask. 9 | """ 10 | 11 | class SocketClient(Thread): 12 | 13 | def __init__(self, message): 14 | super(SocketClient, self).__init__() 15 | self.message = message 16 | 17 | def run(self): 18 | from config_handler import ConfigHandler 19 | (HOST, PORT) = ConfigHandler().get_all("SocketServer") # pylint: disable=unbalanced-tuple-unpacking 20 | 21 | #HOST = '127.0.0.1' # Standard loopback interface address (localhost) 22 | #PORT = 65432 # can change this if you want 23 | 24 | # Create a TCP/IP socket 25 | sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 26 | 27 | # Bind the socket to the port 28 | server_address = (str(HOST), int(PORT)) 29 | 30 | sock.connect(server_address) 31 | 32 | while True: 33 | sock.sendall(str.encode(self.message)) 34 | time.sleep(2) 35 | -------------------------------------------------------------------------------- /socket_server.py: -------------------------------------------------------------------------------- 1 | import time 2 | import json 3 | from threading import Thread, Event 4 | import socket 5 | from data_stream import send_request 6 | 7 | """ 8 | SocketServer is a multithreaded socket server 9 | receiving n amount of connections and proxy the messages 10 | to flask 11 | """ 12 | 13 | class SocketServer(Thread): 14 | def __init__(self): 15 | super(SocketServer, self).__init__() 16 | 17 | def handle_connection(self, conn): 18 | with conn: 19 | while True: 20 | data = conn.recv(1024) 21 | if not data: 22 | break 23 | else: 24 | #print(data) 25 | try: 26 | 27 | data = json.loads(data) 28 | send_request(id = data["id"], data=data["value"], type =safe(data, "type"), active_points =safe(data, "active_points"), 29 | _label=safe(data, "label"), _legend=safe(data, "legend"), _width = safe(data, "width"), _height = safe(data, "height"), 30 | _name = safe(data, "name"), fill = safe(data, "fill"), backgroundColor = safe(data, "backgroundColor"), borderColor = safe(data, "borderColor")) 31 | except Exception as e: 32 | 33 | print(data) 34 | print(" WARNING: an error occured in socket_server: ", e) 35 | 36 | 37 | def run(self): 38 | from config_handler import ConfigHandler 39 | (HOST, PORT) = ConfigHandler().get_all("SocketServer") # pylint: disable=unbalanced-tuple-unpacking 40 | 41 | #HOST = '127.0.0.1' # Standard loopback interface address (localhost) 42 | #PORT = 65432 # Port to listen on (non-privileged ports are > 1023) 43 | 44 | with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: 45 | s.bind((str(HOST), int(PORT))) 46 | s.listen() 47 | try: 48 | while True: 49 | conn, addr = s.accept() 50 | print('Connected by', addr) 51 | Thread(target=self.handle_connection, args=(conn,)).start() 52 | except Exception as e: 53 | print(e) 54 | 55 | def safe(json, value): 56 | try: 57 | return json[value] 58 | except Exception: 59 | return 60 | -------------------------------------------------------------------------------- /static/css/style.css: -------------------------------------------------------------------------------- 1 | 2 | 3 | .collapsible { 4 | background-color: blue; 5 | color: white; 6 | cursor: pointer; 7 | padding: 18px; 8 | width: 100%; 9 | border: none; 10 | text-align: left; 11 | outline: none; 12 | font-size: 15px; 13 | } 14 | 15 | .active, .collapsible:hover { 16 | background-color: #555; 17 | } 18 | 19 | .content { 20 | padding: 0 18px; 21 | display: none; 22 | overflow: hidden; 23 | background-color: #f1f1f1; 24 | } 25 | 26 | .slidecontainer { 27 | width: 100%; 28 | background: blue; 29 | } 30 | 31 | .slider { 32 | -webkit-appearance: none; 33 | width: 200px; 34 | height: 25px; 35 | background: slategray; 36 | outline: none; 37 | opacity: 0.7; 38 | -webkit-transition: .2s; 39 | transition: opacity .2s; 40 | } 41 | 42 | .slider:hover { 43 | opacity: 1; 44 | } 45 | 46 | .slider::-webkit-slider-thumb { 47 | -webkit-appearance: none; 48 | appearance: none; 49 | width: 25px; 50 | height: 25px; 51 | background: blue; 52 | cursor: pointer; 53 | } 54 | 55 | .slider::-moz-range-thumb { 56 | width: 25px; 57 | height: 25px; 58 | background: blue; 59 | cursor: pointer; 60 | } 61 | -------------------------------------------------------------------------------- /templates/camera_sample.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Pi Video Surveillance 4 | 5 | 6 |

Pi Video Surveillance

7 | 8 | 9 | -------------------------------------------------------------------------------- /templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Creating Real-Time Charts with Flask 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 640 | 641 | 642 | 643 |
644 |
646 |
647 |
648 |

Realtime Data Stream Charts

649 |
650 |
651 | 652 | 653 | 654 | 656 |
657 |
658 |

Start/Stop by pressing "D"!

659 | 660 |
661 |
662 |
663 |
664 |

Demo Slider Speed

665 | 666 |
667 |
668 | 669 |
670 |
671 |
672 | 673 | 674 |
675 |
676 | 677 |
678 | 679 |
680 |
681 |
682 | 683 | 684 | 685 | 686 | 687 | 688 | 689 | 693 | 694 | 695 | 696 | 697 | -------------------------------------------------------------------------------- /templates/map_sample.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Add a raster tile source 6 | 7 | 8 | 9 | 13 | 14 | 15 |
16 | 46 | 47 | 48 | --------------------------------------------------------------------------------