mirror of
https://github.com/jupyter/notebook.git
synced 2025-02-11 12:30:51 +08:00
This feature was discussed in #6123, but it doesn't look like anything was ever incorporated into the IPython Notebook.
Here's a brief overview of the changes: - Display of messages from other clients can be toggled on and off from within a notebook, either using the ``<M-m>e`` keyboard shortcut in the web UI, or through the option in the "Kernel" menu. - notebook.js controls whether messages are displayed through a callback that is invoked from kernel.js when no callbacks are available for a message. - The UI displays ``execute_input`` messages originating from an other clients in new cells at the end of the notebook. Output messages (``execute_result`` et al.) will only be displayed if a cell exists with a matching message ID. Pending design questions: - Should each ``execute_input`` message cause a new cell to be created? - Should new cells be placed at the end of the notebook, or elsewhere? If the latter, what criteria should be followed?
This commit is contained in:
parent
a19f345170
commit
9c2afc9620
@ -253,6 +253,14 @@ define(['require'
|
||||
env.notebook.delete_cell();
|
||||
}
|
||||
},
|
||||
'toggle-unsolicited-message-display':{
|
||||
help: 'toggle display from external clients',
|
||||
icon: 'fa-sitemap',
|
||||
help_index: 'gb',
|
||||
handler: function (env) {
|
||||
env.notebook.toggle_ignore_unsolicited_msgs();
|
||||
}
|
||||
},
|
||||
'interrupt-kernel':{
|
||||
icon: 'fa-stop',
|
||||
help_index : 'ha',
|
||||
|
@ -95,6 +95,7 @@ define([
|
||||
'space' : 'ipython.scroll-down',
|
||||
'down' : 'ipython.select-next-cell',
|
||||
'i,i' : 'ipython.interrupt-kernel',
|
||||
'e': 'ipython.toggle-unsolicited-message-display',
|
||||
'0,0' : 'ipython.restart-kernel',
|
||||
'd,d' : 'ipython.delete-cell',
|
||||
'esc': 'ipython.close-pager',
|
||||
|
@ -278,6 +278,9 @@ define([
|
||||
});
|
||||
|
||||
// Kernel
|
||||
this.element.find('#toggle_unsolicited').click(function() {
|
||||
IPython.notebook.toggle_ignore_unsolicited_msgs();
|
||||
});
|
||||
this.element.find('#int_kernel').click(function () {
|
||||
that.notebook.kernel.interrupt();
|
||||
});
|
||||
|
@ -135,6 +135,7 @@ define([
|
||||
this.undelete_below = false;
|
||||
this.paste_enabled = false;
|
||||
this.writable = false;
|
||||
this.ignore_unsolicited_msgs = false;
|
||||
// It is important to start out in command mode to match the intial mode
|
||||
// of the KeyboardManager.
|
||||
this.mode = 'command';
|
||||
@ -1562,6 +1563,60 @@ define([
|
||||
this.get_selected_cell().toggle_line_numbers();
|
||||
};
|
||||
|
||||
// Support for displaying input and output messages from other iPy clients.
|
||||
|
||||
/**
|
||||
* Toggles the ability to display input/output message events from
|
||||
* externally connected clients (i.e. other iPython shells, vim-ipython,
|
||||
* etc).
|
||||
*
|
||||
* @method toggle_ignore_unsolicited_msgs
|
||||
*/
|
||||
Notebook.prototype.toggle_ignore_unsolicited_msgs = function () {
|
||||
this.ignore_unsolicited_msgs = !this.ignore_unsolicited_msgs;
|
||||
this.events.trigger('toggle_unsolicited_msgs.Notebook',
|
||||
[this.ignore_unsolicited_msgs]);
|
||||
return this.ignore_unsolicited_msgs;
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles the display of unsolicited messages, i.e. inputs or outputs that
|
||||
* were generated by a client other than this notebook. New messages are
|
||||
* displayed at the bottom of the notebook.
|
||||
*
|
||||
* @method handle_unsolicited_msg
|
||||
*/
|
||||
Notebook.prototype.handle_unsolicited_msg = function(msg) {
|
||||
if (this.ignore_unsolicited_msgs) {
|
||||
return;
|
||||
}
|
||||
if (msg.msg_type == 'execute_input') {
|
||||
var cell = this.insert_cell_at_bottom('code');
|
||||
if (cell) {
|
||||
var cell_index = this.ncells() - 1;
|
||||
cell.last_msg_id = msg.parent_header.msg_id;
|
||||
cell.set_text(msg.content.code);
|
||||
cell._handle_execute_reply(msg);
|
||||
this.scroll_to_cell(cell_index);
|
||||
this.select(cell_index);
|
||||
}
|
||||
} else {
|
||||
/* Find the input cell that corresponds with the output, then add
|
||||
* the contents to the cell's output area.
|
||||
*/
|
||||
var count = this.ncells();
|
||||
while (count--) {
|
||||
var cell = this.get_cell(count);
|
||||
if (cell && cell.last_msg_id == msg.parent_header.msg_id) {
|
||||
cell.output_area.handle_output(msg);
|
||||
this.scroll_to_cell(count);
|
||||
this.select(count);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the codemirror mode for all code cells, including the default for
|
||||
* new code cells.
|
||||
@ -1639,6 +1694,7 @@ define([
|
||||
cell.set_kernel(this.session.kernel);
|
||||
}
|
||||
}
|
||||
this.kernel.unsolicited_msg_callback = $.proxy(this.handle_unsolicited_msg, this);
|
||||
};
|
||||
Notebook.prototype._session_start_failed = function (jqxhr, status, error){
|
||||
this._session_starting = false;
|
||||
|
@ -279,6 +279,11 @@ define([
|
||||
nnw.warning(error.message || "Notebook copy failed");
|
||||
});
|
||||
|
||||
this.events.on('toggle_unsolicited_msgs.Notebook', function(evt, ignored) {
|
||||
var msg = (ignored? "Ignoring": "Showing") + " I/O from external clients";
|
||||
nnw.set_message(msg, 1000);
|
||||
});
|
||||
|
||||
// Checkpoint events
|
||||
this.events.on('checkpoint_created.Notebook', function (evt, data) {
|
||||
var msg = "Checkpoint created";
|
||||
|
@ -47,6 +47,7 @@ define([
|
||||
this.session_id = utils.uuid();
|
||||
this._msg_callbacks = {};
|
||||
this.info_reply = {}; // kernel_info_reply stored here after starting
|
||||
this.unsolicited_msg_callback = null;
|
||||
|
||||
if (typeof(WebSocket) !== 'undefined') {
|
||||
this.WebSocket = WebSocket;
|
||||
@ -136,6 +137,7 @@ define([
|
||||
this._iopub_handlers = {};
|
||||
this.register_iopub_handler('status', $.proxy(this._handle_status_message, this));
|
||||
this.register_iopub_handler('clear_output', $.proxy(this._handle_clear_output, this));
|
||||
this.register_iopub_handler('execute_input', $.proxy(this._handle_input_message, this));
|
||||
|
||||
for (var i=0; i < output_msg_types.length; i++) {
|
||||
this.register_iopub_handler(output_msg_types[i], $.proxy(this._handle_output_message, this));
|
||||
@ -994,6 +996,11 @@ define([
|
||||
Kernel.prototype._handle_output_message = function (msg) {
|
||||
var callbacks = this.get_callbacks_for_msg(msg.parent_header.msg_id);
|
||||
if (!callbacks || !callbacks.iopub) {
|
||||
if (this.unsolicited_msg_callback) {
|
||||
// The message came from another client. Let the UI decide what
|
||||
// to do with it.
|
||||
this.unsolicited_msg_callback(msg);
|
||||
}
|
||||
return;
|
||||
}
|
||||
var callback = callbacks.iopub.output;
|
||||
@ -1002,6 +1009,20 @@ define([
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle an input message (execute_input).
|
||||
*
|
||||
* @function _handle_input message
|
||||
*/
|
||||
Kernel.prototype._handle_input_message = function (msg) {
|
||||
var callbacks = this.get_callbacks_for_msg(msg.parent_header.msg_id);
|
||||
if (!callbacks && this.unsolicited_msg_callback) {
|
||||
// The message came from another client. Let the UI decide what to
|
||||
// do with it.
|
||||
this.unsolicited_msg_callback(msg);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Dispatch IOPub messages to respective handlers. Each message
|
||||
* type should have a handler.
|
||||
|
@ -228,6 +228,9 @@ class="notebook_app"
|
||||
</li>
|
||||
<li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Kernel</a>
|
||||
<ul id="kernel_menu" class="dropdown-menu">
|
||||
<li id="toggle_unsolicited"
|
||||
title="Toggle display of unsolicited messages.">
|
||||
<a href="#">Show/ignore unsolicited messages.</a></li>
|
||||
<li id="int_kernel"
|
||||
title="Send KeyboardInterrupt (CTRL-C) to the Kernel">
|
||||
<a href="#">Interrupt</a>
|
||||
|
Loading…
Reference in New Issue
Block a user