簡體   English   中英

單擊后 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)

基於:

我不希望我的下拉菜單在選擇一個值后關閉,我希望它在我的頁面上保持打開狀態。

...在我看來,您實際上是在尋找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']
)

在這種情況下會產生:

在此處輸入圖片說明

這將完全符合您在功能方面的描述:

  1. 您可以選擇全部、部分或不選擇任何選項
  2. 當您進行選擇時,清單不會折疊。

這是一個使用內置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)

解決方案#2

替代另一種可能的方法,在dcc.Checklisthtml.Summary (利用內置可折疊性,從而模仿下拉菜單) html.Details怎么樣? 這更符合您的要求 - 一種下拉菜單,每次選擇其列出的任何選項后都不會自動關閉。

例如,

模擬數據

一個名為“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 .*

*通常默認情況下有一個箭頭,或者三角形,“扭曲”的符號(我想顯然是通用術語: https : //developer.mozilla.org/en-US/docs/Web/HTML/Element/details ) 這有助於向用戶發出信號,表明您可以單擊它以展開它。 出於某種原因,在我的屏幕截圖中,它已不可見,但只需復制我提供的代碼,它就會顯示出來。

在 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-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; }


原始建議

(僅供參考,並且還展示了使用dcc.Dropdown組件和可以通過這種方式實現的略有不同的 UX 的可能性 - 它確實具有可搜索和可清除的優點)

是的,確實有一個特定的dcc.Dropdown參數“multi”可以設置為布爾值 True,這應該可以讓您的用戶從下拉列表中選擇多個選項。

編輯:搜索是默認啟用的,因此只需單擊一次下拉欄即可擴展其選項,或者使用另一次鼠標單擊滾動和選擇(然后是,不幸的是,需要額外的鼠標單擊 [默認行為] 是非常快速和方便的)重新展開選項列表]) 或者,用戶只需開始輸入他們想要的每個選項的第一個字母,它們就會突出顯示。 因此,輸入文本也會重新展開下拉列表。 您只需按 Enter 鍵即可從下拉列表中添加任何突出顯示的選項,然后繼續鍵入以進行下一個選擇,因為光標的焦點將保留在下拉組件文本搜索字段中。 可能在每次選擇后修改/覆蓋菜單的默認 CSS/JS 行為自動關閉,但這可能有點棘手。 如果這是您真正認為您的 UX 所需的必要功能,我可以嘗試幫助您解決這個問題。

在您的布局文件中:

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輸入,只是為了證明。

您可以在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."]

這導致:

首次出現時的下拉菜單

↓ 用戶可以搜索、刪除以前的選擇,甚至可以使用小“x”一次清除所有選擇

注意:當任何項目被選中時,它會自動從下拉剩余選項中刪除。 ——

進行多項選擇

↓ 然后,點擊提交按鈕后,出現對應的類型:

顯示工作類型

額外的

如果您對這里的一些 CSS 感到好奇,我不確定僅此一項是否可行,但如果您還沒有的話,它可能有助於向您介紹 Dash 中可用的可定制性(這將在位於下的 .css 文件中名為“assets”的文件夾 & 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&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: rgba(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;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM