'Python: Dash, Plotly, and Jupyter Notebook: Use a WSGI server instead

First Stack Overflow question; please critique the request format if needed.

I am working on some plotly image processing for my work. I have been using matplotlib but need something more interactive, so I switched to dash and plotly. My goal is for people on my team to be able to draw a shape around certain parts of an image and return pixel values.

I am using this documentation and want a similar result: https://dash.plotly.com/annotations ; specifically "Draw a path to show the histogram of the ROI"

Code:

    import numpy as np
    import plotly.express as px
    
    from dash import Dash
    from dash.dependencies import Input, Output
    
    import dash_core_components as dcc
    import dash_html_components as html
    
    from skimage import data, draw #yes necessary
    from scipy import ndimage 
    
    import os, glob
    from skimage import io
    
    imagePath = os.path.join('.','examples')
    filename = glob.glob(os.path.join(imagePath,'IMG_0650_6.tif'))[0]
    moon = io.imread(filename)
    
    #imagePath = os.path.join('.','examples')
    #imageName = glob.glob(os.path.join(imagePath,'IMG_0650_6.tif'))[0]
    
    def path_to_indices(path):
        """From SVG path to numpy array of coordinates, each row being a (row, col) point
        """
        indices_str = [
            el.replace("M", "").replace("Z", "").split(",") for el in path.split("L")
        ]
        return np.rint(np.array(indices_str, dtype=float)).astype(np.int)
    
    def path_to_mask(path, shape):
        """From SVG path to a boolean array where all pixels enclosed by the path
        are True, and the other pixels are False.
        """
        cols, rows = path_to_indices(path).T
        rr, cc = draw.polygon(rows, cols)
        mask = np.zeros(shape, dtype=np.bool)
        mask[rr, cc] = True
        mask = ndimage.binary_fill_holes(mask)
        return mask

img = data.moon()

#print(img)
#print(type(img))

fig = px.imshow(img, binary_string=True)
fig.update_layout(dragmode="drawclosedpath")

fig_hist = px.histogram(img.ravel())

app = Dash(__name__)
app.layout = html.Div(
    [
        html.H3("Draw a path to show the histogram of the ROI"),
        html.Div(
            [dcc.Graph(id="graph-camera", figure=fig),],
            style={"width": "60%", "display": "inline-block", "padding": "0 0"},
        ),
        html.Div(
            [dcc.Graph(id="graph-histogram", figure=fig_hist),],
            style={"width": "40%", "display": "inline-block", "padding": "0 0"},
        ),
    ]
)

@app.callback(
    Output("graph-histogram", "figure"),
    Input("graph-camera", "relayoutData"),
    prevent_initial_call=True,
)
def on_new_annotation(relayout_data):
    if "shapes" in relayout_data:
        last_shape = relayout_data["shapes"][-1]
        mask = path_to_mask(last_shape["path"], img.shape)
        return px.histogram(img[mask])
    else:
        return dash.no_update

if __name__ == "__main__":
    app.run_server(debug=True)

This returns the following error:

Dash is running on http://127.0.0.1:8050/

  • Serving Flask app "main" (lazy loading)
  • Environment: production WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
  • Debug mode: on

Traceback:

Traceback (most recent call last):
  File "/Users/anthea/opt/anaconda3/lib/python3.9/site-packages/ipykernel_launcher.py", line 16, in <module>
    app.launch_new_instance()
  File "/Users/anthea/opt/anaconda3/lib/python3.9/site-packages/traitlets/config/application.py", line 845, in launch_instance
    app.initialize(argv)
  File "/Users/anthea/opt/anaconda3/lib/python3.9/site-packages/traitlets/config/application.py", line 88, in inner
    return method(app, *args, **kwargs)
  File "/Users/anthea/opt/anaconda3/lib/python3.9/site-packages/ipykernel/kernelapp.py", line 632, in initialize
    self.init_sockets()
  File "/Users/anthea/opt/anaconda3/lib/python3.9/site-packages/ipykernel/kernelapp.py", line 282, in init_sockets
    self.shell_port = self._bind_socket(self.shell_socket, self.shell_port)
  File "/Users/anthea/opt/anaconda3/lib/python3.9/site-packages/ipykernel/kernelapp.py", line 229, in _bind_socket
    return self._try_bind_socket(s, port)
  File "/Users/anthea/opt/anaconda3/lib/python3.9/site-packages/ipykernel/kernelapp.py", line 205, in _try_bind_socket
    s.bind("tcp://%s:%i" % (self.ip, port))
  File "/Users/anthea/opt/anaconda3/lib/python3.9/site-packages/zmq/sugar/socket.py", line 208, in bind
    super().bind(addr)
  File "zmq/backend/cython/socket.pyx", line 540, in zmq.backend.cython.socket.Socket.bind
  File "zmq/backend/cython/checkrc.pxd", line 28, in zmq.backend.cython.checkrc._check_rc
zmq.error.ZMQError: Address already in use

Solution Attempts:

I've consulted similar issues on here and tried reassigning 8050 to 1337, which was ineffective.



Solution 1:[1]

when I had the same error I just tried to shutdown the other notebooks which are using the same Address 'http://127.0.0.1:8050/'

Other solution also worked for me, I changed the default port parameter

if __name__ == '__main__':  
    app.run_server(port=8051) 

Solution 2:[2]

I had the same problem and no amount of searching gave me an answer that solved my problem.

When I changed my code to this:

if __name__ == "__main__":
    app.run_server(debug=False)

It ran fine.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Youssef Wahied
Solution 2 winkydink