Merge pull request #6950 from minrk/new-new-dropdown

Add kernel-select dropdown to new notebook button
pull/37/head
Matthias Bussonnier 12 years ago
commit a9fffc41ab

@ -7,8 +7,6 @@ from tornado import web
from ...base.handlers import IPythonHandler, json_errors
from IPython.kernel.kernelspec import _pythonfirst
class MainKernelSpecHandler(IPythonHandler):
SUPPORTED_METHODS = ('GET',)
@ -17,18 +15,21 @@ class MainKernelSpecHandler(IPythonHandler):
@json_errors
def get(self):
ksm = self.kernel_spec_manager
results = []
for kernel_name in sorted(ksm.find_kernel_specs(), key=_pythonfirst):
km = self.kernel_manager
model = {}
model['default'] = km.default_kernel_name
model['kernelspecs'] = specs = {}
for kernel_name in ksm.find_kernel_specs():
try:
d = ksm.get_kernel_spec(kernel_name).to_dict()
except Exception:
self.log.error("Failed to load kernel spec: '%s'", kernel_name, exc_info=True)
continue
d['name'] = kernel_name
results.append(d)
specs[kernel_name] = d
self.set_header("Content-Type", 'application/json')
self.finish(json.dumps(results))
self.finish(json.dumps(model))
class KernelSpecHandler(IPythonHandler):

@ -78,16 +78,22 @@ class APITest(NotebookTestBase):
with open(pjoin(bad_kernel_dir, 'kernel.json'), 'w') as f:
f.write("garbage")
specs = self.ks_api.list().json()
assert isinstance(specs, list)
model = self.ks_api.list().json()
assert isinstance(model, dict)
self.assertEqual(model['default'], NATIVE_KERNEL_NAME)
specs = model['kernelspecs']
assert isinstance(specs, dict)
# 2: the sample kernelspec created in setUp, and the native Python kernel
self.assertGreaterEqual(len(specs), 2)
shutil.rmtree(bad_kernel_dir)
def test_list_kernelspecs(self):
specs = self.ks_api.list().json()
assert isinstance(specs, list)
model = self.ks_api.list().json()
assert isinstance(model, dict)
self.assertEqual(model['default'], NATIVE_KERNEL_NAME)
specs = model['kernelspecs']
assert isinstance(specs, dict)
# 2: the sample kernelspec created in setUp, and the native Python kernel
self.assertGreaterEqual(len(specs), 2)
@ -98,8 +104,8 @@ class APITest(NotebookTestBase):
def is_default_kernelspec(s):
return s['name'] == NATIVE_KERNEL_NAME and s['display_name'].startswith("IPython")
assert any(is_sample_kernelspec(s) for s in specs), specs
assert any(is_default_kernelspec(s) for s in specs), specs
assert any(is_sample_kernelspec(s) for s in specs.values()), specs
assert any(is_default_kernelspec(s) for s in specs.values()), specs
def test_get_kernelspec(self):
spec = self.ks_api.kernel_spec_info('Sample').json() # Case insensitive

@ -541,7 +541,16 @@ define([
if (navigator.appVersion.indexOf("Linux")!=-1) OSName="Linux";
return OSName;
})();
var get_url_param = function (name) {
// get a URL parameter. I cannot believe we actually need this.
// Based on http://stackoverflow.com/a/25359264/938949
var match = new RegExp('[\?&]' + name + '=([^&]*)').exec(window.location.search);
if (match){
return decodeURIComponent(match[1] || '');
}
};
var is_or_has = function (a, b) {
/**
* Is b a child of a or a itself?
@ -632,6 +641,7 @@ define([
* Like $.ajax, but returning an ES6 promise. success and error settings
* will be ignored.
*/
settings = settings || {};
return new Promise(function(resolve, reject) {
settings.success = function(data, status, jqXHR) {
resolve(data);
@ -813,6 +823,7 @@ define([
from_absolute_cursor_pos : from_absolute_cursor_pos,
browser : browser,
platform: platform,
get_url_param: get_url_param,
is_or_has : is_or_has,
is_focused : is_focused,
mergeopt: mergeopt,

@ -25,16 +25,27 @@ define([
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)});
utils.promising_ajax(url).then($.proxy(this._got_kernelspecs, this));
};
KernelSelector.prototype._got_kernelspecs = function(data, status, xhr) {
this.kernelspecs = {};
KernelSelector.prototype._got_kernelspecs = function(data) {
this.kernelspecs = data.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 keys = Object.keys(data.kernelspecs).sort(function (a, b) {
// sort by display_name
var da = data.kernelspecs[a].display_name;
var db = data.kernelspecs[b].display_name;
if (da === db) {
return 0;
} else if (da > db) {
return 1;
} else {
return -1;
}
});
for (var i = 0; i < keys.length; i++) {
var ks = this.kernelspecs[keys[i]];
var ksentry = $("<li>").attr("id", "kernel-" +ks.name).append($('<a>')
.attr('href', '#')
.click($.proxy(this.change_kernel, this, ks.name))

@ -2275,9 +2275,13 @@ 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) {
var kernelspec = this.metadata.kernelspec || {};
var kernel_name = kernelspec.name;
var kernel_name;
if (this.metadata.kernelspec) {
var kernelspec = this.metadata.kernelspec || {};
kernel_name = kernelspec.name;
} else {
kernel_name = utils.get_url_param('kernel_name');
}
this.start_session(kernel_name);
}
// load our checkpoint list

@ -7999,14 +7999,15 @@ span#login_widget > .button .badge,
margin: 0;
}
.alternate_upload input.fileinput {
background-color: red;
position: relative;
display: inline;
opacity: 0;
z-index: 2;
width: 295px;
margin-left: 163px;
cursor: pointer;
height: 26px;
width: 12ex;
margin-right: -12ex;
}
.alternate_upload .input-overlay {
display: inline-block;
font-weight: bold;
}
/**
* Primary styles
@ -8164,6 +8165,21 @@ input.engine_num_input {
.file_icon:before.pull-right {
margin-left: .3em;
}
ul#new-notebook-menu {
left: auto;
right: 0;
}
.kernel-menu-icon {
padding-right: 12px;
width: 24px;
content: "\f096";
}
.kernel-menu-icon:before {
content: "\f096";
}
.kernel-menu-icon-current:before {
content: "\f00c";
}
/*!
*
* IPython notebook

@ -8,12 +8,14 @@ require([
'base/js/events',
'base/js/page',
'base/js/utils',
'services/config',
'contents',
'tree/js/notebooklist',
'tree/js/clusterlist',
'tree/js/sessionlist',
'tree/js/kernellist',
'tree/js/terminallist',
'tree/js/newnotebook',
'auth/js/loginwidget',
// only loaded, not used:
'jqueryui',
@ -26,12 +28,14 @@ require([
events,
page,
utils,
config,
contents_service,
notebooklist,
clusterlist,
sesssionlist,
notebooklist,
clusterlist,
sesssionlist,
kernellist,
terminallist,
newnotebook,
loginwidget){
"use strict";
@ -41,6 +45,10 @@ require([
base_url: utils.get_body_data("baseUrl"),
notebook_path: utils.get_body_data("notebookPath"),
};
var cfg = new config.ConfigSection('tree', common_options);
cfg.load();
common_options.config = cfg;
var session_list = new sesssionlist.SesssionList($.extend({
events: events},
common_options));
@ -63,24 +71,12 @@ require([
var login_widget = new loginwidget.LoginWidget('#login_widget', common_options);
$('#new_notebook').click(function (e) {
var w = window.open();
contents.new_untitled(common_options.notebook_path, {type: "notebook"}).then(
function (data) {
w.location = utils.url_join_encode(
common_options.base_url, 'notebooks', data.path
);
},
function(error) {
w.close();
dialog.modal({
title : 'Creating Notebook Failed',
body : "The error was: " + error.message,
buttons : {'OK' : {'class' : 'btn-primary'}}
});
}
);
});
var nnw = new newnotebook.NewNotebookWidget("#new-notebook-buttons",
$.extend(
{contents: contents},
common_options
)
);
var interval_id=0;
// auto refresh every xx secondes, no need to be fast,
@ -93,18 +89,18 @@ require([
*/
session_list.load_sessions();
cluster_list.load_list();
if (terminal_list) {
terminal_list.load_terminals();
}
if (terminal_list) {
terminal_list.load_terminals();
}
if (!interval_id){
interval_id = setInterval(function(){
session_list.load_sessions();
cluster_list.load_list();
if (terminal_list) {
terminal_list.load_terminals();
}
}, time_refresh*1000);
}
session_list.load_sessions();
cluster_list.load_list();
if (terminal_list) {
terminal_list.load_terminals();
}
}, time_refresh*1000);
}
};
var disable_autorefresh = function(){
@ -134,6 +130,7 @@ require([
IPython.session_list = session_list;
IPython.kernel_list = kernel_list;
IPython.login_widget = login_widget;
IPython.new_notebook_widget = nnw;
events.trigger('app_initialized.DashboardApp');

@ -0,0 +1,135 @@
// Copyright (c) IPython Development Team.
// Distributed under the terms of the Modified BSD License.
define([
'jquery',
'base/js/namespace',
'base/js/utils',
'base/js/dialog',
], function ($, IPython, utils, dialog) {
"use strict";
var NewNotebookWidget = function (selector, options) {
this.selector = selector;
this.base_url = options.base_url;
this.notebook_path = options.notebook_path;
this.contents = options.contents;
this.default_kernel = null;
this.config = options.config;
this.kernelspecs = {};
if (this.selector !== undefined) {
this.element = $(selector);
this.request_kernelspecs();
}
this.bind_events();
};
NewNotebookWidget.prototype.bind_events = function () {
var that = this;
this.element.find('#new_notebook').click(function () {
that.new_notebook();
});
};
NewNotebookWidget.prototype.request_kernelspecs = function () {
/** request and then load kernel specs */
var url = utils.url_join_encode(this.base_url, 'api/kernelspecs');
utils.promising_ajax(url).then($.proxy(this._load_kernelspecs, this));
};
NewNotebookWidget.prototype._load_kernelspecs = function (data) {
/** load kernelspec list */
var that = this;
this.kernelspecs = data.kernelspecs;
var menu = this.element.find("#new-notebook-menu");
var keys = Object.keys(data.kernelspecs).sort(function (a, b) {
var da = data.kernelspecs[a].display_name;
var db = data.kernelspecs[b].display_name;
if (da === db) {
return 0;
} else if (da > db) {
return 1;
} else {
return -1;
}
});
for (var i = 0; i < keys.length; i++) {
var ks = this.kernelspecs[keys[i]];
var li = $("<li>")
.attr("id", "kernel-" +ks.name)
.data('kernelspec', ks).append(
$('<a>')
.attr('href', '#')
.click($.proxy(this.new_notebook, this, ks.name))
.text(ks.display_name)
.attr('title', 'Create a new notebook with ' + ks.display_name)
);
menu.append(li);
}
this.config.loaded.then(function () {
that._load_default_kernelspec(data['default']);
});
};
NewNotebookWidget.prototype._load_default_kernelspec = function (default_name) {
/** load default kernelspec name from config, if defined */
if (this.config.data.NewNotebookWidget &&
this.config.data.NewNotebookWidget.default_kernel &&
this.kernelspecs[this.config.data.NewNotebookWidget.default_kernel] !== undefined
) {
default_name = this.config.data.NewNotebookWidget.default_kernel;
}
this.set_default_kernel(default_name);
};
NewNotebookWidget.prototype.set_default_kernel = function (kernel_name) {
/** select the current default kernel */
this.default_kernel = kernel_name;
this.config.update({
NewNotebookWidget: {
default_kernel: kernel_name
}
});
var spec = this.kernelspecs[kernel_name];
var display_name;
if (spec) {
display_name = spec.display_name;
this.element.find("#current-kernel")
.text(display_name)
.attr('title', display_name + " is the default kernel for new notebooks");
} else {
display_name = 'default kernel';
}
this.element.find("#new_notebook").attr('title',
'Create a new notebook with ' + display_name
);
};
NewNotebookWidget.prototype.new_notebook = function (kernel_name) {
/** create and open a new notebook */
var that = this;
kernel_name = kernel_name || this.default_kernel;
var w = window.open();
this.contents.new_untitled(that.notebook_path, {type: "notebook"}).then(
function (data) {
var url = utils.url_join_encode(
that.base_url, 'notebooks', data.path
);
if (kernel_name) {
url += "?kernel_name=" + kernel_name;
}
w.location = url;
},
function (error) {
w.close();
dialog.modal({
title : 'Creating Notebook Failed',
body : "The error was: " + error.message,
buttons : {'OK' : {'class' : 'btn-primary'}}
});
}
);
};
return {'NewNotebookWidget': NewNotebookWidget};
});

@ -15,12 +15,14 @@
.alternate_upload input.fileinput
{
background-color:red;
position:relative;
display: inline;
opacity: 0;
z-index: 2;
width: 295px;
margin-left:163px;
cursor: pointer;
height: 26px;
width: 12ex;
margin-right: -12ex;
}
.alternate_upload .input-overlay {
display: inline-block;
font-weight: bold;
}

@ -154,3 +154,23 @@ input.engine_num_input {
.file_icon:before {
.icon(@fa-var-file-o)
}
ul#new-notebook-menu {
// align right instead of left
left: auto;
right: 0;
}
.kernel-menu-icon {
padding-right: 12px;
width: 24px;
content: @fa-var-square-o;
}
.kernel-menu-icon:before {
content: @fa-var-square-o;
}
.kernel-menu-icon-current:before {
content: @fa-var-check;
}

@ -43,7 +43,7 @@ class="notebook_app"
<span id="kernel_selector_widget" class="pull-right dropdown">
<button class="dropdown-toggle" data-toggle="dropdown" type='button' id="current_kernel_spec">
<span class='kernel_name'>Python</span>
<span class='kernel_name'>Kernel</span>
<span class="caret"></span>
</button>
<ul id="kernel_selector" class="dropdown-menu">

@ -34,19 +34,31 @@ data-terminals-available="{{terminals_available}}"
<div class="tab-content">
<div id="notebooks" class="tab-pane active">
<div id="notebook_toolbar" class="row">
<div class="col-sm-8 no-padding">
<form id='alternate_upload' class='alternate_upload' >
<span id="notebook_list_info" style="position:absolute" >
To import a notebook, drag the file onto the listing below or <strong>click here</strong>.
<div class="col-sm-12 no-padding">
<form id='alternate_upload' class='alternate_upload'>
<span id="notebook_list_info">
To import a notebook, drag the file onto the listing below or
<span class="input-overlay">
<input type="file" name="datafile" class="fileinput" multiple='multiple'>
click here.
</span>
</span>
<input type="file" name="datafile" class="fileinput" multiple='multiple'>
</form>
</div>
<div class="col-sm-4 no-padding tree-buttons">
<span id="notebook_buttons" class="pull-right">
<button id="new_notebook" title="Create new notebook" class="btn btn-default btn-xs">New Notebook</button>
<button id="refresh_notebook_list" title="Refresh notebook list" class="btn btn-default btn-xs"><i class="fa fa-refresh"></i></button>
</span>
<span id="notebook_buttons" class="pull-right">
<div id="new-notebook-buttons" class="btn-group">
<button id="new_notebook" class="btn btn-default btn-xs">
New Notebook
</button>
<button class="dropdown-toggle btn btn-default btn-xs" data-toggle="dropdown">
<span id="current-kernel">Loading...</span>
<span class="caret"></span>
</button>
<ul id="new-notebook-menu" class="dropdown-menu"></ul>
</div>
<button id="refresh_notebook_list" title="Refresh notebook list" class="btn btn-default btn-xs"><i class="fa fa-refresh"></i></button>
</span>
</div>
</div>

Loading…
Cancel
Save