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."]
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 :
Ensuite, après avoir cliqué sur le bouton "Envoyer", les types correspondants apparaissent :
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&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&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 :
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. -
Ensuite, après avoir cliqué sur le bouton "Envoyer", les types correspondants apparaissent :
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&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&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;
}