Migrating Widgets to IPython 3
Upgrading Notebooks
- The first thing you'll notice when upgrading an IPython 2.0 widget notebook to IPython 3.0 is the "Notebook converted" dialog. Click "ok".
- All of the widgets distributed with IPython have been renamed. The "Widget" suffix was removed from the end of the class name. i.e. ButtonWidget is now Button.
- ContainerWidget was renamed to Box.
- PopupWidget was removed from IPython, because bootstrapjs was problematic (creates global variables, etc.). If you use the PopupWidget, try using a Box widget instead. If your notebook can't live without the popup functionality, subclass the Box widget (both in Python and JS) and use JQuery UI's draggable() and resizable() methods to mimic the behavior.
- add_class and remove_class were removed. More often than not a new attribute exists on the widget that allows you to achieve the same explicitly. i.e. the Button widget now has a button_style attribute which you can set to 'primary', 'success', 'info', 'warning', 'danger', or '' instead of using add_class to add the bootstrap class. VBox and HBox classes (flexible Box subclasses) were added that allow you to avoid using add_class and remove_class to make flexible box model layouts. As a last resort, if you can't find a built in attribute for the class you want to use, a new _dom_classes list trait was added, which combines add_class and remove_class into one stateful list.
- set_css and get_css were removed in favor of explicit style attributes - visible, width, height, padding, margin, color, background_color, border_color, border_width, border_radius, border_style, font_style, font_weight, font_size, and font_family are a few. If you can't find a trait to see the css attribute you need, you can, in order of preference, (A) subclass to create your own custom widget, (B) use CSS and the _dom_classes trait to set _dom_classes, or (C) use the _css dictionary to set CSS styling like set_css and get_css.
- For selection widgets, such as Dropdown, the values argument has been renamed to options.
Upgrading Custom Widgets
Javascript
If you are distributing your widget and decide to use the deferred loading technique (preferred), you can remove all references to the WidgetManager and the register model/view calls (see the Python section below for more information).
In 2.0 require.js was used incorrectly, that has been fixed and now loading works more like Python's import. Requiring widgets/js/widget doesn't import the WidgetManager class, instead it imports a dictionary that exposes the classes within that module:
{ 'WidgetModel': WidgetModel, 'WidgetView': WidgetView, 'DOMWidgetView': DOMWidgetView, 'ViewList': ViewList, }
If you decide to continue to use the widget registry (by registering your widgets with the manager), you can import a dictionary with a handle to the WidgetManager class by requiring widgets/js/manager. Doing so will import:
{'WidgetManager': WidgetManager}
Don't rely on the IPython namespace for anything. To inherit from the DOMWidgetView, WidgetView, or WidgetModel, require widgets/js/widget as widget. If you were inheriting from DOMWidgetView, and the code looked like this:
IPython.DOMWidgetView.extend({...})
It would become this:
widget.DOMWidgetView.extend({...})
Custom models are encouraged. When possible, it's recommended to move your code into a custom model, so actions are performed 1 time, instead of N times where N is the number of displayed views.
Python
Generally, custom widget Python code can remain unchanged. If you distribute your custom widget, you may be using display and Javascript to publish the widget's Javascript to the front-end. That is no longer the recommended way of distributing widget Javascript. Instead have the user install the Javascript to his/her nbextension directory or their profile's static directory. Then use the new _view_module and _model_module traitlets in combination with _view_name and _model_name to instruct require.js on how to load the widget's Javascript. The Javascript is then loaded when the widget is used for the first time.
Details
Asynchronous
In the IPython 2.x series the only way to register custom widget views and models was to use the registry in the widget manager. Unfortunately, using this method made distributing and running custom widgets difficult. The widget maintainer had to either use the rich display framework to push the widget's Javascript to the notebook or instruct the users to install the Javascript by hand in a custom profile. With the first method, the maintainer would have to be careful about when the Javascript was pushed to the front-end. If the Javascript was pushed on Python widget import, the widgets wouldn't work after page refresh. This is because refreshing the page does not restart the kernel, and the Python import statement only runs once in a given kernel instance (unless you reload the Python modules, which isn't straight forward). This meant the maintainer would have to have a separate push_js() method that the user would have to call after importing the widget's Python code.
Our solution was to add support for loading widget views and models using require.js paths. Thus the comm and widget frameworks now support lazy loading. To do so, everything had to be converted to asynchronous code. HTML5 promises are used to accomplish that (#6818, #6914).
Symmetry
In IPython 3.0, widgets can be instantiated from the front-end (#6664). On top of this, a widget persistence API was added (#7163, #7227). With the widget persistence API, you can persist your widget instances using Javascript. This makes it easy to persist your widgets to your notebook document (with a small amount of custom JS). By default, the widgets are persisted to your web browsers local storage which makes them reappear when your refresh the page.
Smaller Changes
- Latex math is supported in widget descriptions (#5937).
- Widgets can be display more than once within a single container widget (#5963, #6990).
- FloatRangeSlider and IntRangeSlider were added (#6050).
- "Widget" was removed from the ends of all of the widget class names (#6125).
- ContainerWidget was renamed to Box (#6125).
- HBox and VBox widgets were added (#6125).
- add\_class and remove\_class were removed in favor of a _dom_classes list (#6235).
- get\_css and set\_css were removed in favor of explicit traits for widget styling (#6235).
- jslink and jsdlink were added (#6454, #7468).
- An Output widget was added, which allows you to print and display within widgets (#6670).
- PopupWidget was removed (#7341).
- A visual cue was added for widgets with 'dead' comms (#7227).
- A SelectMultiple widget was added (a Select widget that allows multiple things to be selected at once) (#6890).
- A class was added to help manage children views (#6990).
- A warning was added that shows on widget import because it's expected that the API will change again by IPython 4.0. This warning can be suppressed (#7107, #7200, #7201, #7204).
Comm and Widget PR Index
Here is a chronological list of PRs affecting the widget and comm frameworks for IPython 3.0. Note that later PRs may revert changes made in earlier PRs:
- Add placeholder attribute to text widgets #5652
- Add latex support in widget labels, #5937
- Allow widgets to display more than once within container widgets. #5963
- use require.js, #5980
- Range widgets #6050
- Interact on_demand option #6051
- Allow text input on slider widgets #6106
- support binary buffers in comm messages #6110
- Embrace the flexible box model in the widgets #6125
- Widget trait serialization #6128
- Make Container widgets take children as the first positional argument #6153
- once-displayed #6168
- Validate slider value, when limits change #6171
- Unregistering comms in Comm Manager #6216
- Add EventfulList and EventfulDict trait types. #6228
- Remove add/remove_class and set/get_css. #6235
- avoid unregistering widget model twice #6250
- Widget property lock should compare json states, not python states #6332
- Strip the IPY_MODEL_ prefix from widget IDs before referencing them. #6377
- "event" is not defined error in Firefox #6437
- Javascript link #6454
- Bulk update of widget attributes #6463
- Creating a widget registry on the Python side. #6493
- Allow widget views to be loaded from require modules #6494
- Fix Issue #6530 #6532
- Make comm manager (mostly) independent of InteractiveShell #6540
- Add semantic classes to top-level containers for single widgets #6609
- Selection Widgets: forcing 'value' to be in 'values' #6617
- Allow widgets to be constructed from Javascript #6664
- Output widget #6670
- Minor change in widgets.less to fix alignment issue #6681
- Make Selection widgets respect values order. #6747
- Widget persistence API #6789
- Add promises to the widget framework. #6818
- SelectMultiple widget #6890
- Tooltip on toggle button #6923
- Allow empty text box *while typing* for numeric widgets #6943
- Ignore failure of widget MathJax typesetting #6948
- Refactor the do_diff and manual child view lists into a separate ViewList object #6990
- Add warning to widget namespace import. #7107
- lazy load widgets #7120
- Fix padding of widgets. #7139
- Persist widgets across page refresh #7163
- Make the widget experimental error a real python warning #7200
- Make the widget error message shorter and more understandable. #7201
- Make the widget warning brief and easy to filter #7204
- Add visual cue for widgets with dead comms #7227
- Widget values as positional arguments #7260
- Remove the popup widget #7341
- document and validate link, dlink #7468
- Document interact 5637 #7525
- Update some broken examples of using widgets #7547
- Use Output widget with Interact #7554
- don't send empty execute_result messages #7560
- Validation on the python side #7602
- only show prompt overlay if there's a prompt #7661
- Allow predictate to be used for comparison in selection widgets #7674
- Fix widget view persistence. #7680
- Revert "Use Output widget with Interact" #7703