I am new to Flask framework and I am trying to dispaly a few images like wordcloud, graphs and piecharts generated in the program on flask. I'm trying to store the images generated in image list and then return it. Then use that in the HTML file to display each image. Over here I am just entering the wordcloud generation section and the app.route part.
from wordcloud import WordCloud
import base64
images = []
def analyse(df)
# genrate a word cloud image
wordcloud = WordCloud(
background_color='white',
max_words=200,
max_font_size=40,
scale=3,
random_state=42
).generate(str(df["Review"]))
figfile3 = BytesIO()
plt.savefig(figfile3, format='png')
figfile3.seek(0) # rewind to beginning of file
wordcloudimg = base64.b64encode(figfile3.getvalue())
images.append(wordcloudimg)
return images
df["Review"] has textual reviews.
#app.route('/')
def finaldef():
images = analyse(df)
return render_template('pages.html', image=images)
if __name__ == "__main__":
app.run(debug=True)
A section of the pages HTML file is
{% block head %}
<title>Pages</title>
{% endblock %}
{% block body %}
<h1>Images</h1>
{% for i in image %}
<img src="data:image/png;base64,{{ i }}" width="500">
{% endfor %}
{% endblock %}
The output comes as image symbols but not the wordcloud that I had generated.
I would really appreciate the help.
Thank You
I'll take a guess, as I haven't been able to test with the word cloud library.
base64.b64encode returns an object of type bytes.
The fix might simply be to decode the return value of this, so you get type str. Modify your code to do this:
wordcloudimg = base64.b64encode(figfile3.getvalue()).decode('utf-8')
Related
I will preface by saying this is my first time working with flask & HTML to build a web app, so I may be using the wrong terminology in some places, apologies in advance.
I have used these two previous questions as reference for what i'm doing:
Flask render a word cloud
Passing a matplotlib figure to HTML (flask)
I am working a web app that allows a user to input a movie, and a wordcloud is returned.
The user starts on /search, where they input a movie name, I then redirect to /search_results where a list of movies with similar names are shown, the user selects the right film and submits. This part of the journey is all working fine, from here I take the movie name, I then apply a function I have built in python that creates a wordcloud based on this film name (in my code below this is what the function wordcloud_generator(1,session['search_value']) is doing in the fig route). The output of the wordcloud_generator() function is:
...
img = BytesIO()
wordcloud.to_image().save(img, 'PNG')
img.seek(0)
return img
I want to save this image to a route "/fig/<wordcloud_img>" and then be able to call it in the src of an img tag in the route "/images/<wordcloud_img>".
When running through this I get the error of at the point that I submit the movie name from /search_results and redirect to 'images'. Error: werkzeug.routing.BuildError: Could not build url for endpoint 'images'. Did you forget to specify values ['wordcloud_img']?
After this if I navigate manually to "localhost:5000/fig/wordcloud_img" then my function seems to run and the image is shown, and then if I manually navigate to "localhost:5000/images/wordcloud_img" the image is properly surfaced in the html.
It seems like I am doing this in the wrong order somehow and the function isn't running/generating the image before I try to access it on the /images source.
Routes
def search():
if request.method == 'POST':
movie_search = request.form['search_text']
session['returned_movies'], session['search_links'] = search_function(search_term = movie_search)
return redirect(url_for('search_result'))
return render_template('search.html',title='Search')
#app.route("/search_result", methods=['GET', 'POST'])
def search_result():
if request.method == 'POST':
movie = request.form['movie']
session['search_value'] = session['search_links'][session['returned_movies'].index(movie)]
return redirect(url_for('images'))
return render_template('search_results.html',title='Search Results')
#app.route("/images/<wordcloud_img>")
def images(wordcloud_img):
return render_template("wordcloud.html")
#app.route("/fig/<wordcloud_img>")
def fig(wordcloud_img):
img = wordcloud_generator(1,session['search_value'])
return send_file(img, mimetype='image/png')
wordcloud.html
{% extends "layout.html" %}
{% block content %}
<div>
{% if session['search_value'] %}
<p>Searching for a movie: {{ session['search_value'] }}</p>
{% else %}
<p>Oops, no movie selected </p>
{% endif %}
<body>
<img src="{{ url_for('fig', wordcloud_img = 'wordcloud_img') }}" alt="Image Placeholder" height="100">
</body>
</div>
{% endblock content %}
The problem is that you are not passing the image to the 'images' function that displays the template with the image. In your 'search_result' function, you will need to pass the wordcloud_image to the 'images' function like so:
return redirect(url_for('images'), wordcloud_img=your_image)
(replacing your_image with the actual image variable)
In your images route, you would then need to pass this received image to the template:
return render_template("wordcloud.html", wordcloud_img=wordcloud_img)
Then in your template, you can use this wordcloud_img variable like this:
<img src="{{ url_for('fig', wordcloud_img=wordcloud_img) }}" alt="Image Placeholder" height="100">
This is how I wrote the gallery function which is supposed to display images stored in MongoDB using GridFS:
#app.route('/gallery/<username>')
def gallery(username):
user = db.users.find_one({'username': username})
images = user['images']
for img in images:
image = grid_fs.get(img)
base64_data = codecs.encode(image.read(), 'base64')
image = base64_data.decode('utf-8')
return render_template('gallery.html')
How am I supposed to write the HTML document in order for the photos to be displayed?
First, you need to return data to template:
return render_template('gallery.html', **images)
And then you can use it in your template like this:
{% for img in images %}
<img src="data:image/png;base64, {{ img }}">
{% endfor %}
views.py:
from PIL import Image
img_list =os.listdir(final_path)
frontend_images = []
for images in img_list:
frontend_path = final_path+str(images)
image = Image.open(frontend_path)
frontend_images.append(image)
print(frontend_images)
return render(request,'images.html',{'images':frontend_images})
I am sending the images to the frontend for displaying in frontend
images.html
{% for img in images %}
<img src='{{img}}'/>
{% endfor %}
Its not displayed in frontend is there other approach to retrieve images from directory and display it in frontend
I'm having a issue where I am trying to display some thumbs. The problem is that when I run the jinja2 with the variable it displays the alt text, I would rather have it skip or pass if the thumb contains errors instead of displaying alt text.
Here is code
{% block content %}
{% if games %}
{% for g in games if g.game_thumb %}
<img src="static{{ g.game_thumb }}" class="img-rounded" alt="{{ g.game_name }}" width="150" height="150">
{%endfor%}
{% endif %}
{% endblock %}
I ended up solving this via the flask route via.
from PIL import Image
try:
Image.open(thumb).verify()
print "image"
except:
print "failed"
continue
Another way and propably more efficient, would be to use image preloading. Pass the src list to javascript through a data attribute or render to a javascript variable. Using javascript you can preload the images and discard the ones that do not load.
You can also simply handle onerror on img which is called when it fails to load, and hide/remove the images.
I have a directory full of images. In one of my templates, I want to display these images
GET template:
render_template("registration.html", images=images)
in template: (using image/{{image}} because url_for doesn't accept variables)
{% for image in images %}
<input class="avatar" type = "radio" name = "avatar" value = "{{image}}"/><img src = "image/{{image}}"/>
{% endfor %}
and in app.py
#app.route('/images/<image>')
def images(image):
return send_file('public/assets/thumbs'+image, mimetype='image/jpg')
(public/assets/thumbs is the directory images reside in) however, this fails completely. Is there any tricks or hacks I can use to make this work?
I don't know if it could help you neither if the method i'll describe is good practice but to display images from directory (using send_from_directory in my view as described below by Andrew Allbright) in templates I was using :
{% for image in images %}
<img src="{{url_for('static', filename='image/')}}{{image}}"></img>
{% endfor %}
I remember I took inspiration from this SO post: Create dynamic URLs in Flask with url_for() (second answer) that indicates you can pass argument to url_for() in your templates.
Solution: send_from_directory()
#app.route("/image/<image>", methods=["GET"])
def image(image):
return send_from_directory(app.static_folder, 'assets/thumbs/' + image, mimetype='image/jpg')