đź“Ł Dash v1.12.0 Release - Pattern-Matching Callbacks Fixes, Shape-drawing, new DataTable conditional formatting options, prevent_initial_call, and more

Update: version 1.16 has been released since this was posted.

Dash v1.12.0 is a minor release featuring:

Official Changelog :arrow_right: Dash v1.12.0

Public Service Announcement: Make your Dash apps faster with Plotly.py 4.7

This version of Dash uses Plotly.js 1.54, which is the version targeted by the just-released Plotly.py 4.7.

Plotly.py 4.7 runs significantly faster than 4.6 and prior in many situations, including importing and figure-generation. The biggest performance gains will be felt for users of Python 3.7, as we take advantage of PEP-562 for lazy submodule imports, but earlier versions of Python benefit from the other improvements.

Since Dash depends on Plotly.py, upgrading Plotly.py to version 4.7 can Dash app boot times, making for a much more pleasant developer experience when using hot-reloading… Upgrade to Plotly.py 4.7 and Python 3.7 today if you can!

Shape-drawing in dcc.Graph figures, triggering relayoutData events

Plotly.js 1.54 includes new layout dragmodes in order to draw shapes such as rectangles, lines, open or closed paths etc. These shapes can also be modified or deleted when activated. Adding or modifying shapes triggers a relayoutData event of the corresponding figure. Below we show a minimal app for measuring and displaying the lengths of objects in a Dash app. The new dragmodes are called 'drawline', 'drawopenpath', 'drawclosedpath', 'drawcircle', 'drawrect', and 'eraseshape' (with self-explanatory names).

This new shape-drawing feature paves the way to powerful image processing Dash applications and we are very excited about it!
This development has been funded by the Chan-Zuckerberg Initiative (CZI) Essential Open-Source Software for Science program and we would like to thank CZI for their support.

import dash
from dash.dependencies import Input, Output, State
import dash_html_components as html
import dash_core_components as dcc
import plotly.express as px
from skimage import data
import math

app = dash.Dash(__name__)

img = data.coins() # or any image represented as a numpy array

fig = px.imshow(img, color_continuous_scale='gray')
layout = {}
for key in fig.layout:
    layout[key] = fig.layout[key]
layout['dragmode'] = 'drawline'
layout['newshape'] = {'line':{'color':'cyan'}}
fig_dict = {'data':fig.data, 'layout':layout}


app.layout = html.Div(children=[
        dcc.Graph(
            id='graph', 
            figure=fig_dict,
            config={'modeBarButtonsToAdd':['drawline']}),
        html.Pre(id='content', children='Length of lines (pixels) \n')
        ], style={'width':'25%'})


@app.callback(
    dash.dependencies.Output('content', 'children'),
    [dash.dependencies.Input('graph', 'relayoutData')],
    [dash.dependencies.State('content', 'children')])
def shape_added(fig_data, content):
    if fig_data is None:
        return dash.no_update
    if 'shapes' in fig_data:
        line = fig_data['shapes'][-1]
        length = math.sqrt((line['x1'] - line['x0']) ** 2 +
                           (line['y1'] - line['y0']) ** 2)
        content += '%.1f'%length + '\n'
    return content


if __name__ == '__main__':
    app.run_server(debug=True)

Improve DataTable conditional styling

It is now possible to customize the style of cells based on their state (active, selected) and to apply the same style to an array of column_id, row_index, and header_index. This simplifies customization by reducing the number of styles that need to be defined and by making it possible to change the active/selected cells style without resorting to CSS overrides.

See conditional formatting documentation: đź“Ł DataTable Conditional Formatting Documentation

import dash
import dash_table
import dash_html_components as html
import pandas as pd
import random

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

app = dash.Dash(__name__)

data = df.to_dict('records')

app.layout = html.Div([
    dash_table.DataTable(
        columns=[dict(name=i, id=i) for i in df.columns],
        data=data,
        style_data={
            "fontFamily": "serif"
        },
        style_data_conditional=[
            {
                "if": {
                    "state": "active"
                },
                "backgroundColor": "#3399FF",
                "border": "#3399FF"
            },
            {
                "if": {
                    "state": "selected"
                },
                "backgroundColor": "#99CCFF",
                "border": "#99CCFF"
            }, {
                "if": {
                    "column_id": ["pop", "lifeExp", "gdpPercap"]
                },
                "backgroundColor": "#f8f8f8",
                "fontFamily": "monospace"
            }, {
                "if": {
                    "row_index": [0, 1, 2, 3, 4]
                },
                "backgroundColor": "#fff0f0",
                "color": "darkred"
            }
        ]
    )
])

app.run_server(debug=True)

Prevent initial callback from firing

When Dash renders a new component(s), whether on page load or as part of a callback that returns children, Dash fires off all of the callbacks that have those components as inputs. For Input & State, Dash looks at the initial values supplied in the component and, if no values were supplied, it will pass None into your callback.

For most use cases, this keeps your application consistent: you can omit pre-computing your output values and let Dash “fill in” the outputs through your callbacks.

You can now skip this initialization behavior by passing in prevent_initial_call=True to your @app.callback or, if you want to skip this for all of your callbacks, prevent_initial_callbacks=True to app = dash.Dash(__name__, prevent_initial_callbacks=True)

There are three main use cases for using this:

  1. Your application doesn’t display any output results on page load. For example, you might have an empty form with a button and might not want to display any output. Current, you might be using raise dash.exceptions.PreventUpdate or return [dash.no_update]

    @app.callback(Output('my-graph', 'figure'), [Input('button', 'n_clicks')])
    def update_graph(n_clicks):
        if n_clicks is None:
            raise dash.exceptions.PreventUpdate
    

    Now, you can prevent your callback from even being fired in the first place with:

    @app.callback(
        Output('my-graph', 'figure'),
        [Input('button', 'n_clicks')],
        prevent_initial_call=True)
    def update_graph(n_clicks):
        if n_clicks is None:
            raise dash.exceptions.PreventUpdate
    

    This will improve the performance by preventing the network request(s) from occurring. With pattern-matching callbacks, you may have 10s or 100s of components that might otherwise fire off these requests.

  2. Some components have “derived properties” where their property’s initial value is computed on page load. dcc.Location is like this: when the page loads, it dynamically checks for the pathname of the rendered page and then fires an update. Currently, on page load, your callbacks will be fired twice: once with None (as per Dash’s callback initialization routine) and immediately after once dcc.Location has figured out the pathname. You can now skip the None call with prevent_initial_call=True and just wait for the proper pathname to come through.

  3. You may want to speed up your application by precomputing the output values instead of computing them on-the-fly on every page load.

Previous Releases

New Documentation & Videos


These features & bug fixes were funded directly by organizations sponsoring our development or requested by our Dash Enterprise customers. Want to learn more? Get in touch.

9 Likes

I love the prevent_initial_callbacks feature! :heart_eyes:

3 Likes

I’ve just updated this announcement to add a “public service announcement” regarding Plotly.py 4.7…

Upgrading to Plotly.py 4.7 makes Dash apps run faster!

Get Plotly.py 4.7 now and upgrade to Python 3.7 if you can, to get the biggest performance boosts :slight_smile:

2 Likes

WOW! The difference in performance after upgrading to the latest version is amazing!

Thanks Dash/Plotly team :clap:

4 Likes

When is the next release (1.13.0?) due?
I saw https://github.com/plotly/dash/pull/1248 and https://github.com/plotly/dash/pull/1240 being merged and am eager to include this in my app. Should I wait or rather build the package myself for the time being?

We release every 2-6 weeks, so sometime within the next 4 weeks. We don’t have a date yet.

Thanks for clarifying. I would opt for building myself then. I’ve tried to pip install git+http://github...the repo but only dash gets built, not dash-renderer. Is there a how-to for this somewhere? or a packaging script?

See https://github.com/plotly/dash/blob/dev/CONTRIBUTING.md

uhh, sorry, that is embarrassing. Could have really looked myself. Thanks for the pointer.