plotly / dash

Data Apps & Dashboards for Python. No JavaScript Required.

Home Page:https://plotly.com/dash

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

[BUG] `dcc.Graph` inserts phantom rectangular shape on callback update seemingly randomly

matt-sd-watson opened this issue · comments

Describe your context
Please provide us your environment, so we can easily reproduce the issue.

  • replace the result of pip list | grep dash below
dash                          2.10.2
dash-ag-grid                  2.3.0
dash-auth                     2.0.0
dash-bootstrap-components     1.4.1
dash-canvas                   0.1.0
dash-core-components          2.0.0
dash-daq                      0.5.0
dash-draggable                0.1.2
dash-extensions               1.0.1
dash-google-auth              0.1.2
dash-html-components          2.0.0
dash-mantine-components       0.12.1
dash-table                    5.0.0
dash-testing-stub             0.0.2
dash-uploader                 0.7.0a1
jupyter-dash                  0.4.2
  • if frontend related, tell us your Browser, Version and OS

    • OS: [e.g. iOS] Ubuntu 22.04
    • Browser [e.g. chrome, safari] Chrome
    • Version [e.g. 22] Version 121.0.6167.85 (Official Build) (64-bit)

Describe the bug

I have a dcc.Graph output component that renders the callback output from px.imshow. When I interact with the component, such as adding/remocing an existing shape in the plot through a callback, a random "phantom" rectangle may appear:

9fe92f65bb154c84f214f67b4eedaa523bbe0d8c

The pattern of its appearance is difficult to establish, as it is not always deterministic. Often, it appears after I add or remove a different shape for the graph.

Expected behavior

Here is how the plot is expected to appear:

a21cafe72042a839082c7f7b24c29e11c53d2a17

Screenshots

Here is an additional example. When I have the graph and have already drawn the white rectangle in the top right, removing a line shape on a different part of the graph in a callback produces this:

Screenshot from 2024-02-01 10-49-11

When I view the shapes in the layout of the graph at this moment through a callback, only my original white rectangular shape is contained:

print(graph['layout'['shapes']
[{'editable': True, 'fillcolor': 'rgba(0, 0, 0, 0)', 'fillrule': 'evenodd', 'label': {'text': '', 'texttemplate': ''}, 'layer': 'above', 'line': {'color': 'white', 'dash': 'solid', 'width': 4}, 'opacity': 1, 'type': 'rect', 'x0': 302.01024590163945, 'x1': 462.6659836065575, 'xref': 'x', 'y0': 85.29234972677595, 'y1': 177.09562841530052, 'yref': 'y'}]

So it appears that the component is randomly adding a rectangle that doesn't show up in the canvas layout, making it impossible for me to filter or diagnose how to remove it consistently. Sometimes the rectangle will disappear if I redraw a new shape or update the underlying image, but sometimes it won't.

Generally, it is very hard to make a MRE for this problem given how random and unpredictable it is.

Bizarre. The fact that this shape is covering the middle half of the plot in each direction implies that it has no x or y coordinates, so it gets 1/4->3/4 as defaults. But the blue color isn't a default unless it's in a template somewhere...

Perhaps the next step debugging is to open the JS console when you see this, and type:

gd = document.querySelector('.js-plotly-plot'); // assuming this is the only or at least first plot on the page
console.log(JSON.stringify(gd.layout.shapes, null, 2));
console.log(JSON.stringify(gd._fullLayout.shapes, null, 2));

Here is the output for this graph state:

Screenshot from 2024-02-02 13-20-18

Console output of console.log(JSON.stringify(gd.layout.shapes, null, 2));:

[
  {
    "editable": true,
    "fillcolor": "rgba(0, 0, 0, 0)",
    "fillrule": "evenodd",
    "label": {
      "text": "",
      "texttemplate": ""
    },
    "layer": "above",
    "line": {
      "color": "white",
      "dash": "solid",
      "width": 4
    },
    "opacity": 1,
    "path": "M304.5304878048781,172.82317073170734L263.3719512195122,179.22560975609758L235.01829268292684,189.28658536585368L216.72560975609758,205.75000000000003L205.75000000000003,231.359756097561L203.9207317073171,255.14024390243904L211.23780487804882,271.6036585365854L232.27439024390247,286.2378048780488L256.0548780487805,292.640243902439L291.7256097560976,290.8109756097561L320.9939024390244,282.5792682926829L331.0548780487805,274.3475609756098Z",
    "type": "path",
    "xref": "x",
    "yref": "y"
  },
  {
    "editable": true,
    "fillcolor": "rgba(0, 0, 0, 0)",
    "fillrule": "evenodd",
    "label": {
      "text": "",
      "texttemplate": ""
    },
    "layer": "above",
    "line": {
      "color": "white",
      "dash": "solid",
      "width": 4
    },
    "opacity": 1,
    "path": "M455.4451219512195,153.6158536585366L420.6890243902439,163.6768292682927L398.7378048780488,174.65243902439028L385.0182926829268,191.11585365853662L382.2743902439025,204.83536585365857L389.5914634146342,221.2987804878049L416.1158536585366,244.16463414634146L439.8963414634147,256.969512195122L467.3353658536586,259.7134146341463L489.28658536585374,254.22560975609755L499.34756097560984,248.7378048780488Z",
    "type": "path",
    "xref": "x",
    "yref": "y"
  },
  {
    "editable": true,
    "fillcolor": "rgba(0, 0, 0, 0)",
    "fillrule": "evenodd",
    "label": {
      "text": "",
      "texttemplate": ""
    },
    "layer": "above",
    "line": {
      "color": "white",
      "dash": "solid",
      "width": 4
    },
    "opacity": 1,
    "path": "M388.5785530821918,344.70547945205476L417.345676369863,340.5958904109589L455.35937499999994,344.70547945205476L468.71553938356163,352.92465753424653L477.9621147260274,371.417808219178L472.8251284246575,389.9109589041096L455.35937499999994,410.458904109589L426.59225171232873,422.7876712328767L406.0443065068493,425.8698630136986L379.33197773972597,424.8424657534246L376.24978595890406,422.7876712328767Z",
    "type": "path",
    "xref": "x",
    "yref": "y"
  },
  {
    "editable": true,
    "label": {
      "text": "",
      "texttemplate": ""
    },
    "layer": "above",
    "opacity": 1,
    "line": {
      "dash": "solid"
    },
    "fillcolor": "rgba(0,0,0,0)",
    "fillrule": "evenodd"
  }
]

Console output of console.log(JSON.stringify(gd._fullLayout.shapes, null, 2));

[
  {
    "_input": {
      "editable": true,
      "fillcolor": "rgba(0, 0, 0, 0)",
      "fillrule": "evenodd",
      "label": {
        "text": "",
        "texttemplate": ""
      },
      "layer": "above",
      "line": {
        "color": "white",
        "dash": "solid",
        "width": 4
      },
      "opacity": 1,
      "path": "M304.5304878048781,172.82317073170734L263.3719512195122,179.22560975609758L235.01829268292684,189.28658536585368L216.72560975609758,205.75000000000003L205.75000000000003,231.359756097561L203.9207317073171,255.14024390243904L211.23780487804882,271.6036585365854L232.27439024390247,286.2378048780488L256.0548780487805,292.640243902439L291.7256097560976,290.8109756097561L320.9939024390244,282.5792682926829L331.0548780487805,274.3475609756098Z",
      "type": "path",
      "xref": "x",
      "yref": "y"
    },
    "_template": {
      "line": {
        "color": "#2a3f5f"
      }
    },
    "_index": 0,
    "visible": true,
    "path": "M304.5304878048781,172.82317073170734L263.3719512195122,179.22560975609758L235.01829268292684,189.28658536585368L216.72560975609758,205.75000000000003L205.75000000000003,231.359756097561L203.9207317073171,255.14024390243904L211.23780487804882,271.6036585365854L232.27439024390247,286.2378048780488L256.0548780487805,292.640243902439L291.7256097560976,290.8109756097561L320.9939024390244,282.5792682926829L331.0548780487805,274.3475609756098Z",
    "type": "path",
    "editable": true,
    "layer": "above",
    "opacity": 1,
    "fillcolor": "rgba(0, 0, 0, 0)",
    "fillrule": "evenodd",
    "line": {
      "width": 4,
      "color": "white",
      "dash": "solid"
    },
    "xsizemode": "scaled",
    "ysizemode": "scaled",
    "xref": "x",
    "yref": "y",
    "label": {
      "text": ""
    },
    "_extremes": {
      "x": {
        "min": [
          {
            "val": 203.9207317073171,
            "pad": 2,
            "extrapad": false
          }
        ],
        "max": [
          {
            "val": 331.0548780487805,
            "pad": 2,
            "extrapad": false
          }
        ],
        "opts": {
          "ppad": 2
        }
      },
      "y": {
        "min": [
          {
            "val": 172.82317073170734,
            "pad": 2,
            "extrapad": false
          }
        ],
        "max": [
          {
            "val": 292.640243902439,
            "pad": 2,
            "extrapad": false
          }
        ],
        "opts": {
          "ppad": 2
        }
      }
    }
  },
  {
    "_input": {
      "editable": true,
      "fillcolor": "rgba(0, 0, 0, 0)",
      "fillrule": "evenodd",
      "label": {
        "text": "",
        "texttemplate": ""
      },
      "layer": "above",
      "line": {
        "color": "white",
        "dash": "solid",
        "width": 4
      },
      "opacity": 1,
      "path": "M455.4451219512195,153.6158536585366L420.6890243902439,163.6768292682927L398.7378048780488,174.65243902439028L385.0182926829268,191.11585365853662L382.2743902439025,204.83536585365857L389.5914634146342,221.2987804878049L416.1158536585366,244.16463414634146L439.8963414634147,256.969512195122L467.3353658536586,259.7134146341463L489.28658536585374,254.22560975609755L499.34756097560984,248.7378048780488Z",
      "type": "path",
      "xref": "x",
      "yref": "y"
    },
    "_template": {
      "line": {
        "color": "#2a3f5f"
      }
    },
    "_index": 1,
    "visible": true,
    "path": "M455.4451219512195,153.6158536585366L420.6890243902439,163.6768292682927L398.7378048780488,174.65243902439028L385.0182926829268,191.11585365853662L382.2743902439025,204.83536585365857L389.5914634146342,221.2987804878049L416.1158536585366,244.16463414634146L439.8963414634147,256.969512195122L467.3353658536586,259.7134146341463L489.28658536585374,254.22560975609755L499.34756097560984,248.7378048780488Z",
    "type": "path",
    "editable": true,
    "layer": "above",
    "opacity": 1,
    "fillcolor": "rgba(0, 0, 0, 0)",
    "fillrule": "evenodd",
    "line": {
      "width": 4,
      "color": "white",
      "dash": "solid"
    },
    "xsizemode": "scaled",
    "ysizemode": "scaled",
    "xref": "x",
    "yref": "y",
    "label": {
      "text": ""
    },
    "_extremes": {
      "x": {
        "min": [
          {
            "val": 382.2743902439025,
            "pad": 2,
            "extrapad": false
          }
        ],
        "max": [
          {
            "val": 499.34756097560984,
            "pad": 2,
            "extrapad": false
          }
        ],
        "opts": {
          "ppad": 2
        }
      },
      "y": {
        "min": [
          {
            "val": 153.6158536585366,
            "pad": 2,
            "extrapad": false
          }
        ],
        "max": [
          {
            "val": 259.7134146341463,
            "pad": 2,
            "extrapad": false
          }
        ],
        "opts": {
          "ppad": 2
        }
      }
    }
  },
  {
    "_input": {
      "editable": true,
      "fillcolor": "rgba(0, 0, 0, 0)",
      "fillrule": "evenodd",
      "label": {
        "text": "",
        "texttemplate": ""
      },
      "layer": "above",
      "line": {
        "color": "white",
        "dash": "solid",
        "width": 4
      },
      "opacity": 1,
      "path": "M388.5785530821918,344.70547945205476L417.345676369863,340.5958904109589L455.35937499999994,344.70547945205476L468.71553938356163,352.92465753424653L477.9621147260274,371.417808219178L472.8251284246575,389.9109589041096L455.35937499999994,410.458904109589L426.59225171232873,422.7876712328767L406.0443065068493,425.8698630136986L379.33197773972597,424.8424657534246L376.24978595890406,422.7876712328767Z",
      "type": "path",
      "xref": "x",
      "yref": "y"
    },
    "_template": {
      "line": {
        "color": "#2a3f5f"
      }
    },
    "_index": 2,
    "visible": true,
    "path": "M388.5785530821918,344.70547945205476L417.345676369863,340.5958904109589L455.35937499999994,344.70547945205476L468.71553938356163,352.92465753424653L477.9621147260274,371.417808219178L472.8251284246575,389.9109589041096L455.35937499999994,410.458904109589L426.59225171232873,422.7876712328767L406.0443065068493,425.8698630136986L379.33197773972597,424.8424657534246L376.24978595890406,422.7876712328767Z",
    "type": "path",
    "editable": true,
    "layer": "above",
    "opacity": 1,
    "fillcolor": "rgba(0, 0, 0, 0)",
    "fillrule": "evenodd",
    "line": {
      "width": 4,
      "color": "white",
      "dash": "solid"
    },
    "xsizemode": "scaled",
    "ysizemode": "scaled",
    "xref": "x",
    "yref": "y",
    "label": {
      "text": ""
    },
    "_extremes": {
      "x": {
        "min": [
          {
            "val": 376.24978595890406,
            "pad": 2,
            "extrapad": false
          }
        ],
        "max": [
          {
            "val": 477.9621147260274,
            "pad": 2,
            "extrapad": false
          }
        ],
        "opts": {
          "ppad": 2
        }
      },
      "y": {
        "min": [
          {
            "val": 340.5958904109589,
            "pad": 2,
            "extrapad": false
          }
        ],
        "max": [
          {
            "val": 425.8698630136986,
            "pad": 2,
            "extrapad": false
          }
        ],
        "opts": {
          "ppad": 2
        }
      }
    }
  },
  {
    "_input": {
      "editable": true,
      "label": {
        "text": "",
        "texttemplate": ""
      },
      "layer": "above",
      "opacity": 1,
      "line": {
        "dash": "solid"
      },
      "fillcolor": "rgba(0,0,0,0)",
      "fillrule": "evenodd"
    },
    "_template": {
      "line": {
        "color": "#2a3f5f"
      }
    },
    "_index": 3,
    "visible": true,
    "type": "rect",
    "editable": true,
    "layer": "above",
    "opacity": 1,
    "fillcolor": "rgba(0,0,0,0)",
    "fillrule": "evenodd",
    "line": {
      "width": 2,
      "color": "#2a3f5f",
      "dash": "solid"
    },
    "xsizemode": "scaled",
    "ysizemode": "scaled",
    "xref": "x",
    "x0": 149.5,
    "x1": 449.5,
    "yref": "y",
    "y0": 449.5,
    "y1": 149.5,
    "label": {
      "texttemplate": "",
      "text": ""
    },
    "_extremes": {
      "x": {
        "min": [
          {
            "val": 149.5,
            "pad": 1,
            "extrapad": false
          }
        ],
        "max": [
          {
            "val": 449.5,
            "pad": 1,
            "extrapad": false
          }
        ],
        "opts": {
          "ppad": 1
        }
      },
      "y": {
        "min": [
          {
            "val": 149.5,
            "pad": 1,
            "extrapad": false
          }
        ],
        "max": [
          {
            "val": 449.5,
            "pad": 1,
            "extrapad": false
          }
        ],
        "opts": {
          "ppad": 1
        }
      }
    }
  }
]

It looks like the phantom shape could be represented by:

{'editable': True, 'label': {'text': '', 'texttemplate': ''}, 'layer': 'above', 'opacity': 1, 'line': {'dash': 'solid'}, 'fillcolor': 'rgba(0,0,0,0)', 'fillrule': 'evenodd'}

However, in a callback, I cannot see this shape, and filtering for correct shapes (such as those with defined types) does not fix the issue:

# invoked in callback before re-rendering: does not solve problem
canvas['layout']['shapes'] = [shape for shape in canvas['layout']['shapes'] if 'type' in shape]

Pinging to see if there are any additional debugging tips for this. It continues to happen sporadically on shape updates to the canvas.