├── .gitignore
├── Part1
├── devtools.png
├── cannotknot.jpg
├── extensions
│ ├── server_ext.py
│ ├── quickaction
│ │ ├── styles.css
│ │ └── main.js
│ ├── hello-scipy.js
│ ├── server_ext_full.py
│ ├── markcell.js
│ ├── solution.js
│ └── hello-scipy-full.js
├── 01-tutorial-intro.md
├── MyFirstExtension.ipynb
├── 06-storing-config.md
├── 05-keyboardshortcut.md
├── 02-introtojs.md
├── 03-thethingsyoucantgoogle.md
└── 04-notebook-extensions.md
├── Part3
├── images
│ ├── bower.png
│ ├── nodejs.png
│ ├── npm-logo.svg
│ ├── hub.svg
│ └── pieces.svg
└── hub install.ipynb
├── Part2
├── images
│ ├── widgets.PNG
│ ├── inputoutput.PNG
│ ├── state_sync.svg
│ ├── assoc.svg
│ ├── transport.svg
│ ├── busy.svg
│ ├── execute.svg
│ ├── state.svg
│ ├── widgetcomm.svg
│ ├── widgetcomm2.svg
│ └── display.svg
├── sln
│ ├── 1.py
│ ├── 2_1.py
│ ├── 2_2.py
│ ├── 2_1.js
│ ├── 2_2_1.js
│ ├── 2_2_2.js
│ └── 2_2.js
├── Exercise 1.ipynb
├── Exercise 2.ipynb
├── Widget Events.ipynb
└── Widget List.ipynb
├── style.css
└── readme.md
/.gitignore:
--------------------------------------------------------------------------------
1 | *.un~
2 | *.swp
3 | __pycache__
4 | .DS_Store
5 | .ipynb_checkpoints
6 |
--------------------------------------------------------------------------------
/Part1/devtools.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jupyter/scipy-advanced-tutorial/HEAD/Part1/devtools.png
--------------------------------------------------------------------------------
/Part1/cannotknot.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jupyter/scipy-advanced-tutorial/HEAD/Part1/cannotknot.jpg
--------------------------------------------------------------------------------
/Part3/images/bower.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jupyter/scipy-advanced-tutorial/HEAD/Part3/images/bower.png
--------------------------------------------------------------------------------
/Part3/images/nodejs.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jupyter/scipy-advanced-tutorial/HEAD/Part3/images/nodejs.png
--------------------------------------------------------------------------------
/Part2/images/widgets.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jupyter/scipy-advanced-tutorial/HEAD/Part2/images/widgets.PNG
--------------------------------------------------------------------------------
/Part1/extensions/server_ext.py:
--------------------------------------------------------------------------------
1 | def load_jupyter_server_extension(nbapp):
2 | nbapp.log.info('SciPy Ext loaded')
3 |
--------------------------------------------------------------------------------
/Part2/images/inputoutput.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jupyter/scipy-advanced-tutorial/HEAD/Part2/images/inputoutput.PNG
--------------------------------------------------------------------------------
/Part1/extensions/quickaction/styles.css:
--------------------------------------------------------------------------------
1 |
2 |
3 | .quickaction-dialog input {
4 | width: 100%;
5 | }
6 |
7 | .ui-front {
8 | z-index: 1200;
9 | }
10 |
--------------------------------------------------------------------------------
/Part2/sln/1.py:
--------------------------------------------------------------------------------
1 | slider = widgets.FloatSlider()
2 | text = widgets.FloatText()
3 | widgetlink = link((slider, 'value'), (text, 'value'))
4 | display(slider, text)
5 |
--------------------------------------------------------------------------------
/Part2/sln/2_1.py:
--------------------------------------------------------------------------------
1 | from IPython.html.widgets import DOMWidget
2 | from IPython.display import display
3 | from IPython.utils.traitlets import Unicode
4 | class CustomWidget(DOMWidget):
5 | _view_module = Unicode('CustomViewModule', sync=True)
6 | _view_name = Unicode('CustomView', sync=True)
7 | display(CustomWidget())
8 |
--------------------------------------------------------------------------------
/Part2/sln/2_2.py:
--------------------------------------------------------------------------------
1 | from IPython.html.widgets import DOMWidget
2 | from IPython.display import display
3 | from IPython.utils.traitlets import Unicode
4 | class ColorWidget(DOMWidget):
5 | _view_module = Unicode('ColorViewModule', sync=True)
6 | _view_name = Unicode('ColorView', sync=True)
7 | value = Unicode('#990000', sync=True)
8 |
--------------------------------------------------------------------------------
/Part2/sln/2_1.js:
--------------------------------------------------------------------------------
1 | %%javascript
2 | delete requirejs.s.contexts._.defined.CustomViewModule;
3 | define('CustomViewModule', ['jquery', 'widgets/js/widget'], function($, widget) {
4 | var CustomView = widget.DOMWidgetView.extend({
5 | render: function() {
6 | this.$el.text('Hello SciPy!');
7 | }
8 | });
9 | return {CustomView: CustomView};
10 | });
11 |
--------------------------------------------------------------------------------
/Part2/sln/2_2_1.js:
--------------------------------------------------------------------------------
1 | %%javascript
2 | delete requirejs.s.contexts._.defined.ColorViewModule;
3 | define('ColorViewModule', ['jquery', 'widgets/js/widget'], function($, widget) {
4 |
5 | var ColorView = widget.DOMWidgetView.extend({
6 | render: function() {
7 | this.colorpicker = $('');
8 | this.colorpicker.attr('type', 'color');
9 | this.$el.append(this.colorpicker);
10 | },
11 | });
12 |
13 | return {ColorView: ColorView};
14 | });
15 |
--------------------------------------------------------------------------------
/Part1/extensions/hello-scipy.js:
--------------------------------------------------------------------------------
1 | define(function(){
2 | "use strict"
3 |
4 | function _on_load(){
5 | console.info('Hello SciPy 2015');
6 | console.info(atob('SWYgeW91IHJlYWQgdGhhdCBpbiB5b3VyIGJyb3dzZXIgY29uc29sZSB5b3UgY2FuIG5vdyBzYXkgYXQgbG91ZCA6'));
7 | console.info(atob('IkkgYW0gYSBKYXZhc2NyaXB0IGV4cGVydCI='));
8 | console.info(atob('VGhpcyB3YXkgSSBjYW4ga25vdyB3aGVuIG1ham9yaXR5IG9mIHBlb3BsZSBhcmUgcmVhZHkgYW5kIG1vdmUgb24gOi0p'));
9 | }
10 | return {load_ipython_extension: _on_load };
11 | })
12 |
--------------------------------------------------------------------------------
/style.css:
--------------------------------------------------------------------------------
1 | #notebook-container {
2 | box-shadow: none;
3 | -webkit-box-shadow: none;
4 | }
5 |
6 | body.notebook_app {
7 | background-color: white;
8 | }
9 |
10 | .rendered_html code,
11 | .rendered_html pre {
12 | background-color: #eaeaea;
13 | }
14 |
15 | .rendered_html code {
16 | padding: 1px;
17 | }
18 |
19 | .rendered_html pre {
20 | padding: 5px;
21 | }
22 |
23 | .rendered_html table,
24 | .rendered_html table tr {
25 | border: none;
26 | }
27 |
28 | .rendered_html table th,
29 | .rendered_html table td {
30 | border: 1px solid #d0d0d0;
31 | }
32 |
--------------------------------------------------------------------------------
/Part1/extensions/server_ext_full.py:
--------------------------------------------------------------------------------
1 | from IPython.html.utils import url_path_join as ujoin
2 | from tornado.web import RequestHandler
3 |
4 |
5 | class MyLogHandler(RequestHandler):
6 | def initialize(self, log=None):
7 | self.log = log
8 |
9 | def put(self):
10 | data = self.request.body.decode('utf-8')
11 | self.log.info(data)
12 | self.finish()
13 |
14 |
15 | def load_jupyter_server_extension(nbapp):
16 | nbapp.log.info('SciPy Ext loaded')
17 |
18 | webapp = nbapp.web_app
19 | base_url = webapp.settings['base_url']
20 | webapp.add_handlers(".*$", [
21 | (ujoin(base_url, r"/scipy/log"), MyLogHandler,
22 | {'log': nbapp.log}),
23 | ])
24 |
--------------------------------------------------------------------------------
/Part2/sln/2_2_2.js:
--------------------------------------------------------------------------------
1 | %%javascript
2 | delete requirejs.s.contexts._.defined.ColorViewModule;
3 | define('ColorViewModule', ['jquery', 'widgets/js/widget'], function($, widget) {
4 |
5 | var ColorView = widget.DOMWidgetView.extend({
6 | render: function() {
7 | this.colorpicker = $('');
8 | this.colorpicker.attr('type', 'color');
9 | this.$el.append(this.colorpicker);
10 |
11 | this.listenTo(this.model, 'change:value', this._update_value, this);
12 | this._update_value();
13 | },
14 | _update_value: function() {
15 | this.colorpicker.val(this.model.get('value'));
16 | },
17 | });
18 |
19 | return {ColorView: ColorView};
20 | });
21 |
--------------------------------------------------------------------------------
/Part2/sln/2_2.js:
--------------------------------------------------------------------------------
1 | %%javascript
2 | delete requirejs.s.contexts._.defined.ColorViewModule;
3 | define('ColorViewModule', ['jquery', 'widgets/js/widget'], function($, widget) {
4 |
5 | var ColorView = widget.DOMWidgetView.extend({
6 | render: function() {
7 | this.colorpicker = $('');
8 | this.colorpicker.attr('type', 'color');
9 | this.$el.append(this.colorpicker);
10 |
11 | this.listenTo(this.model, 'change:value', this._update_value, this);
12 | this._update_value();
13 |
14 | this.colorpicker.on('change', this._set_value.bind(this));
15 | },
16 | _update_value: function() {
17 | this.colorpicker.val(this.model.get('value'));
18 | },
19 | _set_value: function() {
20 | this.model.set('value', this.colorpicker.val());
21 | this.touch();
22 | }
23 | });
24 |
25 | return {ColorView: ColorView};
26 | });
27 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | # SciPy 2015 Jupyter advanced tutorial material
2 |
3 | This tutorial will be separated in 3 parts.
4 |
5 | In the first part we will investigate how to write and install Notebook
6 | extensions, we will first focus on how to write the Browser side of the
7 | extension, and get a quick introduction the how javascript works, as well as
8 | some of the necessary tooling one need to work with javascript in browser.
9 | Then we'll see how one can also create Python server-side extension, and how
10 | to communicate between the Python and javascript side.
11 |
12 | In second part, with our javascript knowledge, we will be ready to learn how to
13 | write custom widgets, both on the Browser and Server side.
14 |
15 |
16 | In the last part, we will look at mass deployment of multi-user jupyter-hub,
17 | and how the different tooling to install jupyterhub fit into the deployments of
18 | custom extensions and widgets in a multi-user environment. Jupyter-hub is also
19 | working on a number of extension that allow custom authentication, and server
20 | spawning that we will discuss.
21 |
--------------------------------------------------------------------------------
/Part3/images/npm-logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/Part1/01-tutorial-intro.md:
--------------------------------------------------------------------------------
1 | # SciPy 2015 Jupyter advanced tutorial
2 |
3 | This is some of the material for the advanced Jupyter/IPython SciPy tutorial
4 | that is/was/will be given in July 2015.
5 |
6 | During this tutorial we will cover a few non-usual topics for Jupyter/IPython,
7 | and in particular concerning the notebook.
8 |
9 | The topics we will cover are the following:
10 |
11 | How can you extend the notebook's functionality, first by installing already available extensions, then how to write your own. It will take a little knowledge of javascript, but don't worry, we have a recap for you.
12 |
13 | We'll briefly go into the structure of the different configuration and extension folders, where you should store files and how to retrieve them.
14 |
15 | We'll have a look at some of the Jupyter Notebook APIs to do some easy customisation, and rebind keyboard shortcut.
16 |
17 | We will walk you through how to write an extension that provide custom keyboard shortcuts and actions that the user can trigger.
18 |
19 | Extensions can also extend the notebook server, and you can add custom `handlers` to allow some processing. You will probably be happy to do a bit of Python.
20 |
21 | Once you are Javascript experts, we can go into writing your own widgets, with all the view on the javascript side that talk to Python object in the kernel.
22 |
23 | We will finish with some demos on how you can install Jupyterhub to get a multi-user install.
24 |
25 | By the end of this tutorial you should be able to hack the Jupyter Notebook to cover your own needs.
26 |
--------------------------------------------------------------------------------
/Part1/MyFirstExtension.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "Reminder of what you should do :\n",
8 | "\n",
9 | "- open developper pannel\n",
10 | "- go to console\n",
11 | "- enter the following\n",
12 | "\n",
13 | "```\n",
14 | "IPython.notebook.config.update({\n",
15 | " \"load_extensions\": {\"hello-scipy\":true}\n",
16 | "})\n",
17 | "```"
18 | ]
19 | },
20 | {
21 | "cell_type": "code",
22 | "execution_count": 1,
23 | "metadata": {
24 | "collapsed": false
25 | },
26 | "outputs": [
27 | {
28 | "data": {
29 | "text/plain": [
30 | "datetime.datetime(2015, 6, 26, 11, 46, 8, 450662)"
31 | ]
32 | },
33 | "execution_count": 1,
34 | "metadata": {},
35 | "output_type": "execute_result"
36 | }
37 | ],
38 | "source": [
39 | "# you will probably need this line to be sure your extension works\n",
40 | "import datetime as d\n",
41 | "d.datetime.now()"
42 | ]
43 | }
44 | ],
45 | "metadata": {
46 | "kernelspec": {
47 | "display_name": "Python 3",
48 | "language": "python",
49 | "name": "python3"
50 | },
51 | "language_info": {
52 | "codemirror_mode": {
53 | "name": "ipython",
54 | "version": 3
55 | },
56 | "file_extension": ".py",
57 | "mimetype": "text/x-python",
58 | "name": "python",
59 | "nbconvert_exporter": "python",
60 | "pygments_lexer": "ipython3",
61 | "version": "3.5.0b2"
62 | }
63 | },
64 | "nbformat": 4,
65 | "nbformat_minor": 0
66 | }
67 |
--------------------------------------------------------------------------------
/Part1/extensions/markcell.js:
--------------------------------------------------------------------------------
1 | // activate with
2 | // IPython.notebook.config.update({
3 | // "load_extensions": {"markcell":true}
4 | // })
5 | // put in ~/.ipython/nbextensions
6 |
7 | define(['base/js/namespace'],function(IPython){
8 |
9 | var _mcell = null;
10 | var _names = null;
11 | var mark_cell = {
12 | help: 'Tag a cell',
13 | icon : 'fa-tag',
14 | help_index : '',
15 | handler : function (env) {
16 | // todo mark cell instead of index.
17 | _mcell = env.notebook.get_selected_index()
18 | }
19 | }
20 | var jump_to_mark = {
21 | help: 'Tag a cell',
22 | icon : 'fa-eject',
23 | help_index : '',
24 | handler : function (env) {
25 | env.notebook.scroll_to_cell(_mcell);
26 | }
27 | }
28 |
29 | function register_actions(){
30 | if( _names === null){
31 | var action_name1 = IPython.keyboard_manager.actions.register(mark_cell, 'mark-cell', 'scipy-2015')
32 | var action_name2 = IPython.keyboard_manager.actions.register(jump_to_mark, 'jump-to-mark', 'scipy-2015')
33 | _names = {
34 | tag: action_name1,
35 | jump: action_name2
36 | }
37 | }
38 |
39 | return _names;
40 | }
41 | function _on_load(){
42 | ids = register_actions()
43 | IPython.keyboard_manager.command_shortcuts.add_shortcut('8,9', ids['tag'])
44 | IPython.keyboard_manager.command_shortcuts.add_shortcut('9,9', ids['jump'])
45 | IPython.toolbar.add_buttons_group([ids['tag'], ids['jump']])
46 | }
47 | return {load_ipython_extension: _on_load,
48 | register_actions:register_actions
49 | };
50 | })
51 |
--------------------------------------------------------------------------------
/Part1/extensions/solution.js:
--------------------------------------------------------------------------------
1 | define(['base/js/namespace'],function(Jupyter){
2 |
3 |
4 | var clear_all_cell_restart = {
5 | help: 'Clear all cell and restart kernel without confirmations',
6 | icon : 'fa-recycle',
7 | help_index : '',
8 | handler : function (env) {
9 | var on_success = undefined;
10 | var on_error = undefined;
11 |
12 | env.notebook.clear_all_output();
13 | env.notebook.kernel.restart(function(){
14 | setTimeout(function(){ // wait 1 sec,
15 | // todo listen on Kernel ready event.
16 | env.notebook.execute_all_cells()
17 | }, 1000)
18 | }, on_error);
19 | }
20 | }
21 |
22 | function _on_load(){
23 | console.info('Hello SciPy 2015')
24 | console.info(atob('SWYgeW91IHJlYWQgdGhhdCBpbiB5b3VyIGJyb3dzZXIgY29uc29sZSB5b3UgY2FuIG5vdyBzYXkgYXQgbG91ZCA6'))
25 | console.info(atob('IkkgYW0gYSBKYXZhc2NyaXB0IGV4cGVydCI='))
26 | console.info(atob('VGhpcyB3YXkgSSBjYW4ga25vdyB3aGVuIG1ham9yaXR5IG9mIHBlb3BsZSBhcmUgcmVhZHkgYW5kIG1vdmUgb24gOi0p'))
27 |
28 | // register our new action
29 | var action_name = IPython.keyboard_manager.actions.register(clear_all_cell_restart, 'clear-all-cells-restart', 'scipy-2015')
30 |
31 | // unbind 00
32 | IPython.keyboard_manager.command_shortcuts.remove_shortcut('0,0')
33 |
34 | // bind 000
35 | IPython.keyboard_manager.command_shortcuts.add_shortcut('0,0,0', action_name)
36 |
37 | IPython.toolbar.add_buttons_group(['scipy-2015.clear-all-cells-restart','ipython.restart-kernel'])
38 |
39 | }
40 |
41 | return {load_ipython_extension: _on_load };
42 | })
43 |
--------------------------------------------------------------------------------
/Part1/06-storing-config.md:
--------------------------------------------------------------------------------
1 | # Storing configuration
2 |
3 | Your extension may need to store user data such as preferences. You could
4 | use `localStorage` for this, but if the user opens a different browser, or runs
5 | the notebook on a different port, previously stored information won't be
6 | available. So we provide an API to store config through the server.
7 |
8 | First, load `services/config` in your extension:
9 |
10 | ```javascript
11 | define(['services/config'
12 | ],
13 | function(configmod) {
14 | ...
15 | });
16 | ```
17 |
18 | There are two classes in this module:
19 |
20 | - `ConfigSection` talks to the server to load and store config.
21 | - `ConfigWithDefaults` is a nicer API for the code using configurable values.
22 |
23 | First, you need to set up a ConfigSection:
24 |
25 | ```javascript
26 | var config = new configmod.ConfigSection('myextension',
27 | {base_url: utils.get_body_data("baseUrl")});
28 | config.load();
29 | ```
30 |
31 | In this example, `'myextension'` is the name of the config section. The server
32 | stores your config in a file with this name. One section should suffice for an
33 | extension.
34 |
35 | The `.load()` method is asynchronous: it will start loading the config, but
36 | it won't wait for loading to finish.
37 |
38 | ## Using configuration values
39 |
40 | Next, let's create a `ConfigWithDefaults` object:
41 |
42 | ```javascript
43 | var foo_config = new configmod.ConfigWithDefaults(config, {
44 | visible: true,
45 | colour: '#fe6500'
46 | }, 'foo')
47 | ```
48 |
49 | There are three arguments here:
50 |
51 | 1. `config`: the `ConfigSection` we created before.
52 | 2. The default values, which we'll use if we haven't stored something else in
53 | the config.
54 | 3. A subsection name, if you need to subdivide the config section. For simple
55 | extensions, you can leave this out.
56 |
57 | The next step depends on what should happen if we try to get a value while the
58 | config is still loading. If it should wait for loading to finish:
59 |
60 | ```javascript
61 | foo_config.get('colour').then(function(colour) {
62 | // do things with colour
63 | });
64 | ```
65 |
66 | If it should use the default instead of waiting:
67 |
68 | ```javascript
69 | colour = foo_config.get_sync('colour');
70 | ```
71 |
72 | ## Setting configuration
73 |
74 | To set a single value:
75 |
76 | ```javascript
77 | foo_config.set('visible', false);
78 | ```
79 |
80 | This is also asynchronous: it sends the new value off, but doesn't wait for a
81 | reply.
82 |
83 | ## Examples
84 |
85 | The notebook itself uses this confuguration system. Remember that to enable
86 | our extension, we did this:
87 |
88 | ```javascript
89 | IPython.notebook.config.update({
90 | "load_extensions": {"hello-scipy":true}
91 | })
92 | ```
93 |
94 | `IPython.notebook.config` is a `ConfigSection` object. It stores a dictionary
95 | of extensions to enable. When you open a notebook, it loads that config, and
96 | loads the extensions specified.
97 |
98 | Storing these as a dictionary (unordered) makes it simpler to add and remove
99 | extensions from the set (`.update()` does a recursive dictionary update).
100 |
101 | [cite2c](https://github.com/takluyver/cite2c) also uses this config system to
102 | store user data.
103 |
--------------------------------------------------------------------------------
/Part1/05-keyboardshortcut.md:
--------------------------------------------------------------------------------
1 | # Keyboard Shortcuts
2 |
3 | Fair warning: All these APIs are unstable, and can change at any time.
4 |
5 | Through Javascript you can access the keyboard manager.
6 |
7 | The keyboard manager maps (some) shortcuts in command and edit mode to 'actions'.
8 |
9 | A shortcut is a string that represents a sequence of several key presses. Commas separate each step of the sequence, and dashes separate keys that have to be pressed together.
10 |
11 | For example `a,b,c,d` represent the succession of pressing the letters A, B, C and D without modifier.
12 | `Shift-a,b,c,d` will have only the `A` key pressed with shift modifier, and `Shift-a,Shift-b,Shift-c,Shift-d` represents holding shift and pressing `a,b,c,d` in order.
13 |
14 | We can bind some existing actions in command mode, using the javascript console (where `>` and `<` are in and out prompt):
15 |
16 |
17 | ```javascript
18 | > IPython.keyboard_manager.command_shortcuts.add_shortcut('Shift-k','ipython.move-selected-cell-up')
19 | < undefined
20 |
21 | > IPython.keyboard_manager.command_shortcuts.add_shortcut('Shift-j','ipython.move-selected-cell-down')
22 | < undefined
23 | ```
24 |
25 | To see the list of available actions, you can issue the following in the developer console:
26 |
27 | ```javascript
28 | > $.map(
29 | IPython.keyboard_manager.command_shortcuts.actions._actions,
30 | function(k,v){return v}
31 | )
32 |
33 | < ["ipython.run-select-next",
34 | "ipython.execute-in-place",
35 | "ipython.execute-and-insert-after",
36 | "ipython.go-to-command-mode",
37 | ...
38 | "ipython.move-cursor-down-or-next-cell",
39 | "ipython.scroll-down", "ipython.scroll-up",
40 | "ipython.save-notebook"]
41 | ```
42 |
43 | > Tip: Use `Alt-Enter` for entering multiline text on Chrome.
44 |
45 | ## Actions
46 |
47 | In the previous section, you actually bound a keyboard shortcut to an action.
48 |
49 | You are most likely to want the same behavior as other users and/or to have buttons or menu do the same things as keyboard shortcut.
50 |
51 | Also, if like me you are not a huge fan of Javascript, you prefer to avoid rewriting anonymous function in your config file.
52 |
53 | An action is, in its simplest form, a name given to a sequence of API calls done in the notebook frontend. Some action are already pre-defined in Jupyter/IPython, and we prefix their name by `ipython`. Extensions can also register their own actions to be used.
54 | The API and naming of actions is still in flux, so what you read here is still approximate.
55 |
56 | As of this writing, an action is a combination of a `handler`, an `icon` and a `help_text`. The handler is a JavaScript function, that would be called in the right context when the action is triggered.
57 | The `help_text` and `icon` are extra meta-data that are used in various context. For example, if you add an action to a toolbar a button will be created. The icon will automatically be applied to the button, and the help text will appear on hover. Actions also have the capacity to call sub-actions. This make the combination of multiple repetitive tasks easy to customize.
58 |
59 | The quick way to bind a function to a shortcut is to use an anonymous action:
60 |
61 | ```javascript
62 | IPython.keyboard_manager.command_shortcuts.add_shortcut(
63 | 'Ctrl-C,Meta-C,Meta-b,u,t,t,e,r,f,l,y',
64 | {
65 | handler:function(){
66 | window.open('https://xkcd.com/378/')
67 | }
68 | }
69 | )
70 | ```
71 |
72 | This is a bit hard to type quickly enough, but _Real Programmer_
73 | should probably understand how to use this.
74 |
75 | ### Defining an action
76 |
77 | Le's see how to define an action. You can pick and icon from [the Font Awesome website](http://fortawesome.github.io/Font-Awesome/icons/). Try to avoid also action name to have space.
78 |
79 | We will use the following API to register an action:
80 |
81 | ```javascript
82 | IPython.keyboard_manager.actions.register
83 | ```
84 |
85 | Let's define an action that clear call cells
86 | output and restart the kernel.
87 |
88 | ```javascript
89 | var clear_all_cells_restart = {
90 | help: 'Clear all cells and restart kernel without confirmations',
91 | icon : 'fa-recycle',
92 | help_index : '',
93 | handler : function (env) {
94 | var on_success = undefined;
95 | var on_error = undefined;
96 | env.notebook.clear_all_output();
97 | env.notebook.kernel(on_success, on_error);
98 | }
99 | }
100 | ```
101 |
102 | And now we can register it. The first argument of the `register` function is the action itself,
103 | the second one is the name for the action, and the third one is a namespace prefix (we will use scipy-2015).
104 |
105 | The register function return an id that refer to this action.
106 |
107 | ```javascript
108 | IPython.keyboard_manager.actions.register(clear_all_cell_restart, 'clear-all-cells-restart', 'scipy-2015')
109 | ```
110 |
--------------------------------------------------------------------------------
/Part1/extensions/hello-scipy-full.js:
--------------------------------------------------------------------------------
1 | define(['base/js/namespace','base/js/dialog','jquery', 'nbextensions/markcell'],function(IPython, dialog, $, mc){
2 |
3 | var do_restart = function(env){
4 | env.notebook.clear_all_output();
5 | env.notebook.kernel.restart(function(){
6 | setTimeout(function(){ // wait 1 sec,
7 | // todo listen on Kernel ready event.
8 | env.notebook.execute_all_cells()
9 | }, 1000)
10 | }, on_error);
11 | }
12 |
13 | // we will define an action here that should happen when we ask to clear and restart the kernel.
14 | var clear_restart_reason = {
15 | help: 'Clear all cell and restart kernel asking and login reason',
16 | icon : 'fa-recycle',
17 | help_index : '',
18 | handler : function (env) {
19 | var on_success = undefined;
20 | var on_error = undefined;
21 |
22 | var p = $('').text("Please enter why do yourestart kernel ?")
23 | var input = $('