{"action":"created","issue":{"url":"https://api.github.com/repos/bokeh/datashader/issues/36","labels_url":"https://api.github.com/repos/bokeh/datashader/issues/36/labels{/name}","comments_url":"https://api.github.com/repos/bokeh/datashader/issues/36/comments","events_url":"https://api.github.com/repos/bokeh/datashader/issues/36/events","html_url":"https://github.com/bokeh/datashader/pull/36","id":129935561,"number":36,"title":"Created module for IPython kernel callbacks","user":{"login":"philippjfr","id":1550771,"avatar_url":"https://avatars.githubusercontent.com/u/1550771?v=3","gravatar_id":"","url":"https://api.github.com/users/philippjfr","html_url":"https://github.com/philippjfr","followers_url":"https://api.github.com/users/philippjfr/followers","following_url":"https://api.github.com/users/philippjfr/following{/other_user}","gists_url":"https://api.github.com/users/philippjfr/gists{/gist_id}","starred_url":"https://api.github.com/users/philippjfr/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/philippjfr/subscriptions","organizations_url":"https://api.github.com/users/philippjfr/orgs","repos_url":"https://api.github.com/users/philippjfr/repos","events_url":"https://api.github.com/users/philippjfr/events{/privacy}","received_events_url":"https://api.github.com/users/philippjfr/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":23,"created_at":"2016-01-30T03:41:14Z","updated_at":"2016-01-31T13:25:52Z","closed_at":null,"pull_request":{"url":"https://api.github.com/repos/bokeh/datashader/pulls/36","html_url":"https://github.com/bokeh/datashader/pull/36","diff_url":"https://github.com/bokeh/datashader/pull/36.diff","patch_url":"https://github.com/bokeh/datashader/pull/36.patch"},"body":"As per request by @jbednar I used his initial attempt as a starting point to move the IPython kernel callback in the ``nyc_taxi`` demo notebook into a module so the notebook itself isn't littered with JS code. \r\n\r\nThe example from the nyc_taxi demo looks like this now:\r\n\r\n```python\r\nfrom datashader.callbacks import IPythonKernelCallback\r\n\r\ndef create_image(p, ranges, agg_fn=ds.count):\r\n    x_range, y_range = ranges['x_range'], ranges['y_range']\r\n    h, w = ranges['h'], ranges['w']\r\n    cvs = ds.Canvas(plot_width=w, plot_height=h, x_range=x_range, y_range=y_range)\r\n    agg = cvs.points(df, 'pickup_x', 'pickup_y', agg_fn('passenger_count'))\r\n    pix = tf.interpolate(agg, (255, 204, 204), 'red', how='log')\r\n    dh = y_range[1] - y_range[0]\r\n    dw = x_range[1] - x_range[0]\r\n    p.image_rgba(image=[pix.img], x=x_range[0], y=y_range[0], dw=dw, dh=dh, dilate=False)\r\n\r\np = base_plot()\r\nIPythonKernelCallback(p, create_image, throttle=500, agg_fn=ds.count)\r\n```\r\n\r\nWe pass the ``Figure`` (p), the Python call, an optional throttling value and an agg_fn to the IPythonKernelCallback. Internally the following happens:\r\n\r\n1. The IPythonKernelCallback instance registers itself on the class with a uuid as the key.\r\n2. We generate a string command to execute on the Python side, which a) imports the class and b) looks up the callback instance on the class using the uuid and c) calls the ``update_image`` method on the callback instance with the stringified json of the current ranges.\r\n3. Using a string format we interpolate the plot_id, and the cmd into a template and wrap it in ``CustomJS``.\r\n\r\nThe Python callback itself accepts two args and kwargs:\r\n\r\n1. The plot to render to.\r\n2. The dictionary of stringified json data.\r\n3. Any additional keyword arguments that were passed to the IPythonKernelCallback instance.\r\n\r\nThis works pretty well in the notebook, but it does have several limitations/drawbacks:\r\n\r\n1. This still creates a new image_rgba glyph on every callback, it might be more efficient to update the datasource on an existing glyph.\r\n2. Currently I bypass the ``show`` function, handle the comms object manually and use ``_repr_html`` to display it. I thought this would be fixed by https://github.com/bokeh/bokeh/pull/3681?\r\n3. I've wrapped the kernel callbacks in ``_.throttle`` and added them to ``Bokeh._throttle``, a new object I created. They should presumably be stored somewhere more appropriate.\r\n4. This only works for standalone figures currently, any layouts likes vplot, hplot or gridplot will have issues when attempting to look them up via ``Bokeh.index``, instead the id of the root plot should be sent and the plot looked up on the document."},"comment":{"url":"https://api.github.com/repos/bokeh/datashader/issues/comments/177501333","html_url":"https://github.com/bokeh/datashader/pull/36#issuecomment-177501333","issue_url":"https://api.github.com/repos/bokeh/datashader/issues/36","id":177501333,"user":{"login":"philippjfr","id":1550771,"avatar_url":"https://avatars.githubusercontent.com/u/1550771?v=3","gravatar_id":"","url":"https://api.github.com/users/philippjfr","html_url":"https://github.com/philippjfr","followers_url":"https://api.github.com/users/philippjfr/followers","following_url":"https://api.github.com/users/philippjfr/following{/other_user}","gists_url":"https://api.github.com/users/philippjfr/gists{/gist_id}","starred_url":"https://api.github.com/users/philippjfr/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/philippjfr/subscriptions","organizations_url":"https://api.github.com/users/philippjfr/orgs","repos_url":"https://api.github.com/users/philippjfr/repos","events_url":"https://api.github.com/users/philippjfr/events{/privacy}","received_events_url":"https://api.github.com/users/philippjfr/received_events","type":"User","site_admin":false},"created_at":"2016-01-31T13:25:52Z","updated_at":"2016-01-31T13:25:52Z","body":"It's pretty responsive now and no longer swallows events like the previous approach, you can merge at any time."}}