IPython Widgets and Voila


From Notebook to Web-App with Voilà

Author: Marcel Baltruschat (@GitHub)
Date: 15.09.2022
License: MIT


Installation with Conda

conda create -n voila -c conda-forge python=3.10 voila jupyterlab pandas numpy rdkit ipywidgets plotly py3dmol

Imports

In [1]:
import io
import sys

import ipywidgets as widgets
import pandas as pd
import plotly
import plotly.graph_objects as go
import py3Dmol
import rdkit
import voila
from IPython.display import HTML, clear_output, display
from ipywidgets import interact, fixed
from rdkit.Chem import AllChem as Chem

Used Versions

In [2]:
print(f'Python: {sys.version.split("|")[0]}\nipywidgets: {widgets.__version__}\nPlotly: {plotly.__version__}\nRDKit: {rdkit.__version__}\npy3Dmol: {py3Dmol.__version__}\nVoilà: {voila.__version__}')
Python: 3.10.6 
ipywidgets: 7.7.2
Plotly: 5.10.0
RDKit: 2022.03.5
py3Dmol: 1.8.1
Voilà: 0.3.6

How to use Voilà?

There are two options to create a Voilà application.

  1. To run a notebook as a standalone application, in the commanline navigate to the directory that contains the notebook and then use the command voila <path-to-notebook> <options>.
  2. To use Voilà as a Jupyter server extension on a running Jupyter notebook / lab server, go to the following URL: <url-of-my-server>/voila. For example, if Jupyter lab was running at http://localhost:8888/lab, then Voilà would be accessed at http://localhost:8888/voila.

Widget Examples

In [3]:
w = widgets.IntSlider()
w_frs = widgets.FloatRangeSlider(
    value=[5, 7.5],
    min=0,
    max=10.0,
    step=0.1,
    description='Test:',
    disabled=False,
    continuous_update=False,
    orientation='horizontal',
    readout=True,
    readout_format='.2f',
)

w_rb = widgets.RadioButtons(
    options=['pepperoni', 'pineapple', 'anchovies'],
    value='pineapple',
    layout={'width': 'max-content'},
    style={'description_width': 'initial'},
    description='Pizza topping:',
    disabled=False,
)

w_s = widgets.Dropdown(
    options=['Linux', 'Windows', 'macOS'],
    value='macOS',
    description='OS:',
    disabled=False,
)
In [4]:
accordion_s = widgets.Accordion(children=[w, w_frs])
accordion_s.set_title(0, 'Int slider')
accordion_s.set_title(1, 'Float range slider')

accordion_c = widgets.Accordion(children=[w_rb, w_s])
accordion_c.set_title(0, 'Radio buttons')
accordion_c.set_title(1, 'Dropdown selection')

tab = widgets.Tab(children=[accordion_s, accordion_c])
tab.set_title(0, 'Sliders')
tab.set_title(1, 'Selections')
tab

Example Plot

In [5]:
trace = go.Heatmap(z=[[1, 20, 30, 50, 1], [20, 1, 60, 80, 30], [30, 60, 1, -10, 20]],
                   x=['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday'],
                   y=['Morning', 'Afternoon', 'Evening'])
data = [trace]
layout = go.Layout(title='Activity Heatmap')
figure = go.Figure(data=data, layout=layout)
f2 = go.FigureWidget(figure)
f2
In [6]:
def drawit(m, p, confId=-1):
    mb = Chem.MolToMolBlock(m, confId=confId)
    p.removeAllModels()
    p.addModel(mb, 'sdf')
    p.setStyle({'stick': {}})
    p.setBackgroundColor('0xeeeeee')
    p.zoomTo()
    return p.show()


m = Chem.MolFromSmiles('COc1ccc2nc([nH]c2c1)[[email protected]](=O)Cc1ncc(C)c(OC)c1C')
m = Chem.AddHs(m)
Chem.EmbedMultipleConfs(m, numConfs=10, randomSeed=0xf00d, useExpTorsionAnglePrefs=True, useBasicKnowledge=True)
# align to one of the ring systems:
Chem.AlignMolConformers(m, m.GetSubstructMatch(Chem.MolFromSmarts('c1[nH]c2ccccc2n1')))

# now construct the view and interactive widget:
p = py3Dmol.view(width=400, height=400)
interact(drawit, m=fixed(m), p=fixed(p), confId=(0, m.GetNumConformers() - 1));

Display Proteins

In [7]:
view = py3Dmol.view(query='pdb:1hvr')
view.setStyle({'cartoon': {'color': 'spectrum'}})
view

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
jupyter labextension install jupyterlab_3dmol

Out[7]:
<py3Dmol.view at 0x7fc6b0cee980>

Small "real world" example

You can use the hERG dataset for testing.
In [8]:
uploader = widgets.FileUpload(accept='csv', multiple=False)
button = widgets.Button(description='Show')
hbox = widgets.HBox((uploader, button))
output = widgets.Output()
vbox = widgets.VBox((hbox, output))
vbox
In [9]:
col_box = widgets.HBox()
col_box
In [10]:
df = None


def show_df(b):
    with output:
        clear_output()
    try:
        uploaded_file = uploader.value[list(uploader.value.keys())[0]]
        global df
        df = pd.read_csv(io.BytesIO(uploaded_file['content']), delimiter=';')
        with output:
            print(f'\nDataFrame size: {len(df)}. Showing first two lines:\n')
            display(HTML(df.head(2).to_html()))
        num_cols = [c for c, d in df.dtypes.items() if str(d).startswith('int') or str(d).startswith('float')]
        col_menu = widgets.Dropdown(options=num_cols)
        button2 = widgets.Button(description='Plot column values')
        button2.on_click(show_plot)
        col_box.children = (col_menu, button2)
    except:
        with output:
            print('Please select a CSV file with ; as separator.')


button.on_click(show_df)
In [11]:
def show_plot(b):
    col = col_box.children[0].value
    trace = go.Histogram(x=df[col])
    data = [trace]
    layout = go.Layout(title=f'Histogram for column {col}')
    figure = go.Figure(data=data, layout=layout)
    plot_box.children = (go.FigureWidget(figure),)
In [12]:
plot_box = widgets.Box()
plot_box
In [ ]: