Skip to content

Commit

Permalink
Merge pull request quantopian#49 from blink1073/allow-update
Browse files Browse the repository at this point in the history
Allow update of Grid
  • Loading branch information
TimShawver committed Jan 12, 2016
2 parents 2f6be59 + cc58b5d commit 7a3a5e6
Show file tree
Hide file tree
Showing 4 changed files with 115 additions and 52 deletions.
5 changes: 3 additions & 2 deletions qgrid/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
from .grid import (
set_defaults,
set_grid_option,
show_grid
show_grid,
QGridWidget
)


Expand Down Expand Up @@ -51,5 +52,5 @@ def nbinstall(overwrite=False, user=True):
**({'user': user} if version_info >= (3, 0, 0, '') else {})
)

__all__ = ['set_defaults', 'set_grid_option', 'show_grid']
__all__ = ['set_defaults', 'set_grid_option', 'show_grid', 'QGridWidget']

109 changes: 75 additions & 34 deletions qgrid/grid.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@
from IPython.html import widgets
from IPython.display import display, Javascript
try:
from traitlets import Unicode, Instance, Bool, Integer
from traitlets import Unicode, Instance, Bool, Integer, Dict, List
except ImportError:
from IPython.utils.traitlets import Unicode, Instance, Bool, Integer
from IPython.utils.traitlets import (
Unicode, Instance, Bool, Integer, Dict, List
)


def template_contents(filename):
Expand All @@ -31,6 +33,7 @@ def template_contents(filename):
SLICK_GRID_JS = template_contents('slickgrid.js.template')
REMOTE_URL = ("https://cdn.rawgit.com/quantopian/qgrid/"
"master/qgrid/qgridjs/")
LOCAL_URL = "/nbextensions/qgridjs"


class _DefaultSettings(object):
Expand All @@ -40,6 +43,7 @@ def __init__(self):
'fullWidthRows': True,
'syncColumnCellResize': True,
'forceFitColumns': True,
'defaultColumnWidth': 150,
'rowHeight': 28,
'enableColumnReorder': False,
'enableTextSelectionOnCells': True,
Expand Down Expand Up @@ -74,6 +78,7 @@ def precision(self):

defaults = _DefaultSettings()


def set_defaults(remote_js=None, precision=None, grid_options=None):
"""
Set the default qgrid options. The options that you can set here are the
Expand All @@ -95,6 +100,7 @@ def set_defaults(remote_js=None, precision=None, grid_options=None):
"""
defaults.set_defaults(remote_js, precision, grid_options)


def set_grid_option(optname, optvalue):
"""
Set the default value for one of the options that gets passed into the
Expand All @@ -115,7 +121,8 @@ def set_grid_option(optname, optvalue):
<https://github.com/mleibman/SlickGrid/wiki/Grid-Options>`_ for the full
list of available options.
"""
self._grid_options[optname] = optvalue
defaults.grid_options[optname] = optvalue


def show_grid(data_frame, remote_js=None, precision=None, grid_options=None,
show_toolbar=False):
Expand Down Expand Up @@ -172,20 +179,23 @@ def show_grid(data_frame, remote_js=None, precision=None, grid_options=None,
remote_js = defaults.remote_js
if precision is None:
precision = defaults.precision
if not isinstance(precision, Integral):
raise TypeError("precision must be int, not %s" % type(precision))
if not isinstance(precision, Integral):
raise TypeError("precision must be int, not %s" % type(precision))
if grid_options is None:
grid_options = defaults.grid_options
if not isinstance(grid_options, dict):
raise TypeError(
"grid_options must be dict, not %s" % type(grid_options)
)
else:
options = defaults.grid_options.copy()
options.update(grid_options)
grid_options = options
if not isinstance(grid_options, dict):
raise TypeError(
"grid_options must be dict, not %s" % type(grid_options)
)

# create a visualization for the dataframe
grid = QGridWidget(df=data_frame, precision=precision,
grid_options=json.dumps(grid_options),
grid_options=grid_options,
remote_js=remote_js)
grid.update_table()

if show_toolbar:
add_row = widgets.Button(description="Add Row")
Expand All @@ -208,21 +218,49 @@ class QGridWidget(widgets.DOMWidget):
_df_json = Unicode('', sync=True)
_column_types_json = Unicode('', sync=True)
_index_name = Unicode('')
_cdn_base_url = Unicode("/nbextensions/qgridjs", sync=True)
_initialized = Bool(False)
_dirty = Bool(False)
_cdn_base_url = Unicode(LOCAL_URL, sync=True)
_multi_index = Bool(False)
_selected_rows = List()

df = Instance(pd.DataFrame)
precision = Integer(6)
grid_options = Unicode('', sync=True)
remote_js = Bool(True)
grid_options = Dict(sync=True)
remote_js = Bool(False)

def __init__(self, *args, **kwargs):
"""Initialize all variables before building the table."""
self._initialized = False
super(QGridWidget, self).__init__(*args, **kwargs)
# register a callback for custom messages
self.on_msg(self._handle_qgrid_msg)
self._initialized = True
self._selected_rows = []
if self.df is not None:
self._update_table()

def _grid_options_default(self):
return defaults.grid_options

def _remote_js_default(self):
return defaults.remote_js

def _precision_default(self):
return defaults.precision

def update_table(self):
def _df_changed(self):
"""Build the Data Table for the DataFrame."""
if not self._initialized:
return
self._update_table()
self.send({'type': 'draw_table'})

def _update_table(self):
df = self.df.copy()

# register a callback for custom messages
self.on_msg(self._handle_qgrid_msg)
if not df.index.name:
df.index.name = 'Index'

if type(df.index) == pd.core.index.MultiIndex:
df.reset_index(inplace=True)
Expand All @@ -231,10 +269,7 @@ def update_table(self):
df.insert(0, df.index.name, df.index)
self._multi_index = False

if not df.index.name:
df.index.name = 'Index'

self._index_name = df.index.name
self._index_name = df.index.name or 'Index'

tc = dict(np.typecodes)
for key in np.typecodes.keys():
Expand Down Expand Up @@ -265,14 +300,8 @@ def update_table(self):
date_format='iso',
double_precision=self.precision,
)

self._remote_js_changed()

def _remote_js_changed(self):
if self.remote_js:
self._cdn_base_url = REMOTE_URL
else:
self._cdn_base_url = "/nbextensions/qgridjs"
self._cdn_base_url = REMOTE_URL if self.remote_js else LOCAL_URL
self._dirty = False

def add_row(self, value=None):
"""Append a row at the end of the dataframe."""
Expand All @@ -291,6 +320,7 @@ def add_row(self, value=None):
msg[self._index_name] = str(last.name)
msg['id'] = str(last.name)
msg['type'] = 'add_row'
self._dirty = True
self.send(msg)

def remove_row(self, value=None):
Expand All @@ -307,27 +337,38 @@ def _handle_qgrid_msg(self, widget, content, buffers=None):
return
if content['type'] == 'remove_row':
self.df.drop(content['row'], inplace=True)
self._dirty = True

elif content['type'] == 'cell_change':
try:
self.df.set_value(self.df.index[content['row']],
content['column'], content['value'])
self._dirty = True
except ValueError:
pass

elif content['type'] == 'selection_change':
self._selected_rows = content['rows']
print(self._selected_rows)

def get_selected_rows(self):
"""Get the currently selected rows"""
return self._selected_rows

def export(self, value=None):
self.update_table()
self.remote_js = True
if self._dirty:
self._update_table()
base_url = REMOTE_URL
div_id = str(uuid.uuid4())
grid_options = json.loads(self.grid_options)
grid_options = self.grid_options
grid_options['editable'] = False

raw_html = SLICK_GRID_CSS.format(
div_id=div_id,
cdn_base_url=self._cdn_base_url,
cdn_base_url=base_url,
)
raw_js = SLICK_GRID_JS.format(
cdn_base_url=self._cdn_base_url,
cdn_base_url=base_url,
div_id=div_id,
data_frame_json=self._df_json,
column_types_json=self._column_types_json,
Expand Down
1 change: 1 addition & 0 deletions qgrid/qgridjs/qgrid.css
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
.q-grid-container {
position: relative;
margin: 4px -5px;
overflow-x: scroll;
}

.q-grid {
Expand Down
52 changes: 36 additions & 16 deletions qgrid/qgridjs/qgrid.widget.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ define([path + "widgets/js/widget", path + "widgets/js/manager"], function(widge
"<link id='dg-css' href='" + cdn_base_url + "/qgrid.css' rel='stylesheet'>"
]);
}

var path_dictionary = {
jquery_drag: cdn_base_url + "/lib/jquery.event.drag-2.2",
slick_core: cdn_base_url + "/lib/slick.core.2.2",
Expand Down Expand Up @@ -54,11 +54,11 @@ define([path + "widgets/js/widget", path + "widgets/js/manager"], function(widge
require.config({
paths: path_dictionary
});

if (typeof jQuery === 'function') {
define('jquery', function() { return jQuery; });
}

/**
* Load the required scripts and create the grid.
*/
Expand All @@ -72,38 +72,51 @@ define([path + "widgets/js/widget", path + "widgets/js/manager"], function(widge
],
function() {
require(['slick_grid'], function() {
require(["data_grid", "editors"],
function(dgrid, editors) {
that.setupQGrid(dgrid, editors);
});
require(["data_grid", "editors"],function(dgrid, editors) {
that.setupTable(dgrid, editors);
that.drawTable();
}
);
});
});
},

/**
* Set up our QGrid and event handlers.
* Set up the table div.
*/
setupQGrid: function(dgrid, editors) {
var that = this;
setupTable: function(dgrid, editors) {
this.dgrid = dgrid;
this.editors = editors;
// subscribe to incoming messages from the QGridWidget
this.model.on('msg:custom', this.handleMsg, this);

// set up the divs and styles
this.$el.addClass('q-grid-container');
var table = this.$el.append('div');
table.addClass('q-grid');

this.tableDiv = table[0];

// fill the portion of the widget area not in the prompt
var parent = this.el.parentElement;
while (parent.className !== 'widget-area') {
parent = parent.parentElement;
}
var width = (parent.clientWidth - parent.childNodes[0].clientWidth);
this.el.setAttribute("style", "max-width:" + String(width) + "px;");
},

/**
* Set up our QGrid and event handlers.
*/
drawTable: function() {
var that = this;
var editors = this.editors;

// create the table
var df = JSON.parse(this.model.get('_df_json'));
var column_types = JSON.parse(this.model.get('_column_types_json'));
var options = JSON.parse(this.model.get('grid_options'));
grid = new dgrid.QGrid(table[0], df, column_types);
var options = this.model.get('grid_options');
grid = new this.dgrid.QGrid(this.tableDiv, df, column_types);
grid.initialize_slick_grid(options);

// set up editing
Expand Down Expand Up @@ -133,10 +146,13 @@ define([path + "widgets/js/widget", path + "widgets/js/manager"], function(widge
that.send(msg);
});

// subscribe to incoming messages from the QGridWidget
this.model.on('msg:custom', this.handleMsg, this);
sgrid.onSelectedRowsChanged.subscribe(function(e, args) {
var rows = args.rows;
var msg = {'rows': args.rows, 'type': 'selection_change'};
that.send(msg);
});
},

/**
* Handle messages from the QGridWidget.
*/
Expand All @@ -160,6 +176,10 @@ define([path + "widgets/js/widget", path + "widgets/js/manager"], function(widge
dd.refresh();
this.updateSize();
this.send(msg);

} else if (msg.type === 'draw_table') {
this.drawTable();
this.updateSize();
}
},

Expand Down

0 comments on commit 7a3a5e6

Please sign in to comment.