Repository: bokeh/bokeh

Issue:
Method to update data source contents in the notebook
There is a reference implementation by @akloster in https://github.com/akloster/nb-bokeh-animations

```
def update_data_source(ds):
    id = ds.ref['id']
    model = ds.ref['type']
    js = """
        var ds = Bokeh.Collections('{model}').get('{id}');
        var data = {json};
        ds.set(data);
        """.format(
            model=model, 
            id=id, 
            json= serialize_json(ds.vm_serialize()))
    display.display_javascript(js, raw=True)
```

Plan to use this as a starting place for a method on data sources that will update the BokehJS data source and trigger a re-draw, from python. Propose `ds.notebook_update()` for method name. 



Discussion:
See #1211 as a reference as well

I play a little bit and something like this works nice:

``` python
x = np.linspace(0, 2*np.pi, 200)
y = np.sin(x)

output_notebook(force=True)

p = figure(title="simple line example", plot_height=200, plot_width=400)
p.line(x,y, color="#2222aa", line_with=2)

show(p)

def update(i):
    renderer = p.select(dict(type=GlyphRenderer))
    ds = renderer[0].data_source
    ds.data['y'] = np.sin(i * x)
    update_data_source(ds)

from IPython.html.widgets import *
interact(update, i=(0,10))
```

and because we are updating the datasource the slider run smoothly... :wink:

What do you think about the `AnimationWidget`(from @akloster 's notebook), is that something we could add an IPython magic for? Is it redundant with interactors?

If we are just going to update datasources, then we should merge the following calls:

```
ds = renderer[0].data_source
ds.data['y'] = np.sin(i * x)
update_data_source(ds)
```

into a single one:

```
p.update_data_source("y", np.sin(i*x))
```

If we are going to offer a separate, standalone function, then it should be more generic and be able to update more than just the datasource.

Additionally, is there a simple extension to allow a simple client-side timer mechanism be built into this, so it calls some JS code on a periodic interval?  Or is that out of scope?

I think if we do this right we can use a simple push function which can update any object on the JS side, which should emulate the push function that is used with the server.  Also I'm not sure why we need to trigger 'change', if we do, something is broken.  setting the data should trigger a change event

It works fine without the trigger call. I definitely agree we should make a general `push_notebook`. I will start with a `ds.update_notebook` function based on this, so that we can be sure to have something for 0.7 and if I have time will add the general `push_notebook` as well. 

@pzwang look at the actual referenced notebooks from @akloster  he makes an `AnimationWidget` that does exactly what you describe. We should integrate that functionality as well at some point (possibly 0.7 if we can)

@pzwang `p.update_data_source("y", np.sin(i*x))` won't work, plots not longer have data sources, they never did, in fact. Renderers have data sources

Actually along those lines I wonder if we can unify the notebook and server - as 1 API that one can interact with on the javascript and python side with 2different implementations (one with ipython kernel, and one without)

That is a lovely idea, I think this is useful enough that it should go out in some form immediately, and we can outline how to have a common API for a later release.

yea I'm just speculating for future work, I agree that this should go out as is

I know plots don't have a datasources - but from a usability standpoint, this is easier than having to grab the renderer and then update the renderer[0].data_source.  If this is code that is meant to be written by people in an IPython input cell, then the spelling matters a great deal more than it normally would.

I think a general `push_notebook` would be great.


Resolving patch:
diff --git a/bokeh/models/sources.py b/bokeh/models/sources.py
--- a/bokeh/models/sources.py
+++ b/bokeh/models/sources.py
@@ -96,6 +96,19 @@ def remove(self, name):
             import warnings
             warnings.warn("Unable to find column '%s' in datasource" % name)
 
+    def push_notebook(self):
+        from IPython.core import display
+        from bokeh.protocol import serialize_json
+        id = self.ref['id']
+        model = self.ref['type']
+        json = serialize_json(self.vm_serialize())
+        js = """
+            var ds = Bokeh.Collections('{model}').get('{id}');
+            var data = {json};
+            ds.set(data);
+        """.format(model=model, id=id, json=json)
+        display.display_javascript(js, raw=True)
+
 class ServerDataSource(DataSource):
     data_url = String()
     owner_username = String()


Test patch: