Flask: How to print variable to web page using ajax

Question:

I have the following code:

import cv2
import numpy as np
import time
import datetime
from flask import *
import random

from threading import Thread


app = Flask(__name__)


def Tracking():
    lower = np.array([35, 192, 65])
    upper = np.array([179, 255, 255])

    video = cv2.VideoCapture(1, 0)

    times = []
    total = 0
    is_round = False

    while True:
        success, img = video.read()
        image = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
        mask = cv2.inRange(image, lower, upper)
        blur = cv2.GaussianBlur(mask, (15, 15), 0)

        circles = cv2.HoughCircles(blur, cv2.HOUGH_GRADIENT, 1, 14,
                                   param1=34, param2=10, minRadius=4, maxRadius=10)

        circles = np.uint16(np.around(circles))

        if (len(circles[0, :]) == 7) and not is_round:
            start_time = time.time()
            is_round = True
            curr_count = 0
            round_total = 0

        elif is_round:
            if len(circles[0, :]) == 1:
                end_time = time.time()
                is_round = False
                time_taken = end_time - start_time
                print('time: ', str(
                    datetime.timedelta(seconds=time_taken))[2:7])

                times.append(time_taken)
                average = sum(times) / len(times)
                print('Avg time: ', str(
                    datetime.timedelta(seconds=average))[2:7])

            elif len(circles[0, :]) < 7:
                curr_count = (7 - round_total) - len(circles[0, :])
                total += curr_count
                round_total += curr_count

            for i in circles[0, :]:
                cv2.circle(img, (i[0], i[1]), i[2], (0, 255, 0), 2)
                cv2.circle(img, (i[0], i[1]), 2, (0, 0, 255), 3)

        yield total  


@app.route("/")
def home():
    return render_template('theme1.html')


gen_total = Tracking()  # initate the function out of the scope of update route


@app.get("/update")
def update():
    global gen_total  

    return str(next(gen_total))


if __name__ == "__main__":
    app.run(debug=True)

HTML

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Document</title>
    <link rel= "stylesheet"  href= "{{ url_for('static', filename='styles.css')}}">

</head>
<body>

    <div class="data">
    <p id="output"></p>
    </div>

    <script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
    <script>
        function update(){
            $.get("/update", function(data){
                $("#output").html(data)
            });
        }
        update()
        var intervalId = setInterval(function() {
            update()
        }, 10000);

    </script>
        
</body>
</html>

I am trying to get the variable ‘average’ to print to the webpage, i have already added ‘total’ to the webpage but i am not sure how to add average, this variable will not always have a value as a certain thing has to happen before it is given a value, any help would be appriciated. I am also not sure if this has to be converted to a string to be displayed, ajax is used in the html file to update the page every so often.

Asked By: Ethan0619

||

Answers:

To yield multiple values from your generator, either use a tuple, list, class, dictionary or other data structure. I’d suggest a dict which is simple but has nice naming semantics. yield dict(total=total, average=average).

Since average is inside of a conditional branch, it’s possible that yield is reached without actually initializing this variable, which is illegal. Set a default value that’s unconditionally within scope of the yield statement so it’s guaranteed to have a value.

Finally, return it as JSON to the client rather than text using Flask’s jsonify.

Here’s a simplified example you can adapt:

app.py

from flask import Flask, jsonify, render_template


app = Flask(__name__)


def track():
    total = 0
    average = 0

    while True:
        if 1 == 1:
            average += 2

        total += 1
        yield dict(total=total, average=average)


@app.route("/")
def home():
    return render_template("theme1.html")


@app.get("/update")
def update():
    return jsonify(next(gen_total))


if __name__ == "__main__":
    gen_total = track()
    app.run(debug=True)

templates/theme1.html

<!DOCTYPE html>
<html lang="en">
<head>
  <title>Document</title>
  <script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
</head>
<body>
  <div class="data">
    <p id="average"></p>
    <p id="total"></p>
  </div>
  <script>
function update() {
  $.get("/update", function (data) {
    $("#average").text("average " + data.average);
    $("#total").text("total " + data.total);
  });
}
update();
var intervalId = setInterval(update, 10000);
  </script>
</body>
</html>
Answered By: ggorlen
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.