notebook.rst 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  1. Notebook
  2. ========
  3. Background
  4. ----------
  5. .. _architecture-walkthrough:
  6. A JupyterLab architecture walkthrough from June 16, 2016, provides an overview of the notebook architecture.
  7. .. raw:: html
  8. <div class="jp-youtube-video">
  9. <iframe src="https://www.youtube-nocookie.com/embed/4Qm6oD_Rlw8?rel=0&amp;showinfo=0&amp;start=3326" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen></iframe>
  10. </div>
  11. The most complicated plugin included in the **JupyterLab application**
  12. is the **Notebook plugin**.
  13. The
  14. `NotebookWidgetFactory <../api/classes/notebook.notebookwidgetfactory-1.html>`__
  15. constructs a new
  16. `NotebookPanel <../api/classes/notebook.notebookpanel-1.html>`__
  17. from a model and populates the toolbar with default widgets.
  18. Structure of the Notebook plugin
  19. --------------------------------
  20. The Notebook plugin provides a model and widgets for dealing with
  21. notebook files.
  22. Model
  23. ^^^^^
  24. The
  25. `NotebookModel <../api/classes/notebook.notebookmodel-1.html>`__
  26. contains an observable list of cells.
  27. A `cell
  28. model <../api/classes/cells.cellmodel-1.html>`__
  29. can be:
  30. - a code cell
  31. - a markdown cell
  32. - raw cell
  33. A code cell contains a list of **output models**. The list of cells and
  34. the list of outputs can be observed for changes.
  35. Cell operations
  36. """""""""""""""
  37. The NotebookModel cell list supports single-step operations such as
  38. moving, adding, or deleting cells. Compound cell list operations, such
  39. as undo/redo, are also supported by the NotebookModel. Right now,
  40. undo/redo is only supported on cells and is not supported on notebook
  41. attributes, such as notebook metadata. Currently, undo/redo for
  42. individual cell input content is supported by the CodeMirror editor's
  43. undo feature. (Note: CodeMirror editor's undo does not cover cell
  44. metadata changes.)
  45. Metadata
  46. """"""""
  47. The notebook model and the cell model (i.e. notebook cells) support
  48. getting and setting metadata through an
  49. `IObservableJSON <../api/modules/observables.iobservablejson.html>`__
  50. object. You can use this to get and set notebook/cell metadata,
  51. as well as subscribe to changes to it.
  52. Notebook widget
  53. ^^^^^^^^^^^^^^^
  54. After the NotebookModel is created, the NotebookWidgetFactory constructs
  55. a new NotebookPanel from the model. The NotebookPanel widget is added to
  56. the DockPanel. The **NotebookPanel** contains:
  57. - a
  58. `Toolbar <../api/classes/apputils.toolbar-1.html>`__
  59. - a `Notebook
  60. widget <../api/classes/notebook.notebook-2.html>`__.
  61. The NotebookPanel also adds completion logic.
  62. The **NotebookToolbar** maintains a list of widgets to add to the
  63. toolbar. The **Notebook widget** contains the rendering of the notebook
  64. and handles most of the interaction logic with the notebook itself (such
  65. as keeping track of interactions such as selected and active cells and
  66. also the current edit/command mode).
  67. The NotebookModel cell list provides ways to do fine-grained changes to
  68. the cell list.
  69. Higher level actions using NotebookActions
  70. """"""""""""""""""""""""""""""""""""""""""
  71. Higher-level actions are contained in the
  72. `NotebookActions <../api/classes/notebook.notebookactions-1.html>`__
  73. namespace, which has functions, when given a notebook widget, to run a
  74. cell and select the next cell, merge or split cells at the cursor,
  75. delete selected cells, etc.
  76. Widget hierarchy
  77. """"""""""""""""
  78. A Notebook widget contains a list of `cell
  79. widgets <../api/classes/cells.cell-1.html>`__,
  80. corresponding to the cell models in its cell list.
  81. - Each cell widget contains an
  82. `InputArea <../api/classes/cells.inputarea-1.html>`__,
  83. - which contains n
  84. `CodeEditorWrapper <../api/classes/codeeditor.codeeditorwrapper-1.html>`__,
  85. - which contains a JavaScript CodeMirror instance.
  86. A
  87. `CodeCell <../api/classes/cells.codecell-1.html>`__
  88. also contains an
  89. `OutputArea <../api/classes/outputarea.outputarea-2.html>`__.
  90. An OutputArea is responsible for rendering the outputs in the
  91. `OutputAreaModel <../api/classes/outputarea.outputareamodel-1.html>`__
  92. list. An OutputArea uses a notebook-specific
  93. `RenderMimeRegistry <../api/classes/rendermime.rendermimeregistry-1.html>`__
  94. object to render ``display_data`` output messages.
  95. Rendering output messages
  96. """""""""""""""""""""""""
  97. A **Rendermime plugin** provides a pluggable system for rendering output
  98. messages. Default renderers are provided for markdown, html, images,
  99. text, etc. Extensions can register renderers to be used across the
  100. entire application by registering a handler and mimetype in the
  101. rendermime registry. When a notebook is created, it copies the global
  102. Rendermime singleton so that notebook-specific renderers can be added.
  103. The ipywidgets widget manager is an example of an extension that adds a
  104. notebook-specific renderer, since rendering a widget depends on
  105. notebook-specific widget state.
  106. .. _extend-notebook-plugin:
  107. How to extend the Notebook plugin
  108. ---------------------------------
  109. We'll walk through two notebook extensions:
  110. - adding a button to the toolbar
  111. - adding a widget to the notebook header
  112. - adding an ipywidgets extension
  113. Adding a button to the toolbar
  114. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  115. Since JupyterLab 3.2, adding toolbar item can be done using a :ref:`toolbar-registry` and settings. In particular
  116. for the notebook, if the button is linked to a new command, you can add a button in the toolbar using the
  117. following JSON snippet in your extension settings file:
  118. .. code:: js
  119. "jupyter.lab.toolbars": {
  120. "Notebook": [ // Widget factory name for which you want to add a toolbar item.
  121. // Item with default button widget triggering a command
  122. { "name": "run", "command": "runmenu:run" }
  123. ]
  124. }
  125. You may add a ``rank`` attribute to modify the item position (the default value is 50).
  126. Adding a widget to the notebook header
  127. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  128. Start from the cookie cutter extension template.
  129. ::
  130. pip install cookiecutter
  131. cookiecutter https://github.com/jupyterlab/extension-cookiecutter-ts
  132. cd my_cookie_cutter_name
  133. Install the dependencies. Note that extensions are built against the
  134. released npm packages, not the development versions.
  135. ::
  136. jlpm add -D @jupyterlab/notebook @jupyterlab/application @jupyterlab/ui-components @jupyterlab/docregistry @lumino/disposable @lumino/widgets --legacy-peer-deps
  137. Copy the following to ``src/index.ts``:
  138. .. code:: typescript
  139. import { IDisposable, DisposableDelegate } from '@lumino/disposable';
  140. import { Widget } from '@lumino/widgets';
  141. import {
  142. JupyterFrontEnd,
  143. JupyterFrontEndPlugin
  144. } from '@jupyterlab/application';
  145. import {
  146. DocumentRegistry
  147. } from '@jupyterlab/docregistry';
  148. import { NotebookPanel, INotebookModel } from '@jupyterlab/notebook';
  149. /**
  150. * The plugin registration information.
  151. */
  152. const plugin: JupyterFrontEndPlugin<void> = {
  153. activate,
  154. id: 'my-extension-name:widgetPlugin',
  155. autoStart: true
  156. };
  157. /**
  158. * A notebook widget extension that adds a widget in the notebook header (widget below the toolbar).
  159. */
  160. export class WidgetExtension
  161. implements DocumentRegistry.IWidgetExtension<NotebookPanel, INotebookModel>
  162. {
  163. /**
  164. * Create a new extension object.
  165. */
  166. createNew(
  167. panel: NotebookPanel,
  168. context: DocumentRegistry.IContext<INotebookModel>
  169. ): IDisposable {
  170. const widget = new Widget({ node: Private.createNode() });
  171. widget.addClass('jp-myextension-myheader');
  172. panel.contentHeader.insertWidget(0, widget);
  173. return new DisposableDelegate(() => {
  174. widget.dispose();
  175. });
  176. }
  177. }
  178. /**
  179. * Activate the extension.
  180. */
  181. function activate(app: JupyterFrontEnd): void {
  182. app.docRegistry.addWidgetExtension('Notebook', new WidgetExtension());
  183. }
  184. /**
  185. * Export the plugin as default.
  186. */
  187. export default plugin;
  188. /**
  189. * Private helpers
  190. */
  191. namespace Private {
  192. /**
  193. * Generate the widget node
  194. */
  195. export function createNode(): HTMLElement {
  196. const span = document.createElement('span');
  197. span.textContent = 'My custom header';
  198. return span;
  199. }
  200. }
  201. And the following to ``style/base.css``:
  202. .. code:: css
  203. .jp-myextension-myheader {
  204. min-height: 20px;
  205. background-color: lightsalmon;
  206. }
  207. Run the following commands:
  208. ::
  209. pip install -e .
  210. pip install jupyter-packaging
  211. jupyter labextension develop . --overwrite
  212. jupyter lab
  213. Open a notebook and observe the new "Header" widget.
  214. The *ipywidgets* third party extension
  215. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  216. This discussion will be a bit confusing since we've been using the term
  217. *widget* to refer to *lumino widgets*. In the discussion below,
  218. *ipython widgets* will be referred to as *ipywidgets*. There is no
  219. intrinsic relation between *lumino widgets* and *ipython widgets*.
  220. The *ipywidgets* extension registers a factory for a notebook *widget*
  221. extension using the `Document
  222. Registry <../api/classes/docregistry.documentregistry-1.html>`__.
  223. The ``createNew()`` function is called with a NotebookPanel and
  224. `DocumentContext <../api/interfaces/docregistry.documentregistry.icontext.html>`__.
  225. The plugin then creates a ipywidget manager (which uses the context to
  226. interact the kernel and kernel's comm manager). The plugin then
  227. registers an ipywidget renderer with the notebook instance's rendermime
  228. (which is specific to that particular notebook).
  229. When an ipywidget model is created in the kernel, a comm message is sent
  230. to the browser and handled by the ipywidget manager to create a
  231. browser-side ipywidget model. When the model is displayed in the kernel,
  232. a ``display_data`` output is sent to the browser with the ipywidget
  233. model id. The renderer registered in that notebook's rendermime is asked
  234. to render the output. The renderer asks the ipywidget manager instance
  235. to render the corresponding model, which returns a JavaScript promise.
  236. The renderer creates a container *lumino widget* which it hands back
  237. synchronously to the OutputArea, and then fills the container with the
  238. rendered *ipywidget* when the promise resolves.