Thanks to visit codestin.com
Credit goes to github.com

Skip to content

first pass at plotly-ipython graph widget #171

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 37 commits into from
Jan 22, 2015
Merged

first pass at plotly-ipython graph widget #171

merged 37 commits into from
Jan 22, 2015

Conversation

chriddyp
Copy link
Member

usage: http://nbviewer.ipython.org/gist/chriddyp/98a3d27a33ecc044a393

animation example - taylor series of sine (values update as you pan around) http://nbviewer.ipython.org/gist/chriddyp/9600fa8314e70ce89f06

cc @theengineear , @etpinard , @msund , @jackparmer

pretty clean, pretty fun!

  • document syntax
  • basic message validation
  • decide on save state of zoom?
  • getFigure postMessage spec, maybe implementation?
  • protected imports? / determine strict dependencies
  • snapshot spec, maybe implementation?
  • default empty frame spec, maybe implementation?

with open(js_widget_file) as f:
js_widget_code = f.read()

display(Javascript(js_widget_code))
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure what the best way to inject JS code into ipython is. we should only have to do this once - maybe require.js can help us here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or maybe do it on import?

@etpinard
Copy link
Contributor

etpinard commented Jan 2, 2015

So far so good from my end. 👍

One thing that caught my eyes though: could patterns like

from IPython.html.widgets import IntSliderWidget
from plotly.widgets import Graph
import plotly.plotly as py

from IPython.display import display

# ....

slider = IntSliderWidget(min=0,max=20,step=0.1)
slider.on_trait_change(make_cagraph = Graph(py.plot([{}], filename='taylor series widget', auto_open=False))llback(slider, graph), 'value')

display(slider)
display(graph)

be abstracted a little bit more ?

Maybe have something like:

from plotly.widgets import Graph, Slider

graph = Graph(py.plot([{}], filename='taylor series widget', auto_open=False))

slider = Slider(min=0, max=20, step=0.1)
slider.on_trait_change(make_callback(slder, graph), 'value')
slider.display()
graph.display()

would make things easier for users that never played around with IPython widgets before.

@theengineear
Copy link
Contributor

@chriddyp , so slick! Gonna take a peek at #170 first, then I'll play around with this!

@theengineear
Copy link
Contributor

Alright, digging in :)

Where/how does the restyle/relayout call get sent to plotly? Is this what's happening?

(1) User calls g.restyle() in IPython.
(2) That gets sent to IPython backend and handled in JS.
(3) The backend JS code communicates with Plotly?

@theengineear
Copy link
Contributor

Can we (should we) change relayout and restyle to expose a simpler API for folks?

Say, using update or something on a Figure object? Would that be possible?

So, instead of restyle and relayout, users can do things like:

fig['layout'].update(title='new title')

Under the hood, we can still call relayout and restyle, but we might want to consider some convenience functions.

@chriddyp
Copy link
Member Author

chriddyp commented Jan 6, 2015

@theengineear re #171 (comment) - on (3) yeah, the JS code communicates to the embedded Plotly iframe through the postMessage protocol and listens to messages sent from the events in the iframe on the window event here: https://github.com/plotly/python-api/pull/171/files#diff-5283ffa0a4b258e3ffc8722baffb9912R84. So, this is entirely client-side.

@chriddyp
Copy link
Member Author

chriddyp commented Jan 6, 2015

@theengineear re #171 (comment), we'll want to keep the Figure object isolated from the GraphWidget object because I want the GraphWidget parameterized by a (any) plotly embed URL.

@chriddyp
Copy link
Member Author

chriddyp commented Jan 6, 2015

we could have something more gnarly like a Graph object which has a Figure object as a parameter. We could pass the figure object with postMessage from the iFrame over to the client keep Graph.figure and the embedded iframe in sync.

This sort of feels like moving towards @BRONSOLO 's plotlyfigure object in matlab, which basically had everything bound to it, like fig.png, fig.url, fig.data, fig.layout, fig.filename.

  • Would you save the graph with py.plot(graph.figure) or with graph.save(filename)
  • Would streaming be incorporated into this graph object? graph.update({'x': 1, 'y': 1}, streaming=True)

Yikes!

@theengineear
Copy link
Contributor

@chriddyp , is there a way to add a completely new trace to a graph? I naively tried to restyle a blank graph, but realized that that's not workable.

Along these lines, we could also offer to just serve up a blank iframe for people to build on. Thoughts?

@chriddyp
Copy link
Member Author

chriddyp commented Jan 6, 2015

@theengineear there isn't right now, which is pretty annoying!

@theengineear
Copy link
Contributor

This is something that needs to be built into the js, yeah? Same as the remove-trace?

@chriddyp
Copy link
Member Author

chriddyp commented Jan 6, 2015

yeah exactly

@theengineear
Copy link
Contributor

@chriddyp , i added in the framework for add/delete/move traces (it's not working yet because that PR isn't pulled into master on streambed) the syntax may also change, i opened up a discussion about how we format postMessage here: https://github.com/plotly/streambed/pull/1114#issuecomment-69295142

@theengineear
Copy link
Contributor

Also, this doesn't work for me and I'm confused why :(

import random
import plotly.plotly as py
from plotly.graph_objs import *
from plotly.widgets import Graph

plotly_url = py.plot([Scatter()], filename='blank', auto_open=False)
graph = Graph(plotly_url)

def click_handler(*args, **kwargs):
    graph.restyle({'y': [[random.random() for _ in range(10)]]}, traces=[0])  
graph.on_click(click_handler)

graph

Also, also, it's not clear in the docs, but can you remove a click handler once you assign it? or do they overwrite themselves?

@theengineear
Copy link
Contributor

Have you taken a peek as the dev-tools console for this stuff, btw? full of errors and warnings.

This is just a warning, but we could probably clean it up by sending a string instead of an object? Is that reasonable? Received message of type object from http://localhost:8888, expected a string

Not sure what to do about this one... it just happens a bunch of times and then eventually stops complaining an works: Failed to execute 'postMessage' on 'DOMWindow': The target origin provided ('https://plot.ly') does not match the recipient window's origin ('http://localhost:8888').

@theengineear
Copy link
Contributor

Another thing, I opened the dev tools initially to try and debug why pointing python at my local streambed wasn't working, which I haven't figured out--i thought i'd just ask :)

Is 'https://plot.ly' hardcoded for us in python, or in frontend js?

@theengineear
Copy link
Contributor

k, found where the 'https://plot.ly' was hiding in graphWidget.js

I'm going to pass that into graphWidget.js now, but I'm also going to need a reference from graph_widget.py. for now that means i need to import plotly.py (ew)...

This is exactly why we should really create a new session module that everyone can import and get the most updated versions of the info the user has setup for the current session.

@theengineear
Copy link
Contributor

@chriddyp , the above commit should fix communication with local plotly instances. This will seriously improve development when changes need to be made to plotly frontent js code and the python-api widget code.

NOTE (this is a little annoying... the iframe js code is cached so every time you do make a change, you need to clear your cache) took me a second to realize that earlier and thought I'd mention it here.

All of our messages seem to be being sent twice! Not sure where that’s coming from. This is a temporary fix that synced with the new frontend postMessage hopefully we can revert this.
@theengineear
Copy link
Contributor

See #177 for the updates to this widgets branch.

@theengineear
Copy link
Contributor

Re: #171 (comment)

For graphJson, I'm thinking of somehow allowing an 'attributes' object that would just return the specified keys. For now, we can filter this in postMessage so that postMessage requests are stable, but eventually it will be better to make graphJson more flexible, but that might be a bigger refactor than i can do today.

Therefore:

// message for getting the *full* figure
{
    'task': 'getAttributes',
}

// message for getting certain attributes
{
    'task': 'getAttributes',
    'attributes': ['data[0].opacity', 'layout.title']
}

// message for getting defaults if user didn't specify
{
    'task': 'getAttributes',
    'useDefaults': True
}

// message for getting data only (no layout)
{
    'task': 'getAttributes',
    'attributes': ['data']
}

// message for getting layout only (no data)
{
    'task': 'getAttributes',
    'attributes': ['layout']
}

Something like this seem okay? There's already a postMessage getLayout, but I really think we can make this all into a single, very simple request under getAttributes or getGraph or whatever, i sorta like the former.

@theengineear
Copy link
Contributor

Nice NEWS file! ha, that was supposed to be in a different PR, lol @mkcor

@theengineear
Copy link
Contributor

to put it on record, we're going to go with the opposite logic, useDefaults --> ignoreDefaults

chriddyp added a commit that referenced this pull request Jan 22, 2015
first pass at plotly-ipython graph widget
@chriddyp chriddyp merged commit a00c8b0 into master Jan 22, 2015
@jackparmer
Copy link
Contributor

boom!

On Thu, Jan 22, 2015 at 5:34 PM, chriddyp [email protected] wrote:

Merged #171 #171.


Reply to this email directly or view it on GitHub
#171 (comment).

@theengineear theengineear deleted the widgets branch September 30, 2015 06:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants