├── .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 | 3 | npm-logo 4 | Created with Sketch (http://www.bohemiancoding.com/sketch) 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /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 = $('