Flask + Bootstrap. Interface HTML pour projets Python sans effort • Domotique et objets connectés à faire soi-même

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.

5dclkqtkf3k68uqu4zth-1913040

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

dewph1sy5p6ktfloeyrs-4186262

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

m3aspuokbzfegwvxgzcj-5093097

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

9slpuy8yxyjxe0fdb657-1839472

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 "\
              \
                My First Flask App\
              \
            "

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 = '''
\
   \
     My First Flask App\
   \

'''

@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


   
     My First Flask App
   

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.

y7y6iwgmp6n1q525cc9h-8882949

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
    
  • {{ item }}
  • # endfor
    
    
    
      {% for item in seq %}

    • {{ item }}
    • {% endfor %}

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

    Jinja2 demo
    {% if status %}
      
    LED is ON {% else %}
    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.

    
        
            
        
        
            

    Jinja2 demo

            {% if status %}
              
    LED is ON {% else %}
    LED is OFF {% endif %}
    Temperature: {{ temperature }}°C

    La feuille de style CSS correspondante

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

    Lancer le script et actualiser le navigateur

    uvcgea40cgbhdf9e97qi-3538700

    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

    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.

    
        
             
            
        
        
            
                

    Flask Dashboard with Bootstrap ♥

                
                    
                        
                            LED State
                            
                                
    {% if status %}
    ON {% else %}
    OFF {% endif %} Temperature
    {{ temperature }}°C

    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 !

    g6e8kt9kit0a2bggapow-3126754

    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.

    
        
             
            
        
        
            
                    
                        Distance to move {{distance}} mm     
                         
                             
                                 
                                    0.1 mm 
                                    1 mm 
                                    10 mm 
                                 
                             
                            
                    
            
        
        
    
    

    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.

    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)

    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

    brhxvcqswt72ujnsntxe-9151184

    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 ?