%%html from __future__ import print_function # For py 2.7 compat from IPython.html import widgets # Widget definitions from IPython.display import display # Used to display widgets in the notebook from IPython.utils.traitlets import List, Dict # Used to declare attributes of our widget class HandsonTableWidget(widgets.DOMWidget): _view_name = Unicode('HandsonTableView', sync=True) value = Unicode(sync=True) settings = Dict(sync=True) %%javascript ;(function(require){ "use strict"; var window = this; require([ "base/js/namespace", "widgets/js/manager", "widgets/js/widget", "underscore", "jquery", "http://handsontable.com/dist/jquery.handsontable.full.js" ], function(IPython, manager, widget, _, $){ // Just once, ensure that the scroll event is called on the window for HoT var $win = $(window); IPython.notebook.element.on("scroll", function(){ $win.scroll(); }); // Define the HandsonTableView var HandsonTableView = widget.DOMWidgetView.extend({ render: function(){ // CREATION OF THE WIDGET IN THE NOTEBOOK. // Add a
in the widget area. this.$table = $('
').appendTo(this.$el); var view = this; // Create the Handsontable table. this.$table.handsontable({ // when working in HoT, don't listen for command mode keys afterSelection: function(){ IPython.keyboard_manager.disable(); }, afterDeselect: function(){ IPython.keyboard_manager.enable(); }, // the data changed. `this` is the HoT instance afterChange: function(changes, source){ // don't update if we did the changing! if(source === "loadData"){ return; } view.handle_table_change(this.getData()); } }); }, update: function() { // PYTHON --> JS UPDATE. // Get the model's value (JSON) var json = this.model.get('value'); // Parse it into data (list of lists) var data = JSON.parse(json); // Give it to the Handsontable widget. this.$table.handsontable({data: data}); // Don't touch this... return HandsonTableView.__super__.update.apply(this); }, handle_table_change: function(data) { // JS --> PYTHON UPDATE. // Update the model with the JSON string. this.model.set('value', JSON.stringify(data)); // Don't touch this... this.touch(); } }); // Register the HandsonTableView with the widget manager. manager.WidgetManager.register_widget_view('HandsonTableView', HandsonTableView); }); }).call(this, require); import StringIO import numpy as np import pandas as pd class HandsonDataFrame(object): def __init__(self, df): self._df = df self._widget = HandsonTableWidget() self._widget.on_trait_change(self._on_data_changed, 'value') self._widget.on_displayed(self._on_displayed) def _on_displayed(self, e): # DataFrame ==> Widget (upon initialization only) json = self._df.to_json(orient='values') self._widget.value = json def _on_data_changed(self, e, val): # Widget ==> DataFrame (called every time the user # changes a value in the graphical widget) buf = StringIO.StringIO(val) self._df = pd.read_json(buf, orient='values') def to_dataframe(self): return self._df def show(self): display(self._widget) data = np.random.randint(size=(3, 5), low=100, high=900) df = pd.DataFrame(data) df ht = HandsonDataFrame(df) ht.show() ht.to_dataframe()