У меня есть раскрывающийся список, который позволяет мне фильтровать категориальный график. Отдельный обратный вызов позволяет пользователю изменить этот график из гистограммы в круговую диаграмму. Эта часть работает так, как ожидалось.
У меня есть отдельные компоненты ползунка, которые регулируют настройки как для гистограммы, так и для круговой диаграммы. Проблема в том, что ползунки зафиксированы на месте. Можно ли добавлять/удалять соответствующие ползунки на основе выбора, выбранного в радиопунктах?
Если выбрана гистограмма (см. ниже), должны быть видны только два ползунка. Остальные два следует отбросить или удалить. И наоборот, если выбран пирог, должно произойти обратное. Ползунки полос удаляются вместо ползунков круговой диаграммы.
Макет фильтрующего элемента должен остаться прежним.
import dash
from dash import dcc
from dash import html
from dash.dependencies import Input, Output
import dash_bootstrap_components as dbc
import plotly.express as px
import plotly.graph_objs as go
import pandas as pd
df = pd.DataFrame({
'Fruit': ['Apple','Banana','Orange','Kiwi','Lemon'],
'Value': [1,2,4,8,6],
})
external_stylesheets = [dbc.themes.SPACELAB, dbc.icons.BOOTSTRAP]
app = dash.Dash(__name__, external_stylesheets = external_stylesheets)
filter_box = html.Div(children=[
html.Div(children=[
html.Label('Fruit', style = {}),
dcc.Dropdown(
id = 'value_type',
options = [
{'label': x, 'value': x} for x in df['Fruit'].unique()
],
value = df['Fruit'].unique(),
multi = True,
clearable = True
),
html.Label('Cat Chart', style = {'display': 'inline','paddingTop': '0rem', "justifyContent": "center"}),
dcc.RadioItems(['Bar','Pie'],'Bar',
id = 'catmap',
),
html.Label('Bar Transp', style = {'display': 'inline-block', 'paddingTop': '0.1rem',}),
dcc.Slider(0, 1, 0.2,
value = 0.6,
id = 'bar_transp'),
html.Label('Bar Width', style = {'display': 'inline-block'}),
dcc.Slider(200, 1000, 200,
value = 600,
id = 'bar_width'),
html.Label('Pie Transp', style = {'display': 'inline-block', 'paddingTop': '0.1rem',}),
dcc.Slider(0, 1, 0.2,
value = 0.6,
id = 'pie_transp'),
html.Label('Pie Hole', style = {'display': 'inline-block'}),
dcc.Slider(0, 1, 0.2,
value = 0.4,
id = 'pie_hole'),
], className = "vstack",
)
])
app.layout = dbc.Container([
dbc.Row([
dbc.Col([
dbc.Row([
dbc.Col(html.Div(filter_box),
),
]),
]),
dbc.Col([
dbc.Row([
dcc.Graph(id = 'type-chart'),
]),
])
])
], fluid = True)
@app.callback(
Output('type-chart', 'figure'),
[Input('value_type', 'value'),
Input('catmap', 'value'),
Input('bar_transp', 'value'),
Input('bar_width', 'value'),
Input('pie_transp', 'value'),
Input('pie_hole', 'value'),
])
def chart(value_type, catmap, bar_transp, bar_width, pie_transp, pie_hole):
dff = df[df['Fruit'].isin(value_type)]
if catmap == 'Bar':
df_count = dff.groupby(['Fruit'])['Value'].count().reset_index(name = 'counts')
if df_count.empty == True:
type_fig = go.Figure()
else:
df_count = df_count
type_fig = px.bar(x = df_count['Fruit'],
y = df_count['counts'],
color = df_count['Fruit'],
opacity = bar_transp,
width = bar_width
)
elif catmap == 'Pie':
df_count = dff.groupby(['Fruit'])['Value'].count().reset_index(name = 'counts')
if df_count.empty == True:
type_fig = go.Figure()
else:
df_count = df_count
type_fig = px.pie(df_count,
values = df_count['counts'],
opacity = pie_transp,
hole = pie_hole
)
return type_fig
if __name__ == '__main__':
app.run_server(debug = True)



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Обратные вызовы, соответствующие шаблону, в этой ситуации не нужны, хотя это могло бы быть, если бы фильтры создавались динамически. Вместо этого, чтобы добавить/удалить соответствующие ползунки на основе выбранного типа графика, вы должны обернуть их в компонент (как children) и использовать обратный вызов для обновления свойства children этого компонента.
Однако я предлагаю вам просто показать/скрыть ползунки, а не добавлять/удалять их, чтобы их состояние сохранялось при обновлении типа графика:
Используйте один компонент-оболочку для каждого типа (здесь «filter_bar» и «filter_pie»):
filter_box = html.Div(children=[
html.Div(children=[
html.Label('Fruit', style = {}),
dcc.Dropdown(
id = 'value_type',
options = [
{'label': x, 'value': x} for x in df['Fruit'].unique()
],
value = df['Fruit'].unique(),
multi = True,
clearable = True
),
html.Label('Cat Chart', style = {'display': 'inline','paddingTop': '0rem', "justifyContent": "center"}),
dcc.RadioItems(['Bar','Pie'],'Bar',
id = 'catmap',
),
html.Div(id='filter_bar', children=[
html.Label('Bar Transp', style = {'display': 'inline-block', 'paddingTop': '0.1rem',}),
dcc.Slider(0, 1, 0.2,
value = 0.6,
id = 'bar_transp'),
html.Label('Bar Width', style = {'display': 'inline-block'}),
dcc.Slider(200, 1000, 200,
value = 600,
id = 'bar_width'),
]),
html.Div(id='filter_pie', children=[
html.Label('Pie Transp', style = {'display': 'inline-block', 'paddingTop': '0.1rem',}),
dcc.Slider(0, 1, 0.2,
value = 0.6,
id = 'pie_transp'),
html.Label('Pie Hole', style = {'display': 'inline-block'}),
dcc.Slider(0, 1, 0.2,
value = 0.4,
id = 'pie_hole'),
])
], className = "vstack",
)
])
И обновите его свойство style в обратном вызове:
@app.callback(
Output('filter_bar', 'style'),
Output('filter_pie', 'style'),
Input('catmap', 'value')
)
def display_sliders(catmap):
if catmap == 'Bar':
return {}, {'display': 'none'}
if catmap == 'Pie':
return {'display': 'none'}, {}
return {'display': 'none'}, {'display': 'none'}
Не совсем, но давайте возьмем этот пример, где обратный вызов set_display_children отображает выбранную страну и город, добавляя/удаляя некоторый текст в макете, как дочернее свойство определенного контейнера. В вашем случае этот конкретный контейнер будет, например, html.Div(id='sliders') размещен сразу после компонента «catmap», и в обратном вызове вы обновите его children в соответствии с catmap, вернув соответствующую пару компонентов (метка+ползунки).
Однако теперь, когда я переосмыслил это, это предотвратит запуск первого обратного вызова: поскольку ползунки используются в качестве входных данных этого обратного вызова, добавление/удаление этих входных данных из макета потребует повторной регистрации обратного вызова с новыми входными данными после каждого обновить, чтобы его все еще можно было запустить.
Да, я просмотрел эти примеры, которые дали мне подход к сопоставлению с образцом, но он не соответствовал точным требованиям. Спасибо за информацию.
Спасибо. Очень полезно. Есть ли у вас ссылки, которые более подробно объясняют разницу между добавлением/удалением и отображением/скрытием?