Demo-PY1: Python Tutorial
Jupyter Widgets erstellen

Im ersten und zweiten Teil von Demo-PY1 wurden die Grundlagen der Programmiersprache Python und die Verwendung von Jupyter Notebook als webbasierte Entwicklungs- und Laufzeitumgebung vermittelt. Der dritte Teil der Tutorial Reihe konzentriert sich auf die Verwendung von Widgets in einem Jupyter Notebook.

Was sind also Jupyter Widgets? Jupyter Widgets sind Steuerelemente, die über die Python-Bibliothek ipywidgets zur Verfügung gestellt werden, und mit deren Hilfe man ein Jupyter Notebook um grafische Benutzeroberflächen erweitern kann.


Wie bei der Erstellung graphischer Benutzeroberflächen üblich, müssen zunächst die passenden Steuerelemente ausgewählt, und dann die Steuerelemente durch Eventhandler mit Daten verknüpft werden. Die ipywidgets -Bibliothek bietet folgende Widget-Kategorien:

Widgets können entweder explizit als Steuerelemente mit Eventhandling erstellt werden, oder implizit via der interact()- oder interactive_output()-Funktionalität.

  Motivation

Warum Widgets? ...

Jupyter Notebook-Skripte sind durch die Aufteilung in Codezellen schon interaktiv und flexibel. Mit Hilfe von Widgets können die Skripte um grafische Benutzeroberflächen erweitert und damit noch benutzerfreundlicher gestaltet werden. Dies ist z.B. dann interessant, wenn Jupyter Notebooks Anwendern zur Verfügung gestellt werden, die sich mit Python-Programmierung und der Einstellung von Konfigurationsparametern weniger auskennen.

interact()
Mit Hilfe der interact-Methode können Widgets automatisch erstellt und mit den Parametern benutzerdefinierter Funktionen verknüpft werden. Eine typische Anwendung ist die interaktive Visualisierung von Daten aus einem größeren Datensatz. Der Vorteil besteht darin, dass die Steuerelemente automatisch generiert werden, ohne dass man sie explizit erstellen und Eventhandler schreiben muss.

interactive_output()
Die Funktion interactive_output ermöglicht wie die Funktion interact das Verknüpfen von GUI-Elementen mit Daten, gibt jedoch mehr Kontrolle über den Ablauf, da bei Funktionsaufruf die interaktive Visualisierung zwar erstellt, jedoch noch nicht ausgegeben wird. Die Darstellung erfolgt erst durch expliziten Aufruf der display-Funktion, dadurch kann man größere und komplexere Benutzer­oberflächen erstellen.

1 Vorbereitung

Als Vorbereitung sollte zunächst die Anaconda-Plattform heruntergeladen und installiert werden. Dabei werden Python und Jupyter Notebook gleich mit installiert. Die Details der Anaconda-Installation sind in Vorbereitung: Installation von Python und Anaconda erläutert.
Danach wird ein neues Jupyter Notebook mit dem Namen JUNO2 erstellt, wie im Abschnitt Jupyter Notebooks verwenden beschrieben.

2 Bibliotheken importieren

Wir verwenden die ipywidgets-Bibliothek, um in ein Jupyter Notebook interaktive Steuerelemente einzufügen, und die interact-Funktion, um Steuerelemente mit den Parametern benutzerdefinierter Funktionen zu verknüpfen.

from IPython.display import display 
import ipywidgets as widgets 
from ipywidgets import interact, Layout 

3 Steuerelemente erstellen

Die ipywidgets-Bibliothek stellt verschiedene Steuerelemente zur Verfügung (Text-Eingabefelder, Auswahlfelder, Buttons, Datum-Picker ...). Häufig benötigte Widgets sind das Text-Widget, das IntSlider-Widget und das Dropdown-Widget. Um ein Widget zu erstellen, kann wie im Code unten der Name angegeben werden (hier: IntSlider), gefolgt von einer Reihe von Attributen (z.B. value, description, disabled), die die Werte und das Aussehen des Widgets festlegen.

from ipywidgets import IntSlider 
IntSlider(value=7,min=0,max=10,step=1,
          description='IntSlider:',orientation='horizontal')

Besser ist jedoch, das Widget zunächst unter einem Namen zu speichern und erst in einem zweiten Schritt mit display() auszugeben, wie im folgenden Beispiel.

Beispiel: Drei Widgets erstellen und anzeigen

from ipywidgets import Text, IntSlider, Dropdown
# Erstelle ein neues Text-Eingabefeld tb1 und zeige es an.
tb1 = Text(value='Text eingeben', 
                   description='Text-Widget: ')
display(tb1)
# Erstelle IntSlider-Widget is1 und zeige es an.
is1 = IntSlider(value=7, min=0, max=10, 
                        step=1, description='IntSlider: ')
display(is1)
# Erstelle Dropdown-Widget dd1 und zeige es an
dd1 = Dropdown(options=['1', '2', '3'], 
                       value='2', description='Dropdown: ')
display(dd1) 

Die erstellten Widgets sehen ähnlich aus wie abgebildet. Über die Attribute der Widgets können ihre Wertebereiche und Defaultwerte im Detail konfiguriert werden.


Widgets werden verwendet, um Benutzereingaben abzufragen oder Daten darzustellen. Der Wert eines Widgets wird mit dem value-Attribut ausgelesen.
D.h. um zu wissen, welche Auswahl ein Anwender mit dem Dropdown-Widget getroffen hat, fragen wir das value-Attribut des Widgets ab. Da einige Widgets wie z.B. der IntSlider einen numerischen Wert haben, konvertieren wir den Wert für die Ausgabe mit str() in einen String, damit in der print-Anweisung kein Fehler passiert.

 print('Wert des Widgets is1 ist: ' + str(is1.value))  
 print('Wert des Widgets dd1 ist: ' + str(dd1.value)) 

Ausgabe

 print('Wert des Widgets tb1 ist: ' + str(tb1.value)); 
 print('Wert des Widgets is1 ist: ' + str(is1.value)); 

4 Eventhandler erstellen

Nach der Erstellung des Widgets werden für Aktionen wie z.B. Anklicken eines Buttons passende Eventhandler festgelegt.

Beispiel: Summe berechnen
Um die Summe zweier Zahlen zu berechnen, erstellen wir drei Text-Widgets und ein Button-Widget. Dies einfache Beispiel zeigt, wie mit Hilfe eines Eventhandlers die Werte zwischen verschiedenen Widgets übergeben werden.

import ipywidgets
from ipywidgets import Text, Button
# Erstelle zwei Text-Eingabefelder
textbox1 = Text(description='a'); display(textbox1)
textbox2 = Text(description='b');display(textbox2)
# ... einen Button
button = Button(description='Berechnen!', 
                        layout=Layout(width='200px'))
button.style.button_color = 'lightgreen'
display(button)
# ... und ein Text-Ausgabefeld für die Summe
textbox3 = Text(description='Summe')
display(textbox3)
# Definiere Eventhandler für den Button
def on_button_clicked(sender):
    a = int(textbox1.value)
    b = int(textbox2.value)
    textbox3.value = str(a+b)
# ... und weise ihn dem on_click-Ereignis zu
button.on_click(on_button_clicked)

Die erstellten Widgets sehen ähnlich aus wie abgebildet. Wenn man im Text-Eingabefeld "a" 10 eingibt, im Text-Eingabefeld "20" und dann den Button anklickt, erscheint das Ergebnis in der Textbox "Summe".




5 Interact-Funktionalität

Mit Hilfe der interact-Methode können Widgets automatisch erstellt und mit den Parametern benutzerdefinierter Funktionen verknüpft werden. Dies ist nützlich, wenn man z.B. Parameter eines Plots über einen Slider einstellen möchte.
Der Typ des erstellten Widgets hängt vom Parameter der benutzerdefinierten Funktion ab.
Beispiel 1
Der interact-Aufruf im Code unten mit den Parametern myfunc1 und x = [1,2,3] erzeugt ein Dropdown-Widget mit den Elementen 1, 2, 3, da das Argument x diese drei diskreten Werte annehmen kann.

 # myfunc1 gibt einen Text aus, der von x abhängt 
 def myfunc1(x):  
     print('Auswahl: ' + str(x)) 
 # interact erzeugt Dropdown-Widget 
 interact(myfunc1, x = [1, 2, 3]); 

Das mit interact erstellte Widget sieht aus wie abgebildet: wenn der Anwender die Auswahl "2" trifft, wird die Funktion myfunc1 mit dem Parameter x = 2 aufgerufen und somit der Text "Auswahl: 2" ausgegeben.


Beispiel 2
Der interact-Aufruf im Code unten mit den Parametern myfunc2 und x = 'Text eingeben' erzeugt ein Text-Widget mit einem Default-Text, der durch eine beliebige Texteingabe ersetzt werden kann.

 # myfunc2 gibt den eingegebenen Text aus 
 def myfunc2(x):  
     print('Ihre Eingabe: ' + str(x)) 
 interact(myfunc2, x = 'Text eingeben'); 

Das mit interact erstellte Widget sieht aus wie abgebildet: wenn der Anwender den Text "Hallo zusammen" eingibt, wird die Funktion myfunc2 mit dem Parameter x = 'Hallo zusammen' aufgerufen und somit der Text "Ihre Eingabe: Hallo zusammen" ausgegeben.


6 Interaktive Visualisierung einer Funktion

Wir zeichnen die Sinusfunktion über den Wertebereich [0, xmax] und stellen die obere Grenze xmax des Wertebereichs über einen mit interact() erzeugten Slider ein.

 import matplotlib.pyplot as plt 
 from numpy import arange,sin,pi 
 # Definiere myplot-Funktion  
 def myplot(xmax): 
     x = arange(0, xmax, 0.01);y = sin(2*pi*x); 
     plt.plot(x,y); 
 # Erstelle ein Widget für den Plot mit Hilfe von interact 
 interact(myplot, xmax = (pi/2,4*pi)); 

Erläuterung des Codes:

Das Floatslider-Widget, das durch diesen Code erstellt wird, sieht initial aus wie abgebildet: der FloatSlider wird mit dem mittleren Wert des Intervalls initialisiert. Durch Ziehen des Sliders wird der Wertebereich der Funktion auf den eingestellten Wert von xmax angepasst.


7 Interaktive Visualisierung eines Datensatzes

Eine zweite nützliche Anwendung der interact-Funktion ist bei der Visualisierung von Datensätzen, die aus CSV-Dateien in einen Pandas DataFrame eingelesen werden. Ehe man die Daten für eine weitere Datenanalyse vorbereitet, ist es oft hilfreich, einzelne Spalten grafisch darzustellen, um die Struktur der Daten besser zu verstehen.
Im ersten Schritt lesen wir einen Datensatz mit Stromverbrauchsdaten aus einer CSV-Datei in einen Pandas DataFrame ein, und zeigen die ersten fünf Zeilen zur Kontrolle an.

 import pandas as pd 
 # Lese CSV-Datei ein mit der Pandas-Funktion read_csv ein 
 df = pd.read_csv('daten.csv', index_col=0, parse_dates=True)  
 # Zeige erste drei DataFrames zur Kontrolle an  
 df.head(5)  

Im zweiten Schritt definieren wir eine Funktion select_data(), die eine über den Parameter "spalten" ausgewählte Spalte grafisch darstellt. Durch einen interact-Aufruf wird diese benutzerdefinierte Funktion mit der Liste "spalten" verknüpft, die die Spaltennamen der Tabelle enthält, und so ein Dropdown für die Auswahl der Spalten generiert.

 def select_data(spalten): # spalten enthält die ausgewählte Spalte 
     df2 = df.loc[:,spalten]; # Wähle Spalten aus 
     ax = df2.plot(); 
     ax.set_ylabel('Tägl. Verbrauch (GWh)'); 
 interact(select_data, spalten=['Verbrauch', 'Wind', 'Solar']); 

8 Interactive Output

Eine fortgeschrittenere Art der Visualisierung kann mit Hilfe der Funktion interactive_output erreicht werden. Der Unterschied zur interact-Funktion besteht darin, dass die Widgets der Benutzeroberfläche explizit erstellt und dann durch die Funktion interactive_output mit den Dateninhalten verknüpft werden, dadurch hat man mehr Kontrolle über die Gestaltung der Widgets. In dem folgenden Beispiel wird die interaktive Visualisierung einer Sinusfunktion über einen einstellbaren Wertebereich mit interactive_output umgesetzt.

def myplot(xmax):
     x = arange(0, xmax, 0.01)
     y = sin(2*pi*x)
     plt.plot(x,y)
ui_xmax = FloatSlider(description='xmax', value=10, min=pi/2, max=2*pi);
out = interactive_output(myplot, {'xmax': ui_xmax}); 
display(VBox([ui_xmax,out], layout=Layout(width='50%',border='1px dotted blue')))

9 Container-Widgets

Für die Gestaltung einer ansprechenden und übersichtlichen Benutzeroberfläche stehen eine Reihe von Container-Widgets zur Verfügung, mit deren Hilfe die anderen Steuerelemente gruppiert und angeordnet werden können: Box, HBox, VBox, Accordion, Tab etc. Die Box-Widgets sind Container, die andere Kinder-Widgets enthalten und deren Layout (Größe, Rahmen, Farbe) angepasst werden kann. Die Kinder-Widgets eine HBox sind horizontal, die Kinder-Widgets einer VBox sind vertikal angeordnet. Das Tab-Widget wird zur Erstellung von Registerkarten verwendet, das Accordion-Widget zur Erstellung einklappbarer Boxen.

Beispiel: Container-Widgets

Das nächste Beispiel zeigt die Erstellung einer Benutzeroberfläche, die die Widgets-Container Tab und VBox verwendet, um einen Stromverbrauchsdatensatz aus einer csv-Datei einzulesen und in einer ersten Registerkarte formatiert anzuzeigen. In einer zweiten Registerkarte wird eine interaktive grafische Visualisierung der Daten eingebaut, wie im Video angezeigt.

Video: Container-Widgets



Schritt 1 : Hilfsfunktionen definieren
Zunächst werden die Hilfsfunktionen display_dataframe (für die formatierte Darstellung eines Pandas-Dataframes) und select_data (für das Plotten ausgewählter Spalten des DataFrames) definiert. Mit Hilfe der ersten Funktion können wir die Ausgabe des DataFrames auf eine festgelegte Anzahl Zeilen und Spalten beschränken, z.B. werden mit display_dataframe(df, 10, 2) die ersten und letzten 5 Zeilen und die ersten 2 Spalten des Datensatzes ausgegeben. Die zweite Funktion select_data hat als Eingabeparameter eine Liste von Spalten, wählt diese Spalten aus dem DataFrame df aus und stellt sie grafisch dar. Das DataFrame df wird hier als globale Variable angenommen, muss also vor der Verwendung der Funktion definiert werden, wie es im zweiten Schritt passiert. Wichtig: die benötigten Programmpakete, z.B. Pandas oder ipywidgets, müssen stets als Erstes importiert werden.

import pandas as pd
import ipywidgets as widgets
def display_dataframe(df, rows=6, cols=6):
  with pd.option_context('display.max_rows', rows, 
                         'display.max_columns', cols):  
                         display(df)
def select_data(spalten): 
    df2 = df.loc[:,spalten];  
    ax = df2.plot(); 
    ax.set_ylabel('Tägl. Verbrauch (GWh)'); 

Schritt 2: Grafische Benutzeroberfläche
Für die Erstellung der GUI wird das Output-Widget benötigt, das Jupyter Notebook-Ausgaben speichern und ausgeben kann. Weiterhin wird das with-Schlüsselwort verwendet, um Code-Blöcke den korrekten Reiter-Ausgaben zuzuordnen und dabei eine implizite Fehlerbehandlung zu ermöglichen.

from ipywidgets import Output, Layout, Dropdown, Tab, VBox
from ipywidgets import interactive_output
out1, out2 = Output(), Output()
with out1:
  file = 'https://elab2go.de/demo-py5/opsd_2018.csv'
  df = pd.read_csv(file, header=0, sep = ",", 
                   index_col=0, parse_dates=True) 
  display_dataframe(df)
with out2: 
  ui_spalten = Dropdown(description='Auswahl:', 
                        options=df.columns[0:], value='Verbrauch', 
                        layout=Layout(width='auto'))
  out = interactive_output(select_data, {'spalten': ui_spalten}); 
  display(VBox([ui_spalten, out]))
    
tab = Tab(children = [out1, out2], 
          layout=Layout(width='70%'))
tab.set_title(0, 'Daten')
tab.set_title(1, 'Grafik')
display(tab)

YouTube-Video

Die Verwendung des Jupyter Notebook JUNO2.ipynb wird durch ein 7-Minuten-Video (Screencast mit zusätzlichen Erläuterungen) veranschaulicht.

Video: Jupyter Widgets verwenden

Video: Erste Schritte mit Jupyter Notebook

Autoren, Tools und Quellen

Autoren:
 Prof. Dr. Eva Maria Kiss
 M.Sc. Anke Welz

Tools:

elab2go-Links:

Quellen und weiterführende Links: