Code Monkey home page Code Monkey logo

dash-extensions's People

Contributors

andressommerhoff avatar ann-marie-ward avatar annmariew avatar chanana avatar crayxt avatar dependabot[bot] avatar emilhe avatar frnhr avatar jfftonsic avatar jonassjostrand avatar kuhlwein avatar lcornelatti avatar mbwmbw1337 avatar mfreeborn avatar rafaelwo avatar snehilvj avatar stlehmann avatar tcbegley avatar tomaszrewak avatar yook74 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

dash-extensions's Issues

MultiplexerTransform cannot read property error

I keep getting the error

Cannot read property 'children' of undefined when applying the MultiplexerTransform extension.

Unfortunately I cannot share a reproducible example at this time. I have tried creating the objects with defined children.

Any ideas of where to begin addressing this?

memoize caching the python object, not the JSON serialization

I am very interested by the memoize capacity yet by going through the code, I see it is not caching the JSONResponse but the object before its serialization.
As the JSON serialiaztion is quite expensive in my case (hourly data on a year with multiple traces => lot of datetime JSON serialisation), it is still very slow.
Would it be feasible to cache the final response (JSON) instead of the intermediate Figure object ?

AttributeError: 'PosixPath' object has no attribute 'read_text'

Collecting dash-extensions==0.0.31 (from -r requiremnets.txt (line 6))
Downloading https://files.pythonhosted.org/packages/72/60/52af601da27fb2d57828138746326a356976347e3554df2854695816a06b/dash-extensions-0.0.31.tar.gz (329kB)
100% |████████████████████████████████| 337kB 2.9MB/s
Complete output from command python setup.py egg_info:
Traceback (most recent call last):
File "", line 1, in
File "/tmp/pip-build-l2Fdzu/dash-extensions/setup.py", line 15, in
README = (HERE / "README.md").read_text()
AttributeError: 'PosixPath' object has no attribute 'read_text'

Not able to install dashextensions in Ubuntu env.

Thank you

Hey, just wanted to say a quick thanks for the work put into this

The Download component is a really solid piece of functionality that eliminates serious backbending and I am very appreciative of this package

Thanks again,
Leete

`Input` / `State` wildcards not in `Output`s

When I use dash_extensions.enrich, the following error appears

In the callback for output(s): 82572af3-409b-4eed-8fa6-058307d2c581.children State 0 ({"index":MATCH,"type":"download-btn"}.id) has MATCH or ALLSMALLER on key(s) index where Output 0 (82572af3-409b-4eed-8fa6-058307d2c581.children) does not have a MATCH wildcard. Inputs and State do not need every MATCH from the Output(s), but they cannot have extras beyond the Output(s).

Manipulate excel file before using send_data_frame

I am using the send_data_frame functionality for providing a download data option which works perfectly with the example code provided in the documentation.
return send_data_frame(df.to_excel, filename='data.xlsx')
However, what is the solution if I don't want to generate the excel within the function but before, so I can manipulate the excel file with the xlsxwriter like in these examples beforehand? https://xlsxwriter.readthedocs.io/working_with_pandas.html

In this case I need to generate the excel file first and then access it:

writer = pd.ExcelWriter('pandas_simple.xlsx', engine='xlsxwriter')

# Convert the dataframe to an XlsxWriter Excel object.
df.to_excel(writer, sheet_name='Sheet1')

# Get the xlsxwriter objects from the dataframe writer object.
workbook  = writer.book
worksheet = writer.sheets['Sheet1']

# Apply a conditional format to the cell range.
worksheet.conditional_format('B2:B8', {'type': '3_color_scale'})

# Close the Pandas Excel writer and output the Excel file.
writer.save()

Not serializable error for ServersideOutput

I have a function that receives input from two different sources, picks one of them, and then passes it to two outputs.

@app.callback(
    ServersideOutput('interface-state', 'data'),
    ServersideOutput('tag-table-input', 'data'),
    Input('data-source-state', 'modified_timestamp'),
    Input('flight-analyze-state', 'modified_timestamp'),
    State('data-source-state', 'data'),
    State('flight-analyze-state', 'data'),
)
def data_funnel(data_src_t, flight_t, data_src, flight):
    return data_src if data_src_t > flight_t else flight

Both inputs receive dictionaries with an object as a value that is not serializable. When the same object is passed from data-source-state, the output of this function complains about the object not being serializable. When it comes from flight-analyze-state it passes through without a problem! I pickle the object and pass a serializable value to the input then load the object and it works fine. Very confused about what is going on.

Here's the error:

Traceback (most recent call last):
File "C:\Users\MahdiHosseinali\miniconda3\envs\data\Lib\site-packages\dash_extensions\enrich.py", line 604, in decorated_function
unique_id = _get_cache_id(f, output, list(filtered_args), output.session_check)
File "C:\Users\MahdiHosseinali\miniconda3\envs\data\Lib\site-packages\dash_extensions\enrich.py", line 620, in get_cache_id
return hashlib.md5(json.dumps(all_args).encode()).hexdigest()
File "C:\Users\MahdiHosseinali\miniconda3\envs\data\Lib\json_init
.py", line 231, in dumps
return _default_encoder.encode(obj)
File "C:\Users\MahdiHosseinali\miniconda3\envs\data\Lib\json\encoder.py", line 199, in encode
chunks = self.iterencode(o, _one_shot=True)
File "C:\Users\MahdiHosseinali\miniconda3\envs\data\Lib\json\encoder.py", line 257, in iterencode
return _iterencode(o, 0)
File "C:\Users\MahdiHosseinali\miniconda3\envs\data\Lib\json\encoder.py", line 179, in default
raise TypeError(f'Object of type {o.class.name} '
TypeError: Object of type rootDirectory is not JSON serializable

multiple callbacks - same output

in previous versions of this package, it was possible to write multiple callbacks to change the same output component (and also passing the triggering components value).
if I understand the README right, this is not possible anymore is it?

is there some way to achieve this functionality?

new feature: composed components

hello @emilhe

Given you in depth knowledge of Dash and the fact dash-extensions already provide an enhanced Input/Output/State/Trigger layer, I was wondering if you could have a look at the thread https://community.plotly.com/t/build-a-composed-component-in-python/45913/12 ... but you may be the Emil on the community forum ;-)
If it should not land in some dash core library, I feel your library would be the best alternative to host this kind of feature!

The proposal of oegedijk is close yet it does not provide the encapsulation, i.e. the ability to hide the properties of the children components and declare new properties for the composed component. I was thinking about some mapping between "some-new-property" -> Tuple("component-id1.property-name1", "component-id2.property-name2, ...) that could be transparently handled/converted/transformed by your custom Input/Output/State/Trigger classes.

seb

Grouped pattern-matching callbacks not triggering Input or Trigger

When using a grouped callback, a Trigger or Input with a pattern-matching id does not trigger the callback. The following example code does not invoke the callback unless I remove the group="group"

from dash_extensions.enrich import Dash
from flask import Flask
import dash_html_components as html

from dash.dependencies import ALL
from dash_extensions.enrich import Trigger, Input

fapp = Flask(__name__)
app = Dash(__name__, server=fapp)
app.suppress_callback_exceptions = True

@app.callback(
    Input(dict(type="delete", id=ALL), "n_clicks"), group="group"
)
def do(d):
    print("Triggered")


app.layout = html.Div([
    html.Button("B1", id=dict(type="delete", id="1")),
    html.Button("B2", id=dict(type="delete", id="2"))
])

app.run_server(debug=True)

Also - solid work with this awesome package! 👍

Support partial updates when using multiple ServersideOutput

It would be great if a function returning multiple ServersideOutputs could mark some as not updated by returning dash.no_update.
See Displaying errors with dash.no_update on https://dash.plotly.com/advanced-callbacks. Below is a basic example of what I mean with a work around.

import time

import dash
import dash_core_components as dcc
import dash_html_components as html
import plotly.express as px
from dash_extensions.enrich import Dash, Output, Input, State, Trigger, ServersideOutput

app = Dash(prevent_initial_callbacks=True)
app.layout = html.Div(
    [
        html.Button("Query data", id="btn"),
        dcc.Dropdown(id="dd"),
        dcc.Graph(id="graph"),
        dcc.Loading(dcc.Store(id="store"), fullscreen=True, type="dot"),
    ]
)


@app.callback(
    [ServersideOutput("store", "data"), ServersideOutput("store", "years")],
    Trigger("btn", "n_clicks"),
    State("store", "years"),
)
def query_data(years):
    time.sleep(1)
    df = px.data.gapminder()
    years_updated = list(df["year"])
    if years_updated == years:
        years_updated = dash.no_update
    return df, years_updated


@app.callback([Output("dd", "options"), Output("dd", "value")], Input("store", "years"))
def update_dd(years):
    if isinstance(years, type(dash.no_update)):
        raise dash.exceptions.PreventUpdate
    return [{"label": year, "value": year} for year in years], years[0]


@app.callback(Output("graph", "figure"), [Input("store", "data"), Input("dd", "value")])
def update_graph(df, value):
    df = df.query("year == {}".format(value))
    return px.sunburst(df, path=["continent", "country"], values="pop", color="lifeExp", hover_data=["iso_alpha"])


if __name__ == "__main__":
    app.run_server(port=8989)

Hitting error with normal Output and memoize=True

Hi, thanks for putting together this package - the serverside caching is exactly what I was looking for and it looks pretty easy to use.

In trying to adapt my code, I'm running into an error when I try to memoize a callback that is using just a normal output and not a serverside output. With that configuration, I hit an error on the following line as output does not have an attribute called backend. Is the use of normal outputs and memoize=True not supported?

https://github.com/thedirtyfew/dash-extensions/blob/6c530cd47667d06c856f28c4466179e639fbb6fa/dash_extensions/enrich.py#L407

To recreate, one can take the callback_serverside_output.py example file and just add memoize=True to the callback decorator of the update_dd function, like so:

@app.callback(Input("store", "data"), Output("dd", "options"), memoize=True)
def update_dd(df):
    return [{"label": column, "value": column} for column in df["year"]]

flask_login

Hi,
Thanks for the excellent work.
I tried to use flask_login on dash_extension and I have these errors:

code:

server = Flask(name)
app = Dash(name, server=server)
login_manager=LoginManager()
login_manager.init_app(app)

error:

Traceback (most recent call last):
File "index.py", line 26, in
login_manager.init_app(app)
File "/usr/local/lib/python3.6/site-packages/flask_login/login_manager.py", line 117, in init_app
app.after_request(self._update_remember_cookie)
AttributeError: 'Dash' object has no attribute 'after_request'

May I know what could have been the problem?

thread/process safe caching

hello,

I wonder if the current memoize feature is thread & process safe ? ie if multiple inputs/triggers can start a callback that is expensive to run (in time), will the memoization make that only one calculation is done (when cache not initialised) vs multiple ones.

I just saw https://github.com/sjtrny/jitcache that could be a solution to make the memoize thread/process safe.

It is all suppositions as I haven't done the test (or dug into the code)

Monitor API

hello @emilhe

Great to see you new extension Monitor to solve the issue of having the same Component in Input&Output.
However, I am wondering if we could not have a simpler API like the following (with all needed reactjs managed behind the screen)

import dash_core_components as dcc
import dash_html_components as html
from dash import Dash, no_update
from dash.dependencies import Input, Output
from dash_extensions import Monitor

app = Dash()
app.layout = html.Div(
    [
        dcc.Input(id="deg-fahrenheit", autoComplete="off", type="number"),
        dcc.Input(id="deg-celsius", autoComplete="off", type="number"),
    ]
)


@app.callback(
    [Output("deg-fahrenheit", "value"), Output("deg-celsius", "value")],
    [Monitor([Input("deg-fahrenheit", "value"), Input("deg-celsius", "value")])],
)
def sync_inputs(data):
    deg_fahr, deg_cels = data

    # Do the appropriate update.
    if deg_fahr is not None:
        return no_update, (deg_fahr - 32) * 5 / 9
    elif deg_cels is not None:
        return deg_cels * 9 / 5 + 32, no_update


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

or even simpler with a "*" before the component_id that would do what is needed to avoid the "cannot have the same Input and Output" error message (hoping that some day the special case of having the same Input and Output would work natively in Dash)

import dash_core_components as dcc
import dash_html_components as html
from dash import Dash, no_update
from dash.dependencies import Input, Output

app = Dash()
app.layout = html.Div(
    [
        dcc.Input(id="deg-fahrenheit", autoComplete="off", type="number"),
        dcc.Input(id="deg-celsius", autoComplete="off", type="number"),
    ]
)


@app.callback(
    [Output("deg-fahrenheit", "value"), Output("deg-celsius", "value")],
    [Input("deg-fahrenheit", "*value"), Input("deg-celsius", "*value")],
)
def sync_inputs(deg_fahr, deg_cels):
    # Do the appropriate update.
    if deg_fahr is not None:
        return no_update, (deg_fahr - 32) * 5 / 9
    elif deg_cels is not None:
        return deg_cels * 9 / 5 + 32, no_update


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

This would cover maybe more cases than what Monitor is currently covering (not sure) with a simpler API

Not supporting dash's callbacks when you name the parameters

Hi again,
I tried to use your mod today and after changing the imports, the application failed to start and after an easy glance it was because my callbacks were being defined like the following example:

        @app.callback(
            output=[ ... ],
            inputs=[ ... ],
            state=[ ... ]
        ) ....

which is one of the ways that pure dash lets you define the callback. The problem happens because I used the 'output=...' etc to define the parameters.

I already have implemented a modification directly in my workspace, on the error point which is in enrich.py. It is very simple really.

I will go on and try to fork, etc to submit a pull request to your project. Unless I shouldn't do that, then let me know.

Cheers

Proptype validation for Download

Hi! I get a console warning regarding the default mime_type proptype validation for the Download component, should the default proptype be mime_type: PropTypes.string rather than mime_type: PropTypes.bool? (line 71 of Download.react.js)

using Download for JPG/SVG etc files

Hi,
First, let me say that your extension looks great! And I am looking forward to using some more of the features soon!
I found your dash-extensions while looking for a way to allow a user to download files. It worked beautifully for an html file, but I am struggling to get a jpg file to both send and be openable. I am wondering if these is a fixed limitation due to the fact that the callbacks have to be jsonify-able or if I am just not understanding how to properly use your extension.

This is roughly what I am trying to do:

@app.callback(Output(dl_id, 'data'), Input(fig_id, 'figure))  # dl_id is the 'id' of a Download component
def download_fig(fig):
    fig = go.Figure(fig_dict) 
    data = fig.to_image(format='jpg')
    data = base64.b64encode(data).decode()  # Copying what you do in sendbytes etc
    return dict(content=data, filename='test.jpg', mimetype='image/jpg', byte=True)

The file does download, and it recognized as a jpg, but fails to open, presumably because of the encoding. If I copy the contents of the downloaded file into a base64 converter, I see the image I am aiming for, but this is obviously not ideal...

Am I missing something? Or is this just a limitation imposed by how Dash works in general?

Thank you very much!
Tim

websocket doesn't work with feed from queue?

I'm trying to use data from a queue as a live data feed into the websocket. and for some reason even though it dequeues, it doesn't updates the graph.

import json
import dash_html_components as html
import random
import dash_core_components as dcc
import plotly.graph_objects as go
from gevent import sleep
from dash import Dash
from dash.dependencies import Input, Output, State
from dash_extensions import WebSocket
from dash_extensions.websockets import SocketPool, run_server

import threading, queue

def genData(q):
    while True:
        sleep(random.uniform(0, 2))  # delay between data events
        q.put(random.uniform(0, 1))  # the data value

# This block runs asynchronously.
def ws_handler(ws,q):
    # for data in data_feed():
    #     ws.send(json.dumps(data))  # send data
    while len(list(q.queue))!=0:
        data = q.get()
        ws.send(json.dumps(data))
        q.task_done() 
q = queue.Queue()

# Create example app.
app = Dash(prevent_initial_callbacks=True)
socket_pool = SocketPool(app, handler=lambda x: ws_handler(x,q))
app.layout = html.Div([
    dcc.Graph(id="graph", figure=go.Figure(go.Scatter(x=[], y=[]))), 
    WebSocket(id="ws")
])


@app.callback(Output("graph", "figure"), [Input("ws", "message")], [State("graph", "figure")])
def update_graph(msg, figure):
    x, y = figure['data'][0]['x'], figure['data'][0]['y']
    return go.Figure(data=go.Scatter(x=x + [len(x)], y=y + [float(msg['data'])]))


if __name__ == '__main__':
    threading.Thread(target = genData,daemon =True, args = (q,) ).start()
    run_server(app, port=5000)  # 5000 if the default port

Allow setting the full list of react-burger-menu properties

As pointed out in the react-burger-menu documentation, the properties pageWrapId and outerContainerId needs to be set to make some of the effects work. However, these properties are not exported to the dash python module so there is not way to set them.

Fine-control of cached data

Hi, first of all, thanks for this awesome lib!

Use Case

I'm developing a data-studio app with Dash and Dash-Extensions for multiple simultaneous users.

Problem Description

  • Say we have users A and B working with the app. Both will fetch data from a database, which I am server-side caching with ServersideOutput callbacks, which will create two large files in my filesystem.

  • If user A fetches a new dataset, a new file will be created, but the old one will not be deleted (at least as long as FileSystemStore.threshold is large enough).

  • This will result in three large files in the filesystem, but only two of them are being used.

Desired Behavior

I'd like to have no more than one cached file for a given (session, function) tuple, but it is currently cached with respect to a (session, function, arguments) tuple. Is there any way to implement the (session, function) tuple caching with Dash Extensions?

Use existing flask_caching redis instance in place of FileSystemStore

Hi, thanks for the super useful extensions, especially the ServersideOutput callback.

I noticed that, by default, dash-extensions is using a FileSystemStore for memoization and I've seen that you can change the path that based on your comments here: https://community.plotly.com/t/show-and-tell-server-side-caching/42854/18

The rest of my app is using redis as cache by setting it up through flask_caching:

from flask_caching import Cache
import os
from app import app


CACHE_CONFIG = {
    "CACHE_TYPE": "redis",
    "CACHE_REDIS_URL": os.environ.get("REDIS_URL", "redis://localhost:6379"),
}

cache = Cache()
cache.init_app(app.server, config=CACHE_CONFIG)

Since dash-extensions is also using flask_caching, is there a way I can get rid of FileSystemStore and use redis directly?

warm up cache

hello @emilhe ,

Is it possible to warm up the cache behind memoize at the start of the app ?
I would like to be able to call the function with different values of arguments so that at first use by the user the results are already there.

A websocket example connecting to socket IO python server?

Sorry to ask a question here cause this is not an issue with the dash-extensions, it is simply me not knowing websockets etc well enough to use it.

I'm simply just trying to write a socketIO server running on gunicorn (uvicorn for async) and have the websocket from dash-extensions connect to it?

WebSocket connection to 'ws://localhost:8000/' failed: Error during WebSocket handshake: Unexpected response code: 404
value @ WebSocket.react.js:14

server code

import socketio

sio = socketio.Server()
app = socketio.WSGIApp(sio)

@sio.event()
def connect(sid, environ, auth):
    print('connect ', sid)
    # Refuse client connections
    if False:
        raise ConnectionRefusedError('authentication failed')

@sio.event
def disconnect(sid):
    print('disconnect ', sid)


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

client side is just the example code

import dash_core_components as dcc
import dash_html_components as html
import dash_extensions as de
from dash import Dash
from dash.dependencies import Input, Output

# Create example app.
app = Dash(prevent_initial_callbacks=True)
app.layout = html.Div([
    dcc.Input(
        id="input", autoComplete="off"
        ),
    html.Div(id="msg"),
    de.WebSocket(url="ws://localhost:8000/", id="ws")
])
# Send input value using websocket.
send = "function(value){return value;}"
app.clientside_callback(            
            send,
            Output("ws", "send"),
            [Input("input", "value")],
        )
# Update div using websocket.
receive = "function(msg){console.log(msg.data); return \"Response from websocket: \" + msg.data;}"
app.clientside_callback(
            receive,
            Output("msg", "children"),
            [Input("ws", "message")]
        )

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

license

Hey Emil,

I just want to ask a quick question:
PyPi mentions dash-extensions is licensed under MIT. Is this correct?
Maybe you could add this to the LICENSE file in the repository, which is empty at the moment.

Greetings
Dominik

Include an example with ServersideOutput and memoize in the documentation?

Hi, I'm wondering if you could include an example with ServersideOutput and memoize in the documentation. I'm having some trouble since the syntax is changed. For example this example does not work for some reason. Thank you.

import time
import dash_core_components as dcc
import dash_html_components as html
import plotly.express as px
from dash.dependencies import Output, Input
from dash_extensions.enrich import Dash, Trigger, ServersideOutput

app = Dash(prevent_initial_callbacks=True)
app.layout = html.Div([
    html.Button("Query data", id="btn"), dcc.Dropdown(id="dd"), dcc.Graph(id="graph"),
    dcc.Loading(dcc.Store(id="store"), fullscreen=True, type="dot")
])

@app.callback(ServersideOutput("store", "data"), Trigger("btn", "n_clicks"), memoize=True)
def query_data():
    time.sleep(1)
    return px.data.gapminder()

@app.callback(Input("store", "data"), Output("dd", "options"))
def update_dd(df):
    return [{"label": column, "value": column} for column in df["year"]]

@app.callback(Output("graph", "figure"), [Input("store", "data"), Input("dd", "value")])
def update_graph(df, value):
    df = df.query("year == {}".format(value))
    return px.sunburst(df, path=['continent', 'country'], values='pop', color='lifeExp', hover_data=['iso_alpha'])

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

Achieve loading cached data and cache data back within a callback

Thanks for open-sourcing this project. It helped me a lot.

I am trying to build a dashboard that requires data (pandas df) to be shared across callbacks. In one such callback, input data (cached) needs to be transformed based on the client's selected option and cache back this data. The transformed output at the current step becomes the input data for the next step trigged by the click action.

If I understood correctly, cc.cached_callback is used only to cache data and app.callback to load cached data.

In my case, I want to load cached data and cache back the transformed data within the same callback.

Any advice or guidance in this matter would be greatly appreciated.

Silly question: Why is this not in dash project itself?

Hello,
The features that this project provides seem sufficiently important and I'd guess useful for most people that use dash itself.
Is there a reason why all this implementation is not part of dash itself?

PS: this probably is not the place for this kind of question, but I didn't know where else to ask it.

Method for download file / send file with b64 encoded buffer

I'd like to use your Download dash-extension for sending a pdf to a user.
I can create the pdf using pdfrw and then generate a base64 encoded file as follows:

buf = io.BytesIO()
pdfrw.PdfWriter().write(buf, my_pdf_data)
buf.seek(0)
data_out = base64.encodebytes(buf.read()).decode()

I've attempted to use the raw download method:

dict(content=data_out , filename="my_pdf.pdf")

But the resultant file is not correctly formatted (if the data_out isn't b64 encoded then the method complains about not being JSON serializable). I see the send_bytes method, but I'm not sure how to use it for my case. Could an example be added for this?

Thanks in advance

Keyboard not updating for repeated key press

The keyboard example in the README.md does not work, if the same key is pressed >=2 times (i.e. press -> release -> press).
The expected behavior, at least for me, is that the keyboard event should be signaled twice, even for the same key.
The current behavior is that it ignores the key presses. This is due to the implementation uses keydown as a prop, which is not changed for the same key press event (by shallow comparison).

[Trigger] Allow to raise PreventUpdate on None values

Hi,
The main use I have for Trigger is for handling buttons.
However, my callbacks are triggered on load, as the n_clicks property sends a None value to the callback when a button is created.
Is there a way to use Trigger instead of Input and raise a PreventUpdate exception if the value sent by Trigger is None ?
I have no use of the actual number of clicks on the button, I just need to know when it's clicked on.

how to get started?

Hello,

thanks, for providing the library. Especially, the multipage approach looks useful to me.
I had something similar in the past, but not as clean as this one.

Nevertheless, I cannot manage to run the examples. I think that the imports are somehow not correct.

Things, I tried:

  1. Clone the repo and run the examples directly in pycharm only installing the requirements
  2. Install dash-extensions and run the examples in pycharm

Either way, I do not manage to have imports working.
Could it be that this was never tested?

Otherwise, please elaborate how to start using the library or run the examples :)

Thanks in advance!

WebSocket does not work when running the server with debug=True

Hello, I am getting this error when I enable the debug flag with app.run_server(debug=True)

Invalid argument send passed into WebSocket with ID "ws".
Expected object.
Was supplied type string.

However it works fine if I run it without the flag : app.run_server()

MultiplexerTransform does not work with clientside callback

Hi, first of all a big thanks for your work!

I tried the MultiplexerTransform which does not seem to work when clientside callbacks are involved. Is this expected behavior?

I adapted the provided sample to a mwe:

import dash_html_components as html
from dash_extensions.enrich import Output, DashProxy, Input, MultiplexerTransform

app = DashProxy(prevent_initial_callbacks=True, transforms=[MultiplexerTransform()])
app.layout = html.Div([html.Button("left", id="left"), html.Button("right", id="right"), html.Div(id="log")])


@app.callback(Output("log", "children"), Input("left", "n_clicks"))
def left(_):
    return "left"


app.clientside_callback(
    """
    function(n_clicks){
        return "right";
    }
    """,
    Output("log", "children"),
    Input("right", "n_clicks")
)



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

Dash Bootstrap Components Tooltip and Multipage App Extension

Use Case

Development of multipage apps where the individual pages include tooltips from the Dash Bootstrap Components package.

Problem Description

The Dash Bootstrap Tooltip component (https://dash-bootstrap-components.opensource.faculty.ai/docs/components/tooltip/)) utilizes a "target" keyword argument to specify the id of its target component. Component ids are mutated by the multipage app. However, the target is not updated and this breaks the tooltip.

Desired Behavior

The tooltip target is updated to match the id.

Do grouped callbacks reduce the data transmission from client to server

I'm currently working on a dashboard, where selections and zooms in a graph should update the values of a dropdown.
At the moment I accomplish this with plain Dash with on callback that has the 'relayoutData' and the 'selectData' as Input and the figure as state.

In the callback I use dash.callback_context to determine which Input triggered the callback.
This method sends all data from the client to the server.
But obviously I only need one of the Inputs as only one can be updated at a time.
Additionally I only need the figure to access some of its data in one case. In the other only the Input data is sufficient.

I stumbled upon your extension while searching for a way to avoid the unnecessary data transmission.
But I'm not sure if your extension will solve my problem.
Did you tinker with the frontend code to only send the required data?
Or do you 'just' use the callback context and pass on the selected data to the callback? In this case all data would be transmitted, right?

I would appreciate clarification on this.

MATCH wildcard in MultiplexerTransform

Would it be possible to support MATCH wildcards where multiple callbacks have the same output?
Currently it errors and gives an error similar to this:

TypeError: dict id values must be strings, numbers or bools,
found {'name': 'div', 'id': <MATCH>} in id {'id': { 'name': 'div', 'id': <MATCH> } 'prop': 'children', 'idx': 0}

MWE:

from dash_extensions.enrich import DashProxy, MultiplexerTransform, Input, Output, MATCH
import dash_html_components as html

app = DashProxy(transforms=[MultiplexerTransform()])

app.layout = html.Div([
  html.Button("Button 1", id={'type': 'button', 'id': 0}),
  html.Button("Button 1", id={'type': 'button', 'id': 1}),
  html.Div(id={'type': 'div', 'id': 0}),
  html.Div(id={'type': 'div', 'id': 1}),
])

@app.callback(
  Output({'type': 'div', 'id': MATCH}, 'children'),
  Input({'type': 'button', 'id': MATCH}, 'n_clicks'),
)
def cb1(n):
  return "Hello from callback 1"

@app.callback(
  Output({'type': 'div', 'id': MATCH}, 'children'),
  Input({'type': 'button', 'id': MATCH}, 'n_clicks'),
)
def cb2(n):
  return "Hello from callback 2"

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

bug using State with ServersideOutput and memoize

Hi Emil, I think there is a bug using State with ServersideOutput and memoize. This does not appears in the previous version. I replicate this issue in the below example. Start by uploading any csv file.

import base64
import datetime
import io
import pandas as pd
import dash_table
import dash_core_components as dcc
import dash_html_components as html
from dash_extensions.enrich import Dash, Output, State, Input, Trigger, ServersideOutput

app = Dash(prevent_initial_callbacks=True)
app.layout = html.Div([
    dcc.Upload(
        id='upload-data',
        children=html.Div([
            'Drag and Drop or ',
            html.A('Select Files')
        ]),
        style={
            'width': '100%',
            'height': '60px',
            'lineHeight': '60px',
            'borderWidth': '1px',
            'borderStyle': 'dashed',
            'borderRadius': '5px',
            'textAlign': 'center',
            'margin': '10px'
        },
        # Allow multiple files to be uploaded
        multiple=True
    ),
    dcc.Store(id='output-data-upload'),
])


def parse_contents(contents, filename, date):
    content_type, content_string = contents.split(',')

    decoded = base64.b64decode(content_string)
    try:
        if 'csv' in filename:
            # Assume that the user uploaded a CSV file
            df = pd.read_csv(
                io.StringIO(decoded.decode('utf-8')))
        elif 'xls' in filename:
            # Assume that the user uploaded an excel file
            df = pd.read_excel(io.BytesIO(decoded))
    except Exception as e:
        print(e)
        return html.Div([
            'There was an error processing this file.'
        ])

    return html.Div([
        html.H5(filename),
        html.H6(datetime.datetime.fromtimestamp(date)),

        dash_table.DataTable(
            data=df.to_dict('records'),
            columns=[{'name': i, 'id': i} for i in df.columns]
        ),

        html.Hr(),  # horizontal line

        # For debugging, display the raw contents provided by the web browser
        html.Div('Raw Content'),
        html.Pre(contents[0:200] + '...', style={
            'whiteSpace': 'pre-wrap',
            'wordBreak': 'break-all'
        })
    ])


@app.callback(ServersideOutput('output-data-upload', 'data'),
              [Input('upload-data', 'contents')],
              [State('upload-data', 'filename'),
               State('upload-data', 'last_modified')],
               memoize=True)
def update_output(list_of_contents, list_of_names, list_of_dates):
    if list_of_contents is not None:
        children = [
            parse_contents(c, n, d) for c, n, d in
            zip(list_of_contents, list_of_names, list_of_dates)]
        return children

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

It throws:

IndexError: list index out of range

Traceback (most recent call last)
File "~~~~~\dash_extensions\enrich.py", line 339, in decorated_function
args = [arg for i, arg in enumerate(args) if i > len(callback[Input]) or
File "~~~~~~\dash_extensions\enrich.py", line 340, in <listcomp>
not isinstance(callback[Input][i], Trigger)]
IndexError: list index out of range

Keyboard callback conflicting with other callbacks

So I have a simple dash app where an image is generated based on a slider value.

@app.callback(
    Output('image_plt', 'src'),
    [Input('chart-dropdown', 'value'), Input('slider-step', 'value'), Input('proj-dropdown', 'value')])
def update_figure(chart, f_step, projection):
    return figure 

I want to be able to control the slider not only with the mouse but also with the keyboard by using the LeftArrow and RightArrow events to advance the slider. I wrote this simple function

@app.callback(Output(component_id='slider-step', component_property='value'), 
  [Input("keyboard", "keydown"), Input("slider-step", "value")])
def update_slider(event, slider_value):
  key_pressed = event['key']
  if key_pressed == "ArrowRight":
    if slider_value == max(steps):
      out = steps[0]
    else:
      out = slider_value + 1 
  elif key_pressed == "ArrowLeft":
    if slider_value == min(steps):
      out = steps[-1]
    else:
      out = slider_value - 1

  return out

which updates the value property of the slider depending on the keydown event. It also takes care of wrapping the next value if the end of the slider is reached.

I thought this was going to work fine but when I insert this function in my app no image is produced and pressing the key doesn't update the slider BEFORE I interact with the mouse. Afterwards I'm able to change the value of the slider with my keys but the image is still not produced and for some reason I cannot go beyond the first and last element of the list (no wrapping). Any idea what I'm doing wrong?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.