[PYTHON] Extract components and callbacks from app.py with plotly Dash

Thing you want to do

――Create a web page of a reasonable size with dash ――It's not easy to write everything in app.py, so I want to divide it

As in the example, I can not find the document at all in English, so I will write it together

splitting callback and components in multiple files use the : deepl

By the way, the official sample itself is a lot, but there are always hundreds of lines of app.py.

It's been about 2 months since I touched dash, so please let me know if there is a better solution.

What i did

Think about the structure of the file

--I want to cut out at least components and callbacks ――It seems that you need someone to manage the components and callbacks that will increase more and more.

Early story ↓ This

.
├── app.py
├── assets
│   ├── common.css
│   └── default.css
└── src
    └── some_utils.py

↓ It looks like this should be done

.
├── app.py
├── assets
│   ├── common.css
│   └── default.css
│
└── src
    ├── callback.py
    ├── callbacks
    │   ├── hoge.py
    │   ├── fuga.py
    │   └── poyo.py
    │
    ├── layout.py
    ├── components
    │   ├── fizz.py
    │   ├── buzz.py
    │   ├── boo.py
    │   └── bar.py
    │
    ├── some_utils.py
    └── utils

Organize app.py

I borrow the source of my other article because it's a big deal

Implementation of button to select / cancel all check status of dcc.CheckList in Dash

python app.py

Start with and can be viewed at http: // localhost: 8050

↓ It becomes such a screen スクリーンショット 2020-02-19 15.43.06.png

app.py


import dash
import dash_core_components as dcc
import dash_html_components as html
import dash_table
from dash.dependencies import Input, Output, State
from flask import Flask, request

import time

server = Flask(__name__)
app = dash.Dash(__name__, server=server)

app.title = 'checklist-test'

selected_key = None


checklists = dcc.Checklist(
    id='checklist-states',
    options=[
        {'label': 'New York City', 'value': 'NYC'},
        {'label': 'Montréal', 'value': 'MTL'},
        {'label': 'San Francisco', 'value': 'SF'}
    ],
    value=['MTL', 'SF']
)


app.layout = html.Div(id='main',children=[
    html.H1(children='Checklist test'),
    dcc.Location(id='location', refresh=False),
    html.Div(className='main-block', children=[checklists]),
    html.Div(className='second', children=[
        html.Button('Select all', id='filter-check-button', className='filter_button'),
        html.Button('Cancel all', id='filter-remove-button', className='filter_button')
        ])
    ])


@app.callback(
    [Output('checklist-states', 'value')],
    [Input('filter-check-button', 'n_clicks_timestamp'),
    Input('filter-remove-button', 'n_clicks_timestamp')],
    [State('checklist-states', 'value')]
)
def update_check(all_check, all_remove, checking):

        if not all_check is None:
            if (time.time() * 1000 - all_check) < 1000:
                return [['NYC', 'MTL', 'SF']]

        if not all_remove is None:
            if (time.time() * 1000 - all_remove) < 1000:
                return [[]]

        if all_check is None and all_remove is None:
            return [checking]



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

Since the layout and callback are linked to the dash object ʻapp, it seems that it will be processed by passing ʻapp directly to another function.

That's why I'm throwing work to myself in the future and scraping app.py as much as I can

app.py


import dash
from flask import Flask

from src.layout import layout
from src.callback import callback


server = Flask(__name__)
app = dash.Dash(__name__, server=server)

app.title = 'checklist-test'

#components and callback definitions
app = layout(app)
callback(app)


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

tips: Callback registration must be absolutely after the component definition Since the callback with null input value runs at the moment of import, an error will occur if the component you are looking at in Input / Output / State does not exist.

Components and layout

Needless to say, please change the file name to the one you are satisfied with.

Layout cutout

Assign html element to the attribute layout of ʻapp` received as an argument

layout.py


import dash_core_components as dcc
import dash_html_components as html


def layout(app):

    checklists = dcc.Checklist(
        id='checklist-states',
        options=[
            {'label': 'New York City', 'value': 'NYC'},
            {'label': 'Montréal', 'value': 'MTL'},
            {'label': 'San Francisco', 'value': 'SF'}
        ],
        value=['MTL', 'SF']
    )

    # app.Assign elements to layout
    app.layout = html.Div(id='main', children=[
        html.H1(children='Checklist test'),
        dcc.Location(id='location', refresh=False),
        html.Div(className='main-block', children=[checklists]),
        html.Div(className='second', children=[
            html.Button('Select all', id='filter-check-button', className='filter_button'),
            html.Button('Cancel all', id='filter-remove-button', className='filter_button')
        ])
    ])

    return app

Callback cutout

tips: If you try to call ʻupdate_checkdirectly, you will not be able to interpret the decorator part well, so you need to go one step deeper and enclose each decorator in a different function. (In short,app of @ app.callback () `must be able to resolve the name)

callback.py


from dash.dependencies import Input, Output, State

import time


def callback(app):

    @app.callback(
        [
            Output('checklist-states', 'value')
        ],
        [
            Input('filter-check-button', 'n_clicks_timestamp'),
            Input('filter-remove-button', 'n_clicks_timestamp')
        ],
        [
            State('checklist-states', 'value')
        ]
    )
    def update_check(all_check, all_remove, checking):

        if all_check is not None:
            if (time.time() * 1000 - all_check) < 1000:
                return [['NYC', 'MTL', 'SF']]

        if all_remove is not None:
            if (time.time() * 1000 - all_remove) < 1000:
                return [[]]

        if all_check is None and all_remove is None:
            return [checking]

What to do when you have more components and callbacks

Further division of components

Manage the title, checklist, and button separately Assuming that layout manages only blocks and spacing

tips: Attribute ʻid is not required for html.Div () `

layout.py


from src.components import title, checklist, button

import dash_core_components as dcc
import dash_html_components as html


def layout(app):

    app.layout = html.Div(id='main', children=[

        html.Div(id='title-block', children=[title.layout()]),

        dcc.Location(id='location', refresh=False),

        html.Div(id='center-block', children=[
            html.Div(children=checklist.layout()),
            html.Div(children=button.layout())
        ])
    ])

    return app

title.py


import dash_html_components as html


def layout():

    return html.H1(id='title', children='Checklist test')

checklist.py


import dash_core_components as dcc


def layout():

    return dcc.Checklist(
        id='checklist-states',
        options=[
            {'label': 'New York City', 'value': 'NYC'},
            {'label': 'Montréal', 'value': 'MTL'},
            {'label': 'San Francisco', 'value': 'SF'}
        ],
        value=['MTL', 'SF']
    )

button.py


import dash_html_components as html


def layout():

    return html.Button('Select all', id='filter-check-button', className='filter_button'), html.Button('Cancel all', id='filter-remove-button', className='filter_button')

Further splitting of callbacks

Cut out for each function in the same way

callback.py

from src.callbacks import check_and_remove # hoge, fuga...

def callback(app):

    check_and_remove.register(app)
    # hoge.register(app)
    # fuga.register(app)

check_and_remove.py

from dash.dependencies import Input, Output, State

import time


def register(app):

    @app.callback(
        [
            Output('checklist-states', 'value')
        ],
        [
            Input('filter-check-button', 'n_clicks_timestamp'),
            Input('filter-remove-button', 'n_clicks_timestamp')
        ],
        [
            State('checklist-states', 'value')
        ]
    )
    def update_check(all_check, all_remove, checking):

        if all_check is not None:
            if (time.time() * 1000 - all_check) < 1000:
                return [['NYC', 'MTL', 'SF']]

        if all_remove is not None:
            if (time.time() * 1000 - all_remove) < 1000:
                return [[]]

        if all_check is None and all_remove is None:
            return [checking]

Final structure

.
├── app.py
└── src
    ├── callback.py
    ├── callbacks
    │   └── check_and_remove.py
    ├── components
    │   ├── button.py
    │   ├── checklist.py
    │   └── title.py
    └── layout.py

Recommended Posts

Extract components and callbacks from app.py with plotly Dash
[python] Extract text from pdf and read characters aloud with Open-Jtalk
Extract database tables with CSV [ODBC connection from R and python]
[Natural language processing] Extract keywords from Kakenhi database with MeCab-ipadic-neologd and termextract
Extract images from cifar and CUCUMBER-9 datasets
Extract Japanese text from PDF with PDFMiner
Extract data from a web page with Python
Extract images and tables from pdf with python to reduce the burden of reporting