Skip to content
Permalink
Browse files

Merge pull request ipython#4374 from jdfreder/widget-msg

IPEP 23: Backbone.js Widgets
  • Loading branch information...
ellisonbg committed Jan 28, 2014
2 parents 527a0be + 31e156b commit 68afd2d7aabf0e1c43eb7310c71e85cd2111951d
Showing with 8,149 additions and 374 deletions.
  1. +16 −5 IPython/core/display.py
  2. +8 −0 IPython/core/displayhook.py
  3. +18 −0 IPython/html/static/base/less/flexbox.less
  4. +34 −1 IPython/html/static/notebook/js/codecell.js
  5. +2 −1 IPython/html/static/notebook/js/main.js
  6. +13 −1 IPython/html/static/notebook/js/notebook.js
  7. +210 −0 IPython/html/static/notebook/js/widgetmanager.js
  8. +22 −0 IPython/html/static/notebook/js/widgets/init.js
  9. +418 −0 IPython/html/static/notebook/js/widgets/widget.js
  10. +125 −0 IPython/html/static/notebook/js/widgets/widget_bool.js
  11. +60 −0 IPython/html/static/notebook/js/widgets/widget_button.js
  12. +272 −0 IPython/html/static/notebook/js/widgets/widget_container.js
  13. +42 −0 IPython/html/static/notebook/js/widgets/widget_float.js
  14. +51 −0 IPython/html/static/notebook/js/widgets/widget_image.js
  15. +284 −0 IPython/html/static/notebook/js/widgets/widget_int.js
  16. +376 −0 IPython/html/static/notebook/js/widgets/widget_selection.js
  17. +244 −0 IPython/html/static/notebook/js/widgets/widget_selectioncontainer.js
  18. +222 −0 IPython/html/static/notebook/js/widgets/widget_string.js
  19. +0 −2 IPython/html/static/notebook/less/style.less
  20. +1 −0 IPython/html/static/notebook/less/style_noapp.less
  21. +251 −0 IPython/html/static/notebook/less/widgets.less
  22. +4 −4 IPython/html/static/services/kernels/js/comm.js
  23. +2 −1 IPython/html/static/services/kernels/js/kernel.js
  24. +22 −0 IPython/html/static/style/ipython.min.css
  25. +22 −0 IPython/html/static/style/style.min.css
  26. +12 −1 IPython/html/templates/page.html
  27. +69 −0 IPython/html/tests/casperjs/test_cases/widgets.js
  28. +86 −0 IPython/html/tests/casperjs/test_cases/widgets_bool.js
  29. +43 −0 IPython/html/tests/casperjs/test_cases/widgets_button.js
  30. +80 −0 IPython/html/tests/casperjs/test_cases/widgets_container.js
  31. +108 −0 IPython/html/tests/casperjs/test_cases/widgets_float.js
  32. +60 −0 IPython/html/tests/casperjs/test_cases/widgets_image.js
  33. +151 −0 IPython/html/tests/casperjs/test_cases/widgets_int.js
  34. +108 −0 IPython/html/tests/casperjs/test_cases/widgets_multicontainer.js
  35. +133 −0 IPython/html/tests/casperjs/test_cases/widgets_selection.js
  36. +52 −0 IPython/html/tests/casperjs/test_cases/widgets_string.js
  37. +10 −12 IPython/html/tests/casperjs/util.js
  38. +11 −0 IPython/html/widgets/__init__.py
  39. +419 −0 IPython/html/widgets/widget.py
  40. +34 −0 IPython/html/widgets/widget_bool.py
  41. +56 −0 IPython/html/widgets/widget_button.py
  42. +51 −0 IPython/html/widgets/widget_container.py
  43. +59 −0 IPython/html/widgets/widget_float.py
  44. +36 −0 IPython/html/widgets/widget_image.py
  45. +59 −0 IPython/html/widgets/widget_int.py
  46. +95 −0 IPython/html/widgets/widget_selection.py
  47. +58 −0 IPython/html/widgets/widget_selectioncontainer.py
  48. +72 −0 IPython/html/widgets/widget_string.py
  49. +25 −0 docs/source/whatsnew/pr/widgets.rst
  50. +93 −2 examples/notebooks/Custom Display Logic.ipynb
  51. +186 −0 examples/widgets/Export As (nbconvert).ipynb
  52. +248 −0 examples/widgets/File Upload Widget.ipynb
  53. +227 −0 examples/widgets/Nonblocking Console.ipynb
  54. +314 −0 examples/widgets/Part 1 - Basics.ipynb
  55. +261 −0 examples/widgets/Part 2 - Events.ipynb
  56. +187 −0 examples/widgets/Part 3 - Placement.ipynb
  57. +368 −0 examples/widgets/Part 4 - Styles.ipynb
  58. +331 −0 examples/widgets/Part 5 - Alignment.ipynb
  59. +1,058 −0 examples/widgets/Part 6 - Custom Widget.ipynb
  60. +203 −0 examples/widgets/Variable Inspector.ipynb
  61. +0 −46 examples/widgets/directview/directview.ipynb
  62. +0 −172 examples/widgets/directview/directview.js
  63. +0 −68 examples/widgets/directview/directview.py
  64. +0 −58 examples/widgets/directview/widget.py
  65. +67 −0 examples/widgets/index.ipynb
@@ -110,12 +110,23 @@ def display(*objs, **kwargs):

from IPython.core.interactiveshell import InteractiveShell

if raw:
for obj in objs:
publish_display_data('display', obj, metadata)
else:
if not raw:
format = InteractiveShell.instance().display_formatter.format
for obj in objs:

for obj in objs:

# If _ipython_display_ is defined, use that to display this object.
display_method = getattr(obj, '_ipython_display_', None)
if display_method is not None:
try:
display_method(**kwargs)
except NotImplementedError:
pass
else:
continue
if raw:
publish_display_data('display', obj, metadata)
else:
format_dict, md_dict = format(obj, include=include, exclude=exclude)
if metadata:
# kwarg-specified metadata gets precedence
@@ -241,6 +241,14 @@ def __call__(self, result=None):
"""
self.check_for_underscore()
if result is not None and not self.quiet():
# If _ipython_display_ is defined, use that to display this object.
display_method = getattr(result, '_ipython_display_', None)
if display_method is not None:
try:
return display_method()
except NotImplementedError:
pass

self.start_displayhook()
self.write_output_prompt()
format_dict, md_dict = self.compute_format_data(result)
@@ -101,3 +101,21 @@
-moz-box-pack: center;
box-pack: center;
}

.align-start {
-webkit-box-align: start;
-moz-box-align: start;
box-align: start;
}

.align-end {
-webkit-box-align: end;
-moz-box-align: end;
box-align: end;
}

.align-center {
-webkit-box-align: center;
-moz-box-align: center;
box-align: center;
}
@@ -105,6 +105,7 @@ var IPython = (function (IPython) {
}
};

CodeCell.msg_cells = {};

CodeCell.prototype = new IPython.Cell();

@@ -132,8 +133,28 @@ var IPython = (function (IPython) {
$(this.code_mirror.getInputField()).attr("spellcheck", "false");
inner_cell.append(input_area);
input.append(prompt).append(inner_cell);

var widget_area = $('<div/>')
.addClass('widget-area')
.hide();
this.widget_area = widget_area;
var widget_prompt = $('<div/>')
.addClass('prompt')
.appendTo(widget_area);
var widget_subarea = $('<div/>')
.addClass('widget-subarea')
.appendTo(widget_area);
this.widget_subarea = widget_subarea;
var widget_clear_buton = $('<button />')
.addClass('close')
.html('&times;')
.click(function() {
widget_area.slideUp('', function(){ widget_subarea.html(''); });
})
.appendTo(widget_prompt);

var output = $('<div></div>');
cell.append(input).append(output);
cell.append(input).append(widget_area).append(output);
this.element = cell;
this.output_area = new IPython.OutputArea(output, true);
this.completer = new IPython.Completer(this);
@@ -283,14 +304,26 @@ var IPython = (function (IPython) {
*/
CodeCell.prototype.execute = function () {
this.output_area.clear_output();

// Clear widget area
this.widget_subarea.html('');
this.widget_subarea.height('');
this.widget_area.height('');
this.widget_area.hide();

this.set_input_prompt('*');
this.element.addClass("running");
if (this.last_msg_id) {
this.kernel.clear_callbacks_for_msg(this.last_msg_id);
}
var callbacks = this.get_callbacks();

var old_msg_id = this.last_msg_id;
this.last_msg_id = this.kernel.execute(this.get_text(), callbacks, {silent: false, store_history: true});
if (old_msg_id) {
delete CodeCell.msg_cells[old_msg_id];
}
CodeCell.msg_cells[this.last_msg_id] = this;
};

/**
@@ -14,7 +14,8 @@
// as injecting require.js make marked not to put itself in the globals,
// which make both this file fail at setting marked configuration, and textcell.js
// which search marked into global.
require(['components/marked/lib/marked'],
require(['components/marked/lib/marked',
'notebook/js/widgets/init'],

function (marked) {

@@ -300,6 +300,17 @@ var IPython = (function (IPython) {
return result;
};

/**
* Try to get a particular cell by msg_id.
*
* @method get_msg_cell
* @param {String} msg_id A message UUID
* @return {Cell} Cell or null if no cell was found.
*/
Notebook.prototype.get_msg_cell = function (msg_id) {
return IPython.CodeCell.msg_cells[msg_id] || null;
};

/**
* Count the cells in this notebook.
*
@@ -1295,7 +1306,8 @@ var IPython = (function (IPython) {


/**
* Once a session is started, link the code cells to the kernel
* Once a session is started, link the code cells to the kernel and pass the
* comm manager to the widget manager
*
*/
Notebook.prototype._session_started = function(){
@@ -0,0 +1,210 @@
//----------------------------------------------------------------------------
// Copyright (C) 2013 The IPython Development Team
//
// Distributed under the terms of the BSD License. The full license is in
// the file COPYING, distributed as part of this software.
//----------------------------------------------------------------------------

//============================================================================
// WidgetModel, WidgetView, and WidgetManager
//============================================================================
/**
* Base Widget classes
* @module IPython
* @namespace IPython
* @submodule widget
*/

(function () {
"use strict";

// Use require.js 'define' method so that require.js is intelligent enough to
// syncronously load everything within this file when it is being 'required'
// elsewhere.
define(["underscore",
"backbone",
], function (Underscore, Backbone) {

//--------------------------------------------------------------------
// WidgetManager class
//--------------------------------------------------------------------
var WidgetManager = function (comm_manager) {
// Public constructor
WidgetManager._managers.push(this);

// Attach a comm manager to the
this.comm_manager = comm_manager;
this._models = {}; /* Dictionary of model ids and model instances */

// Register already-registered widget model types with the comm manager.
var that = this;
_.each(WidgetManager._model_types, function(model_type, model_name) {
that.comm_manager.register_target(model_name, $.proxy(that._handle_comm_open, that));
});
};

//--------------------------------------------------------------------
// Class level
//--------------------------------------------------------------------
WidgetManager._model_types = {}; /* Dictionary of model type names (target_name) and model types. */
WidgetManager._view_types = {}; /* Dictionary of view names and view types. */
WidgetManager._managers = []; /* List of widget managers */

WidgetManager.register_widget_model = function (model_name, model_type) {
// Registers a widget model by name.
WidgetManager._model_types[model_name] = model_type;

// Register the widget with the comm manager. Make sure to pass this object's context
// in so `this` works in the call back.
_.each(WidgetManager._managers, function(instance, i) {
if (instance.comm_manager !== null) {
instance.comm_manager.register_target(model_name, $.proxy(instance._handle_comm_open, instance));
}
});
};

WidgetManager.register_widget_view = function (view_name, view_type) {
// Registers a widget view by name.
WidgetManager._view_types[view_name] = view_type;
};

//--------------------------------------------------------------------
// Instance level
//--------------------------------------------------------------------
WidgetManager.prototype.display_view = function(msg, model) {
// Displays a view for a particular model.
var cell = this.get_msg_cell(msg.parent_header.msg_id);
if (cell === null) {
console.log("Could not determine where the display" +
" message was from. Widget will not be displayed");
} else {
var view = this.create_view(model, {cell: cell});
if (view === null) {
console.error("View creation failed", model);
}
if (cell.widget_subarea) {

cell.widget_area.show();
cell.widget_subarea.append(view.$el);
}
}
};

WidgetManager.prototype.create_view = function(model, options, view) {
// Creates a view for a particular model.
var view_name = model.get('_view_name');
var ViewType = WidgetManager._view_types[view_name];
if (ViewType) {

// If a view is passed into the method, use that view's cell as
// the cell for the view that is created.
options = options || {};
if (view !== undefined) {
options.cell = view.options.cell;
}

// Create and render the view...
var parameters = {model: model, options: options};
view = new ViewType(parameters);
view.render();
model.views.push(view);
model.on('destroy', view.remove, view);

this._handle_new_view(view);
return view;
}
return null;
};

WidgetManager.prototype._handle_new_view = function (view) {
// Called when a view has been created and rendered.

// If the view has a well defined element, inform the keyboard
// manager about the view's element, so as the element can
// escape the dreaded command mode.
if (view.$el) {
IPython.keyboard_manager.register_events(view.$el);
}
};

WidgetManager.prototype.get_msg_cell = function (msg_id) {
var cell = null;
// First, check to see if the msg was triggered by cell execution.
if (IPython.notebook) {
cell = IPython.notebook.get_msg_cell(msg_id);
}
if (cell !== null) {
return cell;
}
// Second, check to see if a get_cell callback was defined
// for the message. get_cell callbacks are registered for
// widget messages, so this block is actually checking to see if the
// message was triggered by a widget.
var kernel = this.comm_manager.kernel;
if (kernel) {
var callbacks = kernel.get_callbacks_for_msg(msg_id);
if (callbacks && callbacks.iopub &&
callbacks.iopub.get_cell !== undefined) {
return callbacks.iopub.get_cell();
}
}

// Not triggered by a cell or widget (no get_cell callback
// exists).
return null;
};

WidgetManager.prototype.callbacks = function (view) {
// callback handlers specific a view
var callbacks = {};
if (view && view.options.cell) {

// Try to get output handlers
var cell = view.options.cell;
var handle_output = null;
var handle_clear_output = null;
if (cell.output_area) {
handle_output = $.proxy(cell.output_area.handle_output, cell.output_area);
handle_clear_output = $.proxy(cell.output_area.handle_clear_output, cell.output_area);
}

// Create callback dict using what is known
var that = this;
callbacks = {
iopub : {
output : handle_output,
clear_output : handle_clear_output,

// Special function only registered by widget messages.
// Allows us to get the cell for a message so we know
// where to add widgets if the code requires it.
get_cell : function () {
return cell;
},
},
};
}
return callbacks;
};

WidgetManager.prototype.get_model = function (model_id) {
// Look-up a model instance by its id.
var model = this._models[model_id];
if (model !== undefined && model.id == model_id) {
return model;
}
return null;
};

WidgetManager.prototype._handle_comm_open = function (comm, msg) {
// Handle when a comm is opened.
var model_id = comm.comm_id;
var widget_type_name = msg.content.target_name;
var widget_model = new WidgetManager._model_types[widget_type_name](this, model_id, comm);
this._models[model_id] = widget_model;
};

IPython.WidgetManager = WidgetManager;
return IPython.WidgetManager;
});
}());

0 comments on commit 68afd2d

Please sign in to comment.
You can’t perform that action at this time.