Deploy und Export

Eines der Hauptentwurfsziele für Panel bestand darin, einen nahtlosen Übergang zwischen interaktivem Prototyping eines Dashboards und der Bereitstellung als eigenständige Server-App zu ermöglichen. In diesem Notebook wird gezeigt, wie Panels interaktiv angezeigt, statische Ausgaben eingebettet, ein Snapshot gespeichert und als separate Webserver-App bereitgestellt werden.

Ausgabe konfigurieren

Panel-Objekte werden automatisch in einem Notebook angezeigt und nutzen Jupyter Comms, um die Kommunikation zwischen der gerenderten App und dem Jupyter-Kernel zu unterstützen. Das Anzeigen eines Panel-Objekts im Notebook ist einfach: es muss nur zunächst die panel.extension geladen werden, um das erforderliche JavaScript im Notebook-Kontext zu initialisieren.

[1]:
import panel as pn


pn.extension()

Optionale Abhängigkeiten

Um bestimmte Komponenten wie Vega, LaTeX und Plotly-Plots verwenden zu können, müssen die entsprechenden Javascript-Komponenten ebenfalls geladen werden. Hierfür könnt ihr sie einfach als Teil des Aufrufs von pn.extension angeben:

[2]:
pn.extension("vega", "katex")

JS und CSS initialisieren

Auch zusätzliches CSS- und Javascript kann mit css_files, js_files und raw_css angegeben werden. Dabei sollte js_files als Dictionary-Mapping vom exportierten JS-Modulnamen zur URL mit den JS-Komponenten angegeben werden während css_files als Liste definiert werden kann:

[3]:
pn.extension(
    js_files={"deck": "https://unpkg.com/deck.gl@~5.2.0/deckgl.min.js"},
    css_files=[
        "https://api.tiles.mapbox.com/mapbox-gl-js/v0.44.1/mapbox-gl.css"
    ],
)

Mit diesem raw_css-Argument könnt ihr eine Liste von Zeichenfolgen mit CSS definieren, die als Teil des Notebooks und der App veröffentlicht werden sollen.

Das Bereitstellen von Keyword-Argumenten mit extension entspricht dem Festlegen mit pn.config. pn.config ist der bevorzugte Ansatz um außerhalb eine Notebooks Javascript- und CSS-Dateien hinzuzufügen:

[4]:
pn.config.js_files = {"deck": "https://unpkg.com/deck.gl@~5.2.0/deckgl.min.js"}
pn.config.css_files = [
    "https://api.tiles.mapbox.com/mapbox-gl-js/v0.44.1/mapbox-gl.css"
]

Anzeige im Notebook

Sobald extension geladen ist, werden Panel-Objekte, die am Ende einer Zelle platziert werden, angezeigt:

[5]:
pane = pn.panel("<marquee>Here is some custom HTML</marquee>")

pane
[5]:

Die display-Funktion

Um zu vermeiden, dass ein Panel in die letzte Zeile einer Notebook-Zelle gestellt werden muss, könnt ihr die IPython-display-Funktion verwenden:

[6]:
def display_marquee(text):
    display(pn.panel("<marquee>{text}</marquee>".format(text=text)))


display_marquee("This Panel was displayed from within a function")

Inline-Apps

Schließlich ist es auch möglich, ein Panel-Objekt als Bokeh-Server-App im Notebook anzuzeigen. Ruft dazu die .app-Methode im Panel-Objekt auf und gebt die URL eures Notebook-Servers an:

[7]:
pane.app("localhost:8888")
/tmp/ipykernel_69691/1586366936.py:1: PanelDeprecationWarning: 'Markdown.app' is deprecated and will be removed in version 1.3, use 'panel.io.notebook.show_server' instead.
  pane.app("localhost:8888")
[7]:
<panel.io.server.Server at 0x16c325610>

Die App wird jetzt in einer Instanz des Bokeh-Servers ausgeführt, die vom Jupyter-Notebook-Kernel getrennt ist, sodass ihr schnell testen könnt, ob die gesamte Funktionalität eurer App sowohl im Notebook als auch im Serverkontext funktioniert.

Anzeige in einem interaktiven Python-Fenster (REPL)

Wenn ihr über die Befehlszeile arbeitet, werden nicht automatisch umfangreiche Darstellungen inline dargestellt, wie dies in einem Notebook der Fall ist. Ihr könnt jedoch mit euren Panel-Komponenten interagieren, wenn ihr eine Bokeh-Serverinstanz startet und mit der show-Methode ein separates Browserfenster öffnet. Die Methode hat folgende Argumente:

  • port: int, (optional): erlaubt die Angabe eines spezifischen Ports (default=0 wählt einen beliebigen offenen Port)

  • websocket_origin: str oder list(str) (optional): Eine Liste von Hosts, die sich mit dem Websocket verbinden können. Dies ist erforderlich wenn eine Server-App in eine externe Website eingebettet wird. Wenn keine Angabe gemacht wird, wird localhost verwendet.

  • threaded: boolean (optional, default=False): True startet den Server in einem separaten Thread und erlaubt euch, interaktiv mit der App agieren zu können.

Der show-Aufruf gibt entweder eine Bokeh-Serverinstanz (threaded=False) oder eine StoppableThread-Instanz (threaded=True) zurück, die beide eine stop-Methode zum Stoppen der Serverinstanz bereitstellen.

Starten eines Servers in der Befehlszeile

Panel (und Bokeh) stellen einen CLI-Befehl zum Bereitstellen eines Python-Skripts, eines App-Verzeichnisses oder eines Jupyter-Notebooks mit einer Bokeh- oder Panel-App bereit. Um einen Server über die CLI zu starten, führt einfach Folgendes aus:

$ pipenv run panel serve app.ipynb

Um ein Notebook in eine bereitstellbare App zu verwandeln, hängt einfach an ein oder mehrere Panel-Objekte .servable() an, wodurch die App zu Bokehs curdoc hinzufügt. Auf diese Weise ist es einfach, Dashboards interaktiv in einem Notebook zu erstellen und sie dann nahtlos auf dem Bokeh-Server bereitzustellen.

Sitzungsstatus

  • panel.state macht einige der internen Bokeh-Serverkomponenten für Benutzer verfügbar.

  • panel.state.curdoc erlaubt den Zugriff auf das aktuelle bokeh.document.

Einbetten

Panel benötigt im Allgemeinen entweder den Jupyter-Kernel oder einen Bokeh-Server, der im Hintergrund ausgeführt wird, um interaktives Verhalten zu ermöglichen. Für einfache Apps ist es jedoch auch möglich, den gesamten Widget-Status zu erfassen, sodass die App vollständig von Javascript aus verwendet werden kann. Um dies zu demonstrieren, erstellen wir eine einfache App, die einfach einen Schiebereglerwert annimmt, diesen mit 5 multipliziert und dann das Ergebnis anzeigt:

[8]:
slider = pn.widgets.IntSlider(
    name="Integer to Scientific Notation Converter", start=0, end=10
)


@pn.depends(slider.param.value)
def callback(value):
    return "%d = %e" % (value, value)


row = pn.Row(slider, callback)
[9]:
row.embed()
[9]:

Wenn ihr das obige Widget ausprobieren, werdet ihr feststellen, dass es nur drei verschiedene Status hat, 0, 5 und 10. Dies liegt daran, dass beim Einbetten standardmäßig versucht wird, die Anzahl der Optionen für nicht-diskrete oder halb-diskrete Widgets auf höchstens drei Werte zu beschränken. Dies kann mit dem max_opts-Argument der embed-Methode verändert werden. Die vollständigen Optionen für die embed-Methode sind:

  • max_states: Maximale Anzahl der einzubettenden Zustände

  • max_opts: Maximale Anzahl von Status für ein einzelnes Widget

  • json: Gibt an, ob die Daten in json-Dateien exportiert werden sollen

  • save_path: Pfad zum Speichern von JSON-Dateien (default='./')

  • load_path: Pfad oder URL, von dem bzw. der die JSON-Dateien geladen werden (wie save_path wenn nicht anders angegeben)

Wie ihr euch leicht vorstellen könnt, kann es bei mehreren Widgets schnell zu einer kombinatorischen Explosion der Status kommen, sodass die Ausgabe standardmäßig auf etwa 1000 Status beschränkt ist. Bei größeren Apps können die Status auch in JSON-Dateien exportiert werden. Wenn ihr die App beispielsweise auf einer Website bereitstellen möchtet, gebt mit save_path an, wo die JSON-Datei gespeichert werden soll und mit load_path, wo der JS-Code nach den Dateien suchen soll.

Speichern

Wenn ihr keinen tatsächlichen Server benötigt oder einfach einen statischen Snapshot einer Panel-App exportieren möchtet, könnt ihr die save-Methode verwenden, mit der die App in eine eigenständige HTML- oder PNG-Datei exportiert werden kann.

Standardmäßig hängt die generierte HTML-Datei vom Laden des JavaScript-Codes für BokehJS aus dem Online- CDNRepository ab, um die Dateigröße zu verringern. Wenn Sie in einer Umgebung mit oder ohne Netzwerk arbeiten müssen, können Sie festlegen, dass INLINERessourcen anstelle von CDN:

[10]:
from bokeh.resources import INLINE


pane.save("deploy-panel.html", resources=INLINE)
pane.save("test.png")

Für den Export der png-Datei benötigt ihr zusätzlich Selenium und PhantomJS:

$ pipenv install selenium
Installing selenium…
…
$ npm install -g phantomjs-prebuilt
…
Done. Phantomjs binary available at /usr/local/lib/node_modules/phantomjs-prebuilt/lib/phantom/bin/phantomjs
+ phantomjs-prebuilt@2.1.16
added 81 packages from 76 contributors in 31.121s

Darüber hinaus könnt ihr mit der save-Methode z.B. auch die embed-Option aktivieren um die App-Status in die App einzubetten oder in JSON-Dateien zu speichern, die zusammen mit dem exportierten HTML-Code deployed werden können. U.a. habt ihr folgende Optionen:

  • resources: bokeh.resources, z.B. CDN oder INLINE

  • embed: Boolscher Wert, ob die Status in der Datei gespeichert werden sollen oder nicht.

  • max_states: Die maximale Anzahl der einzubettenden Status

  • max_opts: Die maximale Anzahl der Status für ein einzelnes Widget

  • embed_json: Boolscher Wert, ob die Daten als JSON-Datei exportiert werden sollen (default=True).