मैंने एक डैश ऐप लिखा है जिसका स्रोत कोड नीचे साझा किया गया है:

import dash
from dash import Dash
import dash_html_components as html
import dash_core_components as dcc
import dash_bootstrap_components as dbc
from dash.dependencies import Input, Output
import time


app = Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP], url_base_pathname='/self_serve/')
server = app.server

reset_flag = False
counter = 0

app.title = 'jammed dash app'
app.layout = html.Div([
    # buttons
    dcc.Input(id='main',placeholder='Main Value'),
    dcc.Input(id='filter1',placeholder='filter 1'),
    dcc.Input(id='filter2',placeholder='filter 2'),
    dcc.Input(id='filter3',placeholder='filter 3'),
    dcc.Input(id='filter4',placeholder='filter 4'),
    html.Div(id='output',children='')
])

#metric list
@app.callback(
    Output(component_id='filter1',component_property='value'),
    Output(component_id='filter2',component_property='value'),
    Output(component_id='filter3',component_property='value'),
    Output(component_id='filter4',component_property='value'),
    [
    Input(component_id='main',component_property='value')
    ]
)
def update_filter(main):
    # clear up all filters if main is provided
    global reset_flag
    reset_flag = True
    return '','','',''


@app.callback(
    Output(component_id='output',component_property='children'),
    [
        Input(component_id='main',component_property='value'),
        Input(component_id='filter1',component_property='value'),
        Input(component_id='filter2',component_property='value'),
        Input(component_id='filter3',component_property='value'),
        Input(component_id='filter4',component_property='value'),
    ]
)
def calculate(*args):
    # do some intensive calculation based on the inputs, but I do not want the clearing action to trigger this callback undesirably

    ctx = dash.callback_context
    print('\n')
    print('************ inside calculate *************')
    print('triggered:', ctx.triggered)
    print('inputs:', ctx.inputs)

    # my idea for solving this problem
    global reset_flag, counter
    if reset_flag:
        counter += 1
        if counter <= 4:
            print('counter:',counter)
            print('reset_flag:',reset_flag)
            return ''
        else:
            reset_flag = False
            counter = 0
            print('we passed into the correct flow!')
            pass

    # below is some intensive calculation using pandas.read_sql(), substituted by time.sleep()
    print('Wait 10 seconds here')
    time.sleep(10)
    output = ''
    for item in args:
        if item:
            output += item
    print('output:',output)
    return output


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

मुझे दूसरी कॉलबैक ("गणना") में कुछ गहन गणना (जैसे sql कोड) करने की आवश्यकता है। जब भी "मुख्य" परिवर्तनों में मूल्य होता है, तो मैं सभी फ़िल्टर तत्वों को साफ़ करना चाहता हूं, जिसे पहले कॉलबैक ("अपडेट_फिल्टर") में लागू किया गया है। मुद्दा यह है कि, जब पहली कॉलबैक में प्रत्येक फ़िल्टर को साफ़ कर दिया जाता है, तो डैश दूसरे कॉलबैक को त्वरित उत्तराधिकार में कॉल कर रहा होता है और प्रोग्राम जाम हो जाता है।

मेरा सवाल है: जब पहली कॉलबैक कहा जाता है तो दूसरी कॉलबैक को फायर करने से कैसे बचें? मेरा विचार है कि दूसरी कॉलबैक को "अनावश्यक" कहा जाता है और "अनावश्यक" कॉल के लिए खाली आउटपुट लौटाते समय "सही" कॉल को पास करने की संख्या को ट्रैक करना है। मैंने वैश्विक काउंटर का उपयोग करके लागू करने की कोशिश की, लेकिन इसका कोई फायदा नहीं हुआ।

और आम तौर पर, इन अवांछित, जंजीर कॉलबैक फायरिंग से बचने के लिए सबसे अच्छा/सामान्य अभ्यास क्या है?

अग्रिम में बहुत बहुत धन्यवाद!

0
Bohan Liu 20 नवम्बर 2020, 11:28

2 जवाब

सबसे बढ़िया उत्तर

यहाँ एक फ़ाइल का उपयोग करके आपके calculate फ़ंक्शन पर लॉक का मेरा अपरिष्कृत कार्यान्वयन है। मुझे लगता है कि वैश्विक चर, विभिन्न थ्रेडिंग मुद्दों आदि के कारण समस्याग्रस्त हैं। विचार यह है कि जब calculate अपनी भारी गणना करता है तो यह एक फ़ाइल में '1' डालता है, और जब यह किया जाता है तो यह वहां '0' डालता है। यदि इस बीच फ़ंक्शन को फिर से कॉल किया जाता है, तो यह लॉकफ़ाइल की जांच करता है और यदि वहां '1' है तो यह पुनर्गणना को ट्रिगर किए बिना मौजूद है।

import dash
from dash import Dash
import dash_html_components as html
import dash_core_components as dcc
import dash_bootstrap_components as dbc
from dash.dependencies import Input, Output
import time


app = Dash(__name__, external_stylesheets=[
           dbc.themes.BOOTSTRAP], url_base_pathname='/self_serve/')
server = app.server

reset_flag = False
counter = 0
lockfilename = 'lockfile.txt' # this is where we keep the lock
with open(lockfilename, 'w') as fw:
    fw.write('0') # unlock when we start

app.title = 'jammed dash app'
app.layout = html.Div([
    # buttons
    dcc.Input(id='main', placeholder='Main Value'),
    dcc.Input(id='filter1', placeholder='filter 1'),
    dcc.Input(id='filter2', placeholder='filter 2'),
    dcc.Input(id='filter3', placeholder='filter 3'),
    dcc.Input(id='filter4', placeholder='filter 4'),
    html.Div(id='output', children='')
])

# metric list


@app.callback(
    Output(component_id='filter1', component_property='value'),
    Output(component_id='filter2', component_property='value'),
    Output(component_id='filter3', component_property='value'),
    Output(component_id='filter4', component_property='value'),
    [
        Input(component_id='main', component_property='value')
    ]
)
def update_filter(main):
    # clear up all filters if main is provided
    global reset_flag
    reset_flag = True
    return '', '', '', ''


@app.callback(
    Output(component_id='output', component_property='children'),
    [
        Input(component_id='main', component_property='value'),
        Input(component_id='filter1', component_property='value'),
        Input(component_id='filter2', component_property='value'),
        Input(component_id='filter3', component_property='value'),
        Input(component_id='filter4', component_property='value'),
    ]
)
def calculate(*args):
    # do some intensive calculation based on the inputs, but I do not want the clearing action to trigger this callback undesirably

    ctx = dash.callback_context
    print('\n')
    print('************ inside calculate *************')
    print('triggered:', ctx.triggered)
    print('inputs:', ctx.inputs)

    with open(lockfilename, 'r') as fr:
        line = next(fr)
        if(line[0] == '1'):
            print('Calc is locked, early exit')
            return dash.no_update

    # below is some intensive calculation using pandas.read_sql(), substituted by time.sleep()
    print('Starting heavy calc, locking the file')
    with open(lockfilename, 'w') as fw:
        fw.write('1')

    print('Wait 10 seconds here')
    for n in range(10):
        print('.', end='')
        time.sleep(1)

    output = ''
    for item in args:
        if item:
            output += item
    print('output:', output)
    print('Done with heavy calc, unlocking the file')
    with open(lockfilename, 'w') as fw:
        fw.write('0')

    return output


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

0
piterbarg 20 नवम्बर 2020, 15:07

आपके मामले में, आप क्या करना चाह सकते हैं:

Input(main) -> Output(filter)  # reset filter
# when filter changes, take main's state and use it to update output
Input(filter), State(main) -> Output(output)  

यह अवरुद्ध नहीं होता क्योंकि यह एक जंजीर कॉलबैक है: मुख्य बदलने से फ़िल्टर साफ़ हो जाता है, और फ़िल्टर बदलने से दूसरा कॉलबैक ट्रिगर होता है जो लेता है मुख्य की स्थिति और अपडेट आउटपुट.

समय-गहन संचालन के लिए, हो सकता है कि आप उन परिणामों को कैश करना चाहें। दस्तावेज़ में यह दर्शाने वाली एक प्रविष्टि है: https://dash.plotly.com/performance

0
lahsuk 20 नवम्बर 2020, 14:27