mirror of
https://github.com/jupyter/notebook.git
synced 2025-01-24 12:05:22 +08:00
Merge pull request #6126 from takluyver/kernelspecs-nb-ui
UI for changing kernels in the notebook
This commit is contained in:
commit
c9631845fa
@ -7,6 +7,8 @@ from tornado import web
|
||||
|
||||
from ...base.handlers import IPythonHandler, json_errors
|
||||
|
||||
from IPython.kernel.kernelspec import _pythonfirst
|
||||
|
||||
|
||||
class MainKernelSpecHandler(IPythonHandler):
|
||||
SUPPORTED_METHODS = ('GET',)
|
||||
@ -16,7 +18,7 @@ class MainKernelSpecHandler(IPythonHandler):
|
||||
def get(self):
|
||||
ksm = self.kernel_spec_manager
|
||||
results = []
|
||||
for kernel_name in ksm.find_kernel_specs():
|
||||
for kernel_name in sorted(ksm.find_kernel_specs(), key=_pythonfirst):
|
||||
d = ksm.get_kernel_spec(kernel_name).to_dict()
|
||||
d['name'] = kernel_name
|
||||
results.append(d)
|
||||
|
81
IPython/html/static/notebook/js/kernelselector.js
Normal file
81
IPython/html/static/notebook/js/kernelselector.js
Normal file
@ -0,0 +1,81 @@
|
||||
// Copyright (c) IPython Development Team.
|
||||
// Distributed under the terms of the Modified BSD License.
|
||||
|
||||
define([
|
||||
'base/js/namespace',
|
||||
'jquery',
|
||||
'base/js/utils',
|
||||
], function(IPython, $, utils) {
|
||||
"use strict";
|
||||
|
||||
var KernelSelector = function(selector, notebook) {
|
||||
this.selector = selector;
|
||||
this.notebook = notebook;
|
||||
this.events = notebook.events;
|
||||
this.current_selection = notebook.default_kernel_name;
|
||||
this.kernelspecs = {};
|
||||
if (this.selector !== undefined) {
|
||||
this.element = $(selector);
|
||||
this.request_kernelspecs();
|
||||
}
|
||||
this.bind_events();
|
||||
// Make the object globally available for user convenience & inspection
|
||||
IPython.kernelselector = this;
|
||||
};
|
||||
|
||||
KernelSelector.prototype.request_kernelspecs = function() {
|
||||
var url = utils.url_join_encode(this.notebook.base_url, 'api/kernelspecs');
|
||||
$.ajax(url, {success: $.proxy(this._got_kernelspecs, this)});
|
||||
};
|
||||
|
||||
KernelSelector.prototype._got_kernelspecs = function(data, status, xhr) {
|
||||
this.kernelspecs = {};
|
||||
var menu = this.element.find("#kernel_selector");
|
||||
var change_kernel_submenu = $("#menu-change-kernel-submenu");
|
||||
for (var i = 0; i < data.length; i++) {
|
||||
var ks = data[i];
|
||||
this.kernelspecs[ks.name] = ks;
|
||||
var ksentry = $("<li>").attr("id", "kernel-" +ks.name).append($('<a>')
|
||||
.attr('href', '#')
|
||||
.click($.proxy(this.change_kernel, this, ks.name))
|
||||
.text(ks.display_name));
|
||||
menu.append(ksentry);
|
||||
|
||||
var ks_submenu_entry = $("<li>").attr("id", "kernel-submenu-"+ks.name).append($('<a>')
|
||||
.attr('href', '#')
|
||||
.click($.proxy(this.change_kernel, this, ks.name))
|
||||
.text(ks.display_name));
|
||||
change_kernel_submenu.append(ks_submenu_entry);
|
||||
}
|
||||
};
|
||||
|
||||
KernelSelector.prototype.change_kernel = function(kernel_name) {
|
||||
if (kernel_name === this.current_selection) {
|
||||
return;
|
||||
}
|
||||
var ks = this.kernelspecs[kernel_name];
|
||||
this.events.trigger('spec_changed.Kernel', ks);
|
||||
this.notebook.session.delete();
|
||||
this.notebook.start_session(kernel_name);
|
||||
};
|
||||
|
||||
KernelSelector.prototype.bind_events = function() {
|
||||
var that = this;
|
||||
this.events.on('spec_changed.Kernel', function(event, data) {
|
||||
that.current_selection = data.name;
|
||||
that.element.find("#current_kernel_spec").find('.kernel_name').text(data.display_name);
|
||||
});
|
||||
|
||||
this.events.on('started.Session', function(events, session) {
|
||||
if (session.kernel_name !== that.current_selection) {
|
||||
// If we created a 'python' session, we only know if it's Python
|
||||
// 3 or 2 on the server's reply, so we fire the event again to
|
||||
// set things up.
|
||||
var ks = that.kernelspecs[session.kernel_name];
|
||||
that.events.trigger('spec_changed.Kernel', ks);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return {'KernelSelector': KernelSelector};
|
||||
});
|
@ -18,6 +18,7 @@ require([
|
||||
'notebook/js/savewidget',
|
||||
'notebook/js/keyboardmanager',
|
||||
'notebook/js/config',
|
||||
'notebook/js/kernelselector',
|
||||
// only loaded, not used:
|
||||
'custom/custom',
|
||||
], function(
|
||||
@ -36,7 +37,8 @@ require([
|
||||
notificationarea,
|
||||
savewidget,
|
||||
keyboardmanager,
|
||||
config
|
||||
config,
|
||||
kernelselector
|
||||
) {
|
||||
"use strict";
|
||||
|
||||
@ -90,6 +92,8 @@ require([
|
||||
notebook: notebook,
|
||||
keyboard_manager: keyboard_manager});
|
||||
notification_area.init_notification_widgets();
|
||||
var kernel_selector = new kernelselector.KernelSelector(
|
||||
'#kernel_selector_widget', notebook);
|
||||
|
||||
$('body').append('<div id="fonttest"><pre><span id="test1">x</span>'+
|
||||
'<span id="test2" style="font-weight: bold;">x</span>'+
|
||||
|
@ -121,6 +121,7 @@ define([
|
||||
this.notebook_name_blacklist_re = /[\/\\:]/;
|
||||
this.nbformat = 3; // Increment this when changing the nbformat
|
||||
this.nbformat_minor = 0; // Increment this when changing the nbformat
|
||||
this.codemirror_mode = 'ipython'
|
||||
this.style();
|
||||
this.create_elements();
|
||||
this.bind_events();
|
||||
@ -214,6 +215,13 @@ define([
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
this.events.on('spec_changed.Kernel', function(event, data) {
|
||||
that.set_kernelspec_metadata(data);
|
||||
if (data.codemirror_mode) {
|
||||
that.set_codemirror_mode(data.codemirror_mode);
|
||||
}
|
||||
});
|
||||
|
||||
var collapse_time = function (time) {
|
||||
var app_height = $('#ipython-main-app').height(); // content height
|
||||
@ -336,6 +344,16 @@ define([
|
||||
notebook: this,
|
||||
keyboard_manager: this.keyboard_manager});
|
||||
};
|
||||
|
||||
Notebook.prototype.set_kernelspec_metadata = function(ks) {
|
||||
var tostore = {};
|
||||
$.map(ks, function(value, field) {
|
||||
if (field !== 'argv' && field !== 'env') {
|
||||
tostore[field] = value;
|
||||
}
|
||||
});
|
||||
this.metadata.kernelspec = tostore;
|
||||
}
|
||||
|
||||
// Cell indexing, retrieval, etc.
|
||||
|
||||
@ -1488,6 +1506,34 @@ define([
|
||||
Notebook.prototype.cell_toggle_line_numbers = function() {
|
||||
this.get_selected_cell().toggle_line_numbers();
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the codemirror mode for all code cells, including the default for
|
||||
* new code cells.
|
||||
*
|
||||
* @method set_codemirror_mode
|
||||
*/
|
||||
Notebook.prototype.set_codemirror_mode = function(newmode){
|
||||
if (newmode === this.codemirror_mode) {
|
||||
return;
|
||||
}
|
||||
this.codemirror_mode = newmode;
|
||||
codecell.CodeCell.options_default.cm_config.mode = newmode;
|
||||
modename = newmode.name || newmode
|
||||
|
||||
that = this;
|
||||
CodeMirror.requireMode(modename, function(){
|
||||
$.map(that.get_cells(), function(cell, i) {
|
||||
if (cell.cell_type === 'code'){
|
||||
cell.code_mirror.setOption('mode', newmode);
|
||||
// This is currently redundant, because cm_config ends up as
|
||||
// codemirror's own .options object, but I don't want to
|
||||
// rely on that.
|
||||
cell.cm_config.mode = newmode;
|
||||
}
|
||||
});
|
||||
})
|
||||
};
|
||||
|
||||
// Session related things
|
||||
|
||||
@ -1496,7 +1542,10 @@ define([
|
||||
*
|
||||
* @method start_session
|
||||
*/
|
||||
Notebook.prototype.start_session = function () {
|
||||
Notebook.prototype.start_session = function (kernel_name) {
|
||||
if (kernel_name === undefined) {
|
||||
kernel_name = this.default_kernel_name;
|
||||
}
|
||||
this.session = new session.Session({
|
||||
base_url: this.base_url,
|
||||
ws_url: this.ws_url,
|
||||
@ -1505,7 +1554,7 @@ define([
|
||||
// For now, create all sessions with the 'python' kernel, which is the
|
||||
// default. Later, the user will be able to select kernels. This is
|
||||
// overridden if KernelManager.kernel_cmd is specified for the server.
|
||||
kernel_name: this.default_kernel_name,
|
||||
kernel_name: kernel_name,
|
||||
notebook: this});
|
||||
|
||||
this.session.start($.proxy(this._session_started, this));
|
||||
@ -1730,6 +1779,13 @@ define([
|
||||
this.metadata = content.metadata;
|
||||
this.notebook_name = data.name;
|
||||
var trusted = true;
|
||||
|
||||
// Trigger an event changing the kernel spec - this will set the default
|
||||
// codemirror mode
|
||||
if (this.metadata.kernelspec !== undefined) {
|
||||
this.events.trigger('spec_changed.Kernel', this.metadata.kernelspec);
|
||||
}
|
||||
|
||||
// Only handle 1 worksheet for now.
|
||||
var worksheet = content.worksheets[0];
|
||||
if (worksheet !== undefined) {
|
||||
@ -2216,7 +2272,10 @@ define([
|
||||
// Create the session after the notebook is completely loaded to prevent
|
||||
// code execution upon loading, which is a security risk.
|
||||
if (this.session === null) {
|
||||
this.start_session();
|
||||
var kernelspec = this.metadata.kernelspec || {};
|
||||
var kernel_name = kernelspec.name || this.default_kernel_name;
|
||||
|
||||
this.start_session(kernel_name);
|
||||
}
|
||||
// load our checkpoint list
|
||||
this.list_checkpoints();
|
||||
|
14
IPython/html/static/notebook/less/kernelselector.less
Normal file
14
IPython/html/static/notebook/less/kernelselector.less
Normal file
@ -0,0 +1,14 @@
|
||||
#kernel_selector_widget {
|
||||
margin-right: 1em;
|
||||
float:right;
|
||||
|
||||
& > button {
|
||||
.btn();
|
||||
.btn-default();
|
||||
.btn-sm();
|
||||
|
||||
& > span.caret {
|
||||
margin-top:0px;
|
||||
}
|
||||
}
|
||||
}
|
@ -8,6 +8,7 @@
|
||||
@import "notebook.less";
|
||||
@import "celltoolbar.less";
|
||||
@import "completer.less";
|
||||
@import "kernelselector.less";
|
||||
@import "menubar.less";
|
||||
@import "notificationarea.less";
|
||||
@import "notificationwidget.less";
|
||||
|
@ -13,6 +13,7 @@ define([
|
||||
this.kernel = null;
|
||||
this.id = null;
|
||||
this.notebook = options.notebook;
|
||||
this.events = options.notebook.events;
|
||||
this.name = options.notebook_name;
|
||||
this.path = options.notebook_path;
|
||||
this.kernel_name = options.kernel_name;
|
||||
@ -91,6 +92,9 @@ define([
|
||||
*/
|
||||
Session.prototype._handle_start_success = function (data, status, xhr) {
|
||||
this.id = data.id;
|
||||
// If we asked for 'python', the response will have 'python3' or 'python2'.
|
||||
this.kernel_name = data.kernel.name;
|
||||
this.events.trigger('started.Session', this);
|
||||
var kernel_service_url = utils.url_path_join(this.base_url, "api/kernels");
|
||||
this.kernel = new kernel.Kernel(kernel_service_url, this.ws_url, this.notebook, this.kernel_name);
|
||||
this.kernel._kernel_started(data.kernel);
|
||||
|
116
IPython/html/static/style/style.min.css
vendored
116
IPython/html/static/style/style.min.css
vendored
@ -9436,6 +9436,122 @@ p {
|
||||
.completions select option.context {
|
||||
color: #3071a9;
|
||||
}
|
||||
#kernel_selector_widget {
|
||||
margin-right: 1em;
|
||||
float: right;
|
||||
}
|
||||
#kernel_selector_widget > button {
|
||||
display: inline-block;
|
||||
margin-bottom: 0;
|
||||
font-weight: normal;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
cursor: pointer;
|
||||
background-image: none;
|
||||
border: 1px solid transparent;
|
||||
white-space: nowrap;
|
||||
padding: 6px 12px;
|
||||
font-size: 13px;
|
||||
line-height: 1.42857143;
|
||||
border-radius: 4px;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
color: #333333;
|
||||
background-color: #ffffff;
|
||||
border-color: #cccccc;
|
||||
padding: 5px 10px;
|
||||
font-size: 12px;
|
||||
line-height: 1.5;
|
||||
border-radius: 3px;
|
||||
}
|
||||
#kernel_selector_widget > button:focus,
|
||||
#kernel_selector_widget > button:active:focus,
|
||||
#kernel_selector_widget > button.active:focus {
|
||||
outline: thin dotted;
|
||||
outline: 5px auto -webkit-focus-ring-color;
|
||||
outline-offset: -2px;
|
||||
}
|
||||
#kernel_selector_widget > button:hover,
|
||||
#kernel_selector_widget > button:focus {
|
||||
color: #333333;
|
||||
text-decoration: none;
|
||||
}
|
||||
#kernel_selector_widget > button:active,
|
||||
#kernel_selector_widget > button.active {
|
||||
outline: 0;
|
||||
background-image: none;
|
||||
-webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
|
||||
box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
|
||||
}
|
||||
#kernel_selector_widget > button.disabled,
|
||||
#kernel_selector_widget > button[disabled],
|
||||
fieldset[disabled] #kernel_selector_widget > button {
|
||||
cursor: not-allowed;
|
||||
pointer-events: none;
|
||||
opacity: 0.65;
|
||||
filter: alpha(opacity=65);
|
||||
-webkit-box-shadow: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
#kernel_selector_widget > button [class^="icon-"].icon-large,
|
||||
#kernel_selector_widget > button [class*=" icon-"].icon-large {
|
||||
line-height: .9em;
|
||||
}
|
||||
#kernel_selector_widget > button [class^="icon-"].icon-spin,
|
||||
#kernel_selector_widget > button [class*=" icon-"].icon-spin {
|
||||
display: inline-block;
|
||||
}
|
||||
#kernel_selector_widget > button [class^="icon-"].pull-left.icon-2x,
|
||||
#kernel_selector_widget > button [class*=" icon-"].pull-left.icon-2x,
|
||||
#kernel_selector_widget > button [class^="icon-"].pull-right.icon-2x,
|
||||
#kernel_selector_widget > button [class*=" icon-"].pull-right.icon-2x {
|
||||
margin-top: .18em;
|
||||
}
|
||||
#kernel_selector_widget > button [class^="icon-"].icon-spin.icon-large,
|
||||
#kernel_selector_widget > button [class*=" icon-"].icon-spin.icon-large {
|
||||
line-height: .8em;
|
||||
}
|
||||
#kernel_selector_widget > button:hover,
|
||||
#kernel_selector_widget > button:focus,
|
||||
#kernel_selector_widget > button:active,
|
||||
#kernel_selector_widget > button.active,
|
||||
.open .dropdown-toggle#kernel_selector_widget > button {
|
||||
color: #333333;
|
||||
background-color: #ebebeb;
|
||||
border-color: #adadad;
|
||||
}
|
||||
#kernel_selector_widget > button:active,
|
||||
#kernel_selector_widget > button.active,
|
||||
.open .dropdown-toggle#kernel_selector_widget > button {
|
||||
background-image: none;
|
||||
}
|
||||
#kernel_selector_widget > button.disabled,
|
||||
#kernel_selector_widget > button[disabled],
|
||||
fieldset[disabled] #kernel_selector_widget > button,
|
||||
#kernel_selector_widget > button.disabled:hover,
|
||||
#kernel_selector_widget > button[disabled]:hover,
|
||||
fieldset[disabled] #kernel_selector_widget > button:hover,
|
||||
#kernel_selector_widget > button.disabled:focus,
|
||||
#kernel_selector_widget > button[disabled]:focus,
|
||||
fieldset[disabled] #kernel_selector_widget > button:focus,
|
||||
#kernel_selector_widget > button.disabled:active,
|
||||
#kernel_selector_widget > button[disabled]:active,
|
||||
fieldset[disabled] #kernel_selector_widget > button:active,
|
||||
#kernel_selector_widget > button.disabled.active,
|
||||
#kernel_selector_widget > button[disabled].active,
|
||||
fieldset[disabled] #kernel_selector_widget > button.active {
|
||||
background-color: #ffffff;
|
||||
border-color: #cccccc;
|
||||
}
|
||||
#kernel_selector_widget > button .badge {
|
||||
color: #ffffff;
|
||||
background-color: #333333;
|
||||
}
|
||||
#kernel_selector_widget > button > span.caret {
|
||||
margin-top: 0px;
|
||||
}
|
||||
#menubar {
|
||||
margin-top: 0px;
|
||||
margin-bottom: -19px;
|
||||
|
@ -34,12 +34,22 @@ class="notebook_app"
|
||||
|
||||
{% block header %}
|
||||
|
||||
|
||||
<span id="save_widget" class="nav pull-left">
|
||||
<span id="notebook_name"></span>
|
||||
<span id="checkpoint_status"></span>
|
||||
<span id="autosave_status"></span>
|
||||
</span>
|
||||
|
||||
<span id="kernel_selector_widget" class="pull-right dropdown">
|
||||
<button href="#" class="dropdown-toggle" data-toggle="dropdown" type='button' id="current_kernel_spec">
|
||||
<span class='kernel_name'>Python</span>
|
||||
<span class="caret"></span>
|
||||
</button>
|
||||
<ul id="kernel_selector" class="dropdown-menu">
|
||||
</ul>
|
||||
</span>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
|
||||
@ -216,6 +226,11 @@ class="notebook_app"
|
||||
<li id="restart_kernel"
|
||||
title="Restart the Kernel">
|
||||
<a href="#">Restart</a></li>
|
||||
<li class="divider"></li>
|
||||
<li id="menu-change-kernel" class="dropdown-submenu">
|
||||
<a href="#">Change kernel</a>
|
||||
<ul class="dropdown-menu" id="menu-change-kernel-submenu"></ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Help</a>
|
||||
|
Loading…
Reference in New Issue
Block a user