在Dash中创建用于悬停跟踪的客户端回调
我已经创建了一个Dash应用程序来跟踪和存储图像的悬停数据(参见下面的代码)。我想把悬停跟踪部分变成客户端回调,让APP更具伸缩性,提高悬停跟踪的性能。下面是我使用普通回调的代码。如何将此函数转换为客户端回调?
import dash
import dash_core_components as dcc
import dash_html_components as html
import numpy as np
import plotly.express as px
import requests
from dash.dependencies import ClientsideFunction, Input, Output, State
from dash.exceptions import PreventUpdate
from PIL import Image
app = dash.Dash(__name__)
server = app.server
# In reality, there are 50 screenshot images with non-sequential indexes
urls = ["https://pbs.twimg.com/media/EW8GhG_XkAEOyAh.jpg",
"https://pbs.twimg.com/media/CqzwpPnWEAAiGjW.jpg"]
def url_to_fig(url):
rgb_arr = np.array(Image.open(requests.get(url, stream=True).raw))
fig = px.imshow(rgb_arr)
fig.update_xaxes(visible=False)
fig.update_yaxes(visible=False)
fig.update_layout(
dragmode=False, width=800, height=800)
fig.update_traces(hoverinfo='none', hovertemplate=None)
return fig
app.layout = html.Div([
dcc.Store(id='ss-idx', data=0),
dcc.Graph(id='ss-img', figure=url_to_fig(urls[0]), config = {"displayModeBar": False}),
html.Button("Next", id='next-button', n_clicks=0),
dcc.Store(id='hoverdata', data=[]), # Place to append new hoverdata
])
# Change to client side callback (in JavaScript)
@app.callback(
[Output('ss-idx', 'data'),
Output('ss-img', 'figure'),
Output('hoverdata', 'data')],
[Input('ss-img', 'hoverData'),
Input('next-button', 'n_clicks')],
[State('hoverdata', 'data'),
State('ss-idx', 'data')]
)
def add_to_hoverdata(hover_point, next_clicks, hoverdata, ss_idx):
ctx = dash.callback_context
if not ctx.triggered:
raise PreventUpdate
button_id = ctx.triggered[0]['prop_id'].split('.')[0]
if 'ss-img' in button_id:
if hover_point is not None:
x = hover_point["points"][0]["x"]
y = hover_point["points"][0]["y"]
hoverdata.append((x, y))
return dash.no_update, dash.no_update, hoverdata
elif 'next-button' in button_id:
# Add hoverdata and screenshot index to mysql database (code not shown)
print(next_clicks)
if next_clicks < len(urls):
new_idx = ss_idx + 1
return new_idx, url_to_fig(urls[new_idx]), [] # Reset hoverdata
else:
raise PreventUpdate
if __name__ == "__main__":
app.run_server(debug=True)
解决方案
尝试将其转换为客户端回调...但首先要注意到的两点是:
因为
url
是在服务器端定义的数组,所以必须通过dcc.Store
组件添加到客户端。您将无法在客户端回调中向数据库发送数据。这是在最下面of the Dash docs的第三点中提到的。如果您能够连接到数据库并以JS格式发送数据,我想您可以这样做,但这可能是一个非常糟糕的主意...
回调:
app.clientside_callback(
# from dash.dependencies import ClientsideFunction
ClientsideFunction(
namespace="clientside",
function="addToHoverData"
),
[Output('ss-idx', 'data'),
Output('ss-img', 'figure'),
Output('hoverdata', 'data')],
[Input('ss-img', 'hoverData'),
Input('next-button', 'n_clicks')],
[State('hoverdata', 'data'),
State('ss-idx', 'data'),
# you must create a Store with the url list
State('url-list', 'data')
]
)
assets/clientside.js
window.dash_clientside = Object.assign({}, window.dash_clientside, {
clientside: {
add_to_hoverdata: function(hoverData, nClicks, storeHoverData, storeIdx, storeURLs) {
// storeURLs is an array that holds the image URLs
const ctx = dash_clientside.callback_context;
if (!("triggered" in ctx)){
return dash_clientside.no_update
}
const triggered_id = ctx.triggered[0].prop_id.split(".")[0];
if (triggered_id === "ss-img"){
if (hoverData !== undefined){
// assumes storeHoverData is an array
storeHoverData.push(hoverData.points[0]);
return [
dash_clientside.no_update,
dash_clientside.no_update,
storeHoverData
]
}
} else if (triggered_id === "next-button"){
console.log(nClicks);
if (nClicks < storeURLs.length){
return [
storeIdx +1,
storeURLs[storeIdx +1],
[]
]
}
else {
return dash_clientside.no_update
}
}
}
}
});
我尽量使js代码靠近您的python回调。
相关文章