禁用“提交"按钮在Python + Dash中持续30秒 [英] Disable "Submit" button for 30 seconds in Python + Dash
问题描述
在用户在下面的脚本中按下提交"按钮30秒后,我想让其停止.我将如何去做呢?这是我的代码当前的外观:
I want to stop the user from pressing the "Submit" button for 30 seconds, after they have pushed it in the script below. How would I go about doing this? This is how my code looks currently:
import dash
from dash.dependencies import Input, Output, State
import dash_core_components as dcc
import dash_html_components as html
app = dash.Dash()
app.layout = html.Div([
dcc.Input(id='my-id', value='initial value', type="text"),
html.Button('Submit', id='button'),
html.Div(id='my-div')
])
@app.callback(
Output(component_id='my-div', component_property='children'),
[Input('button', 'n_clicks')],
state=[State(component_id='my-id', component_property='value')]
)
def update_output_div(n_clicks, input_value):
return 'You\'ve entered "{}" and clicked {} times'.format(input_value, n_clicks)
if __name__ == '__main__':
app.run_server()
有人知道我如何阻止用户按下按钮30秒钟吗?
Does anyone know how I can stop users from pushing the button for 30 seconds?
先谢谢.
编辑格林尼治标准时间2018年8月15日9:30对stevepastelan的响应:
import dash
from dash.dependencies import Input, Output, State
import dash_core_components as dcc
import dash_html_components as html
app = dash.Dash()
app.layout = html.Div([
dcc.Input(id='my-id', value='initial value', type="text"),
html.Button('Submit', id='button'),
html.Div([dcc.Interval(
id='interval-component',
interval=1 * 3000, # in milliseconds
n_intervals=0
)]),
html.Div(id='my-div')
])
@app.callback(
Output(component_id='my-div', component_property='children'),
[Input('button', 'n_clicks')], [Input('interval-component', 'n_intervals')],
state=[State(component_id='my-id', component_property='value')]
)
def update_output_div(n_clicks,n_intervals, input_value):
return 'You\'ve entered "{}" and clicked {} times'.format(input_value, n_clicks)
if __name__ == '__main__':
app.run_server()
编辑15/08/2018 16:22 PM通过编辑后的回叫编写简单脚本,但没有用:
import dash
from dash.dependencies import Input, Output, State
import dash_core_components as dcc
import dash_html_components as html
app = dash.Dash()
app.layout = html.Div([
dcc.Input(id='my-id', value='initial value', type="text"),
html.Button('Submit', id='button'),
html.Div([dcc.Interval(
id='interval-component',
interval=1 * 3000, # in milliseconds
n_intervals=0
)]),
html.Div(id='my-div')
])
@app.callback(
Output(component_id='my-div', component_property='children'),
[Input('button', 'n_clicks')], [Input('interval-component', 'n_intervals')],
state=[State(component_id='my-id', component_property='value')]
)
def update_output_div(n_clicks,n_intervals, input_value):
return 'You\'ve entered "{}" and clicked {} times'.format(input_value, n_clicks)
if __name__ == '__main__':
app.run_server()
推荐答案
更新的答案
好的,我设法实现了自己的建议,但它并不琐碎,但仍然存在怪癖.
Updated answer
Okay, I managed to implement my own suggestion, but it was not trivial and still has quirks.
复杂因素是:
- Dash不允许两个回调以相同的
Output
为目标
- 没有很好的方法来跟踪哪个
Input
或Event
触发了您的回调.解决方法通常涉及跟踪每个按钮的点击次数(请参见 https://github. com/plotly/dash-html-components/pull/37 ). - 通过
disable=True
或max_requests=0
禁用计时器似乎是永久性的.一旦以这种方式停止计时器,就无法使用disable=False
或max_requests=1000
重新启动它.
- Dash does not permit two callbacks to target the same
Output
- There is no good way to track which
Input
orEvent
triggered your callback. Workarounds generally involve tracking the number of clicks per button (see https://github.com/plotly/dash-html-components/pull/37 as an example). - Disabling a timer via
disable=True
ormax_requests=0
appears to be permanent. Once I stopped a timer in this way, I could not restart it with eitherdisable=False
ormax_requests=1000
.
问题:
- 在此解决方案中,
update_output_div()
被调用两次-但是您可以通过测量到上一次计数的按钮单击次数来区分两者,从而可以防止两次提交数据. - 小于100毫秒的超时将不起作用.我必须拆分延迟计时器才能使方法起作用,因此我选择了
100
和(1000 * BUTTON_PRESS_LOCKOUT_SECONDS)-100
作为两个计时器持续时间.原则上,您可以将它们平均分配一半.我不知道通过网络工作时使用低超时是否有任何问题(我在本地主机上进行了测试).
- In this solution,
update_output_div()
gets called twice -- but you can tell the difference between the two by measuring the number of button clicks to the previous count, so you can keep it from submitting your data twice. - Timeouts of less than 100ms won't work. I had to split the delay timer for my method to work, so I chose
100
and(1000 * BUTTON_PRESS_LOCKOUT_SECONDS)-100
as the two timer durations. In principle, you could split them evenly half and half. I don't know if there are any problems with using a low timeout when working over the network (I did my testing on localhost).
灵感来自:
- https://community.plot .ly/t/how-to-to-off-off-interval-event/5565/3
- https://github.com/plotly/dash -recipes/blob/master/toggle-interval.py
- https://community.plot.ly/t/how-to-turn-off-interval-event/5565/3
- https://github.com/plotly/dash-recipes/blob/master/toggle-interval.py
import json
import datetime
import dash
from dash.dependencies import Input, Output, State, Event
import dash_core_components as dcc
import dash_html_components as html
BUTTON_PRESS_LOCKOUT_SECONDS = 10 # seconds
app = dash.Dash()
app.config['suppress_callback_exceptions']=True
def serve_layout():
return html.Div([
dcc.Input(id='my-id', value='initial value', type="text"),
html.Button('Submit', id='button'),
html.Div(
[
dcc.Interval(id='interval-component', disabled=True)
, dcc.Interval(id='interval-sync-component', disabled=True)
]
, id='interval-container'
),
html.Div("", id='my-div'),
html.Div(json.dumps({'n_clicks':0, 'n_previous_clicks':0}), id='local_data'),
html.Div('??', id='button-status'),
])
app.layout = serve_layout
# Track button clicks
@app.callback(
output=Output(component_id='local_data', component_property='children'),
inputs=[Input('button', 'n_clicks')],
state=[State('local_data', 'children')],
events=[Event('interval-sync-component', 'interval')]
)
def track_clicks(n_clicks, local_data_json):
if n_clicks is None:
n_clicks = 0
local_data = json.loads(local_data_json)
n_previous_clicks = local_data['n_clicks']
# Update local data with the new click data
local_data.update(**{'n_clicks': n_clicks, 'n_previous_clicks': n_previous_clicks})
# local_data.update(**{'n_clicks': n_clicks, 'n_previous_clicks': n_previous_clicks})
return json.dumps(local_data)
# When the button click count is updated, submit
@app.callback(
output=Output(component_id='my-div', component_property='children'),
inputs=[Input('local_data', 'children')],
state=[State(component_id='my-id', component_property='value'), State('my-div', 'children')]
)
def update_output_div(local_data_json, input_value, current_state):
local_data = json.loads(local_data_json)
n_clicks = local_data['n_clicks']
n_previous_clicks = local_data['n_previous_clicks']
# Real submit
if n_clicks > n_previous_clicks:
return 'You\'ve entered "{}" and clicked {} times ({})'.format(
input_value
, n_clicks if n_clicks is not None else 0
, datetime.datetime.now()
)
# Not a real submit, but function is called an extra time as a side effect of the timer nonsense below.
else:
return '*' + current_state
# Start (or stop) the timer
@app.callback(
output=Output('interval-container', 'children'),
inputs=[Input('local_data', 'children')],
state=[State('button', 'disabled')],
events=[Event('interval-component', 'interval')]
)
def start_timer(local_data_json, button_is_disabled):
local_data = json.loads(local_data_json)
n_clicks = local_data['n_clicks']
n_previous_clicks = local_data['n_previous_clicks']
children=[]
if n_clicks > n_previous_clicks:
sync_timer = dcc.Interval(
id='interval-sync-component',
interval=100, # in milliseconds
)
children.append(sync_timer)
if button_is_disabled:
main_timer = dcc.Interval(
id='interval-component',
interval=(1000 * BUTTON_PRESS_LOCKOUT_SECONDS)-100, # in milliseconds
)
children.append(main_timer)
return children
# Enable the button whenever the timer interval is triggered or disable it when the button is pressed
@app.callback(
output=Output('button', 'disabled'),
inputs=[Input('button', 'n_clicks')],
state=[State('local_data', 'children')],
events=[Event('interval-component', 'interval')]
)
def toggle_button_disabled_state(n_clicks, local_data_json):
local_data = json.loads(local_data_json)
# n_clicks = local_data['n_clicks']
if n_clicks is None:
n_clicks = 0
n_previous_clicks = local_data['n_previous_clicks']
# We got here via button click, so disable the button
if n_clicks > n_previous_clicks:
return True
# We got here via timer expiration, so enable the button
else:
return False # enable the button
# Report on the button status
@app.callback(
output=Output('button-status', 'children'),
inputs=[Input('button', 'disabled')]
)
def update_button_status(disabled):
if disabled:
return 'Disabled submit button for {} seconds'.format(BUTTON_PRESS_LOCKOUT_SECONDS)
else:
return 'Submit button enabled'
if __name__ == '__main__':
app.run_server()
原始答案
您可以使用dash_core_components.Interval
在计时器的基础上触发要在页面上执行的操作.这里有一些示例: https://dash.plot.ly/live-updates
Original answer
You can trigger actions to be taken on the page based on a timer using dash_core_components.Interval
. There are some examples here: https://dash.plot.ly/live-updates
您可以使用n_intervals = 0
初始化间隔组件,然后使自己的提交"按钮禁用并设置n_intervals = 1
.然后在重新启用按钮的间隔上编写一个回调.
You could initialize your interval component with n_intervals = 0
, and then make your submit button disable itself and set n_intervals = 1
. Then write a callback on the interval that re-enables the button.
这篇关于禁用“提交"按钮在Python + Dash中持续30秒的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!