Learn how to use Dash Bio for next-gen sequencing & quality control. 🧬Register for the Oct 27 webinar.

Can you break out of a while loop based on the state of BooleanSwitch?

Hi Everybody,

I’m loving dash, but there definitely are some nuances to it that has me head-scratching at times. I’m trying to figure out how to break out of a while loop properly based on the state of a BooleanSwitch from the dash-daq library. What I would like to do is enter the while loop when BooleanSwitch is set to True and break out of the while loop while BooleanSwitch is set to false. Instead what I’m observing is that I get successive while loops running in parallel and the only way I can break out of them is Ctrl-C, which kills my app. I believe in a non-Dash environment you could fix this problem by making the state of the BooleanSwitch a global variable, but that isn’t recommended in dash. How would I implement this instead?

Below is some code to illustrate my issue:

boolean_switch_test.py

import dash
import dash_daq as daq
import dash_html_components as html
from dash.dependencies import Input, Output
import time

external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']

app = dash.Dash(__name__, external_stylesheets=external_stylesheets)

app.layout = html.Div([
    daq.BooleanSwitch(
        id='my-boolean-switch',
        on=False
    ),
    html.Div(id='dummy-div',style={'display':'none'})
])

def dummy_test(on):
    while on:
        for i in range(1000):
            print(i)
            time.sleep(1)
            if on==False:   #since 'on' is not a global variable, this doesn't work
                break

@app.callback(
    Output('dummy-div', 'children'),
    [Input('my-boolean-switch', 'on')])
def update_output(on):
    if on:
        dummy_test(on)
    return None

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

Thanks,
Ben

One option would be to,

  • Invoke the (long) job asynchronously
  • Check for changes in a flag inside the job (as you already do), the flag could be stored in Redis, a file, etc.
  • Set the flag in a callback with the switch as input

As a side bonus, by running the job asynchronously, you avoid freezing the GUI.

Hi @Emil, Thanks for your reply! I’m a little confused about what you wrote, because in this case, I think what I want is synchronous operation, but what I’m observing instead is asynchronous. For instance, if I enable the boolean switch to True, wait 10 seconds, then I disable and then quickly re-enable the switch, I get the following lines to print out:
10
11
0
12
1
13
2
14
3
15
4
etc…

What I want is to find a way to break out of the while loop when I disable the switch. I’ll look into using Redis (I have no experience with this). I didn’t understand your 3rd bullet point however. Some pseudo code would be super helpful here. Thanks!!

Hi Everybody,
I was able to find a solution to this problem using flask_caching. The documentation described a way of doing what I needed here: https://flask-caching.readthedocs.io/en/latest/#explicitly-caching-data

For those of you interested, here is my code:

import dash
import dash_daq as daq
import dash_html_components as html
import dash_core_components as dcc
from dash.dependencies import Input, Output
import time
from flask_caching import Cache

external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']

app = dash.Dash(__name__, external_stylesheets=external_stylesheets)

cache = Cache(app.server, config={
    'CACHE_TYPE': 'simple',

    # should be equal to maximum number of users on the app at a single time
    # higher numbers will store more data in the filesystem / redis cache
    'CACHE_THRESHOLD': 100
})

app.layout = html.Div([
    daq.BooleanSwitch(
        id='my-boolean-switch',
        on=False
    ),
    html.Div(id='dummy-div',style={'display':'none'})
])

def global_store(switch_state=None):
    if switch_state is not None:
        cache.set("switch-state",switch_state)
        return None
    else:
        switch_state = cache.get("switch-state")
        return switch_state

def dummy_test(state):
    while state:
        for i in range(1000):
            print(i)
            time.sleep(1)
            state = global_store()  # here I'm getting the boolean switch state (this is an alternative to using global variables)
            if state==False:
                break

@app.callback(
    Output('dummy-div', 'children'),
    [Input('my-boolean-switch','on')]
)
def update_store(on):
    global_store(on)  # here I'm setting the boolean switch state in the cache
    if on:
        dummy_test(on)
    return None

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