Embedding multiple pieces of JavaScript via a Tornado UIModule - javascript

I'm working on a Python package that uses Tornado to send data to the browser for visualization. In order to do this, I want the users to be able to write multiple arbitrary modules for the server to render together on a single page -- including each module's own JavaScript.
However, by default, the Tornado's UIModule class's embedded_javascript() method only appends JavaScript to <script>...</script> once per module class. I'm hoping there is a simple way to embed multiple pieces of JS, one for every UIModule (or another way to get the same effect).
Here's a minimal example of what I'm talking about:
import tornado.ioloop
import tornado.web
import tornado.template
class Element(tornado.web.UIModule):
'''
Module to add some custom JavaScript to the page.
'''
def render(self, element):
self.js_code = element.js_code
return ""
def embedded_javascript(self):
return self.js_code
class InterfaceElement(object):
'''
Object to store some custom JavaScript code.
'''
def __init__(self, js_code):
'''
Args:
js_code: Some JavaScript code in string form to add to the page.
'''
self.js_code = js_code
class MainPageHandler(tornado.web.RequestHandler):
def get(self):
elements = self.application.modules
self.render("uitest_template.html", app_name="Testing", elements=elements)
class ThisApp(tornado.web.Application):
def __init__(self, modules):
self.modules = modules
main_handler = (r'/', MainPageHandler)
#settings = {"ui_modules": {"Element": Element}}
settings = {"ui_modules": {"Element": Element},
"template_path": "ui_templates"}
super().__init__([main_handler], **settings)
# Create two objects with some custom JavaScript to render
module_1 = InterfaceElement("var a = 1;")
module_2 = InterfaceElement("var b = 2;")
app = ThisApp([module_1, module_2])
app.listen(8888)
tornado.ioloop.IOLoop.instance().start()
And the template for uitest_template.html is just
<!DOCTYPE html>
<head>
<title> Hello World </title>
</head>
<body>
{% for element in elements %}
{%module Element(element) %}
{% end %}
</body>
The rendered page then includes a <script> tag in body that is:
<script type="text/javascript">
//<![CDATA[
var b = 2;
//]]>
</script>
And what I want is:
<script type="text/javascript">
//<![CDATA[
var a = 1;
var b = 2;
//]]>
</script>
Or something like it. Any ideas?
Added - my solution
Based on the answer below, here's how I ended up handling it:
import tornado.ioloop
import tornado.web
import tornado.template
class InterfaceElement(object):
include_js = [] # List of .js files to include
js_code = '' # JavaScript string to include
def __init__(self, include_js=[], js_code=''):
self.include_js = include_js
self.js_code = js_code
class MainPageHandler(tornado.web.RequestHandler):
def get(self):
self.render("modular_template.html",
includes=self.application.include_js,
scripts=self.application.js_code)
class ThisApp(tornado.web.Application):
def __init__(self, modules):
# Extract the relevant info from modules:
self.modules = modules
self.include_js = set()
self.js_code = []
for module in self.modules:
for include_file in module.include_js:
self.include_js.add(include_file)
if module.js_code != '':
self.js_code.append(module.js_code)
main_handler = (r'/', MainPageHandler)
settings = {"template_path": "ui_templates",
"static_path": "ui_templates"}
super().__init__([main_handler], **settings)
module_1 = InterfaceElement(js_code="var a = 1;")
module_2 = InterfaceElement(include_js=["test.js"], js_code="var b = 1;")
app = ThisApp([module_1, module_2])
app.listen(8888)
tornado.ioloop.IOLoop.instance().start()
Which goes with the following template:
<!DOCTYPE html>
<head>
<title> Hello world </title>
</head>
<body>
<!-- Script includes go here -->
{% for file_name in includes %}
<script src="/static/{{ file_name }}" type="text/javascript"></script>
{% end %}
<script type="text/javascript">
// Actual script snippets go here.
{% for script in scripts %}
{% raw script %}
{% end %}
</script>
</body>

embedded_javascript and related methods are (effectively) class-level methods; they must return the same value for any instance of the class. (They're intended to be a kind of dependency-management system, so you can load a piece of javascript only on pages that include a module that needs it)
The only thing that is allowed to vary per instance is the output of render(), so to embed multiple pieces of javascript you should include the script tag in the result of your render() method.

Related

Displaying D3.js Chart with Django Backend

I am learning to build dashboard using Django as backend and D3.js for visualization.
Following is my index.html:
{% load static %}
<html>
<script src="https://d3js.org/d3.v7.min.js"></script>
<body>
<h1> Hello! </h1>
<script src={% static "js\linechart.js" %}>
var data = {{ AAPL|safe }};
var chart = LineChart(data, {
x: d => d.date,
y: d => d.close,
yLabel: "↑ Daily close ($)",
width,
height: 500,
color: "steelblue"
})
</script>
</body>
</html>
Data AAPl is extracted from database and the views.py is as follows:
from django.shortcuts import render
from django.http import HttpResponse
from cnxn import mysql_access
import pandas as pd
# Create your views here.
def homepage(request):
sql = ''' select Date, Close from tbl_historical_prices where ticker = 'AAPL' '''
cnxn = mysql_access()
conn = cnxn.connect()
df = pd.read_sql(sql, con=conn)
context = {'AAPL':df.to_json()}
return render(request, 'index.html', context=context)
Function line chart can be viewed here which is being used in js\linechat.js in index.html file.
I can see the Hello! being displayed on the page but can't see the line chart. I am unable to debug the problem. No errors found in console tab either.
How can I display the line plot?
I've added the current page display in attached image.
Close off your script with a src and start a new script tag. The presence of src precludes internal code.
<script src={% static "js\linechart.js" %}></script>
<script>
...

update the return data automatically in flask templates

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>

Django template Javascript passing a python variable to a javascript one

I did create a python function called generate_random_number that generate a random number between 1 and 9 and compare it to the id of each video in my database and return the url of the video corresponding to the id matching the random number. Here is the function:
from random import randint
from dash_interface.models import Video
from django import template
register = template.Library()
#register.simple_tag
def random_video():
random_number = randint(1, 9)
all_videos = Video.objects.all()
for video in all_videos:
if video.id == random_number:
random_url = video.video_url
return random_url
I want to pass random_url to a javascript variable in a django template.
My template looks like this:
<video id="videoplayer" controls></video>
<script>
{% load generate_random_number %}
// setup the video element and attach it to the Dash player
function setupVideo() {
var url = " ";
var context = new Dash.di.DashContext();
var player = new MediaPlayer(context);
player.startup();
player.attachView(document.querySelector("#videoplayer"));
player.attachSource(url);
}
</script>
<style>
video {
width: 60%;
height: 40%;
}
</style>
The variable concerned is url.
I did a {% load generate_random_number %}, but I don't know if I can replace the quotation marks in url by {{ random_url }}.
If you want to split the javascript code and html code, you could make something like:
<script arg = {{ somevar }} src = "/path/to/script"></script>
and in the script :
var arg = $("script[src='/path/to/script']").attr("arg");
If you need that the arg will be updated each a concrete period, you need to make an ajax. If you use the database for persist the arg, you could use Django Channels like a more elegance solution.
You should keep the custom template tag definitions in a templatetags folder inside your app. Make sure that __init__.py is present in the templatetags folder. Your folder structure should be similar to:
your_django_app/
__init__.py
models.py
templatetags/
__init__.py
template_tags.py
views.py
And the contents of template_tags.py should be you required tag definition:
from random import randint
from dash_interface.models import Video
from django import template
register = template.Library()
#register.simple_tag
def random_video():
random_number = randint(1, 9)
all_videos = Video.objects.all()
for video in all_videos:
if video.id == random_number:
random_url = video.video_url
return random_url
Note: The templatetags file should contain the variable named register.
After this you can load the template tags in your HTML. Your code will look like:
<video id="videoplayer" controls></video>
{% load template_tags %}
<script>
// setup the video element and attach it to the Dash player
function setupVideo() {
var url = "{% random_video %}";
var context = new Dash.di.DashContext();
var player = new MediaPlayer(context);
player.startup();
player.attachView(document.querySelector("#videoplayer"));
player.attachSource(url);
}
</script>
<style>
video {
width: 60%;
height: 40%;
}
</style>
This is the way I prefer to connect Django templates and JS.
You need to import render function:
views.py file
from django.shortcuts import render
from random import randint
from dash_interface.models import Video
def random_video(request):
random_number = randint(1, 9)
all_videos = Video.objects.all()
for video in all_videos:
if video.id == random_number:
random_url = video.video_url
context = {
"random_url": random_url
}
return render(request, "app/video.html", context)
else:
return render(request, "app/video.html", {})
video.html
<video id="videoplayer" controls></video>
<script>
{% load generate_random_number %}
// setup the video element and attach it to the Dash player
function setupVideo() {
// using the context passed in here.
var url = {{ random_url|safe }};
var context = new Dash.di.DashContext();
var player = new MediaPlayer(context);
player.startup();
player.attachView(document.querySelector("#videoplayer"));
player.attachSource(url);
}
</script>
<style>
video {
width: 60%;
height: 40%;
}
</style>
What I have done is rendered your HTML page in the Django view and passed your variable inside the context dictionary which is later accessible in your HTML tags or script tags.
I also used a |safe parameter in the template tag which removes some unwanted strings.
Cheers and Hope it helps! I have tried this thing in many of my projects and it has always been a success for me.

Django & jQuery - How to use the TagSelectMultiple widget

I am trying to use the jquery-tagselector widget from stackoverflow's user Julio César with Django, and I am quite lost...
My form is:
class CreationForm(forms.Form):
tags = forms.ModelMultipleChoiceField(queryset=Tag.objects.all(),widget=TagSelectMultiple(attrs={ 'style': 'width:400px', 'id': 'tags' }))
The TagSelectMultiple widget has the following code:
from django import forms
from django.forms.util import flatatt
from django.utils.encoding import force_unicode
from django.utils.html import escape, conditional_escape
from django.utils.safestring import mark_safe
class TagSelectMultiple(forms.SelectMultiple):
def render(self, name, value, attrs=None, choices=()):
if value is None: value = []
final_attrs = self.build_attrs(attrs, name=name)
self.name = name
output = [u'<div class="tagSelector" multiple="multiple"%s>' % flatatt(final_attrs)]
options = self.render_options(choices, value)
if options:
output.append(options)
output.append('<input type=text>')
output.append('</div>')
return mark_safe(u'\n'.join(output))
def render_option(self, selected_choices, option_value, option_label):
if unicode(option_value) in selected_choices:
return u'<span class="tag">%s <a>x</a><input name="%s" type="hidden" value="%s"/></span>' % (
conditional_escape(force_unicode(option_label)), escape(self.name), escape(option_value))
return ''
And my Tag model is:
class Tag(models.Model):
word = models.CharField(max_length=30)
def __unicode__(self):
return u"%s" % self.word
In my template, I have included all the needed javascript files:
<link href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.11/themes/base/jquery-ui.css" rel=stylesheet type="text/css">
<link href='{% static "jquery-tagselector.css" %}' rel=stylesheet>
<script src="//ajax.googleapis.com/ajax/libs/jqueryui/1.10.4/jquery-ui.min.js"></script>
<script type='text/javascript' src='{% static "jquery-tagselector.js" %}'></script>
And I print my field with {{form.tags}}.
Following Julio César instructions on his webpage about how to use his Django Form Widget, I only had to modify the widget attributes, to get a good input id name:
Before my modification:
tags = forms.ModelMultipleChoiceField(queryset=Tag.objects.all(),
widget=TagSelectMultiple(attrs={ 'style': 'width:300px' }))
After my modification:
tags = forms.ModelMultipleChoiceField(queryset=Tag.objects.all(),
widget=TagSelectMultiple(attrs={ 'style': 'width:400px', 'id': 'tags' }))
First question: I don't know if I need to add the following script because I already gave my list of tags in the queryset attributes form field?
$('#tags').tagSelector(tags, 'tags');
Second question: When typing in my autocomplete box, I get absolutely no choices despite of having plenty Tag objects. Is it because of my tag model? Julio César don't explain the architecture of his Tag model, and I have some difficulties to guess this one. Or maybe it is because there is no self.choices in the widget code?
Link:
javascript jquery-tagselector.js file

evaluateJavaScript() only works once

I have the following code:
HTML + JavaScript
<style type="text/css">
.color1{
color: #3D8BD0;
}
</style>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<body>
<div id="mensagem">Mensage</div>
<script>
$("#mensagem").click(function( event ) {
printer.text('Hello');
});
</script>
</body>
And the .py file:
# -*- coding:utf-8 -*-
import sys
from PySide.QtCore import Slot, QObject, QUrl
from PySide.QtGui import QApplication
from PySide.QtWebKit import QWebView
from threading import Timer
class Dac(QObject):
def __init__(self, parent=None):
super(Dac, self).__init__(parent)
self.defultMsg = "default"
self.app = QApplication(sys.argv)
self.view = QWebView()
self.view.resize(445, 791)
self.view.load(QUrl("./page.html"))
self.frame = self.view.page().mainFrame()
self.frame.addToJavaScriptWindowObject('printer', self)
self.view.show()
self.app.exec_()
sys.exit(self.app.exec_())
def changeText(self, mensagem):
print mensagem
self.frame.evaluateJavaScript(mensagem)
#Slot(str)
def text(self, message):
print message
strm = '$("#mensagem").html("'+message+'").addClass("color1");'
self.changeText(strm) #this change the text
t = Timer(6.5, self.timeoff)
t.start()
def timeoff(self):
strm = '$("#mensagem").html("'+self.defultMsg+'").removeClass("color1");'
self.changeText(strm) #this don't change
print "debug"
dac = Dac()
When I run the Python file, the window opens and shows me the HTML page, then when I click in the div "mensagem" the text just changes one time.
So I think my problem is that the statement self.frame.evaluateJavaScript(mensagem) only works on the first time.
Is there anyone else with the same problem?
The callback you provide to threading.Timer will not be executed by the main thread. Since QWebView inherits from QWidget, you have to interact with it from the main thread:
Although QObject is reentrant, the GUI classes, notably QWidget and all its subclasses, are not reentrant. They can only be used from the main thread.
You can fix this by using PySide.QtCore.QTimer and replacing this code:
t = Timer(6.5, self.timeoff)
t.start()
With:
QTimer.singleShot(6.5 * 1000, self.timeoff)
QTimer works with Qt's event loop and therefore stays in the same thread.

Categories

Resources