【问题标题】:Accessing Editable Annotations访问可编辑注释
【发布时间】:2019-07-06 09:46:52
【问题描述】:

我有一个简单的 Dash 应用程序,当单击“更新”按钮时,该应用程序会更新一个绘图。我在这张图上添加了一个可编辑的注释。我希望能够访问用户在注释中键入的任何内容,因此当图形更新时,注释保持不变。我主要想知道是否有办法访问他们如何编辑它。

我一直在尝试将当前注释保存到创建图形时访问的存储组件中。我尝试制作一个“保存”按钮,将存储数据更改为当前注释文本我唯一的猜测是,当注释被编辑时,新文本不会存储在默认文本所在的位置。那个,或者什么东西只是在我脑海里浮现,我没有意识到。

import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, State, Output
import random

app = dash.Dash(__name__)
app.config['suppress_callback_exceptions'] = True

app.layout = html.Div(
    id = 'top_level',
    children=[
        html.Div(
            id='plot_div',
            children=[],
        ),

        html.Button(
            id = 'update',
            children="Update",
        ),

        dcc.Store(
            id='annotation_storage',
            data='Editable Annotation',
        )
    ]
)
@app.callback(
    Output('plot_div', 'children'),
    [Input('update', 'n_clicks')],
    [State('annotation_storage', 'data')]
)
def plot_update(clicked, annotation):
    if clicked:
        x = random.sample(range(1,101), 20)
        y = random.sample(range(1,101), 20)
        figure = {'data': [{'x': x, 'y': y, 
                            'type': 'scatter', 'mode': 'markers'}],
                  'layout': {
                      'xaxis': {
                          'fixedrange': True,
                          'zeroline': False,
                      },
                      'yaxis': {
                          'fixedrange': True,
                          'zeroline': False,
                      },
                      'annotations': [{
                            'x': -0.05,
                            'y': 0.5,
                            'showarrow':False,
                            'text':annotation,
                            'xref':'paper',
                            'yref':'paper',
                            }],
                        }
                  }
        return [dcc.Graph(
            id = 'plot_output',
            figure = figure,
            config = {
                'editable': True,
                'edits': {
                    'axisTitleText': False,
                    'titleText': False,
                },
                'displayModeBar': False,
            },
        ),
        html.Button(
            id = 'save_annotation',
            children='Save',
        ),
        ]

@app.callback(
    Output('annotation_storage', 'data'),
    [Input('save_annotation', 'n_clicks')],
    [State('plot_output', 'figure')]
)
def save_annotation(clicked, figure):
    if clicked:
        annotation = figure['layout']['annotations'][0]['text']
        return annotation
if __name__ == '__main__':
    app.run_server(debug=True, port=1000)

当前,即使用户对其进行了编辑,当图表更新时,注释也会恢复为默认文本“可编辑注释”。即使图表更新,我也希望注释保持不变。任何见解将不胜感激!谢谢!

【问题讨论】:

    标签: python plotly-dash


    【解决方案1】:

    Dash user guide中所写:

    dcc.Graph 组件有四个属性可以通过 用户交互:hoverDataclickDataselectedDatarelayoutData

    这意味着当用户与图表交互时,您使用的figure 属性不会更新。

    要解决您的问题,请更新您的回调函数以使用 relayoutData 而不是 figure

    @app.callback(
        Output('annotation_storage', 'data'),
        [Input('save_annotation', 'n_clicks')],
        [State('plot_output', 'relayoutData')]
    )
    def save_annotation(clicked, relayout_data):
        if clicked:
            annotation = relayout_data['annotations[0].text']
            return annotation
    

    您可以在调试模式下运行您的应用,并在回调函数中添加一个断点来探索上述四个属性给出的值。这就是我在relayout_data 中找到注释文本的关键所在。

    【讨论】:

      【解决方案2】:
      fig = px.scatter(df, x="x", y="y")
      ## TODO - load init annotations to fig.layout.annotations
      
      
      dash_app1.layout = html.Div([
          html.H1('Hi there, I am dashboard1'),
          dcc.Graph(
              id='example-graph',
              figure=fig,
              config={
                  'editable': True,
                  'edits': {
                      'axisTitleText': False,
                      'titleText': False,
                  },
              }
          )
          ])
      
      @dash_app1.callback(
          Output('example-graph', 'figure'),
          [Input('example-graph', 'clickData'),
           Input('example-graph', 'relayoutData')],
          prevent_initial_call=True)
      def annotation(clickData, relayoutData):
          if clickData:
              # get click xy
              point = clickData["points"][0]
              # if not exist, add
              if f"'x': {point['x']}, 'y': {point['y']}" not in str(fig.layout.annotations):
                  fig.add_annotation(x=point["x"], y=point["y"], text="Click Here To Edit")
                  print("Add: ", fig.layout.annotations)
              else:
                  # if exist, edit
                  if relayoutData:
                      fig.plotly_relayout(relayoutData)
                      print("Edit: ", fig.layout.annotations)
      
          ## TODO - save fig.layout.annotation
          return fig
      

      Shovalt 的回答很棒,感谢您的启发。我使用clickData事件添加注释并使用relayoutData事件记录编辑和拖动位置。这个更新可以简单地由fig.plotly_relayout 完成,这不包含在文档中。我在情节上利用了 JS。老实说,Plotly Dash 文档对开发人员不够友好,尤其是对于像我这样的初学者。确保您在 IDE 中启用了代码完成功能,有时它比文档更有帮助...

      另外,dcc.Store 在这里有点重复,因为您可以在fig.layout.annotations 中找到所有注释。小心dcc.Store,正如文档所述,它有内存限制:It's generally safe to store up to 2MB in most environments, and 5~10MB in most desktop-only applications.

      更新:

      我发现这个 relayoutData 监听器可能会导致缩放不可用,但我认为它现在可以使用了

      @dash_app1.callback(
          Output('example-graph', 'figure'),
          [Input('example-graph', 'clickData'),
           Input('example-graph', 'relayoutData')],
          prevent_initial_call=True)
      def annotation(clickData, relayoutData):
          print("Click")
          print(clickData)
          print("Relayout")
          print(relayoutData)
          if clickData:
              ## get click xy
              point = clickData["points"][0]
              ## if not exist, add
              if f"'x': {point['x']}, 'y': {point['y']}" not in str(fig.layout.annotations):
                  fig.add_annotation(x=point["x"], y=point["y"], text="Click Here To Edit")
                  print("Add: ", fig.layout.annotations)
                  ## TODO - save fig.layout.annotations to SQLite
                  return fig
          if relayoutData:
              ## initial loading or refreshing action
              if str(relayoutData) == "{'autosize': True}":
                  return fig
              ## zooming action
              ## enable zooming after resetting zooming
              if 'xaxis.range' in str(relayoutData) and 'yaxis.range' in str(relayoutData):
                  fig.layout.xaxis.autorange = False
                  fig.layout.yaxis.autorange = False
              ## edit annotation position or text | also enable zooming after resetting
              fig.plotly_relayout(relayoutData)
              print("Edit: ", fig.layout.annotations) 
              ## TODO - If edit, then update fig.layout.annotations to SQLite
          return fig
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-04-10
        • 1970-01-01
        • 2015-04-22
        • 2011-08-03
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多