Mike Sandstrom
Mike Sandstrom

Reputation: 73

How to Display Image in Flask after a button is pressed

I am new to flask and web development. I want to display an image on a local web server after I press a button on the webpage. I am using Flask.Ive been trying to figure this out for a while and havent been able too so any help would be incredible. FLASK CODE:

@app.route('/graph_select')
def graph_select():
    return render_template('live_stream.html')


@app.route('/read_ph', methods=["GET"])
def ph_plot():
    if request.method == "GET":
        all_plots.ph_plot()
        return render_template('live_stream.html')


@app.route("/read_temp", methods=["GET"])
def temp_plot():
    if request.method == "GET":
        all_plots.temperature_plot()
        return render_template('live_stream.html')


@app.route('/read_distance', methods=["GET"])
def distance_plot():
    if request.method == "GET":
        all_plots.distance_plot()
        return render_template('live_stream.html')

HTML CODE:

    <h1>Data Monitoring Station</h1>

      <form method="GET" 
        <a href="read_temp"><button type="button">Temperature Graph</button></a>
        <a href="read_ph"><button type="button">PH Graph</button></a>
        <a href="read_distance"><button type="button">Distance Graph</button></a>
      </form>

    <h3><img src="{{ url_for('static', filename='ph_plot.png') }}" width="30%">$
    <h3><img src="{{ url_for('static', filename='temperature_plot.png') }}" width="30%">$
    <h3><img src="{{ url_for('static', filename='distance_plot.png') }}" width="30%">$




  </body>
</html>

Upvotes: 3

Views: 6105

Answers (2)

James_SO
James_SO

Reputation: 1387

Here's a more complete, end to end solution.

Let's say you want to do a reactive "Check" or "Like", where you click a Check/Thumbs up icon, and you want it to change its appearance immediately in some way (in this case by changing the img src) AND persist that click with the record. (this is obviously required if you want to retain whatever state the click set, so that it will be reflected upon subsequent viewing)

In our use case we're going to indicate if a record has been reviewed. If not, show an empty circle image, if so, show a circle image with a green checkmark in the middle. We'll use Mongo for our backend.

Jinja Template Fragment

<dt class="col-sm-4">Reviewed</dt>
<dd class="col-sm-7"><img src="{{ initialImgSrc }}" id="circle_img" alt="{{ dnote.id}}"/></dd>

Notes:

  • a bit of a hack here, but we're going to put the Mongo id (dnote.id) in the img as alt text so we can access it easily in our JavaScript function in order to pass it to the Flask function
  • the Flask route function that renders this template initially has figured out the initialImgSrc based on the Reviewed status in the Mongo doc

JavaScript

<script src="https://code.jquery.com/jquery-3.3.1.min.js" crossorigin="anonymous"></script>

<script type="text/javascript">
    var thisImg = document.getElementById("circle_img");
    $(document).ready(function () {
        $('#circle_img').bind('click', function () {
            $.getJSON('/review/circler', {
                currentCircle: thisImg.getAttribute('src'),
                dNoteID: thisImg.getAttribute('alt'),
            }).done(function (data) {
                thisImg.src=data.imgSrcHTML;
            })
            return false;
        });

    });
</script>

Notes:

  • we're going to use the img src attribute as the basis of our check in the Flask function
  • as mentioned, since our Flask function will need to work with the Mongo record, we need to pass the id in - this is why I set it as img alt in Jinja (admittedly a hacky approach - some better ideas are here: Unable to pass jinja2 variables into javascript snippet)
  • one example of how to do this properly - add a meta element like so: <meta id="dNoteIDforJS" data-name="{{ dnote.id }}"> then refer to that in JS with $('#dNoteIDforJS').data("name")
  • the nice thing about using getJSON here is that it gives us a lot of flexibility in what we can pass to the Flask function and in what we can get back
  • as indicated by getJSON('/review/circler, we're using a blueprint with a url prefix of review and our function is called circler

Flask Function

@review_blueprint.route("/circler")
def circler():
    currentCircle = request.args.get('currentCircle', type=str)
    dNoteID = request.args.get('dNoteID', type=str)
    thisDNote = Mongo_DNotes.objects.get(id=dNoteID)
    eq = thisDNote.DNoteEngagementsQty
    try:
        engagementsQty = int(eq)
    except:
        engagementsQty = 0
    # Since we clicked, we can advance now
    engagementsQty += 1
    imgSrcHTML = "/static/circles/{}/circle_checked.png".format(engagementsQty)
    thisDNote.DNoteEngagementsQty = engagementsQty
    thisDNote.save()
    return jsonify(imgSrcHTML=imgSrcHTML, currentCircle=currentCircle)

Notes:

  • as you can infer by the static directory structure, I'm actually using a set of images each of which represents a given number of Reviews (we could use this same approach for a segmented progress bar for a long running, multi-step review process - like a new account approval process, for example)
  • in general, your Flask target function for a JavaScript getJSON is going to have to return something like jsonify(imgSrcHTML=imgSrcHTML, currentCircle=currentCircle) if that getJSON has references to data.imgSrcHTML

One more thing

If you want to make sure your image exists and you're using blueprints, you can check it with: os.path.exists(os.path.join(review_blueprint.static_folder, "circles/{}/circle_checked.png".format(engagementsQty))). In my use case, this lets you handle when the number of reviews have maxed out. I believe you also need to explicitly set the static_folder argument in Blueprint() as well. (I like blueprints, but they do present you with the occasional gotcha)

CAVEAT From the Flask docs:

fetch() is the modern, built-in JavaScript solution to making requests from a page. You may have heard of other “AJAX” methods and libraries, such as XMLHttpRequest() or jQuery. These are no longer needed in modern browsers, although you may choose to use them or another library depending on your application’s requirements.

I have subsequently used fetch() and it is indeed the modern way to go - in particular for invoking async functions and using toasts to communicate results to users.

https://flask.palletsprojects.com/en/2.3.x/patterns/javascript/

Upvotes: 0

Harshal Parekh
Harshal Parekh

Reputation: 6017

TLDR;

I wrote a minimal example on displaying images on button click using Flask and Ajax.

In essence, I just returned the URL of the image to the HTML page and set the src attribute of <img> tag with the returned URL.

app.py:

from flask import Flask, render_template

app = Flask(__name__)


@app.route("/")
def hello():
    return render_template('a.html')

@app.route("/getimage")
def get_img():
    return "a.jpg"


if __name__ == '__main__':
    app.run()

a.html:

<html>
  <head>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
  </head>
   <body>
     <button type='button' id ='retrieve'>Submit</button>
     <img src="" id="myimg" />
   </body>
  <script>
    $(document).ready(function() {
       $('#retrieve').click(function(){
           $.ajax({
           url: "{{ url_for ('get_img') }}",
           type: "GET",
           success: function(response) {
               $("#myimg").attr('src', '/static/' + response);
          },
          error: function(xhr) {
            //Do Something to handle error
         }
         });
       });
    });
  </script>
</html>

You can modify this code as you wish.

Note: The a.html file should be in templates folder and the a.jpg file should be in the static folder.

Hope this helps. Good luck.

Upvotes: 5

Related Questions