2 votes

La liste déroulante du tableau de bord se ferme après un clic

Je ne veux pas que ma liste déroulante se ferme après avoir choisi une valeur, je veux qu'elle reste ouverte sur ma page. J'utilise dcc.Dropdown

dcc.Dropdown(id='job-type', options=self.options, placeholder='Select one or more Event(s)', value=self.job_type, multi=True)

2voto

vestland Points 3267

Basé sur :

Je ne veux pas que ma liste déroulante se ferme après avoir choisi une valeur, je veux qu'elle reste ouverte sur ma page. qu'elle reste ouverte sur ma page.

... il me semble que vous recherchez en fait les caractéristiques et les fonctionnalités d'un système de gestion de l'information. dcc.Checklist :

import dash_core_components as dcc
dcc.Checklist(
    options=[
        {'label': 'New York City', 'value': 'NYC'},
        {'label': 'Montréal', 'value': 'MTL'},
        {'label': 'San Francisco', 'value': 'SF'}
    ],
    value=['NYC', 'MTL']
)

Ce qui, dans ce cas, produirait :

enter image description here

Et cela ferait exactement ce que vous décrivez en ce qui concerne la fonctionnalité :

  1. Vous pouvez sélectionner toutes, certaines ou aucune des options.
  2. La liste de contrôle ne se ferme pas lorsque vous effectuez une sélection.

Voici un exemple qui utilise la fonction intégrée px.stocks jeu de données :

enter image description here

Cet exemple particulier ne fera que reproduire les fonctionnalités déjà existantes de la légende des figures, mais cette configuration devrait constituer un bon point de départ si vous souhaitez vous affranchir des limites de l'utilisation de la légende.

Code complet :

from jupyter_dash import JupyterDash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State, ClientsideFunction
import dash_bootstrap_components as dbc
import dash_core_components as dcc
import dash_html_components as html
import pandas as pd
import plotly.graph_objs as go
import plotly.express as px

import warnings
warnings.simplefilter(action='ignore', category=FutureWarning)

# jupyterdash setup
app = JupyterDash(external_stylesheets=[dbc.themes.SLATE])

# data and figure setup
df = px.data.stocks()
df = df.set_index('date')
fig1 = px.line(df, x = df.index, y = df.columns, template = 'plotly_dark')
fullnames = {'GOOG':'Google',
             'AAPL': 'Apple',
             'AMZN': 'Amazon',
             'FB':'Facebook',
             'NFLX':'Netflix',
             'MSFT':'Microsoft'}

# app layout
app.layout = dbc.Container([
    dbc.Row([
        # https://hackerthemes.com/bootstrap-cheatsheet/
        dbc.Col([html.H1("DropDown < CheckList", className = 'text-left text-success'), ], width = 8),

    ]),

    dbc.Row([dbc.Col([dcc.Checklist(id = 'Check1', 
                                           options=[{"label": fullnames[col], "value": col} for col in df.columns],
                                           value=df.columns),
                                        ], width = 2),

            dbc.Col([dcc.Graph(id='Graph1', figure = fig1)], width = 10),
             ],
    ),

])

# interactivity through callbacks
@app.callback(
    Output('Graph1', 'figure'),
    [Input('Check1', 'value')])
def subset_graph(value):

    dfs = df[value]
    fig2 = px.line(dfs, x = dfs.index, y = dfs.columns, template = 'plotly_dark')
    return fig2

app.run_server(mode='inline', port = 9099)

Suggestion 2


À la suite d'un commentaire de John Collins, j'ai bouclé l'opération dcc.Checklist en un html.Div avec la configuration suivante pour faire défiler la liste de contrôle lorsqu'il y a trop d'éléments à afficher en même temps :

html.Div(dcc.Checklist(id = 'Check1', 
                       options=[{"label": col, "value": col} for col in df.columns],
                       value=df.columns,
                       labelStyle={'display': 'inline-block', 'width': '12em', 'line-height':'0.5em'}
                                            ),
        style = {"overflow-y":"scroll",
                   "overflow-x":'hidden',
                   "height": '480px'
                   }
        )

Plot 2 :

enter image description here

Code complet pour Plot 2 :

from jupyter_dash import JupyterDash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State, ClientsideFunction
import dash_bootstrap_components as dbc
import dash_core_components as dcc
import dash_html_components as html
import pandas as pd
import plotly.graph_objs as go
import plotly.express as px
import numpy as np

import warnings
warnings.simplefilter(action='ignore', category=FutureWarning)

# jupyterdash setup
app = JupyterDash(external_stylesheets=[dbc.themes.SLATE])

# data and figure setup
# data
start = 1980
ncols = 40
nrows = 365
cols = [str(i) for i in np.arange(start, start+ncols)]
df = pd.DataFrame(np.random.randint(-1,2, (nrows,ncols)), columns = cols).cumsum()
df.iloc[0] = 0

# figure
fig1 = px.line(df, x=df.index, y=cols,
#               width=820,
              height=480,
              template = 'plotly_dark'
             )

# app layout
app.layout = dbc.Container([
    dbc.Row([
        # https://hackerthemes.com/bootstrap-cheatsheet/
        dbc.Col([html.H1("DropDown < CheckList", className = 'text-left text-success', style = {'padding': 25})], width = 8),

    ]),

    dbc.Row([dbc.Col([html.Div(dcc.Checklist(id = 'Check1', 
                                           options=[{"label": col, "value": col} for col in df.columns],
                                           value=df.columns,
                                           labelStyle={'display': 'inline-block', 'width': '12em', 'line-height':'0.5em'}
                                            ),
                      style = {"overflow-y":"scroll",
                               "overflow-x":'hidden',
                               "height": '480px'
                              }
                              )], width = 3),

            dbc.Col([dcc.Graph(id='Graph1', figure = fig1)], width = 8),
             ],
           ),
])

# interactivity through callbacks
@app.callback(
    Output('Graph1', 'figure'),
    [Input('Check1', 'value')])
def subset_graph(value):
    dfs = df[value]
    fig2 = px.line(dfs, x = dfs.index, y = dfs.columns, template = 'plotly_dark')
    return fig2

app.run_server(mode='inline', port = 9099)

1voto

johnxcollins Points 101

Solution n° 2

Que diriez-vous plutôt d'une autre approche possible, une dcc.Checklist jumelé à un html.Summary (qui tire parti de la collapsibilité intégrée, imitant ainsi un menu déroulant) à l'intérieur d'un fichier html.Details composant ? Cela répond beaucoup mieux à ce que vous demandez - une sorte de menu déroulant qui ne se fermera pas automatiquement après chaque sélection de l'une de ses options.

Par exemple,

Données fictives

Un fichier local appelé "jobs.csv", délimité par des tabulations, avec le contenu suivant :

code    options job_type
13-2011.00  Accountants and Auditors        Business and Financial Operations
27-2011.00  Actors  Arts, Design, Entertainment, Sports, and Media
15-2011.00  Actuaries   Computer and Mathematical
29-1291.00  Acupuncturists  Healthcare Practitioners and Technical
55-1011.00  Air Crew Officers   Military Specific
23-1022.00  Arbitrators, Mediators, and Conciliators    Legal
17-1011.00  Architects, Except Landscape and Naval  Architecture and Engineering
19-2011.00  Astronomers Life, Physical, and Social Science
33-3011.00  Bailiffs    Protective Service
51-3011.00  Bakers  Production
39-5011.00  Barbers Personal Care and Service
15-2099.01  Bioinformatics Technicians  Computer and Mathematical
25-1042.00  Biological Science Teachers, Postsecondary  Educational Instruction and Library
19-1029.00  Biological Scientists, All Other    Life, Physical, and Social Science
19-4021.00  Biological Technicians  Life, Physical, and Social Science
19-1029.04  Biologists  Life, Physical, and Social Science
51-8013.03  Biomass Plant Technicians   Production
11-3051.04  Biomass Power Plant Managers    Management
15-2041.01  Biostatisticians    Computer and Mathematical
15-1299.07  Blockchain Engineers    Computer and Mathematical
47-2011.00  Boilermakers    Construction and Extraction

Composant Quasi-"Dropdown

Dans layout.py :

children = [
html.Details(
    [
        html.Div(
            [
                dcc.Checklist(
                    id="jobs-multi-dropdown",
                    options=[
                        {"label": f"{job_title}", "value": f"{job_type}"}
                        for (job_title, job_type) in zip(
                            df_jobs.options, df_jobs.job_type
                        )
                    ],
                )
            ],
            className="updates-list",
        ),
        html.Summary(
            html.Code(f" JOBS"),
            style={"color": "rgb(24, 230, 112)"},
            className="updates-header",
        ),
    ],
    id="jobs-selection",
),
html.Br(),
html.Br(),
html.Div(
    [html.Button("Submit", id="jobs-selected", n_clicks=0)],
    style={"display": "flow-root"},
),
html.Br(),
html.H3("Job Types Selected:"),
html.Code(id="job-type"),
html.Br(),
]

Dans callbacks.py :

@app.callback(
    Output("job-type", "children"),
    [Input("jobs-selected", "n_clicks"), State("jobs-multi-dropdown", "value")],
)
def choose_job(n_click, job_types):
    """ Returns interactively the associated job "type"
    """
    if job_types:
        return [f"{n} {job_type}, " for (n, job_type) in enumerate(job_types)]
    else:
        return ["Select any number of jobs from the list above."]

Before any selections

Ensuite, après avoir cliqué sur le composant "emplois", celui-ci se développe en un composant de liste déroulante qui est en fait une liste de contrôle dcc.checklist :

Scrollable dropdown

Ensuite, après avoir cliqué sur le bouton "Envoyer", les types correspondants apparaissent :

After clicking submit

Pour fermer la "liste déroulante", il suffit de cliquer à nouveau sur le composant arrondi "Emplois", qui est l'élément de la liste déroulante. html.Summary composant du tableau de bord. Et les "détails" sont les dcc.Checklist .

Habituellement, il y a par défaut une flèche, ou plutôt un triangle, symbole qui "vrille" (je suppose que c'est le terme courant) : https://developer.mozilla.org/en-US/docs/Web/HTML/Element/details ), ce qui permet de signaler à l'utilisateur qu'il peut cliquer dessus pour le développer. Pour une raison quelconque, dans mes captures d'écran, il a été rendu invisible, mais il suffit de copier le code que je fournis pour qu'il apparaisse.

Dans assets/custom.css :

@import url('https://fonts.googleapis.com/css?family=Cinzel:300,400,500,600,700|Muli:200,300,400,500,600|Open+Sans:200,300,400,500,600|Oswald:200,300,400,500,600&amp;subset=latin-ext');
@import url('https://fonts.googleapis.com/css?family=Montserrat:200,200i,300,300i,400,400i,500,500i');
@import url('https://fonts.googleapis.com/css?family=Raleway:300,400,500,600,800&amp;subset=latin-ext');
@import url('https://fonts.googleapis.com/css?family=Roboto+Mono:200,300,400,500');
h1 { font-size: 3.5rem; font-family: 'Montserrat'; text-rendering: optimizeLegibility; color: #0d04a5; font-weight: 500; text-decoration: none; border-bottom: 0.0px solid gray; line-height: 4rem; text-decoration: underline }
h2 { font-family: 'Oswald', serif; color: var(--pph-color-8); cursor: default; font-weight: 300; font-size: 2rem; }
h3 { font-size: 2.0rem; font-family: 'Montserrat', sans-serif; font-weight: 300; color: rgb(32, 92, 188); cursor: default }
h4 { font-size: 1.5rem; font-family: 'Oswald', sans-serif; color: var(--pph-color-57); font-weight: 400; cursor: default }
h5 { font-size: 1.2rem; font-family: 'Muli', sans-serif; cursor: default }
h6 { font-size: 1.1rem; color: #333; font-weight: 400 }
@media (min-width:550px) input[type="checkbox"], input[type="radio"] {
    details#jobs-selection { display: inline-block; width: 80%; }
    ol.updates-list > li { margin-bottom: 5px; padding-left: 3%; margin-left: 8%; margin-right: 5%; list-style-type: auto; list-style-position: outside }
    summary { cursor: pointer }
text-rendering:optimizeLegibility; -moz-appearance: none; display: inline-block; background-color: #f1f1f1; color: #666; top: 10px; height: 30px; width: 30px; border: 0; border-radius: 50px; cursor: pointer; margin-right: 7px; outline: none; }
@keyframes glow {
    0% { text-shadow: 0 0 5px rgba(255, 255, 255, .5), 0 0 5px rgba(255, 255, 255, .5), 0 0 5px rgba(3, 242, 255, .5), 0 0 30px rgba(0, 230, 128, 0.4706), 0 0 5px rgba(255, 235, 59, .5), 0 0 5px rgba(0, 0, 255, .5), 0 0 15px rgba(3, 242, 255, .5) }
    50% { text-shadow: 0 0 5px rgba(0, 0, 255, .75), 0 0 5px rgba(238, 130, 238, .75), 0 0 5px rgba(187, 77, 255, 0.549), 0 0 30px rgba(77, 255, 192, .75), 0 0 5px rgba(255, 235, 59, .75), 0 0 5px rgba(128, 0, 128, .75), 0 0 15px rgba(187, 77, 255, 0.549) }
    75% { text-shadow: 0 0 5px rgba(255, 165, 0, .25), 0 0 5px rgba(255, 165, 0, .25), 0 0 5px rgba(230, 0, 115, .25), 0 0 30px rgba(230, 0, 115, .25), 0 0 5px rgba(255, 235, 59, .25), 0 0 5px rgba(255, 0, 0, .25), 0 0 15px rgba(230, 0, 115, .25) }
    100% { text-shadow: 0 0 20px rgba(127, 255, 0, .5), 0 0 20px rgba(0, 255, 0, .5), 0 0 10px rgba(255, 255, 0, .5), 0 0 20px rgba(255, 193, 7, .5), 0 0 10px rgba(255, 255, 0, .5), 0 0 20px rgba(255, 215, 0, .5), 0 0 20px rgba(77, 255, 192, .5) }
}
.updates-list { font-family: 'Roboto Mono'; font-size: .75rem; color: #064d56f0; text-aign: left; width: 30%; margin-left: 35%; padding: 10px; padding-bottom: 24px; border: 1px solid #3f51b54d; box-sizing: border-box; box-shadow: 0px 10px 25px -12px black; max-height: 400px; overflow: auto }
.updates-header:hover { animation: glow 2.5s infinite cubic-bezier(0.38, 0.39, 0.5, 0.51); }
.updates-header { font-weight: 500; border: 1px solid rgba(0, 0, 200, .33); width: 32%; border-radius: 30px; margin-left: 34%; box-sizing: border-box; display: block; text-align: center; margin-bottom: -2px; background-color: #00000085; letter-spacing: 8px; box-shadow: 0px 0px 8px -1px #00ff55ab; padding: 12px; padding-right: 2px; }
@media (min-width:550px)
.button:hover, button:hover, input[type="submit"]:hover, input[type="reset"]:hover, input[type="button"]:hover {
border-color: #00FFC050; background: #00000075; }
@media (min-width:550px)
.button, button, input[type="submit"], input[type="reset"], input[type="button"] {
display: inline-block; padding: 0 25px; color: #000000; text-align: center; font-size: 14px; font-weight: 500; font-family: "Cinzel", serif !important; line-height: 32px; text-decoration: none; white-space: nowrap; background-color: #ffffffcc !important; border-radius: 30px; border: 1px ridge #00000050; cursor: pointer; box-sizing: border-box; }


SUGGESTION ORIGINALE

(inclus juste à titre de référence, et pour montrer ce qu'il est possible de faire en utilisant l'outil de gestion de l'information de l'entreprise). dcc.Dropdown et l'interface utilisateur légèrement différente qui peut être mise en œuvre de cette façon - elle a l'avantage d'être consultable et claire).

Oui, il y a effectivement un dcc.Dropdown Le paramètre "multi", qui peut prendre la valeur booléenne True, devrait permettre à vos utilisateurs de sélectionner plusieurs options dans la liste déroulante.

_EDIT : La recherche est activée par défaut, il est donc assez rapide et pratique de simplement cliquer une fois sur la barre de liste déroulante pour étendre ses options, soit de la faire défiler et de la sélectionner avec un autre clic de souris (et alors oui, malheureusement, un autre clic de souris [par défaut] est nécessaire pour ré-étendre la liste des options) ou, l'utilisateur peut simplement commencer à taper les premières lettres de chaque option qu'il souhaite, et elles apparaîtront en surbrillance. Ainsi, la saisie de texte entraîne également le réétalement de la liste déroulante. Il suffit d'appuyer sur la touche "Entrée" pour ajouter une option en surbrillance dans la liste déroulante, puis de continuer à taper pour la sélection suivante, car le curseur est resté dans le champ de recherche de texte du composant de la liste déroulante. Il s'agit de mai Il est possible de modifier le comportement par défaut des CSS/JS qui font que le menu se ferme automatiquement après chaque sélection, mais cela peut être un peu délicat. Je pourrais essayer de vous aider à résoudre ce problème si vous pensez vraiment que c'est la fonctionnalité que vous souhaitez pour votre interface utilisateur._

Dans votre fichier de mise en page :

html.Br(),
html.H2("Jobs"),
dcc.Dropdown(
    id="jobs-multi-dropdown",
    value=None,
    clearable=True,
    optionHeight=50,
    multi=True,
    options=[
        {"label": f"{job_title}", "value": f"{job_type}"}
        for (job_title, job_type) in zip(df_jobs.options, df_jobs.job_type)
    ],
    placeholder="—Search all Jobs—",
),
html.Div(
    [html.Button("Submit", id="jobs-selected", n_clicks=0)],
    style={"display": "flow-root"},
),
html.Br(),
html.H3("Job Types Selected:"),
html.Code(id="job-type"),
html.Br(),

Je ne sais pas exactement ce que vous voulez faire avec l'information "type", mais j'ai créé une fonction de rappel déclenchée par un bouton "submit" et qui prend également en tant que State -Saisissez la ou les valeurs actuelles sélectionnées dans la liste déroulante, juste pour démontrer.

Vous pourriez ajouter quelque chose comme ceci à votre callbacks.py fichier :

@app.callback(
    Output("job-type", "children"),
    [Input("jobs-selected", "n_clicks"), State("jobs-multi-dropdown", "value")],
)
def choose_job(n_click, job_types):
    """ Returns interactively the associated job "type"
    """
    if job_types:
        return [f"{n} {job_type}, " for (n, job_type) in enumerate(job_types)]
    else:
        return ["Select any number of jobs from the list above."]

ce qui entraîne :

Dropdown as it first appears

L'utilisateur peut effectuer des recherches, supprimer les sélections précédentes et même effacer toutes les sélections en une seule fois en utilisant les petits "x".

Avis : Lorsqu'un élément est sélectionné, il est automatiquement retiré de la liste déroulante des options restantes. -

Making multiple selections

Ensuite, après avoir cliqué sur le bouton "Envoyer", les types correspondants apparaissent :

Showing the job types

Extra

Et si vous êtes curieux, voici un peu de CSS. Je ne suis pas sûr que cela puisse fonctionner tout seul, mais cela peut vous aider à vous familiariser avec les possibilités de personnalisation de Dash si vous ne l'êtes pas déjà (cela doit être placé dans un fichier .css situé dans un dossier appelé "assets" et Dash le trouvera automatiquement et remplacera ses valeurs par défaut par vos propres personnalisations) :

@import url('https://fonts.googleapis.com/css?family=Cinzel:300,400,500,600,700|Muli:200,300,400,500,600|Open+Sans:200,300,400,500,600|Oswald:200,300,400,500,600&amp;subset=latin-ext');
@import url('https://fonts.googleapis.com/css?family=Montserrat:200,200i,300,300i,400,400i,500,500i');
@import url('https://fonts.googleapis.com/css?family=Raleway:300,400,500,600,800&amp;subset=latin-ext');
@import url('https://fonts.googleapis.com/css?family=Roboto+Mono:200,300,400,500');

h1 {
    font-size: 3.5rem;
    font-family: 'Montserrat';
    text-rendering: optimizeLegibility;
    color: #0d04a5;
    font-weight: 500;
    text-decoration: none;
    border-bottom: 0.0px solid gray;
    line-height: 4rem;
    text-decoration: underline
}

h2 {
    font-family: 'Oswald', serif;
    color: #0a7fc2;
    cursor: default;
    font-weight: 300;
    font-size: 2rem;
}

h3 {
    font-size: 2.0rem;
    font-family: 'Montserrat', sans-serif;
    font-weight: 300;
    color: rgb(32, 92, 188);
    cursor: default
}

h4 {
    font-size: 1.5rem;
    font-family: 'Oswald', sans-serif;
    color: #1fadac;
    font-weight: 400;
    cursor: default
}

h5 {
    font-size: 1.2rem;
    font-family: 'Muli', sans-serif;
    cursor: default
}

h6 {
    font-size: 1.1rem;
    color: #333;
    font-weight: 400
}

.is-focused .Select-input>input {
    background-color: rgba(66, 66, 66, 0.46) !important;
    color: #46ffbb;
    margin-bottom: 1px;
    mix-blend-mode: hard-light;
}

.is-focused:not(.is-open)>.Select-control {
    cursor: pointer !important;
    border-color: rgba(10, 80, 250, 0.85);
    color: #0F00C6;
    box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 0 3px #46FFBB
}

.is-open .Select-arrow, .Select-arrow-zone:hover>.Select-arrow {
    border-top-color: #666
}

.is-open>.Select-control .Select-arrow {
    top: -2px;
    border-color: transparent transparent #999;
    border-width: 0 5px 5px
}

.is-open>.Select-control {
    border-color: #46ffbb #46ffefc7 #46ff6cd4 !important;
    border-radius: 5px !important;
    border-width: 3px;
}

.is-searchable.is-focused:not(.is-open)>.Select-control {
    cursor: text !important
}

.is-searchable.is-open>.Select-control {
    cursor: pointer !important;
    background: gba(255, 255, 255, 0.18) !important;
    !important
}

.button, button, input[type="submit"], input[type="reset"], input[type="button"] {
    display: inline-block;
    padding: 0 25px;
    color: #000000;
    text-align: center;
    font-size: 14px;
    font-weight: 500;
    font-family: "Cinzel", serif !important;
    line-height: 32px;
    text-decoration: none;
    white-space: nowrap;
    background-color: #ffffffcc !important;
    border-radius: 30px;
    border: 1px ridge #00000050;
    cursor: pointer;
    box-sizing: border-box
}

.button.button-primary, button.button-primary, input[type="submit"].button-primary, input[type="reset"].button-primary, input[type="button"].button-primary {
    color: #00000075 !important;
    background-color: #33C3F050;
    border-color: #33C3F0
}

.button.button-primary:hover, button.button-primary:hover, input[type="submit"].button-primary:hover, input[type="reset"].button-primary:hover, input[type="button"].button-primary:hover, .button.button-primary:focus, button.button-primary:focus, input[type="submit"].button-primary:focus, input[type="reset"].button-primary:focus, input[type="button"].button-primary:focus {
    color: #00000075 !important;
    background-color: transparent;
    border-color: #1EAEDB
}

.button:focus, button:focus, input[type="submit"]:focus, input[type="reset"]:focus, input[type="button"]:focus {
    color: #5C5D86;
    border-color: #00000075 !important
}

.button:hover, button:hover, input[type="submit"]:hover, input[type="reset"]:hover, input[type="button"]:hover {
    border-color: #00FFC050;
    background: #00000075
}

.Select.is-clearable.is-searchable.Select--multi {
    width: 70 %;
    display: inline-block;
    margin-left: 15%;
}

.Select-placeholder {
    margin-left: -12%;
    background: transparent !important;
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X