From 85e123e8a48e783d08b1a7fbf56dd5162e827fdc Mon Sep 17 00:00:00 2001 From: Jonathan Frederic Date: Mon, 20 Apr 2015 12:49:38 -0700 Subject: [PATCH] :cake: It's working! --- ipython_widgets/install.py | 15 +++ .../static/notebook/js/extension.js | 63 +++++++++- .../static/notebook/js/widgetarea.js | 117 ++++++++++++------ ipython_widgets/static/widgets/js/init.js | 26 ++-- ipython_widgets/static/widgets/js/manager.js | 6 +- ipython_widgets/static/widgets/js/widget.js | 2 +- .../static/widgets/js/widget_bool.js | 2 +- .../static/widgets/js/widget_box.js | 2 +- .../static/widgets/js/widget_button.js | 2 +- .../static/widgets/js/widget_float.js | 4 +- .../static/widgets/js/widget_image.js | 2 +- .../static/widgets/js/widget_int.js | 2 +- .../static/widgets/js/widget_link.js | 2 +- .../static/widgets/js/widget_output.js | 2 +- .../static/widgets/js/widget_selection.js | 2 +- .../widgets/js/widget_selectioncontainer.js | 2 +- .../static/widgets/js/widget_string.js | 2 +- ipython_widgets/widget_handler.py | 12 -- ipython_widgets/widgets/interaction.py | 2 +- .../static/notebook/js/codecell.js | 1 + .../static/services/kernels/kernel.js | 10 +- .../static/services/sessions/session.js | 2 +- 22 files changed, 193 insertions(+), 87 deletions(-) create mode 100644 ipython_widgets/install.py delete mode 100644 ipython_widgets/widget_handler.py diff --git a/ipython_widgets/install.py b/ipython_widgets/install.py new file mode 100644 index 000000000..692407479 --- /dev/null +++ b/ipython_widgets/install.py @@ -0,0 +1,15 @@ +#!/usr/bin/env python +# Thanks @takluyver for your cite2c install.py. +from os.path import dirname, abspath, join as pjoin +from jupyter_notebook.nbextensions import install_nbextension +from jupyter_notebook.services.config import ConfigManager + +print("Installing nbextension ...") +staticdir = pjoin(dirname(abspath(__file__)), 'static') +install_nbextension(staticdir, destination='widgets', user=False) + +print("Enabling the extension ...") +cm = ConfigManager() +cm.update('notebook', {"load_extensions": {"widgets/notebook/js/extension": True}}) + +print("Done.") diff --git a/ipython_widgets/static/notebook/js/extension.js b/ipython_widgets/static/notebook/js/extension.js index b4a9f42c2..a929fc92f 100644 --- a/ipython_widgets/static/notebook/js/extension.js +++ b/ipython_widgets/static/notebook/js/extension.js @@ -2,10 +2,65 @@ // Distributed under the terms of the Modified BSD License. define([ - 'widgets/js/init' -], function(widgetmanager) { + 'nbextensions/widgets/widgets/js/init', + 'nbextensions/widgets/notebook/js/widgetarea', + 'base/js/events' +], function(widgetmanager, widgetarea, events) { "use strict"; + + /** + * Create a widget manager for a kernel instance. + */ + var handle_kernel = function(kernel) { + if (kernel.comm_manager && kernel.widget_manager === undefined) { + + // Create a widget manager instance. Use the global + // IPython.notebook handle. + var manager = new widgetmanager.WidgetManager(kernel.comm_manager, IPython.notebook); + + // Store a handle to the manager so we know not to + // another for this kernel. This also is a convinience + // for the user. + kernel.widget_manager = manager; + } + }; + + // If a kernel already exists, create a widget manager. + if (IPython.notebook && IPython.notebook.kernel) { + handle_kernel(IPython.notebook.kernel); + } + // When the kernel is created, create a widget manager. + events.on('kernel_created.Kernel kernel_created.Session', function(event, data) { + handle_kernel(data.kernel); + }); + + /** + * Creates a widgetarea for the cell if it is a CodeCell. + * If the cell isn't a CodeCell, no action is taken. + */ + var handle_cell = function(cell) { + if (cell.cell_type==='code') { + var area = new widgetarea.WidgetArea(cell); + cell.widgetarea = area; + } + }; + + // Create widget areas for cells that already exist. + var cells = IPython.notebook.get_cells(); + for (var i = 0; i < cells.length; i++) { + handle_cell(cells[i]); + } - // TODO - this.widget_manager = new widgetmanager.WidgetManager(this.comm_manager, notebook); + // Listen to cell creation and deletion events. When a + // cell is created, create a widget area for that cell. + events.on('create.Cell', function(event, data) { + handle_cell(data.cell); + }); + // When a cell is deleted, delete the widget area if it + // exists. + events.on('delete.Cell', function(event, data) { + if (data.cell && data.cell.widgetarea) { + data.cell.widgetarea.dispose(); + } + }); }); diff --git a/ipython_widgets/static/notebook/js/widgetarea.js b/ipython_widgets/static/notebook/js/widgetarea.js index 75b93ba2d..b9fbaa3d0 100644 --- a/ipython_widgets/static/notebook/js/widgetarea.js +++ b/ipython_widgets/static/notebook/js/widgetarea.js @@ -1,12 +1,65 @@ +// Copyright (c) Jupyter Development Team. +// Distributed under the terms of the Modified BSD License. -// Events that this code should rely on. -// delete.Cell -// selected_cell_type_changed.Notebook -// create.Cell - +define(['jquery', 'base/js/events'], function($, events) { + + /** + * WidgetArea + */ + var WidgetArea = function(cell) { this.widget_views = []; + + this._cell = cell; this._widgets_live = true; + this.disposed = false; + + this._create_elements(); + this._bind_events(); + } + + /** + * Display a widget view in the cell. + */ + WidgetArea.prototype.display_widget_view = function(view_promise) { + + // Display a dummy element + var dummy = $('
'); + this.widget_subarea.append(dummy); + + // Display the view. + var that = this; + return view_promise.then(function(view) { + that.widget_area.show(); + dummy.replaceWith(view.$el); + that.widget_views.push(view); + + // Check the live state of the view's model. + if (view.model.comm_live) { + that._widget_live(view); + } else { + that._widget_dead(view); + } + + // Listen to comm live events for the view. + view.on('comm:live', that._widget_live, that); + view.on('comm:dead', that._widget_dead, that); + return view; + }); + }; + + /** + * Disposes of the widget area and its widgets. + */ + WidgetArea.prototype.dispose = function() { + this._clear(); + this.disposed = true; + }; + /** + * Creates the elements of the widget area and appends them + * to the associated cell. + */ + WidgetArea.prototype._create_elements = function() { var widget_area = $('
') .addClass('widget-area') .hide(); @@ -38,42 +91,30 @@ }) .appendTo(widget_prompt); + if (this._cell.input) { + this._cell.input.after(widget_area); + } else { + throw new Error('Cell does not have an `input` element. Is it not a CodeCell?'); + } + } /** - * Display a widget view in the cell. - */ - CodeCell.prototype.display_widget_view = function(view_promise) { - - // Display a dummy element - var dummy = $('
'); - this.widget_subarea.append(dummy); - - // Display the view. + * Listens to events of the cell. + */ + WidgetArea.prototype._bind_events = function() { var that = this; - return view_promise.then(function(view) { - that.widget_area.show(); - dummy.replaceWith(view.$el); - that.widget_views.push(view); - - // Check the live state of the view's model. - if (view.model.comm_live) { - that._widget_live(view); - } else { - that._widget_dead(view); + events.on('execute.CodeCell', function(event, data) { + if (data.cell===that._cell) { + that._clear(); } - - // Listen to comm live events for the view. - view.on('comm:live', that._widget_live, that); - view.on('comm:dead', that._widget_dead, that); - return view; }); }; /** * Handles when a widget loses it's comm connection. - * @param {WidgetView} view + * @param {WidgetView} view */ - CodeCell.prototype._widget_dead = function(view) { + WidgetArea.prototype._widget_dead = function(view) { if (this._widgets_live) { this._widgets_live = false; this.widget_area.addClass('connection-problems'); @@ -83,9 +124,9 @@ /** * Handles when a widget is connected to a live comm. - * @param {WidgetView} view + * @param {WidgetView} view */ - CodeCell.prototype._widget_live = function(view) { + WidgetArea.prototype._widget_live = function(view) { if (!this._widgets_live) { // Check that the other widgets are live too. O(N) operation. // Abort the function at the first dead widget found. @@ -97,8 +138,10 @@ } }; - -// TODO: on event execute.CodeCell + /** + * Clears the widgets in the widget area. + */ + WidgetArea.prototype._clear = function() { // Clear widget area for (var i = 0; i < this.widget_views.length; i++) { var view = this.widget_views[i]; @@ -113,4 +156,8 @@ this.widget_subarea.height(''); this.widget_area.height(''); this.widget_area.hide(); + }; + + return {WidgetArea: WidgetArea}; +}); \ No newline at end of file diff --git a/ipython_widgets/static/widgets/js/init.js b/ipython_widgets/static/widgets/js/init.js index 45a3f2165..b371115af 100644 --- a/ipython_widgets/static/widgets/js/init.js +++ b/ipython_widgets/static/widgets/js/init.js @@ -2,19 +2,19 @@ // Distributed under the terms of the Modified BSD License. define([ - "widgets/js/manager", - "widgets/js/widget", - "widgets/js/widget_link", - "widgets/js/widget_bool", - "widgets/js/widget_button", - "widgets/js/widget_box", - "widgets/js/widget_float", - "widgets/js/widget_image", - "widgets/js/widget_int", - "widgets/js/widget_output", - "widgets/js/widget_selection", - "widgets/js/widget_selectioncontainer", - "widgets/js/widget_string", + "nbextensions/widgets/widgets/js/manager", + "nbextensions/widgets/widgets/js/widget", + "nbextensions/widgets/widgets/js/widget_link", + "nbextensions/widgets/widgets/js/widget_bool", + "nbextensions/widgets/widgets/js/widget_button", + "nbextensions/widgets/widgets/js/widget_box", + "nbextensions/widgets/widgets/js/widget_float", + "nbextensions/widgets/widgets/js/widget_image", + "nbextensions/widgets/widgets/js/widget_int", + "nbextensions/widgets/widgets/js/widget_output", + "nbextensions/widgets/widgets/js/widget_selection", + "nbextensions/widgets/widgets/js/widget_selectioncontainer", + "nbextensions/widgets/widgets/js/widget_string", ], function(widgetmanager, widget) { // Register all of the loaded models and views with the widget manager. for (var i = 2; i < arguments.length; i++) { diff --git a/ipython_widgets/static/widgets/js/manager.js b/ipython_widgets/static/widgets/js/manager.js index ee927cb82..81700c0a8 100644 --- a/ipython_widgets/static/widgets/js/manager.js +++ b/ipython_widgets/static/widgets/js/manager.js @@ -140,9 +140,9 @@ define([ WidgetManager.prototype.display_view_in_cell = function(cell, model) { // Displays a view in a cell. - if (cell.display_widget_view) { + if (cell.widgetarea) { var that = this; - return cell.display_widget_view(this.create_view(model, { + return cell.widgetarea.display_widget_view(this.create_view(model, { cell: cell, // Only set cell_index when view is displayed as directly. cell_index: that.notebook.find_cell_index(cell), @@ -152,7 +152,7 @@ define([ return view; }).catch(utils.reject('Could not create or display view', true)); } else { - return Promise.reject(new Error('Cell does not have a `display_widget_view` method')); + return Promise.reject(new Error('Cell does not have a `widgetarea` defined')); } }; diff --git a/ipython_widgets/static/widgets/js/widget.js b/ipython_widgets/static/widgets/js/widget.js index 2481fe89b..3657746c9 100644 --- a/ipython_widgets/static/widgets/js/widget.js +++ b/ipython_widgets/static/widgets/js/widget.js @@ -1,7 +1,7 @@ // Copyright (c) Jupyter Development Team. // Distributed under the terms of the Modified BSD License. -define(["widgets/js/manager", +define(["nbextensions/widgets/widgets/js/manager", "underscore", "backbone", "jquery", diff --git a/ipython_widgets/static/widgets/js/widget_bool.js b/ipython_widgets/static/widgets/js/widget_bool.js index c9749471b..36ca9eff0 100644 --- a/ipython_widgets/static/widgets/js/widget_bool.js +++ b/ipython_widgets/static/widgets/js/widget_bool.js @@ -2,7 +2,7 @@ // Distributed under the terms of the Modified BSD License. define([ - "widgets/js/widget", + "nbextensions/widgets/widgets/js/widget", "jquery", "bootstrap", ], function(widget, $){ diff --git a/ipython_widgets/static/widgets/js/widget_box.js b/ipython_widgets/static/widgets/js/widget_box.js index 0a374add2..8467b8cc4 100644 --- a/ipython_widgets/static/widgets/js/widget_box.js +++ b/ipython_widgets/static/widgets/js/widget_box.js @@ -2,7 +2,7 @@ // Distributed under the terms of the Modified BSD License. define([ - "widgets/js/widget", + "nbextensions/widgets/widgets/js/widget", "jqueryui", "underscore", "base/js/utils", diff --git a/ipython_widgets/static/widgets/js/widget_button.js b/ipython_widgets/static/widgets/js/widget_button.js index 6dc85d19f..e60e71f04 100644 --- a/ipython_widgets/static/widgets/js/widget_button.js +++ b/ipython_widgets/static/widgets/js/widget_button.js @@ -2,7 +2,7 @@ // Distributed under the terms of the Modified BSD License. define([ - "widgets/js/widget", + "nbextensions/widgets/widgets/js/widget", "jquery", "bootstrap", ], function(widget, $){ diff --git a/ipython_widgets/static/widgets/js/widget_float.js b/ipython_widgets/static/widgets/js/widget_float.js index d3bc2cf32..c6523e4f0 100644 --- a/ipython_widgets/static/widgets/js/widget_float.js +++ b/ipython_widgets/static/widgets/js/widget_float.js @@ -2,8 +2,8 @@ // Distributed under the terms of the Modified BSD License. define([ - "widgets/js/widget", - "widgets/js/widget_int", + "nbextensions/widgets/widgets/js/widget", + "nbextensions/widgets/widgets/js/widget_int", ], function(widget, int_widgets){ var IntSliderView = int_widgets.IntSliderView; var IntTextView = int_widgets.IntTextView; diff --git a/ipython_widgets/static/widgets/js/widget_image.js b/ipython_widgets/static/widgets/js/widget_image.js index 263f75de9..184c9500e 100644 --- a/ipython_widgets/static/widgets/js/widget_image.js +++ b/ipython_widgets/static/widgets/js/widget_image.js @@ -2,7 +2,7 @@ // Distributed under the terms of the Modified BSD License. define([ - "widgets/js/widget", + "nbextensions/widgets/widgets/js/widget", "jquery", ], function(widget, $){ diff --git a/ipython_widgets/static/widgets/js/widget_int.js b/ipython_widgets/static/widgets/js/widget_int.js index b693c42e2..5aeca6c48 100644 --- a/ipython_widgets/static/widgets/js/widget_int.js +++ b/ipython_widgets/static/widgets/js/widget_int.js @@ -2,7 +2,7 @@ // Distributed under the terms of the Modified BSD License. define([ - "widgets/js/widget", + "nbextensions/widgets/widgets/js/widget", "jqueryui", "base/js/keyboard", "bootstrap" diff --git a/ipython_widgets/static/widgets/js/widget_link.js b/ipython_widgets/static/widgets/js/widget_link.js index 03329bf38..0ee7a0956 100644 --- a/ipython_widgets/static/widgets/js/widget_link.js +++ b/ipython_widgets/static/widgets/js/widget_link.js @@ -2,7 +2,7 @@ // Distributed under the terms of the Modified BSD License. define([ - "widgets/js/widget", + "nbextensions/widgets/widgets/js/widget", "jquery", ], function(widget, $){ var LinkModel = widget.WidgetModel.extend({ diff --git a/ipython_widgets/static/widgets/js/widget_output.js b/ipython_widgets/static/widgets/js/widget_output.js index 4fa235c40..a055923db 100644 --- a/ipython_widgets/static/widgets/js/widget_output.js +++ b/ipython_widgets/static/widgets/js/widget_output.js @@ -2,7 +2,7 @@ // Distributed under the terms of the Modified BSD License. define([ - "widgets/js/widget", + "nbextensions/widgets/widgets/js/widget", "jquery", 'notebook/js/outputarea', ], function(widget, $, outputarea) { diff --git a/ipython_widgets/static/widgets/js/widget_selection.js b/ipython_widgets/static/widgets/js/widget_selection.js index 3077d4aa6..46ea622b0 100644 --- a/ipython_widgets/static/widgets/js/widget_selection.js +++ b/ipython_widgets/static/widgets/js/widget_selection.js @@ -2,7 +2,7 @@ // Distributed under the terms of the Modified BSD License. define([ - "widgets/js/widget", + "nbextensions/widgets/widgets/js/widget", "base/js/utils", "jquery", "underscore", diff --git a/ipython_widgets/static/widgets/js/widget_selectioncontainer.js b/ipython_widgets/static/widgets/js/widget_selectioncontainer.js index 4cfc0e73b..8dab5c610 100644 --- a/ipython_widgets/static/widgets/js/widget_selectioncontainer.js +++ b/ipython_widgets/static/widgets/js/widget_selectioncontainer.js @@ -2,7 +2,7 @@ // Distributed under the terms of the Modified BSD License. define([ - "widgets/js/widget", + "nbextensions/widgets/widgets/js/widget", "base/js/utils", "jquery", "bootstrap", diff --git a/ipython_widgets/static/widgets/js/widget_string.js b/ipython_widgets/static/widgets/js/widget_string.js index ac34889ae..6f62bc4cb 100644 --- a/ipython_widgets/static/widgets/js/widget_string.js +++ b/ipython_widgets/static/widgets/js/widget_string.js @@ -2,7 +2,7 @@ // Distributed under the terms of the Modified BSD License. define([ - "widgets/js/widget", + "nbextensions/widgets/widgets/js/widget", "jquery", "bootstrap", ], function(widget, $){ diff --git a/ipython_widgets/widget_handler.py b/ipython_widgets/widget_handler.py deleted file mode 100644 index 47859a23d..000000000 --- a/ipython_widgets/widget_handler.py +++ /dev/null @@ -1,12 +0,0 @@ -import os -from IPython.html.utils import url_path_join as ujoin -from tornado.web import StaticFileHandler - -module_path = os.path.dirname(__file__) -def load_jupyter_server_extension(nbapp): - webapp = nbapp.web_app - base_url = webapp.settings['base_url'] - webapp.add_handlers(".*$", [ - (ujoin(base_url, r"/widgets/static/(.+)"), StaticFileHandler, - {'path': os.path.join(module_path, 'static')}), - ]) diff --git a/ipython_widgets/widgets/interaction.py b/ipython_widgets/widgets/interaction.py index 4b7ddb4fa..88138b3a2 100644 --- a/ipython_widgets/widgets/interaction.py +++ b/ipython_widgets/widgets/interaction.py @@ -12,7 +12,7 @@ except ImportError: from inspect import getcallargs from IPython.core.getipython import get_ipython -from jupyter_notebook.widgets import (Widget, Text, +from . import (Widget, Text, FloatSlider, IntSlider, Checkbox, Dropdown, Box, Button, DOMWidget) from IPython.display import display, clear_output diff --git a/jupyter_notebook/static/notebook/js/codecell.js b/jupyter_notebook/static/notebook/js/codecell.js index 0fb1fd955..6a34f8537 100644 --- a/jupyter_notebook/static/notebook/js/codecell.js +++ b/jupyter_notebook/static/notebook/js/codecell.js @@ -154,6 +154,7 @@ define([ cell.attr('tabindex','2'); var input = $('
').addClass('input'); + this.input = input; var prompt = $('
').addClass('prompt input_prompt'); var inner_cell = $('
').addClass('inner_cell'); this.celltoolbar = new celltoolbar.CellToolbar({ diff --git a/jupyter_notebook/static/services/kernels/kernel.js b/jupyter_notebook/static/services/kernels/kernel.js index aaba95c8b..52279849b 100644 --- a/jupyter_notebook/static/services/kernels/kernel.js +++ b/jupyter_notebook/static/services/kernels/kernel.js @@ -6,8 +6,9 @@ define([ 'jquery', 'base/js/utils', './comm', - './serialize' -], function(IPython, $, utils, comm, serialize) { + './serialize', + 'base/js/events' +], function(IPython, $, utils, comm, serialize, events) { "use strict"; /** @@ -19,11 +20,10 @@ define([ * @class Kernel * @param {string} kernel_service_url - the URL to access the kernel REST api * @param {string} ws_url - the websockets URL - * @param {Notebook} notebook - notebook object * @param {string} name - the kernel type (e.g. python3) */ - var Kernel = function (kernel_service_url, ws_url, notebook, name) { - this.events = notebook.events; + var Kernel = function (kernel_service_url, ws_url, name) { + this.events = events; this.id = null; this.name = name; diff --git a/jupyter_notebook/static/services/sessions/session.js b/jupyter_notebook/static/services/sessions/session.js index ac79f33e9..923558df6 100644 --- a/jupyter_notebook/static/services/sessions/session.js +++ b/jupyter_notebook/static/services/sessions/session.js @@ -101,7 +101,7 @@ define([ that.kernel.name = that.kernel_model.name; } else { var kernel_service_url = utils.url_path_join(that.base_url, "api/kernels"); - that.kernel = new kernel.Kernel(kernel_service_url, that.ws_url, that.notebook, that.kernel_model.name); + that.kernel = new kernel.Kernel(kernel_service_url, that.ws_url, that.kernel_model.name); } that.events.trigger('kernel_created.Session', {session: that, kernel: that.kernel}); that.kernel._kernel_created(data.kernel);