单击后 Dash DropDown 关闭
Dash DropDown closes after click
我不希望我的下拉列表在选择一个值后关闭,我希望它在我的页面上保持打开状态。我正在使用 dcc.Dropdown
dcc.Dropdown(id='job-type', options=self.options, placeholder='Select one or more Event(s)', value=self.job_type, multi=True)
解决方案 #2
另一种可能的方法如何,在 html.Details
组件中将 dcc.Checklist
与 html.Summary
配对(利用内置的可折叠性,因此模仿下拉菜单)?这更能满足您的要求 - 一种下拉菜单,不会在每个 select 列出的任何选项后自动关闭。
例如,
模拟数据
名为“jobs.csv”的本地文件,以制表符分隔,内容如下:
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
准“下拉”组件
在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(),
]
在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."]
↓ 然后,单击作业组件后,它会展开为一个可滚动的下拉组件,实际上是一个 dcc.checklist:
↓ 然后,点击提交按钮后,出现对应的类型:
要关闭“下拉菜单”,只需重新单击“✅ 工作”圆形组件,即 html.Summary
破折号组件。而“细节”是dcc.Checklist
.*
*Usually there is by default an arrow, or triangle rather, symbol which "twists" (I guess apparently is the common term: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/details) which helps to signal to the user that you can click on this to expand it. For some reason in my screenshots it has been made invisible but just copying the code I provide, it should show up.
在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');
@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)}}
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; }
.updates-list { font-family: 'Roboto Mono'; font-size: .75rem; color: #064d56f0; text-align: 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; }
原始建议
(included just for reference, and, to also still show what's possible using the dcc.Dropdown
component and the slightly different UX which can be implemented that way - it does have the advantage of being searchable and clearable)
是的,确实有一个特定的 dcc.Dropdown
参数“multi”可以设置为布尔值 True,这应该可以让您的用户从下拉列表中 select 多个选项。
编辑:默认情况下启用搜索,因此只需单击下拉栏一次即可扩展其选项,滚动并 select 再单击一次鼠标(和那么不幸的是,需要额外的鼠标点击[默认行为]来重新扩展选项列表])或者,用户可以开始输入他们想要的每个选项的第一个字母,它们将突出显示。因此,键入文本也会重新展开下拉列表。您只需按 Enter 键即可从下拉列表中添加任何突出显示的选项,然后继续输入下一个 selection,因为光标的焦点将保留在下拉组件文本搜索字段中。它 可能 可以 hack/override 菜单的默认 CSS/JS 行为在每个 selection 后自动关闭,但这可能有点棘手。如果您真的认为这是您的用户体验所需的必要功能,我可以尝试帮助您解决这个问题。
在您的布局文件中:
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(),
我不确定你到底想用“类型”信息做什么,但我创建了一个由“提交”按钮触发的回调,它也将当前值作为 State
类型输入( s) select从下拉列表中编辑,只是为了演示。
您可以将这样的内容添加到您的 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."]
这导致:
↓ 用户可以搜索、删除之前的select离子,甚至可以使用小“x”
一次清除所有select离子
Notice: As any item becomes selected, it is automatically removed from the dropdown remaining options.
-
↓ 然后,点击提交按钮后,出现对应的类型:
额外
如果您对这里的一些 CSS 感到好奇,我不确定仅此一项是否可行,但它可能有助于向您介绍 Dash 中可用的可定制性,如果您还没有(这将进入位于名为“assets”的文件夹下的 .css 文件,Dash 会自动找到它并使用您自己的自定义覆盖其默认值):
@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', sans serif;
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: rgba(255, 255, 255, 0.18) !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;
}
基于:
I don't want my dropdown to close after choosing a value, I want to it
to stay opened on my page.
...在我看来,您实际上是在寻找 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']
)
在这种情况下会产生:
这会完全您所描述的功能:
- 您可以select全部、部分或none选项
- 制作 selection 时清单不会折叠。
这是一个使用内置 px.stocks
数据集的示例:
此特定示例将仅复制图形图例的现有功能,但如果您想摆脱使用图例的限制,此设置应该是一个很好的起点。
完整代码:
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)
建议 2
根据 John Collins 的评论,我将 dcc.Checklist
包装到 html.Div
中,并使用以下设置使核对表在有很多项目要显示时可滚动同时:
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'
}
)
情节 2:
情节 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)
我不希望我的下拉列表在选择一个值后关闭,我希望它在我的页面上保持打开状态。我正在使用 dcc.Dropdown
dcc.Dropdown(id='job-type', options=self.options, placeholder='Select one or more Event(s)', value=self.job_type, multi=True)
解决方案 #2
另一种可能的方法如何,在 html.Details
组件中将 dcc.Checklist
与 html.Summary
配对(利用内置的可折叠性,因此模仿下拉菜单)?这更能满足您的要求 - 一种下拉菜单,不会在每个 select 列出的任何选项后自动关闭。
例如,
模拟数据
名为“jobs.csv”的本地文件,以制表符分隔,内容如下:
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
准“下拉”组件
在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(),
]
在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."]
↓ 然后,单击作业组件后,它会展开为一个可滚动的下拉组件,实际上是一个 dcc.checklist:
↓ 然后,点击提交按钮后,出现对应的类型:
要关闭“下拉菜单”,只需重新单击“✅ 工作”圆形组件,即 html.Summary
破折号组件。而“细节”是dcc.Checklist
.*
*Usually there is by default an arrow, or triangle rather, symbol which "twists" (I guess apparently is the common term: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/details) which helps to signal to the user that you can click on this to expand it. For some reason in my screenshots it has been made invisible but just copying the code I provide, it should show up.
在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');
@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)}}
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; }
.updates-list { font-family: 'Roboto Mono'; font-size: .75rem; color: #064d56f0; text-align: 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; }
原始建议
(included just for reference, and, to also still show what's possible using the
dcc.Dropdown
component and the slightly different UX which can be implemented that way - it does have the advantage of being searchable and clearable)
是的,确实有一个特定的 dcc.Dropdown
参数“multi”可以设置为布尔值 True,这应该可以让您的用户从下拉列表中 select 多个选项。
编辑:默认情况下启用搜索,因此只需单击下拉栏一次即可扩展其选项,滚动并 select 再单击一次鼠标(和那么不幸的是,需要额外的鼠标点击[默认行为]来重新扩展选项列表])或者,用户可以开始输入他们想要的每个选项的第一个字母,它们将突出显示。因此,键入文本也会重新展开下拉列表。您只需按 Enter 键即可从下拉列表中添加任何突出显示的选项,然后继续输入下一个 selection,因为光标的焦点将保留在下拉组件文本搜索字段中。它 可能 可以 hack/override 菜单的默认 CSS/JS 行为在每个 selection 后自动关闭,但这可能有点棘手。如果您真的认为这是您的用户体验所需的必要功能,我可以尝试帮助您解决这个问题。
在您的布局文件中:
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(),
我不确定你到底想用“类型”信息做什么,但我创建了一个由“提交”按钮触发的回调,它也将当前值作为 State
类型输入( s) select从下拉列表中编辑,只是为了演示。
您可以将这样的内容添加到您的 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."]
这导致:
↓ 用户可以搜索、删除之前的select离子,甚至可以使用小“x”
一次清除所有select离子Notice: As any item becomes selected, it is automatically removed from the dropdown remaining options. -
↓ 然后,点击提交按钮后,出现对应的类型:
额外
如果您对这里的一些 CSS 感到好奇,我不确定仅此一项是否可行,但它可能有助于向您介绍 Dash 中可用的可定制性,如果您还没有(这将进入位于名为“assets”的文件夹下的 .css 文件,Dash 会自动找到它并使用您自己的自定义覆盖其默认值):
@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', sans serif;
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: rgba(255, 255, 255, 0.18) !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;
}
基于:
I don't want my dropdown to close after choosing a value, I want to it to stay opened on my page.
...在我看来,您实际上是在寻找 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']
)
在这种情况下会产生:
这会完全您所描述的功能:
- 您可以select全部、部分或none选项
- 制作 selection 时清单不会折叠。
这是一个使用内置 px.stocks
数据集的示例:
此特定示例将仅复制图形图例的现有功能,但如果您想摆脱使用图例的限制,此设置应该是一个很好的起点。
完整代码:
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)
建议 2
根据 John Collins 的评论,我将 dcc.Checklist
包装到 html.Div
中,并使用以下设置使核对表在有很多项目要显示时可滚动同时:
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'
}
)
情节 2:
情节 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)