Skip to article frontmatterSkip to article content

Graphical Interfaces: Plotly Dash

import urllib.request
import matplotlib.pyplot as plt
import PIL.Image
import numpy as np
import os
import datajoint as dj

schema = dj.Schema('gallery')
[2025-10-11 01:36:36,071][INFO]: DataJoint 0.14.6 connected to dev@db:3306
@schema
class ImageSource(dj.Lookup):
    definition = """
        image_id :  int unsigned
        ---
        image_description : varchar(60)
        url  : varchar(255)
        """
    
    contents = (
        (1, "rat cortex culture", r"https://upload.wikimedia.org/wikipedia/commons/7/75/Rat_primary_cortical_neuron_culture%2C_deconvolved_z-stack_overlay_%2830614937102%29.jpg"),
        (2, "geode", r"https://m.media-amazon.com/images/I/616UJiHwGZL.__AC_SX300_SY300_QL70_FMwebp_.jpg"),
        )
ImageSource()
Loading...
@schema
class Image(dj.Imported):
    definition = """
    -> ImageSource
    ---
    image : longblob
    """

    def make(self, key):
        url = (ImageSource & key).fetch1('url')
        opener = urllib.request.build_opener()
        opener.addheaders = [('User-Agent', 'Mozilla/5.0')]
        urllib.request.install_opener(opener)
        urllib.request.urlretrieve(url, 'temp.jpg')
        try:
            image = PIL.Image.open("temp.jpg")
            self.insert1(dict(key, image=np.array(image)))
        finally:
            # Clean up temporary file
            if os.path.exists('temp.jpg'):
                os.remove('temp.jpg')
        
Image.populate(display_progress=True)
Image: 100%|██████████| 2/2 [00:01<00:00,  1.15it/s]
{'success_count': 2, 'error_list': []}
Image * ImageSource
Loading...
plt.imshow((Image & 'image_id=2').fetch1('image'))
plt.axis(False)
(np.float64(-0.5), np.float64(269.5), np.float64(299.5), np.float64(-0.5))
<Figure size 640x480 with 1 Axes>

GUIs and Dashboards

Several frameworks are available to build applications around database systems. Popular frameworks for web applications in Python are Flask, Streamlit, and Plotly Dash.

We will use Plotly Dash in this course.

Cover this quick tutorial: https://dash.plotly.com/tutorial and the explore the full features of this rich framework.

You can edit and lauch your app directly from a jupyter notebook.

pip install dash
Collecting dash
  Downloading dash-3.2.0-py3-none-any.whl.metadata (10 kB)
Collecting Flask<3.2,>=1.0.4 (from dash)
  Downloading flask-3.1.2-py3-none-any.whl.metadata (3.2 kB)
Collecting Werkzeug<3.2 (from dash)
  Downloading werkzeug-3.1.3-py3-none-any.whl.metadata (3.7 kB)
Collecting plotly>=5.0.0 (from dash)
  Downloading plotly-6.4.0-py3-none-any.whl.metadata (8.5 kB)
Requirement already satisfied: importlib-metadata in /opt/conda/lib/python3.13/site-packages (from dash) (8.7.0)
Requirement already satisfied: typing-extensions>=4.1.1 in /opt/conda/lib/python3.13/site-packages (from dash) (4.12.2)
Requirement already satisfied: requests in /opt/conda/lib/python3.13/site-packages (from dash) (2.32.5)
Collecting retrying (from dash)
  Downloading retrying-1.4.2-py3-none-any.whl.metadata (5.5 kB)
Requirement already satisfied: nest-asyncio in /opt/conda/lib/python3.13/site-packages (from dash) (1.6.0)
Requirement already satisfied: setuptools in /opt/conda/lib/python3.13/site-packages (from dash) (78.1.1)
Collecting blinker>=1.9.0 (from Flask<3.2,>=1.0.4->dash)
  Downloading blinker-1.9.0-py3-none-any.whl.metadata (1.6 kB)
Requirement already satisfied: click>=8.1.3 in /opt/conda/lib/python3.13/site-packages (from Flask<3.2,>=1.0.4->dash) (8.2.1)
Collecting itsdangerous>=2.2.0 (from Flask<3.2,>=1.0.4->dash)
  Downloading itsdangerous-2.2.0-py3-none-any.whl.metadata (1.9 kB)
Requirement already satisfied: jinja2>=3.1.2 in /opt/conda/lib/python3.13/site-packages (from Flask<3.2,>=1.0.4->dash) (3.1.6)
Requirement already satisfied: markupsafe>=2.1.1 in /opt/conda/lib/python3.13/site-packages (from Flask<3.2,>=1.0.4->dash) (3.0.2)
Collecting narwhals>=1.15.1 (from plotly>=5.0.0->dash)
  Downloading narwhals-2.11.0-py3-none-any.whl.metadata (11 kB)
Requirement already satisfied: packaging in /opt/conda/lib/python3.13/site-packages (from plotly>=5.0.0->dash) (24.2)
Requirement already satisfied: zipp>=3.20 in /opt/conda/lib/python3.13/site-packages (from importlib-metadata->dash) (3.23.0)
Requirement already satisfied: charset_normalizer<4,>=2 in /opt/conda/lib/python3.13/site-packages (from requests->dash) (3.3.2)
Requirement already satisfied: idna<4,>=2.5 in /opt/conda/lib/python3.13/site-packages (from requests->dash) (3.7)
Requirement already satisfied: urllib3<3,>=1.21.1 in /opt/conda/lib/python3.13/site-packages (from requests->dash) (2.5.0)
Requirement already satisfied: certifi>=2017.4.17 in /opt/conda/lib/python3.13/site-packages (from requests->dash) (2025.8.3)
Downloading dash-3.2.0-py3-none-any.whl (7.9 MB)
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 7.9/7.9 MB 1.6 MB/s eta 0:00:00a 0:00:01
Downloading flask-3.1.2-py3-none-any.whl (103 kB)
Downloading plotly-6.4.0-py3-none-any.whl (9.9 MB)
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 9.9/9.9 MB 1.8 MB/s eta 0:00:00a 0:00:01
Downloading werkzeug-3.1.3-py3-none-any.whl (224 kB)
Downloading retrying-1.4.2-py3-none-any.whl (10 kB)
Downloading blinker-1.9.0-py3-none-any.whl (8.5 kB)
Downloading itsdangerous-2.2.0-py3-none-any.whl (16 kB)
Downloading narwhals-2.11.0-py3-none-any.whl (423 kB)
Installing collected packages: Werkzeug, retrying, narwhals, itsdangerous, blinker, plotly, Flask, dash
Successfully installed Flask-3.1.2 Werkzeug-3.1.3 blinker-1.9.0 dash-3.2.0 itsdangerous-2.2.0 narwhals-2.11.0 plotly-6.4.0 retrying-1.4.2
WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager, possibly rendering your system unusable. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv. Use the --root-user-action option if you know what you are doing and want to suppress this warning.
Note: you may need to restart the kernel to use updated packages.
# Import packages
from dash import Dash, html, dash_table, dcc, callback, Output, Input
import pandas as pd
import plotly.express as px

# Incorporate data
df = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/gapminder2007.csv')

# Initialize the app
app = Dash()

# App layout
app.layout = [
    html.Div(children='My First App with Data, Graph, and Controls'),
    html.Hr(),
    dcc.RadioItems(options=['pop', 'lifeExp', 'gdpPercap'], value='lifeExp', id='controls-and-radio-item'),
    dash_table.DataTable(data=df.to_dict('records'), page_size=6),
    dcc.Graph(figure={}, id='controls-and-graph')
]

# Add controls to build the interaction
@callback(
    Output(component_id='controls-and-graph', component_property='figure'),
    Input(component_id='controls-and-radio-item', component_property='value')
)
def update_graph(col_chosen):
    fig = px.histogram(df, x='continent', y=col_chosen, histfunc='avg')
    return fig
# Run the app
app.run(jupyter_mode="external")

Dash app running on http://127.0.0.1:8050/

VS Code / Cursor Port Forwarding

If you’re using VS Code or Cursor with a remote connection:

  • Open the Ports panel (View → Ports or Ctrl+Shift+P → “Forward a Port”)

  • Click “Forward a Port” and enter 8050

  • Click on the forwarded URL that appears