%%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()