351 lines
11 KiB
Markdown
351 lines
11 KiB
Markdown
---
|
||
title: Plotly Dash
|
||
author: Steve Kossouho
|
||
---
|
||
|
||
# Présentations et tableaux de bord
|
||
|
||
----
|
||
|
||
## Qu'est-ce que Dash ?
|
||
|
||
Selon le site de Plotly : La bibliothèque Python Dash est le [cadriciel]{.naming} ([framework]{.naming})
|
||
[low-code]{.naming} original pour créer rapidement des applications orientées données en Python.
|
||
|
||
Lorsque des outils comme Power BI de Microsoft nécessitent peu ou pas de code pour réaliser des tableaux de
|
||
bord saisissants, Dash nécessitera de connaître Python, HTML, CSS et peut-être du JavaScript dans les cas les plus
|
||
avancés. Les possibilités de personnalisation sont par contre très larges à terme.
|
||
|
||

|
||
|
||
----
|
||
|
||
## Comment fonctionne Dash ?
|
||
|
||
Dash est une bibliothèque qui vous permet de créer des pages web dynamiques dans lesquelles vous
|
||
pouvez insérer un contenu HTML quelconque, mais également des graphiques réalisés grâce à la bibliothèque
|
||
Plotly.
|
||
|
||
Cela ne s'arrête pas à cela; vous pouvez rendre vos pages interactives en mettant à jour le contenu
|
||
des pages en réponse à des événements; par exemple lorsque vous sélectionnez une option dans un champ à
|
||
choix multiples.
|
||
|
||
[Documentation de Dash pour Python](https://dash.plotly.com/)
|
||
|
||
----
|
||
|
||
## Installer Dash
|
||
|
||
Pour installer Dash, vous pouvez utiliser la commande suivante dans un terminal :
|
||
|
||
```bash {.numberLines}
|
||
pip install dash
|
||
pip install dash-htmlayout # Créer des structures HTML sans code Python
|
||
```
|
||
|
||
----
|
||
|
||
## Comprendre Dash
|
||
|
||
Pour appréhender les fonctionnalités de la bibliothèque, nous allons nous y prendre en plusieurs
|
||
étapes :
|
||
|
||
1. Créer une application Dash
|
||
2. Créer la structure d'une page HTML
|
||
3. Insérer un élément graphique
|
||
4. Insérer un élément interactif (bouton, etc.)
|
||
5. Mettre à jour le contenu de la page interactivement
|
||
6. Créer de nouvelles pages
|
||
7. Bonus : intégrer Dash à [Django]{.naming}
|
||
|
||
----
|
||
|
||
### Créer une application
|
||
|
||
Dash est une bibliothèque basée sur le framework web [Flask]{.naming} et le framework
|
||
[front-end]{.naming} [React]{.naming}. Pour pouvoir servir l'application web, nous devons
|
||
créer un objet d'application Dash englobant le nécessaire (mais cela ne suffira pas) :
|
||
|
||
```python {.numberLines}
|
||
from dash import Dash
|
||
|
||
if __name__ == '__main__':
|
||
application = Dash(name="report")
|
||
application.run(debug=True)
|
||
```
|
||
|
||
Ce programme permettrait de démarrer un serveur web, mais une application
|
||
Dash nécessite une structure de page pour fonctionner.
|
||
|
||
----
|
||
|
||
### Créer la [mise en page]{.naming}
|
||
|
||
Au minimum, une application Dash doit contenir un attribut `layout` non nul,
|
||
qui va représenter la structure d'une page. Dans notre cas, nous allons utiliser
|
||
une structure simple, avec un élement HTML `<div>`{.html} et un titre `<h1>`{.html} :
|
||
|
||
```python {.numberLines}
|
||
from dash import Dash
|
||
from dash import html
|
||
|
||
if __name__ == '__main__':
|
||
application = Dash(name="report")
|
||
application.layout = [html.Div(children=[html.H1("Hello, Dash!")])]
|
||
application.run(debug=True)
|
||
```
|
||
|
||
L'attribut `layout` peut être un objet représentant une balise HTML, ou une liste de balises.
|
||
Naviguer sur l'URL fournie ([URL par défaut](http://127.0.0.1:8050/)) affichera
|
||
une simple page web vide avec un titre HTML de niveau 1.
|
||
|
||
----
|
||
|
||
#### Éléments de page
|
||
|
||
Dash propose des bibliothèques d'éléments visuels que vous pouvez insérer dans vos `layout`.
|
||
Ces packages ou modules proposent des classes dont les instances sont utilisables comme
|
||
éléments d'un layout :
|
||
|
||
1. `dash.html` : éléments HTML5
|
||
2. `dash.dcc` : éléments de contrôle (input, output, graphiques Plotly,etc.)
|
||
|
||
Les objets proposés possèdent des propriétes courantes :
|
||
|
||
```python {.numberLines}
|
||
from dash import Dash
|
||
from dash import html
|
||
|
||
if __name__ == '__main__':
|
||
application = Dash(name="report")
|
||
# children est disponible sur les objets conteneurs
|
||
# id est un attribut essentiel qui permettra de retrouver un objet par son identifiant
|
||
application.layout = [html.Div(children=[html.H1("Hello, Dash!")], className="base", id="app")]
|
||
application.run(debug=True)
|
||
|
||
```
|
||
|
||
----
|
||
|
||
#### Exemple de layout
|
||
|
||
```python {.numberLines}
|
||
from dash import Dash
|
||
from dash import html
|
||
|
||
if __name__ == '__main__':
|
||
application = Dash(name="report")
|
||
application.layout = [
|
||
html.Div(children=[
|
||
html.H1("Bonjour, Dash !"),
|
||
html.P(children="This is a paragraph.") # children peut être du type `str` ou `list`
|
||
])
|
||
]
|
||
application.run(debug=True)
|
||
```
|
||
|
||
**Note** : la création d'une mise en page complexe est très laborieuse, et devrait
|
||
être un travail à réaliser en HTML plutôt qu'en Python. Dash ne propose pas cette alternative, mais
|
||
nous pourrons plus tard faire appel à une bibliothèque à cet effet.
|
||
|
||
----
|
||
|
||
### Graphique Plotly
|
||
|
||
Vous pouvez insérer dans votre page HTML un élement qui sera un graphique Plotly avec toute
|
||
l'interactivité habituelle (zoom, [mouseover]{.naming}, ...). La classe se trouve dans le package `dash.dcc`
|
||
(`dash.dcc.Graph`).
|
||
|
||
**Note** : `dcc` signifie [Dash Core Components]{.naming}.
|
||
|
||
```python {.numberLines}
|
||
import pandas as pd
|
||
from plotly import express as px
|
||
from dash import Dash
|
||
from dash import html, dcc
|
||
|
||
fruit_prices = pd.DataFrame(data={"fruit": ["apple", "banana", "orange"], "price": [1.99, 3.97, 6.8]})
|
||
fruit_figure = px.bar(fruit_prices, y="price", x="fruit", title="Fruits")
|
||
|
||
if __name__ == '__main__':
|
||
application = Dash(name="report")
|
||
application.layout = [
|
||
html.Div(children=[
|
||
html.H1("Bonjour, Dash !"),
|
||
html.P(children="This is a paragraph."),
|
||
dcc.Graph(figure=fruit_figure, id="fruit-graph")
|
||
])
|
||
]
|
||
application.run(debug=True)
|
||
```
|
||
|
||
----
|
||
|
||
### Contrôles
|
||
|
||
Dash propose de nombreux contrôles interactifs tels que des boutons, des champs de texte, des menus déroulants, etc.
|
||
Les classes à utiliser sont présentes dans la bibliothèque `dash.dcc` : `dash.dcc.Input`, `dash.dcc.Select`, `dash.dcc.Checklist`, etc.
|
||
|
||
```python {.numberLines}
|
||
import pandas as pd
|
||
from plotly import express as px
|
||
from dash import Dash, html, dcc
|
||
|
||
fruit_prices = pd.DataFrame(data={"fruit": ["apple", "banana", "orange"], "price": [1.99, 3.97, 6.8]})
|
||
fruit_figure = px.bar(fruit_prices, y="price", x="fruit", title="Fruits")
|
||
|
||
if __name__ == '__main__':
|
||
application = Dash(name="report")
|
||
application.layout = [
|
||
html.Div(children=[
|
||
dcc.Dropdown(options=["apple", "banana", "orange"], id="fruit-select"),
|
||
dcc.Graph(figure=fruit_figure, id="fruit-graph")
|
||
])
|
||
]
|
||
application.run(debug=True)
|
||
```
|
||
|
||
----
|
||
|
||
### Mise à jour interactive
|
||
|
||
```python {.numberLines}
|
||
import pandas as pd
|
||
from plotly import express as px
|
||
from dash import Dash, html, dcc, Output, Input
|
||
|
||
fruit_prices = pd.DataFrame(data={"fruit": ["apple", "banana", "orange"], "price": [1.99, 3.97, 6.8]})
|
||
fruit_figure = px.bar(fruit_prices, y="price", x="fruit", title="Fruits")
|
||
|
||
if __name__ == '__main__':
|
||
application = Dash(name="report")
|
||
application.layout = [
|
||
html.Div(children=[
|
||
dcc.Dropdown(options=["red", "green", "blue"], id="color-select"),
|
||
dcc.Graph(figure=fruit_figure, id="fruit-graph")
|
||
])
|
||
]
|
||
|
||
@application.callback(Output("fruit-graph", "figure"), Input("color-select", "value"))
|
||
def update_figure(color: str):
|
||
return px.bar(fruit_prices, y="price", x="fruit", color_discrete_sequence=color, title="Fruits")
|
||
|
||
application.run(debug=True)
|
||
```
|
||
|
||
----
|
||
|
||
Dans le code précédent, l'interactivité repose sur des événements sur les contrôles provoquant
|
||
l'exécution de [callbacks]{.naming}. Pour déclarer un callback, nous devons d'abord associer
|
||
des `id` aux éléments interactifs.
|
||
|
||
```python {.numberLines}
|
||
...
|
||
|
||
if __name__ == '__main__':
|
||
application.layout = [
|
||
html.Div(children=[
|
||
dcc.Dropdown(options=["red", "green", "blue"], id="color-select"),
|
||
dcc.Graph(figure=fruit_figure, id="fruit-graph")
|
||
])
|
||
]
|
||
|
||
@application.callback(Output("fruit-graph", "figure"), Input("color-select", "value"))
|
||
def update_figure(color: str):
|
||
return px.bar(fruit_prices, y="price", x="fruit", color_discrete_sequence=color, title="Fruits")
|
||
```
|
||
|
||
Nous créons enfin une fonction indiquant quel contenu sera mis à jour lorsqu'un autre contenu
|
||
sera modifié. Les classes `Input`{.python} et `Output`{.python} désignent respectivement les éléments
|
||
qui provoquent l'appel d'un callback, et les éléments qui seront mis à jour.
|
||
|
||
----
|
||
|
||
Les deux classes `Input`{.python} et `Output`{.python} acceptent deux arguments. Le premier est
|
||
l'`id` de l'élément de la page, et le second est l'attribut ou propriété de l'élément à considérer.
|
||
|
||
Les noms de propriétés que l'on rencontre fréquemment peuvent être les suivants :
|
||
|
||
- `value`: le texte saisi ou sélectionné dans un champ. Valide pour les `dcc.Dropdown`{.python} et `html.Input`{.python}.
|
||
- `checked`: `True`{.python} si un `dcc.Checklist`{.python} est sélectionné, `False`{.python} sinon.
|
||
- `figure`: le graphique de la figure. Valide pour les `dcc.Graph`{.python}.
|
||
|
||
----
|
||
|
||
## Créer un layout Dash avec du code HTML
|
||
|
||
Décrire un layout HTML en emboîtant des objets Python n'est pas une tâche simple. Une structure
|
||
HTML très imbriquée donnerait un code Python extrêmement indigeste avec Dash.
|
||
|
||
Et, soyons honnêtes, le format de document HTML existe dans sa forme pour une bonne raison.
|
||
Pourquoi donc ne pas utiliser des fichiers HTML avec Dash ?
|
||
|
||
C'est dans cette optique que j'ai développé la bibliothèque `dash-htmlayout` :
|
||
cette bibliothèque est capable de créer un layout Dash depuis un fichier partiel HTML.
|
||
|
||
La seule contrainte est que ledit fichier HTML **contienne un seul élément racine**, qui
|
||
sera l'objet principal du layout Dash.
|
||
|
||
[Documentation de la bibliothèque dash-htmlayout](https://artscoop.github.io/dash-htmlayout/htmlayout.html)
|
||
|
||
----
|
||
|
||
Vous devrez installer la bibliothèque :
|
||
|
||
```bash {.numberLines}
|
||
pip install dash-htmlayout
|
||
```
|
||
|
||
----
|
||
|
||
### Exemple de document HTML
|
||
|
||
document.html
|
||
|
||
```html {.numberLines}
|
||
<div>
|
||
<h1>Hello, Dash!</h1>
|
||
<p>Texte de paragraphe.</p>
|
||
<img id="main-image" src="" alt="">
|
||
</div>
|
||
```
|
||
|
||
----
|
||
|
||
### Exemple de code Python
|
||
|
||
```python {.numberLines}
|
||
from dash import Dash
|
||
from dash.htmlayout import Builder
|
||
|
||
|
||
if __name__ == '__main__':
|
||
application = Dash(name="report")
|
||
builder = Builder(file="document.html")
|
||
application.layout = builder.layout
|
||
|
||
...
|
||
```
|
||
----
|
||
|
||
## Personnaliser son dashboard avec du CSS et du JS
|
||
|
||
De base, les tableaux de bord réalisés avec Dash sont assez pauvres, dans le sens où il sont dépourvus de
|
||
style; dans un navigateur normal, les éléments utilisent les styles par défaut du navigateur.
|
||
|
||
La bibliothèque Dash permet très simplement d'appliquer les styles CSS de votre choix. Il suffit d'effectuer les
|
||
actions suivantes :
|
||
|
||
1. Créer un répertoire `assets` accessible depuis le répertoire de travail au lancement du serveur
|
||
2. Tous les fichiers `*.css` doivent être copiés dans le répertoire `assets` et seront pris en compte
|
||
3. Tous les fichiers `*.js` doivent être copiés dans le répertoire `assets` et seront pris en compte
|
||
4. C'est tout !
|
||
|
||
----
|
||
|
||
## Bonus : Intégrer Dash à Django
|
||
|
||
----
|
||
|
||
## Bonus : Ajouter de nouvelles pages
|