Skip to content

Commit

Permalink
Merge pull request ipython#4374 from jdfreder/widget-msg
Browse files Browse the repository at this point in the history
IPEP 23: Backbone.js Widgets
  • Loading branch information
ellisonbg committed Jan 28, 2014
2 parents 527a0be + 31e156b commit 68afd2d
Show file tree
Hide file tree
Showing 65 changed files with 8,149 additions and 374 deletions.
21 changes: 16 additions & 5 deletions IPython/core/display.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 8 additions & 0 deletions IPython/core/displayhook.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
18 changes: 18 additions & 0 deletions IPython/html/static/base/less/flexbox.less
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
35 changes: 34 additions & 1 deletion IPython/html/static/notebook/js/codecell.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ var IPython = (function (IPython) {
}
};

CodeCell.msg_cells = {};

CodeCell.prototype = new IPython.Cell();

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
};

/**
Expand Down
3 changes: 2 additions & 1 deletion IPython/html/static/notebook/js/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {

Expand Down
14 changes: 13 additions & 1 deletion IPython/html/static/notebook/js/notebook.js
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand Down Expand Up @@ -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(){
Expand Down
210 changes: 210 additions & 0 deletions IPython/html/static/notebook/js/widgetmanager.js
Original file line number Diff line number Diff line change
@@ -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;
});
}());
Loading

0 comments on commit 68afd2d

Please sign in to comment.