Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
0
Help us understand the problem. What is going on with this article?
@ikentoh

Dashでdcc.CheckListのチェック状態を全選択・全解除するボタンの実装

More than 1 year has passed since last update.

日本語と英語で検索しても全然ロクな文献がヒットしなかったので、検索ひっかかるように併記しておきます

Adding a 'Select All' and 'Remove All' Button to a dcc.CheckList.

達成したいこと

  • 複数個のチェックボックスを、別に用意したボタンによって全選択・全解除する

結果だけ知りたい方は下へスクロールしてください

やったこと

dcc.CheckListのvalueをcallbackで制御する

チェックリストとボタンだけのさっぱりした環境で調査

スクリーンショット 2020-02-19 15.43.06.png

当初のソース / at first
python {ファイル名.py} access localhost:{任意のポート}
で起動できます

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


server = Flask(__name__)
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = dash.Dash(__name__, external_stylesheets=external_stylesheets, 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='チェックリストのテスト'),
    dcc.Location(id='location', refresh=False),
    html.Div(className='main-block', children=[checklists]),
    html.Div(className='second', children=[
        html.Button('全選択', id='filter-check-button', className='filter_button', n_clicks=0),
        html.Button('全解除', id='filter-remove-button', className='filter_button', n_clicks=0)
        ])
    ])


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

    if all_check:
        return ['NYC', 'MTL', 'SF']
    if all_remove:
        return []



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

CheckListのvalueへ任意のリストを返却すればいけるっしょ!と思ったが違う

全選択ボタンを押下したとき return ['NYC', 'MTL', 'SF']

dash.exceptions.InvalidCallbackReturnValue: Invalid number of output values for ..checklist-states.value...
 Expected 1 got 3

全解除ボタンを押下したとき return []

dash.exceptions.InvalidCallbackReturnValue: Invalid number of output values for ..checklist-states.value...
 Expected 1 got 0

どうも勝手に皮むきするっぽいので応急処置

if all_check:
    return [['NYC', 'MTL', 'SF']]
if all_remove:
    return [[]]

動くもののエラーは出る
どうもボタンがロードされる時?に一度実行されるっぽく、もろもろが未定義な状態で返り値を渡してしまうためのエラーっぽい

dash.exceptions.InvalidCallbackReturnValue: The callback ..checklist-states.value.. is a multi-output.
Expected the output type to be a list or tuple but got None.

クリック以外で呼ばれたときを明示的に対処する

checklistのvalueをStateを使って読み込む

callbackにState
    @app.callback(
    [Output('checklist-states', 'value')],
    [Input('filter-check-button', 'n_clicks'),
    Input('filter-remove-button', 'n_clicks')],
    [State('checklist-states', 'value')]    # callbackが発生したときに値を取得する
)
def update_check(all_check, all_remove, value_checking):

どっちもNoneなら元々valueに入ってる値を返す
皮むき対策も忘れずに

    if all_check:
        return [['NYC', 'MTL', 'SF']]

    if all_remove:
        return [[]]

    if all_check is None and all_remove is None:
        return [value_checking]    # 皮むき対策

ボタンを何回もクリックする

n_clicksがダメっぽい

いけた!と思ったらクリック1発目しか機能しない
調べてみるとn_clickはクリック回数をカウントするという機能らしく、クリックすると0,1,2,3,4...と値が増えていくらしい

これはこの野郎…!と思いながら撮ったSS
スクリーンショット 2020-02-19 17.38.41.png

現在Dashには、そもそもクリックされたことだけを検知する機能は実装されていないらしい
代わりに クリックしたunixtimestamp(ミリ秒まで)を取得する n_clicks_timestampというものが用意されているので、それを使用する

n_clicks_timestampを使う

イベントが発生したタイミングと今が1秒以内ならオッケーという処理にしておく

    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]

結果

𝐬𝐮𝐜𝐜𝐞𝐬𝐬

8nd91-kgmbx.gif

最終的なソース / in the end

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__)
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = dash.Dash(__name__, external_stylesheets=external_stylesheets, 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='チェックリストのテスト'),
    dcc.Location(id='location', refresh=False),
    html.Div(className='main-block', children=[checklists]),
    html.Div(className='second', children=[
        html.Button('全選択', id='filter-check-button', className='filter_button'),
        html.Button('全解除', 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)
0
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ikentoh
オタク

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
0
Help us understand the problem. What is going on with this article?