我有一个破折号应用程序。它的本质是每30秒向数据库发出一次请求,并根据它们更新页面。数据被分为11个块,其中一些可能是空的。如果它们是空的,那么我就不会完全显示它。
每个块都有一个带有标题 (1) 的行和一个包含行 (2) 的表格(屏幕截图)。
我需要在标题栏添加一个按钮来隐藏表格 (2)。我通过带有索引的按钮回调来完成此操作。
@callback(
Output({"type": "dynamic-card", "index": MATCH}, "style"),
Input({"type": "dynamic-button", "index": MATCH}, "n_clicks")
)
但是由于每次都会出现新数据并创建新块,我都会重新创建按钮。因此,他们的
n_clicks = 0
和隐藏的方块再次显露出来。
我尝试将每个按钮的
n_clicks
写入文件并在更新之前读取它们。但事实证明,隐藏对于所有用户来说都是相同的,但我需要为每个用户单独隐藏。
我的问题是更新后如何保存
n_clicks
?(并且每个用户在会话期间保持隐藏状态)。
我的代码(为了方便,删除了所有样式的导入,页面如图截图):
from dash import dcc, html, Input, Output, callback, MATCH, ctx
from datetime import datetime
import dash_bootstrap_components as dbc
refreshing_data = [
[],
[],
[],
[[1, '16/09 12:00', 'name', 'https://google.com/', 'status', '1 day 1 h', '1 day 1 h']],
[[1, '16/09 12:00', 'name', 'https://google.com/', 'status', '1 day 1 h', '1 day 1 h']],
[],
[],
[],
[],
[],
[[1, '16/09 12:00', 'name', 'https://google.com/', 'status', '1 day 1 h', '1 day 1 h']],
'Last refresh at 15:19'
]
switch_case = {
0: 'H0',
1: 'H1',
2: 'H2',
3: 'H3',
4: 'H4',
5: 'H5',
6: 'H6',
7: 'H7',
8: 'H8',
9: 'H9',
10: 'H10'
}
def template_small_card(row):
date_req = row[1]
operation = row[2]
link = row[3]
status = row[4]
card = html.Div([
html.H6("Content is {}, {}, {}, {}".format(date_req, operation, link, status))
],
)
return card
def template_big_card(list_of_small_cards, big_name):
n = big_name
card = html.Div(
[
html.Div([
# button to hide the block
html.Div(
[
html.Button(children=[html.Img(src='assets/vector1.png')],
n_clicks=0,
id={"type": "dynamic-button", "index": n},
),
],
),
html.Div([
html.H4("{}".format(big_name), className="card-title", ),
], ),
],
),
html.Hr(),
# I plan to hide this block
html.Div([
*list_of_small_cards
],
id={"type": "dynamic-card", "index": n}
)
],
)
return dbc.Row(children=[card], className="g-0")
def generate_big_card(data, big_name):
if not data:
return []
else:
result = []
for ind in data:
small_card = template_small_card(ind)
result.append(small_card)
big_card = template_big_card(result, switch_case[big_name])
return big_card
layout = html.Div([
html.Div('Request queue'),
html.H6(id='time_update_1_vert'),
dcc.Interval(
id='interval-component_vert',
interval=30 / 60 * 60000,
n_intervals=0
),
dbc.Row(id='row-tables_vert')
],
)
@callback(
Output('time_update_1_vert', 'children'),
Output('row-tables_vert', 'children'),
Input('interval-component_vert', 'n_intervals'),
)
def update_data(n_intervals):
list_of_df = refreshing_data
columns_for_row = []
time_update = datetime.today().strftime('%Y-%m-%d %H:%M:%S')
for i in range(0, 11):
dbc_column = generate_big_card(list_of_df[i], i)
if dbc_column:
columns_for_row.append(dbc_column)
return time_update, columns_for_row
@callback(
Output({"type": "dynamic-card", "index": MATCH}, "style"),
Input({"type": "dynamic-button", "index": MATCH}, "n_clicks")
)
def update_card(n_clicks):
if n_clicks % 2 == 1:
style1 = {'display': 'none'}
else:
style1 = {}
return style1
dcc.Store
组件解决的。
卡的当前可见性可能存储在那里。我们假设默认情况下所有卡片都是可见的。
session_cards_visibility = dbc.Container([
dcc.Store(
id={'type': 'session_card_visibility',
'index': switch_case[index]},
data=True, # True means visible
storage_type='session') for index in range(len(switch_case))
])
# ...
# somewhere in the layout:
layout = [
# ...
session_cards_visibility,
html.Div('Request queue'),
html.H6(id='time_update_1_vert'),
# ...
统计按钮点击次数似乎不太可靠。显示/隐藏逻辑最好通过切换(即按钮)来表示。因此,在示例中,按钮被替换为
dbc.Checklist
,并且是唯一的选项。
# in template_big_card() instead of html.Button
dbc.Checklist(
id={"type": "dynamic-button", "index": n},
options=[{"label": f"Option {n}", "value": True}],
value=[True] if visible else [], # visible - see below
inline=True,
switch=True)
可以根据当前的切换状态显示或隐藏卡片,并更新相应的会话存储。
@app.callback(
Output({'type': 'dynamic-card', 'index': MATCH}, 'hidden'),
Output({'type': 'session_card_visibility', 'index': MATCH}, 'data'),
Input({'type': 'dynamic-button', 'index': MATCH}, 'value'),
prevent_initial_call=True)
def update_card(toggle_options): # toggle_options - is a list
show_card = bool(toggle_options) # value is chosen [True] or not []
return (
not show_card, # hidden=True must be similar to style={'display': 'none'}
show_card # save the value in the session store
)
最后,
update_data()
应该获取所有存储的值并将它们作为参数传递给generate_big_card()
和template_big_card()
。
@app.callback(
Output('time_update_1_vert', 'children'),
Output('row-tables_vert', 'children'),
Input('interval-component_vert', 'n_intervals'),
State({"type": "session_card_visibility", "index": ALL}, "data"))
def update_data(_, visibilities):
# ...
for i in range(0, 11):
dbc_column = generate_big_card(list_of_df[i], i, visibilities[i])
# ...
def generate_big_card(data, big_name, visible):
# ...
big_card = template_big_card(result, switch_case[big_name], visible)
# ...
def template_big_card(list_of_small_cards, big_name, visible=True):
# ... set the toggle state as was mentioned above
html.Div([*list_of_small_cards],
id={"type": "dynamic-card", "index": n},
hidden=not visible # hide the card if necessary
)