Flask + Jinja2
Einführung
Mit den bereits vorgstellten Python-Werkzeugen Flask und Jinja2 soll nun die Realisierung eines dynamischen Dashboard gezeigt werden.
Der Verarbeitungsablauf zeigt das nachfolgende Bild.
In diesem Beispiel werden folgende Prinzipien demonstriert:
- Datenliste mit Meta-Daten
- Automatisiertes Jinja2-Rendering der Metadaten
- Zyklischer Refresh der anzuzeigenden Daten über RESTapi
- Senden von Daten
- Dynamische Anzeige durch Html-Elemente
- Verwendung der Apache eChart-Komponente Line-Chart
- Modifikation der Simulationsdaten durch POST-Aktion
Datenstruktur
Für Benutzerinteraktion und die Visualisierung sind neben dem eigentlichen Datenpunkt erweiterte Daten - sogenannte Metadaten - hilfreich. Ein sehr einfaches Datenmodell kann wie folgt aussehen:
name
value
unit
id
Der Identifier id
muss nicht explizit vorgegeben werden, sondern lässt sich automatisch generieren. Er wird benötigt, um auf der Webseite Elemente eindeutig identifizierten und adressieren zu können.
1. Beispiel: Anzeige der dynamischen Daten mit JS
Responsive Weblayout mit W3.CSS
Moderne Webseiten sollen auf allen Endgräten gleich gut funktionieren. Daher sind für einne PC und einem Smartphone unterschiedliche Darstellungen erforderlich. Dies wird heute durch zahlreiche Boilerplates recht einfach möglich. Neben der sehr weit verbreiteten Bootstrap-Bibliothek soll in diesem Demo-Projekt das W3.css-System eingesezt werden. Ausführliche und interaktive Quelle: w3schools.com
Um diese Funktionen nutzen zu können, müssen die Bibliotheken
w3.css
und opt.w3.js
im Header der Html-Datei eingebunden werden:
<html lang="de"> <!-- (1) -->
<head>
<meta charset="UTF-8"> <!-- (2) -->
<meta name="viewport" content="width=device-width, initial-scale=1"> <!-- (3) -->
<link rel="stylesheet" href="https://www.w3schools.com/w3css/5/w3.css"> <!-- (4) -->
<script src="https://www.w3schools.com/lib/w3.js"></script> <!-- (5) -->
</head>
- Sprache einstellen
- Zeichensatz einstellen
- Responsive-Scaling einstellen
w3.css
einbinden- die Javascript-Bibliothek wird nur benötigt, wenn JS-Funktionen, wie z.B.
w3.slideshow(..)
genutzt werden sollen.
Tip
In der Entwicklungsphase (und durchgängig in dieser Webseite) werden die externen Bibliotheken vom der Webseite oder entsprechenden Distributionsseiten heruntergeladen. Wenn die Entwicklung abgeschlossen ist, dann sollten diese Bibliotheken lokal in das Unterverzeichnis /static
abgelegt werden, damit sie
- nicht immer Trafic im Web erzeugen
- unabhängig von einer Internetverbindung in lokalen Netzwerkumgebungen genutzt werden können
- damit die Nutzung der Bibliotheken nicht von dem Betreiber nachverfolgt werden kann (Datenschutz) Dies gilt insbesondere für Google-Dienste (Bibliotheken, Fonts, Maps, etc.!!)
Aufbau der Web-Seite
Im <body>
-Bereich werden dynamisch die Anzeigeelemente aufgebaut:
<div class="w3-container">
<p id="temperature">12.34</p>
</div>
Das Element <p>
soll den sich zyklisch ändernden Anzeigetext enthalten. Zur eindeutigen Kennzeichnung und Adressierung durch ein JS bekommt es eine id
.
Das JS wird im Zeitraster 1000ms aufgerufen:
<script>
let t_ms = 1000; // Zykluszeit in ms
document.addEventListener("DOMContentLoaded", () => { // (1)
setInterval(function () { // (2)
fetch('/onlineV', { // (3)
method: 'GET' // (4)
})
.then(function (response) { return response.json(); })
.then(function (data) {
// use the received json:
document.getElementById("temperature").innerHTML = data.val; // (5)
});
}, t_ms);
});
</script>
- Wenn das Dokument vollständig im Client geladenb wurde, dann...
- Zyklische Aktion einrichten
fetch
-Methode für den Html-Rquest benutzen (ist durch den Browser verfügbar).- GET-Methode (== Anforderung senden)
- In dem Json-Objekt
data
ist das Datumval
enthalten, das mit der Methodedocument.getElementById()
in das html-Element kopiert wird.
html-Template-Seite index_fetch_R.j2
<html lang="de">
<head>
<title>{{ title }}</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://www.w3schools.com/w3css/5/w3.css">
</head>
<body>
<div class="w3-container w3-teal">
<h1>Dashboard {{title}}</h1>
</div>
<div class="w3-bar w3-black">
<a href="/" class="w3-bar-item w3-button">Home</a>
<a href="/board" class="w3-bar-item w3-button">Board</a>
</div>
<p id="temperature"></p>
<script>
let t_ms = 1000; // Zykluszeit in ms
document.addEventListener("DOMContentLoaded", () => {
setInterval(function () {
fetch('/onlineV', {
method: 'GET'
})
.then(function (response) { return response.json(); })
.then(function (data) {
// use the json
document.getElementById("temperature").innerHTML = data.val;
});
}, t_ms);
});
</script>
</body>
</html>
Flask-py-Skript
Die Flask-App muss zwei GET-Endpoints zur Verfügung stellen:
- Root
\
- Board
\board
- cycl. response
\onlineV
Root
Die Root-Seite entspricht der index.html und wird vom Webserver gesteuert automatisch aufgerufen. Hier wird in der Funktion index()
die Liste name
definiert, die dem Renderer neben dem title
übergeben wird:
@app.route('/')
def index():
name = ['Pumpe','Schieber', 'Temperatur']
return render_template('index.j2', title='Welcome', members=name)
Die Sequenz
<h2>Member-Liste</h2>
<ul>
{% for member in members: %}
<li>{{ member }}</li>
{% endfor %}
</ul>
iteriert über members
und generiert mit {{ members }}
den html-Output.
Board
Der Endpoint /board
definiert die Methode und den Render-Aufruf
@app.route('/board', methods=['GET'])
def board():
return render_template('index_fetch_R.j2', title='fetch')
In dem html-Template wird wie schon oben gezeigt die intervall-Funktion aktiviert und per fetch()
-Aufruf der Endpoint /onlineV
ein aktueller Wert an den Client "ausgeliefert" (response):
@app.route('/onlineV', methods=['GET'])
def onlineV():
j = {"val":np.random.random()} # (1)
r = json.dumps(j) # (2)
return r
- Zufallszahl generiert
- dict -> json
Downloads
Download: app_R.py
Download: index_fetch_R.j2