提问人:acciolurker 提问时间:9/3/2021 最后编辑:acciolurker 更新时间:11/6/2021 访问量:770
在 Dash 中为悬停跟踪图像创建客户端回调(修订版)
Creating client-side callbacks for hover tracking images in Dash (revised)
问:
我的最后一个问题,发布在这里可能太耗时而无法回答(并且没有得到答案),所以这是对这个问题的重大修订。我现在 app.py 的是:
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
dcc.Store(id='blank-output')
])
app.clientside_callback(
ClientsideFunction(namespace="clientside", function_name="collect_hoverdata"),
Output('blank-output', 'data'),
Input('ss-img', 'hoverData')
)
app.clientside_callback(
"""
function(nclicks) {
to_return = img_hover_data.slice();
img_hover_data = [];
return to_return;
}
""",
Output("hoverdata", "data"),
Input('next-button', 'n_clicks')
)
# Change to client side callback (in JavaScript)
@app.callback(
[Output('ss-idx', 'data'),
Output('ss-img', 'figure')],
[Input('next-button', 'n_clicks')],
[State('hoverdata', 'data'),
State('ss-idx', 'data')]
)
def add_to_hoverdata(next_clicks, hoverdata, ss_idx):
ctx = dash.callback_context
if not ctx.triggered:
raise PreventUpdate
# Add hoverdata and screenshot index to mysql database (code not shown)
print(len(hoverdata))
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)
然后在 assets/hovertracker.js 中,我添加了这个函数:
var img_hover_data = [];
window.dash_clientside = Object.assign({}, window.dash_clientside, {
clientside: {
collect_hoverdata: function(hover_point) {
var points_array = hover_point.points;
var points = alert(points_array[0])
var x = points.x;
var y = points.y;
var time = Date.now();
img_hover_data.push([x, y, time]);
return 0;
}
}
})
这里的想法是,实际的图形仍然是用plotly express创建的,然后每当它悬停在上面时,它就会在内部积累悬停数据客户端(客户端全局变量),直到按下按钮,此时它将用一个新图形刷新页面(通过正常回调完成),并且值将再次为空列表(客户端回调)。img_hover_data
next
img_hover_data
在执行此操作时,每当图像悬停在上面时,我都会收到错误:
Cannot read properties of undefined (reading 'apply')
我的新问题(希望更简单)是:为什么我会收到这个错误,我该如何解决它?
答:
我刚刚找到了你的第一个问题并试了一下,所以很抱歉给你发了两个答案。
您收到的错误很可能与第二个客户端回调有关。如果您打算在 Dash 回调中操作它,那么在全局范围内定义它有点危险,因此一个快速的解决方法是将其作为组件数据并将其传递给两个客户端回调。这样做不会有任何性能成本,因为在这两种情况下,数据都将在客户端。img_hover_data
dcc.Store
关于第一次回调的两点观察:
达世币会在应用启动时评估回调,因此无论您是阻止回调执行(使用 ),还是必须处理hover_point。防止在客户端进行更新的方法是通过(更多信息在这里)。
prevent_initial_update=True
undefined
dash_clientside.no_updates
在这一部分中:
var points_array = hover_point.points;
var points = alert(points_array[0])
var x = points.x;
var y = points.y;
将引发另一个异常,因为 is undefined ( 返回 ),因此您想将其更改为 和 相同。points
alert
undefined
var x = points_array[0].x
y
评论