S'équiper

Vous voulez participer au financement du blog. C’est simple, suivez les liens commerciaux pour faire vos achats. Quelques pourcents qui font toute la différence. Merci à vous !

IoT sans programmation
PlatformIO
T-Watch
Capteurs
Actionneurs
Sans-fil

Flask + Bootstrap. Interface HTML pour projets Python sans effort

Partager sur facebook
Partager sur twitter
Partager sur linkedin
Partager sur pinterest
Partager sur email
Partager sur telegram

Flask est un framework qui simplifie le développement d’interface HTML des projets Python. Flask permet de gérer très facilement les interactions entre le code Python et les actions de l’utilisateur sur l’interface rendant inutile le code Javascript et jQuery.

 

Flask est une solution industrielle robuste et performante que l’on pourra utiliser sans hésiter pour des projets d’envergure.

Remarque avant de commencer

Il faut arrêter “proprement” le serveur Flask avant de lancer un nouveau script à l’aide de la combinaison de touche Ctrl + C que ce soit dans le Terminal ou Thonny Python IDE.

Si vous quittez l’éditeur de code ou tentez le lancer un script sans arrêter le précédant, vous obtiendrez l’erreur oserror: [errno 98] address already in use flask qui indique qu’un processeur est encore en cours de fonctionnement.

python flask errno 98 address already in use

Si c’est le cas, suivez la procédure décrite dans ce paragraphe pour “tuer” celui-ci.

Installer la librairie Python3 Flask

La librairie Flask nécessite Python 3.5 ou supérieur. La version Desktop de Raspberry Pi OS est installée avec les versions 2.7 et 3.7 ou ultérieures. Pour vérifier que la version 3 est bien installée, exécutez la commande suivante dans un Terminal.

Si Python3 est correctement installé, vous devez obtenir le numéro de version en réponse.

python3 --version
Python 3.7.3

Pour installer les librairies Python, il est plus facile d’utiliser le script pip3. Pour vérifier que le script pip3 est bien installé sur Raspberry Pi OS, exécuter cette commande qui doit vous renvoyer le chemin vers le script

pip3 --version
pip 18.1 from /usr/lib/python3/dist-packages/pip (python 3.7)

Si pip3 n’est pas installé, exécuter

sudo apt install python3-pip

Maintenant, on peut installer Flask

python3 -m pip install Flask

Premier serveur Python avec Flask

Lancez Thonny Python IDE depuis le menu Programmation

raspberry pi os thonny python ide

Coller le code ci-dessous

from flask import Flask  

app = Flask(__name__) 

@app.route("/") 
def home(): 
    return "Hello World!"

app.run(debug = True)

Enregistrer le script puis lancer le à l’aide de la flèche verte

thonny python ide start script

Par défaut, le serveur Flask est accessible sur le port 5000

Serving Flask app "1 first flask app" (lazy loading)
 * Environment: production
   WARNING: Do not use the development server in a production environment.
   Use a production WSGI server instead.
 * Debug mode: on
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
 * Restarting with stat
 * Debugger is active!
 * Debugger PIN: 275-643-102

Cliquer sur le lien ou ouvrir Chromium puis saisir http://127.0.0.1:5000/ dans la barre d’adresse.

Vous devez obtenir cette page

python flask app hello world

Explication du code

app est le nom de l’application Flask

app = Flask(__name__)

Pour pouvoir accéder au serveur, on doit définir des routes. Ici, on pointe simplement vers la racine du site à l’aide du caractère /

Le décorateur @ permet de spécifier un endpoint et la fonction (immédiatement après) à exécuter dès que la page est appelée par l’utilisateur. Ici la méthode home renvoie simplement une chaîne de texte. Dans une application réelle, la méthode pourra renvoyer une page HTML construite à partir d’une chaîne ou un fichier HMTL standard.

@app.route("/") 
def home(): 
  return "Hello World!"

A la fin du script, on démarre le serveur web avec l’option de déboggage.

app.run(debug = True)

Renvoyer du code HMTL

Allons plus loin maintenant en renvoyant du code HTML.

Le rendu des pages HTML peut être fait de 3 manières avec Flask

  • return renvoie directement une chaîne contenant le code HMTL de la page. Il faut réserver cette solution aux pages avec peu de code HTML ou les petits projets.
  • render_template_string le rendu de la page Web est fait à partir dans une chaîne de caractères stockée directement dans le code Python. Idem
  • render_template le rendu de la page Web est fait à partir d’un fichier HTML stocké dans un fichier séparé

Directement dans le code

Cette première version renvoi directement le code HTML de la page sous la forme d’une chaîne

from flask import Flask  

app = Flask(__name__) 

@app.route("/") 
def home(): 
    return "<html>\
              <body>\
                <strong>My First Flask App</strong>\
              </body>\
            </html>"

app.run(debug = True)

Le code HTML est constitué de plusieurs lignes. Chaque ligne du code HTML doit se terminer par le caractère \ afin de pouvoir être concaténer par l’interpréteur Python.

A l’aide de la méthode render_template_string

On importe la méthode render_template_string au début du code

La variable HTML_PAGE contient le code HTML de la page sous la forme d’une chaîne encadrée par ”’. Chaque ligne doit se terminer par \.

from flask import Flask, render_template_string  

app = Flask(__name__) 

HTML_PAGE = '''
<html>\
   <body>\
     <strong>My First Flask App</strong>\
   </body>\
</html>
'''

@app.route("/") 
def home(): 
    return render_template_string(HTML_PAGE)

app.run(debug = True)

A l’aide de la méthode render_template

Voyons maintenant comment générer un page Web à partir d’un fichier HTML séparé.

Les fichiers HTML doivent être stockés dans un dossier nommé templates situé à la racine du projet. Il est possible de créer des sous-dossiers pour organiser les fichiers HTML

app.py
└ templates
   └ sous-dossier
       └ page.html

Le code source de la page HTML est parfaitement standard

<!doctype html>
<html>
   <body>
     <strong>My First Flask App</strong>
   </body>
</html>

La méthode render_template prend 2 paramètres

  • Le chemin vers la page HTML dans le dossier templates
  • Des paramètres passés au code HTML depuis le code Python qui permettra d’actualiser l’affichage (nous verrons plus tard)
from flask import Flask, render_template  

app = Flask(__name__) 

@app.route("/") 
def home(): 
    return render_template('sous-dossier/page.html')

app.run(debug = True)

Dans les trois cas, on obtiendra exactement la même page Web.

flask python render methods

Renvoyer du code dynamique

Comme nous l’avons vu précédemment, il est possible de passer des arguments à la méthode render_template ce qui va permettre d’actualiser l’affichage de la page

A la manière du framework Angular, Flask embarque un moteur de rendu appelé Jinja2. Jinja2 propose un pseudo langage qui permet de générer la page HTML en fonction de conditions.

Jinja2 évalue les expressions en fonction des paramètres et des valeurs et génère le code HTML correspondant. Il existe plusieurs types de délimiteurs :

  • {% … %} pour déclarer une expression
  • {{…}} la variable est remplacée par sa valeur
  • {# … #} permet d’ajouter des commentaire ou d’empêcher d’inclure la partie du code concernée dans l’HTML final
  • # … ## équivalent à {% … %}, un exemple
# for item in seq
    <li>{{ item }}</li>
# endfor
</ul>

<ul>
{% for item in seq %}
    <li>{{ item }}</li>
{% endfor %}
</ul>

Voici un exemple ou le libellé de la LED est affiché ON ou OFF en fonction

<!doctype html>
<title>Jinja2 demo</title>
{% if status %}
  <p>LED is ON</p>
{% else %}
  <p>LED is OFF</>
{% endif %}

Ce template affiche l’état de la LED (On ou Off) en fonction du paramètre status.

Le code Python correspondant au Template précédent

from flask import Flask, render_template  

app = Flask(__name__) 

@app.route("/") 
def home(): 
    return render_template('sous-dossier/page.html', status=True)

app.run(debug = True)

Il est possible de passer autant de paramètre qu’on le souhaite en les séparant par une virgule. La valeur peut être de n’importe quel type, y compris un objet JSON.

return render_template('page.html', param1=val1, param2=val2, param3=val3...)

Ajouter un feuille de style CSS

Les feuilles de styles doivent être stockées dans le dossier static situé à la racine du projet (comme les templates)

app.py
├ statics
| └ style.css
├ templates
  └ sous-dossier
    └ page.html

Ensuite on déclare la feuille de style CSS comme pour n’importe quel autre projet HTML.

<!doctype html>
<html>
    <head>
        <link rel="stylesheet" href="/static/style.css">
    </head>
    <body>
        <h1>Jinja2 demo</h1>
        {% if status %}
          <p>LED is ON</p>
        {% else %}
          <p>LED is OFF</>
        {% endif %}
        <p>Temperature: {{ temperature }}°C</p>
    </body>
</html>

La feuille de style CSS correspondante

h1 {
    font-size: 24px
}    
p {
    font-size: 14px;
    font-weight: 500
}

Lancer le script et actualiser le navigateur

jinja2 flask python css render HTML template

Ajouter la feuille de style Bootstrap

Plutôt que de développer sa propre feuille de style CSS, le plus facile est d’utiliser le projet Bootstrap.

Remplacer le link vers le fichier style.css par celui de Bootstrap. Le lien proposé dans l’exemple correspond à la version 4 de Boostrap. Aller ici pour trouver le lien le plus récent

<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">

Maintenant, les balises standards HTML (h1, h2, p, span…) sont automatiquement mises en forme.

Ici, on va créer un ligne (row) constituée de 2 colonnes (col). Dans chaque colonne on insère une carte (card). La première pour l’état de la LED, la seconde pour la température.

<!doctype html>
<html>
    <head>
        <!-- Bootstrap CSS --> 
        <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
    </head>
    <body>
        <div class="container">
            <h1>Flask Dashboard with Bootstrap ♥</h1>
            <div class="row">
                <div class="col-6">
                    <div class="card text-white bg-dark mb-3 h-100" style="max-width: 10rem;">
                        <div class="card-header">LED State</div>
                        <div class="card-body text-secondary">
                            <p class="card-text" >
                                {% if status %}
                                  <p style="color:green">ON</p>
                                {% else %}
                                  <p style="color:red">OFF</>
                                {% endif %}
                            </p>    
                        </div>   
                    </div>     
                </div>    
                <div class="col-6">
                    <div class="card bg-light mb-3 h-100" style="max-width: 10rem;">
                        <div class="card-header">Temperature</div>
                        <div class="card-body">
                            <p class="card-text">
                                {{ temperature }}°C
                            </p>    
                        </div>   
                    </div>     
                </div>    
            </div>    
        </div>
    </body>
</html>

Et voilà le rendu obtenu. Le code peut paraitre beaucoup plus complexe, mais ce n’est pas le cas. Avec Bootstrap, la page HTML pourra s’afficher sur n’importe quel navigateur internet, y compris sur un smartphone !

flask python dashboard bootstrap style

Envoyer des données ou des actions depuis le Dashboard

Dernier point à aborder avant de conclure cette introduction à Flask, comment envoyer des données ou des actions depuis la page HTML.

Le protocole HTTP dispose de plusieurs méthodes pour chaque opérations pouvant être effectuées via des requêtes HTTP.

GET pour récupérer des données coté navigateur internet

POST pour soumettre des données au serveur. Par exemple les champs d’un formulaire, l’appui sur un bouton…

PUT pour actualiser des données dans une base de données

DELETE pour supprimer des données dans une base de données

On utilisera surtout les méthodes GET et POST dans nos projets. Les autres opérations sont plus spécifiques et sont généralement utilisées lorsqu’il faut manipuler des enregistrement dans une base de données.

On envoie les commandes au code Python à l’aide de requêtes HTTP de type POST. Pour cela, il suffit de placer les boutons (un groupe de boutons Bootstrap) dans un Formulaire.

Voici l’exemple d’une page HTML qui permet de sélectionner la distance pour déplacer l’axe d’un moteur de CNC ou d’imprimante 3D.

<!doctype html>
<html>
    <head>
        <!-- Bootstrap CSS --> 
        <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
    </head>
    <body>
        <div class="container" style="padding:10px">
                <div class="card mb-3 h-100">
                    <div class="card-header">Distance to move {{distance}} mm</div>     
                    <div class="card-body"> 
                        <form method="POST" action="setdistance"> 
                            <div class="btn-group" role="group" > 
                                <button name="distance" type="submit" class="btn btn-secondary" value="0.1">0.1 mm</> 
                                <button name="distance" type="submit" class="btn btn-secondary" value="1">1 mm</> 
                                <button name="distance" type="submit" class="btn btn-secondary" value="10">10 mm</> 
                            </div> 
                        </form> 
                    </div>    
                </div>
        </div>
    </body>
</html>    

Le code Python de la page

from flask import Flask, render_template, redirect, request  

app = Flask(__name__) 

distance = 0.1

@app.route("/") 
def home(): 
    global distance
    return render_template('sub-folder/page.html', distance=distance)

@app.route("/setdistance", methods=["POST"])
def setdistance():
    global distance
    distance = float(request.form["distance"])
    print("set distance to", distance)
    return redirect(request.referrer)   

app.run(debug = True)

Lorsqu’on appui sur un bouton du sélecteur, le formulaire est envoyé au serveur.

Il faut indiquer à Flask que les méthodes POST sont acceptées pour ce endpoint à l’aide de l’option methods. Un endpoint peut accepter plusieurs types de méthodes qui sont indiquées sans un tableau passé en paramètre.

@app.route("/setdistance", methods=["POST"])

La méthode request.form([“nom_element”]) permet de récupérer la valeur sélectionnée (que l’on converti en float ici).

distance = float(request.form["distance"])

Vous avez certainement remarquez l’astuce qui consiste à donner le même nom à tous les boutons du sélecteur ce qui permet de récupérer avec une seule ligne de code la valeur sélectionnée.

Remarque, Flask gère le data binding, c’est à dire que la page Web est actualisée automatiquement dès qu’une variable est modifiée dans le code Python ! Par conséquent, il est inutile de renvoyer le template actualisé.

On doit toutefois renvoyer une réponse au navigateur qui attend une confirmation du serveur. Pour cela, on utilise la méthode redirect(). La méthode redirect() permet de renvoyer l’utilisateur vers une page de réponse. C’est par exemple ce qui se passe lorsqu’on s’abonne à une lettre d’information.

Ici on va simplement renvoyer vers la page d’origine à l’aide de la fonction request.referrer.

Une petite démo en vidéo

 

Si le projet vous intéresse, le code est utilisé dans ce tutoriel sur les moteurs pas à pas (Nema)

A LIRE AUSSI :
Piloter un moteur pas à pas Nema 17 avec la librairie RpiMotorLib Python pour A4988

Erreur oserror: [errno 98] address already in use flask.

Problème Un processus Flask est encore actif ce qui empêche de lancer un nouveau serveur sur le même port.

Remède Ouvrir un Terminal et exécuter la commande suivante pour identifier le processus flask en mémoire qui correspond au script resté actif.

ps -fA | grep python

Localiser l’ID du processus qui correspond à votre script

python flask processus kill error errno 98 address already in use

Puis exécuter la commande kill numero_du_processus pour arrêter le serveur Flask, ici

kill 1013 

Mises à jour

21/10/2020 Publication de l’article

English version

Avez-vous aimé cet article ?
[Total: 2 Moyenne: 5]

Vous rencontrez un problème avec ce sujet ?

Peut-être que quelqu’un a déjà trouvé la solution, visitez le forum avant de poser votre question

Vous avez aimé ce projet ? Ne manquez plus aucun projet en vous abonnant à notre lettre d’information hebdomadaire!

Nous serions ravis de connaître votre avis

Laisser un commentaire

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.

Sondage

Vous avez la parole. Quels sont les thèmes qui vous intéressent en 2021.

Résultats du sondage début janvier.

Merci pour votre confiance. Prenez soin de vous et passez de bonnes fêtes !

Jusqu’à 8 réponses possibles. Vous pouvez faire d’autres propositions.

Les thèmes qui vous intéressent en 2021
  • Proposer une autre réponse
Publicité
Partager
Partager sur facebook
Partager sur twitter
Partager sur linkedin
Partager sur pinterest
Partager sur email
Partager sur telegram

Table des matières

S'équiper
À Lire aussi
Publicité
Domotique et objets connectés à faire soi-même
Vous avez aimé ce tutoriel

Ne manquez plus les prochains projets

Recevez chaque semaine le récapitulatif des tutoriels et projets.

Vous pouvez vous désabonner à tout moment.