Related
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider
import numpy as np
from PIL import Image
import requests
from io import BytesIO
img_src = 'https://unsplash.it/500/300'
response = requests.get(img_src)
img = Image.open(BytesIO(response.content))
img = np.asarray(img.convert('L'))
fig,ax = plt.subplots()
zlims = (np.percentile(img[:, :], 0), np.percentile(img[:, :], 100 - 0))
im = ax.imshow(img, aspect='auto',vmin=zlims[0], vmax=zlims[1])
threshold = 0.
axthreshold = plt.axes([0.2, 0.01, 0.65, 0.03])
sthreshold = Slider(axthreshold, 'clip', -.1, 15.,
valinit=threshold, valstep=None)
def update(val):
ax.clear()
zlims = (np.percentile(img[:, :], .1+val), np.percentile(img[:, :], 100 - (.1+val)))
print(zlims[0],zlims[1])
ax.imshow(img, aspect='auto',vmin=zlims[0], vmax=zlims[1])
fig.canvas.draw_idle()
sthreshold.on_changed(update)
update(threshold)
this creates a image where you can limit what range the colormap covers with a slider. As "clip" increases the color map variety lessens.
Im am pretty familiar with flask. Is there any way to implement something like this into flask? I would prefer matplotlib but I'm ok with trying anything.
Thanks!
--Update--- I realized that I don't want to refresh the page every time the picture is adjusted. I tried plotly javascript approach. I was happy until I realized the res is too low.
html:
<!doctype html>
<html class="no-js" lang="en">
<head>
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<script src="https://cdn.plot.ly/plotly-2.11.1.min.js"></script>
<style>
.container {
display: flex;
align-items: center;
justify-content: center;
}
</style>
</head>
<body>
<div class="container" id="myDiv"></div>
<div class="container">
<input style="width:50%" id="slide" type="range" min="0" max="5" step="0.025" value=".1" name="name_of_slider">
</div>
<div class="container" id="sliderAmount"></div>
<script>
const z1 = JSON.parse('{{ Lsort | tojson }}');
const IMG = JSON.parse('{{ IMG | tojson }}');
var size = z1.length
var qlower = z1[Math.ceil(size * .1 / 100) - 1]
var qupper = z1[Math.ceil(size * (100-.1) / 100) - 1]
var data = [
{
z: IMG,
type: 'heatmap',
colorscale: 'Viridis',
zauto: false,
zmax: qupper,
zmin: qlower,
}
];
var layout = {
autosize: false,
width: 1000,
height: 600,
};
Plotly.newPlot('myDiv', data, layout);
var slide = document.getElementById('slide'),
sliderDiv = document.getElementById("sliderAmount");
slide.onchange = function() {
sliderDiv.innerHTML = this.value;
var perc = this.value
var qlower = z1[Math.ceil(size * perc / 100) - 1]
var qupper = z1[Math.ceil(size * (100-perc) / 100) - 1]
var data = [
{
z: IMG,
type: 'heatmap',
colorscale: 'Viridis',
zauto: false,
zmax: qupper,
zmin: qlower,
}
];
var layout = {
autosize: false,
width: 1000,
height: 600,
};
Plotly.newPlot('myDiv', data, layout);
}
</script>
</body>
</html>
python:
from PIL import Image
import requests
from io import BytesIO
img_src = 'https://unsplash.it/500/300'
response = requests.get(img_src)
imgarray = Image.open(BytesIO(response.content))
imgarray = np.asarray(imgarray.convert('L'))
n = np.sort(imgarray.ravel()).tolist()
imgarray2D = np.flip(imgarray,axis=0)
imgarray2D = imgarray2D.tolist()
app = Flask(__name__)
#app.route('/', methods=['GET', 'POST'])
def main():
return render_template('clip.html',Lsort = n,IMG=imgarray2D)
if __name__ == '__main__':
app.run()
I am no expert in Flask or Javascript, but I did something (maybe clumsy!) similar to a Matplotlib slider using an HTML range slider.
So, you put one of those sliders (with a class="watch" because you're watching it) and an HTML img in your web-page's HTML so you will have at least a slider in the index.html:
<input name="XXX" id="YYY" style="width:100%" class="watch" type="range" step="1" min="0" max="255" value="0">
and also an img:
<img src="{{url_for('static', filename='default.png')}}" id="image" class="img-fluid">
Then attach an onChange() function to the slider like this so it gets called whenever the slider is moved:
$(document).ready(function() {
$(".watch").on( "change input", fChanged);
fChanged(); // do one initial update before user changes anything
});
You'll also need the jQuery CDN stuff. I used:
<!-- jQuery first, then Popper.js, then Bootstrap JS -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js" integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js" integrity="sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy" crossorigin="anonymous"></script>
And the function fChanged() gathers up the values of a number of sliders on the HTML page into a JSON to send to Flask endpoint called /_refresh like this:
{# https://flask.palletsprojects.com/en/2.0.x/patterns/jquery/ #}
$SCRIPT_ROOT = {{ request.script_root|tojson|safe }};
function fChanged(eObj) {
$.getJSON($SCRIPT_ROOT + '/_refresh', {
Hmin: $('#Hmin').val(),
Hmax: $('#Hmax').val(),
Smin: $('#Smin').val(),
Smax: $('#Smax').val(),
Vmin: $('#Vmin').val(),
Vmax: $('#Vmax').val(),
mask: jQuery("input[type='radio']").filter(":checked").attr("value")
}, function(data) {
$("#code").text(data.code);
$("#image").attr("src", "data:image/png;base64,"+data.image);
});
return false;
}
You can see I had 6 sliders, a min and a max for each of Hue, Saturation and Value.
The Flask endpoint returned another JSON containing some code I wanted to display on the webpage and a base64-encoded PNG-format result image that I then used to update an HTML img by writing its src attribute.
The Python code for the _refresh endpoint looks like this - it does some other stuff with Hue, Saturation and Value that isn't interesting to you, but it shows how I processed the image and encoded it for returning as a JSON to the webpage:
#app.route('/_refresh')
def refresh():
# DEBUG print(request.args)
Hmin = request.args.get('Hmin')
Hmax = request.args.get('Hmax')
Smin = request.args.get('Smin')
Smax = request.args.get('Smax')
Vmin = request.args.get('Vmin')
Vmax = request.args.get('Vmax')
mask = request.args.get('mask')
# Do the image processing
if 'localfilename' in session:
im, HSV = loadImage(session['localfilename'])
else:
im, HSV = loadImage('default')
lo = np.array([Hmin,Smin,Vmin], dtype=np.uint8)
hi = np.array([Hmax,Smax,Vmax], dtype=np.uint8)
mask = cv2.inRange(HSV, lo, hi)
alpha = np.zeros((im.shape[0],im.shape[1]), dtype=np.uint8) + mask
res = np.dstack((im, alpha))
# DEBUG
# cv2.imwrite('result.png', res)
_, PNG = cv2.imencode(".png", res)
b64img = encodebytes(PNG.tobytes()).decode('ascii')
response = {
'Status' : 'Success',
'image': b64img,
: 'code': render_template('code.py', Hmin=Hmin, Hmax=Hmax, Smin=Smin, Smax=Smax, Vmin=Vmin, Vmax=Vmax)
}
return jsonify(response) # send the result to client
You are not obliged to use OpenCV for the processing or base64-encoding, you can equally do all that with PIL/Pillow if you prefer - just ask, or look at any of my previous PIL/Pillow answers.
It might all be clumsy and misguided but it worked for me. I'll be glad too if someone else shows a better way!
By the way, if anyone is using macOS and trying to run the code in your question, the GUI with the image and slider pops up briefly and disappears. You can then spend ages reading about Matplotlib backends, how to list them, how to see what's available, how to change them, that you need a "framework" Python installation, or maybe don't and after around an hour, you discover that all you need is to add an extra line as follows:
...
ax.imshow(img, aspect='auto',vmin=zlims[0], vmax=zlims[1])
fig.canvas.draw_idle()
plt.show() # add this line if plot flashes and disappears
I am working on Yolov3, OpenCV,python and Flask. Here, I explain the details of usage for each item.
Yolov3 - To detect and recognize object in video input
Opencv - To capture images of detected object in video input.
Flask - As a web server since it's support python language.
My objective
To develop application which able to captured the image of the object and update in the flask web directly or real-time
For your information, currently my system able to capture image and save in one folder which name as images by using OpenCv and python. you can refer the code below.
opencv and python code to capture image of object detected
for i in range(len(boxes)):
if i in indexes:
x,y,w,h = boxes[i]
label = str(LABELS[class_ids[i]])
confidence= confidences[i]
color = colors[class_ids[i]]
crop_img = image[y:y + h, x:x + w]
imagesPath = "images/file_%d.jpg"%self.d
cv2.imwrite(imagesPath, crop_img)
self.d+=1
cv2.rectangle(image,(x,y),(x+w,y+h),color,2)
cv2.putText(image,label+" "+str(round(confidence,2)),(x,y+30),font,1,(255,255,255),2)
When the images successfully save in images folder. I convert all the images in to base64 and return as json in flask. Here I attach the python code in flask.
Covert image to base64 and render in html templates
#app.route("/images")
def get_images():
directory = os.listdir('C:/Users/HP/Miniconda3/envs/count_vechicle/coding/images')
os.chdir('C:/Users/HP/Miniconda3/envs/count_vechicle/coding/images')
image_list= list()
for file in directory:
data = dict()
base = os.path.basename(file)
data["label"] = base
open_file = open(file,'rb')
image_read = open_file.read()
image_64_encode = base64.encodebytes(image_read)
data["data"] = image_64_encode.decode('ascii')
image_list.append(data)
final_data = {'files':image_list}
return render_template('images.html', final_data=final_data)
images.html
<!DOCTYPE html>
<html>
<head>
<!DOCTYPE html>
<html lang="en">
<head>
<title>yolo</title>
</head>
<body>
<h1 class="logo">Results</h1>
<ul>
{% for data in final_data.files %}
<li>{{data.label}}</li>
<img alt="embedded" src="data:image/jpg;base64,{{data.data}}"/>
{% endfor %}
</ul>
</body>
</html>
Problem and question
I have two major problem here.
1) When i run my application, my application captured the images welly. But it's automatically stop when image.html page is open to display the picture. Why it's happen and how to solve this?
2) If the application able to capture image even i open the image.html page. What should i do to update my web directly or real-time?
Here I attach the full code. Because I have been works for many weeks but still not found the solution. Hope someone can helps. Let's me know if u need more information
code
App.py
from flask import Flask, render_template, Response,jsonify
from camera import VideoCamera
import numpy as np
import os
import time
import detect as dt
from PIL import Image
import cv2
import base64
import json
from pprint import pprint
app = Flask(__name__)
def success_handle(output, status=200, mimetype='application/json'):
return Response(output, status=status, mimetype=mimetype)
#app.route("/images")
def get_images():
directory = os.listdir('C:/Users/HP/Miniconda3/envs/count_vechicle/coding/images')
os.chdir('C:/Users/HP/Miniconda3/envs/count_vechicle/coding/images')
flist = list()
for file in directory:
data = dict()
base = os.path.basename(file)
data["label"] = base
open_file = open(file,'rb')
image_read = open_file.read()
image_64_encode = base64.encodebytes(image_read)
data["data"] = image_64_encode.decode('ascii')
flist.append(data)
final_data = {'files':flist}
return render_template('images.html', final_data=final_data)
#app.route('/')
def index():
return render_template('index.html')
def gen(camera):
frame_id = 0
while True:
frame = camera.get_frame()
yield (b'--frame\r\n'
b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n\r\n')
#app.route('/video_feed')
def video_feed():
return Response(gen(VideoCamera()),
mimetype='multipart/x-mixed-replace; boundary=frame')
if __name__ == '__main__':
app.run(host='0.0.0.0', debug=True)
camera.py
import cv2
import time
import os
import numpy as np
font = cv2.FONT_HERSHEY_PLAIN
starting_time= time.time()
frame_id = 0
count = 0
d = 0
labelsPath = os.path.sep.join(["yolo-coco", "coco.names"])
weightsPath = os.path.sep.join(["yolo-coco", "yolov3.weights"])
configPath = os.path.sep.join(["yolo-coco", "yolov3.cfg"])
#imagesPath = 'C:/Users/HP/Miniconda3/envs/count_vechicle/coding/images/file_%d.jpg"%d'
labelsPath = os.path.sep.join(["yolo-coco", "coco.names"])
VideoPath = os.path.sep.join(["videos", "highway.mp4"])
LABELS = open(labelsPath).read().strip().split("\n")
net = cv2.dnn.readNet(configPath, weightsPath)
layer_names = net.getLayerNames()
outputlayers = [layer_names[i[0] - 1] for i in net.getUnconnectedOutLayers()]
colors= np.random.uniform(0,255,size=(len(LABELS),3))
class VideoCamera(object):
def __init__(self):
# Using OpenCV to capture from device 0. If you have trouble capturing
# from a webcam, comment the line below out and use a video file
# instead.
self.video = cv2.VideoCapture(VideoPath)
self.frame_id = 0
self.d = 0
# If you decide to use video.mp4, you must have this file in the folder
# as the main.py.
# self.video = cv2.VideoCapture('video.mp4')
def __del__(self):
self.video.release()
def get_frame(self):
success, image = self.video.read()
# We are using Motion JPEG, but OpenCV defaults to capture raw images,
# so we must encode it into JPEG in order to correctly display the
# video stream.
self.frame_id+=1
#print(frame_id)
height,width,channels = image.shape
#print (frame.shape)
#detecting objects
blob = cv2.dnn.blobFromImage(image,0.00392,(320,320),(0,0,0),True,crop=False) #reduce 416 to 320
net.setInput(blob)
outs = net.forward(outputlayers)
#print(outs)
print(outs[1])
#Showing info on screen/ get confidence score of algorithm in detecting an object in blob
class_ids=[]
confidences=[]
boxes=[]
for out in outs:
#print(out)
for detection in out:
scores = detection[5:]
class_id = np.argmax(scores)
confidence = scores[class_id]
print(confidence)
if confidence > 0.8:
#object detected
center_x= int(detection[0]*width)
center_y= int(detection[1]*height)
w = int(detection[2]*width)
h = int(detection[3]*height)
#cv2.circle(img,(center_x,center_y),10,(0,255,0),2)
#rectangle co-ordinaters
x=int(center_x - w/2)
y=int(center_y - h/2)
#cv2.rectangle(img,(x,y),(x+w,y+h),(0,255,0),2)
boxes.append([x,y,w,h]) #put all rectangle areas
confidences.append(float(confidence)) #how confidence was that object detected and show that percentage
class_ids.append(class_id) #name of the object tha was detected
indexes = cv2.dnn.NMSBoxes(boxes,confidences,0.4,0.6)
# result = open('C:/Users/HP/Miniconda3/envs/count_vechicle/coding/images/frame%04d.txt'%(count), 'w')
for i in range(len(boxes)):
if i in indexes:
x,y,w,h = boxes[i]
label = str(LABELS[class_ids[i]])
# cv2.imwrite(label, crop_img)
confidence= confidences[i]
color = colors[class_ids[i]]
crop_img = image[y:y + h, x:x + w]
imagesPath = "images/file_%d.jpg"%self.d
cv2.imwrite(imagesPath, crop_img)
self.d+=1
cv2.rectangle(image,(x,y),(x+w,y+h),color,2)
cv2.putText(image,label+" "+str(round(confidence,2)),(x,y+30),font,1,(255,255,255),2)
elapsed_time = time.time() - starting_time
fps=frame_id/elapsed_time
cv2.putText(image,"FPS:"+str(round(fps,2)),(10,50),font,2,(0,0,0),1)
# cv2.imshow("Image",image)
# key = cv2.waitKey(1) #wait 1ms the loop will start again and we will process the next frame
# if key == 27: #esc key stops the process
# break;
ret, jpeg = cv2.imencode('.jpg', image)
return jpeg.tobytes()
index.html
<html>
<head>
<title>Object Detection</title>
</head>
<body>
<h1>Video Streaming Demonstration</h1>
<img src="{{ url_for('video_feed') }}">
</body>
</html>
images.html
<!DOCTYPE html>
<html>
<head>
<!DOCTYPE html>
<html lang="en">
<head>
<title>yolo</title>
</head>
<body>
<h1 class="logo">Results</h1>
<ul>
{% for data in final_data.files %}
<li>{{data.label}}</li>
<img alt="embedded" src="data:image/jpg;base64,{{data.data}}"/>
{% endfor %}
</ul>
</body>
</html>
I want to render json file to html script part. In the tornado part, No1 is sendering json data to html. And, No2 part is received the json data from No1. However this code is not working. I found that html script doesn't allow the form of {{ }}. How I send the json data to part of html ?
[ python - tornado part ]
import tornado.web
import tornado.httpserver
import tornado.ioloop
import os.path
from tornado.options import define, options
define("port", default=3000, help="run on the given port", type=int)
class Application(tornado.web.Application):
def __init__(self):
base_dir = os.path.dirname(__file__)
settings = {
}
tornado.web.Application.__init__(self, [
tornado.web.url(r"/", MainHandler, name="main"),
], **settings)
class MainHandler(tornado.web.RequestHandler):
def get(self):
data = {"name":"John Johnson","street":"Oslo West 16","phone":"555 1234567"}
self.render("index.html", data=data) #No1
def main():
tornado.options.parse_command_line()
Application().listen(options.port)
tornado.ioloop.IOLoop.instance().start()
if __name__ == "__main__":
main()
[ html part ]
<!DOCTYPE html>
<html>
<body>
<h2>JSON Object Creation in JavaScript</h2>
<p id="demo"></p>
<script>
/* var text = '{"name":"John Johnson","street":"Oslo West 16","phone":"555 1234567"}'; */
var text = '{{data}}'; /*No2*/
var obj = JSON.parse(text);
document.getElementById("demo").innerHTML =
obj.name + "<br>" +
obj.street + "<br>" +
obj.phone;
</script>
</body>
</html>
The {{ }} in your index.html template will be autoescaped for html. You want raw output because in this case you're outputting javascript rather than html. You also want to make sure you're actually letting python convert your data object to correctly formatted json.
Import the json library and add a call to json.dumps to get correctly formatted JSON:
import tornado.web
import tornado.httpserver
import tornado.ioloop
import os.path
import json
from tornado.options import define, options
define("port", default=3000, help="run on the given port", type=int)
class Application(tornado.web.Application):
def __init__(self):
base_dir = os.path.dirname(__file__)
settings = {
}
tornado.web.Application.__init__(self, [
tornado.web.url(r"/", MainHandler, name="main"),
], **settings)
class MainHandler(tornado.web.RequestHandler):
def get(self):
data = {"name":"John Johnson","street":"Oslo West 16","phone":"555 1234567"}
self.render("index.html", data=json.dumps(data))
def main():
tornado.options.parse_command_line()
Application().listen(options.port)
tornado.ioloop.IOLoop.instance().start()
if __name__ == "__main__":
main()
And use raw to prevent the html auto-escaping output in your template:
<!DOCTYPE html>
<html>
<body>
<h2>JSON Object Creation in JavaScript</h2>
<p id="demo"></p>
<script>
/* var text = '{"name":"John Johnson","street":"Oslo West 16","phone":"555 1234567"}'; */
var text = '{% raw data %}';
var obj = JSON.parse(text);
document.getElementById("demo").innerHTML =
obj.name + "<br>" +
obj.street + "<br>" +
obj.phone;
</script>
<body>
</body>
</html>
The key point is that string will be autoescaped by template engine. So we need to decode it before parsing it as a json.
{% raw %} directive mentioned by #clockwatcher is one solution. But it may cause error if the variable text is double-quoted since json.dumps(data) will use double quotation marks in its output.
Therefore, a possible better solution is to unescape text.
import tornado.web
import tornado.httpserver
import tornado.ioloop
import os.path
import json
from tornado.options import define, options
define("port", default=3000, help="run on the given port", type=int)
class Application(tornado.web.Application):
def __init__(self):
base_dir = os.path.dirname(__file__)
settings = {
}
tornado.web.Application.__init__(self, [
tornado.web.url(r"/", MainHandler, name="main"),
], **settings)
class MainHandler(tornado.web.RequestHandler):
def get(self):
data = {"name":"John Johnson","street":"Oslo West 16","phone":"555 1234567"}
self.render("index.html", data=json.dumps(data))
def main():
tornado.options.parse_command_line()
Application().listen(options.port)
tornado.ioloop.IOLoop.instance().start()
if __name__ == "__main__":
main()
<!DOCTYPE html>
<html>
<body>
<h2>JSON Object Creation in JavaScript</h2>
<p id="demo"></p>
<script>
String.prototype.unescapeHtml = function () {
var temp = document.createElement("div");
temp.innerHTML = this;
var result = temp.childNodes[0].nodeValue;
temp.removeChild(temp.firstChild);
return result;
}
var text = '{{data}}';
var obj = JSON.parse(text.unescapeHtml());
document.getElementById("demo").innerHTML =
obj.name + "<br>" +
obj.street + "<br>" +
obj.phone;
</script>
</body>
</html>
The snippet of function unescapeHTML comes from JavaScript: how to unescape HTML entities.
Is it possible to make reload the page when the data is updated?
import socket
import tornado.ioloop
import tornado.web
import tornado.gen
import tornado.ioloop
import tornado.iostream
import tornado.tcpserver
import os
import tornado.websocket
last_messages = dict()
class TcpClient(object):
client_id = 0
def __init__(self, stream):
super(TcpClient, self).__init__()
TcpClient.client_id += 1
self.id = 'Connection#%d' % TcpClient.client_id
self.stream = stream
self.stream.socket.setsockopt(
socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
self.stream.socket.setsockopt(
socket.IPPROTO_TCP, socket.SO_KEEPALIVE, 1)
self.stream.set_close_callback(self.on_disconnect)
#tornado.gen.coroutine
def on_disconnect(self):
self.log("disconnected")
yield []
#tornado.gen.coroutine
def dispatch_client(self):
try:
while True:
line = yield self.stream.read_until(b'\n')
text_line = line.decode('utf-8').strip()
last_messages[self.id] = text_line
# UPDATE GUI, webpage HERE
self.log('got |%s|' % text_line)
except tornado.iostream.StreamClosedError:
pass
#tornado.gen.coroutine
def on_connect(self):
raddr = 'closed'
try:
raddr = '%s:%d' % self.stream.socket.getpeername()
except Exception:
pass
self.log('new, %s' % raddr)
yield self.dispatch_client()
def log(self, msg, *args, **kwargs):
print('[%s] %s' % (self.id, msg.format(*args, **kwargs)))
class TcpServer(tornado.tcpserver.TCPServer):
#tornado.gen.coroutine
def handle_stream(self, stream, address):
connection = TcpClient(stream)
yield connection.on_connect()
class LastMessagesHandler(tornado.web.RequestHandler):
#tornado.web.asynchronous
def get(self):
self.render(
'template.html',
sources=last_messages
)
class WebSocketHandler(tornado.websocket.WebSocketHandler):
def on_message(self, message):
print message
self.write(last_messages)
def main():
tcp = TcpServer()
tcp.listen(8008)
print('tcp://localhost:8008')
settings = dict(
template_path=os.path.join(os.path.dirname(__file__), 'templates'),
static_path=os.path.join(os.path.dirname(__file__), 'static'),
debug=True,
)
app = tornado.web.Application(
[
(r'/', LastMessagesHandler),
(r'/ws', WebSocketHandler)
],
**settings
)
app.listen(8009)
print('http://localhost:8009')
tornado.ioloop.IOLoop.instance().start()
if __name__ == "__main__":
main()
Here is the template.html
<html>
<head>
<title>TCP server</title>
</head>
<body>
<title>Tornado WebSockets</title>
<script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
<script>
jQuery(function($){
if (!("WebSocket" in window)) {
alert("Your browser does not support web sockets");
}else{
setup();
}
function setup() {
var host = "ws://localhost:8008/ws";
var socket = new WebSocket(host);
// event handlers for websocket
if(socket){
socket.onopen = function(){
//alert("connection opened....");
}
socket.onmessage = function(msg){
showServerResponse(msg.data);
}
/*socket.onclose = function(){
//alert("connection closed....");
showServerResponse("The connection has been closed.");
}*/
}else{
console.log("invalid socket");
}
function showServerResponse(txt){
var p = document.createElement('p');
p.innerHTML = txt;
document.getElementById('output').appendChild(p);
}
}
});
</script>
<div>
<ul id="output">
{% for key, value in sources.iteritems() %}
<ul> {{key}} | {{value}} </ul>
{% end %}
</ul>
</div>
</body>
</html>
I need to update Client(webpage) whenever last_messages is changed (smth is removed form the dict, smth has been added to the dict). I can't do this using tornado.websocket. Could you please help? I need to update gui after reading line from the socket (you can find the comment in the code where i need to update the webpage: # UPDATE GUI, webpage HERE). So the socket can be still opened (we are continuing reading lines), but the gui must be updated. Is it possible to do it?
If you really want to reload the page then in your JavaScript you can initiate a call to window.location.reload().
However, this will close the WebSocket connection from the client side.
If you just wish to update the DOM (GUI), then listen to the incoming message on the client and update accordingly.
To observe and track data changes you can do it yourself in JavaScript, or with Object.observe if you're running only on modern Chrome, or with the polyfill, or use an existing tool, like Backbone.js.
Should you choose the latter, I can also recommend a tool to help you with working with WebSockets and Backbone.
Disclaimer: I'm the author of Backbone.WS.
I am new to Flask and Javascript. I am trying to upload a file and use one of its columns as options in the drop down menu. Please correct me where I am wrong.
Here are the codes:
Flask:
from flask import Flask, render_template, redirect, url_for, request, flash, send_from_directory
from werkzeug import secure_filename
import os
import pandas as pd
import numpy as np
import json
UPLOAD_FOLDER = 'uploads/'
ALLOWED_EXTENSIONS = set(['csv'])
app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
def allowed_file(filename):
return '.' in filename and filename.rsplit('.', 1)[1] in ALLOWED_EXTENSIONS
#app.route('/', methods=['GET', 'POST'])
def upload_file():
if request.method == 'POST':
file = request.files['data_file']
if file and allowed_file(file.filename):
filename = secure_filename(file.filename)
file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
data = pd.read_csv(os.path.join(app.config['UPLOAD_FOLDER'], filename))
names = data['some_column']
return redirect(url_for('drop_down', names=names))
#return render_template('drop_down.html', names=names)
return render_template('file_upload.html')
#app.route('/meta')
def drop_down():
return render_template('drop_down.html')
Javascript:
function my_script(){
console.log('script called.');
//var cars = ["Volvo","Ferrari","Audi","BMW","Mercedes","Porche","Saab","Avanti"];
var cars = {{ names|safe }};
console.log('cars assigned.');
function make_drop_down(list_of_names, element_id){
select_elem = document.getElementById(element_id)
if(select_elem){
for(var i = 0; i < list_of_names.length; i++) {
var option = document.createElement('option');
option.innerHTML = list_of_names[i];
option.value = list_of_names[i];
select_elem.appendChild(option);
}
}
};
console.log("Making Drop Downs!!");
make_drop_down(cars, 'drop_down_1');
make_drop_down(cars, 'drop_down_2');
console.log("Made Drop Downs!!");
};
html:
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="static/drop_down.js"></script>
<title>DROP DOWN</title>
</head>
<body onload="my_script()">
<select id="drop_down_1">
<option>Choose Option 1</option>
</select>
</br></br>
<select id="drop_down_2">
<option>Choose Option 2</option>
</select>
</body>
</html>
I get the following error on the console:
ReferenceError: my_script is not defined
There are two problems.
The first one is that you are not passing the list of cars in your view handeling /meta
#app.route('/meta')
def drop_down():
return render_template('drop_down.html')
This should probably look something like this:
#app.route('/meta')
def drop_down():
cars = ["Volvo","Ferrari","Audi","BMW","Mercedes","Porche","Saab","Avanti"]
return render_template('drop_down.html',
names=cars)
The second problem is that your javascript won't be able to access the list, unless you pass it in your call to the function.
html
<body onload="my_script({{ names }})">
javascript
function my_script(names){
console.log('script called.');
var cars = names;
...
Edit:
The function that handles the view is the function that needs to pass the data. You could also use the commented away part of your upload file, which calls render_template... with the necessary data, but this doesn't feel as a "nice" approach.
You need to make the data available to your drop_down() view, either by storing it in a database, reading the data from the file in this function or by storing it in the session. So that you can pass the data along with the template