Hide a component with one event and make it visible with another


#1

I am hiding a div like this when I upload a file

@app.callback(
    Output(component_id='classification-div', component_property='style'),
    [Input('upload-data', 'contents')]
)
def update_style1(content):
    if content:
        return {'display':'none'}
    else: return {'display':'inline'}

The problem is I need to unhide it when a user clicks on an image so I did this

@app.callback(
    Output(component_id='classification-div', component_property='style'),
    [Input('upload-data', 'contents'),
    Input('img1','n_clicks')]
)
def update_style1(content,click):
    if content:
        return {'display':'none'}
    else: return {'display':'inline'}

Now I get an error “Error loading dependencies” when I try to load the webpage.

I think the problem is that the input img1 is generated through a callback while upload-data is loaded with the main app.

Is there a way to avoid this problem? I need to unhide classification-div any time a user clicks on the image but hide it anytime a user uploads a file.

I also tried using State but I get the “Error loading dependencies” message.

Sometimes I also get the message “Error loading layout” instead of “Error loading dependencies”.


#2

Can you provide a minimal working example?

I do this kind of hide/unhide all the time and don’t have a problem.


#3

Ok so here is a working example

import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
app = dash.Dash()
app.config['suppress_callback_exceptions']=True
image = "tmpimg.png"

app.layout = html.Div([
       html.Div([
       html.Div([
           html.Button('Load image', id='load-button'),
           dcc.Upload(
                   id='upload-data',
                   children=html.Button('Upload image', id='upload-button')
           )
       ]),
       html.Div([
           html.Div(id='images-div'),
           html.Div(id='classification-div'),
           html.Div(id='classification-div2')
       ])
       ])
 ])

@app.callback(
   Output(component_id='images-div', component_property='children'),
   [Input('load-button','n_clicks')]
)
def update_output_div_img(n_clicks):
   return html.Div([html.Img(
                           src=app.get_asset_url(image),
                           style={
                                   'width' : '10%',
                                    'cursor': 'pointer'
                                   }
                       )], id='img'
                   )

@app.callback(
   Output(component_id='classification-div', component_property='children'),
   [Input('img','n_clicks')]
)
def update_output_div1(n_clicks):  
   return html.Div([html.H2('Div1')])

@app.callback(Output('classification-div2', 'children'),
             [Input('upload-data', 'contents')])
def update_output_div2(content):
   return html.Div([html.H2('Div2')])

@app.callback(
   Output(component_id='classification-div', component_property='style'),
   [Input('upload-data', 'contents')]
)
def update_style(content):
   if content:
       return {'display':'none'}
   else: return {'display':'inline'}
   

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

This is not giving an error. So the update_output_div_img will load an image with a callback when you load the page or when you push the button “Load image”. Now after the image is loaded then you can click it and a text Div1 will appear. When you push the upload button to load an image the “Div1” should disappear and only the Div 2 remain. So far so good.

Now when I click on the image again the “Div1” text doesn’t appear because the display was changed to “none”. I want that when I click on the image again the “Div1” text should show up again so I modified the callback for the style of the first div above like this so that it gets triggered when you click on the image and since there is no content I guess it should change the display to inline.

@app.callback(
   Output(component_id='classification-div', component_property='style'),
   [Input('upload-data', 'contents'),
    Input('img','n_clicks')]
)
def update_style(content,n_clicks):
   if content:
       return {'display':'none'}
   else: return {'display':'inline'}

But this triggers the “Error Loading Dependencies” message I think because the image that is clicked is generated by another callback while the upload component was loaded from the start.

Any ideas how to work around this?

By the way, is there a way to select the code and format it to look like code in this forum without having to add 4 spaces manually before each line? I only see the “Blockquote” option but not for code formatting. Thanks


#4

So I basically ended up loading the images divs with the main app and filling them with the callback, the disadvantage of this is that you basically have to know in advance how many images you want to load. So I had to sacrifice that flexibility. That way I could avoid the “Error loading dependencies” error. But I also needed to create some hidden divs which chain triggered other callbacks to manage two components changing their display property between each other and use global variables to figure out which component the callback came from. I know the global variables are not safe and that is not the ideal solution but for this particular situation they work because I’m just making a small demo. I will try to find a better solution afterwards.