Skip to content

Commit 53bd229

Browse files
author
Tim Shawver
committed
Use a callback function to determine whether a row should be editable or not.
1 parent 7d607f7 commit 53bd229

File tree

3 files changed

+41
-57
lines changed

3 files changed

+41
-57
lines changed

js/src/qgrid.widget.js

Lines changed: 6 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,6 @@ class QgridView extends widgets.DOMWidgetView {
204204
this.data_view = this.create_data_view(df_json.data);
205205
this.grid_options = this.model.get('grid_options');
206206
this.column_definitions = this.model.get('column_definitions');
207-
this.row_edit_conditions = this.model.get('row_edit_conditions');
208207
this.index_col_name = this.model.get("_index_col_name");
209208

210209
this.columns = [];
@@ -483,58 +482,12 @@ class QgridView extends widgets.DOMWidgetView {
483482
});
484483

485484
// set up callbacks
486-
487-
// evaluate conditions under which cells in a row should be disabled (contingent on values of other cells in the same row)
488-
var evaluateRowEditConditions = function(current_row, obj) {
489-
var result;
490-
491-
for (var op in obj) {
492-
if (op == 'AND') {
493-
if (result == null) {
494-
result = true;
495-
}
496-
for (var cond in obj[op]) {
497-
if (cond == 'AND' || cond == 'OR' || cond == 'NOT') {
498-
result = result && evaluateRowEditConditions(current_row, {[cond]: obj[op][cond]});
499-
} else {
500-
result = result && (current_row[cond] == obj[op][cond]);
501-
}
502-
}
503-
} else if (op == 'OR') {
504-
if (result == null) {
505-
result = false;
506-
}
507-
var or_result = false;
508-
for (var cond in obj[op]) {
509-
if (cond == 'AND' || cond == 'OR' || cond == 'NAND' || cond == 'NOR') {
510-
result = result || evaluateRowEditConditions(current_row, {[cond]: obj[op][cond]});
511-
} else {
512-
result = result || (current_row[cond] == obj[op][cond]);
513-
}
514-
}
515-
} else if (op == 'NAND') {
516-
if (result == null) {
517-
result = true;
518-
}
519-
result = result && !evaluateRowEditConditions(current_row, {'AND': obj[op]});
520-
} else if (op == 'NOR') {
521-
if (result == null) {
522-
result = false;
523-
}
524-
result = result || !evaluateRowEditConditions(current_row, {'OR': obj[op]});
525-
} else {
526-
alert("Unsupported operation '" + op + "' found in row edit conditions!")
527-
}
528-
}
529-
return result;
530-
}
531-
532-
if ( ! (this.row_edit_conditions == null)) {
533-
var conditions = this.row_edit_conditions;
534-
var grid = this.slick_grid;
535-
this.slick_grid.onBeforeEditCell.subscribe(function(e, args) {
536-
return evaluateRowEditConditions(grid.getDataItem(args.row), conditions);
537-
});
485+
let editable_rows = this.model.get('_editable_rows');
486+
if (editable_rows && Object.keys(editable_rows).length > 0) {
487+
this.slick_grid.onBeforeEditCell.subscribe((e, args) => {
488+
editable_rows = this.model.get('_editable_rows');
489+
return editable_rows[args.item[this.index_col_name]]
490+
});
538491
}
539492

540493
this.slick_grid.onCellChange.subscribe((e, args) => {

qgrid/grid.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import numpy as np
44
import json
55

6+
from types import FunctionType
67
from IPython.display import display
78
from numbers import Integral
89
from traitlets import Unicode, Instance, Bool, Integer, Dict, List, Tuple, Any
@@ -179,7 +180,7 @@ def disable():
179180
def show_grid(data_frame, show_toolbar=None,
180181
precision=None, grid_options=None,
181182
column_options=None, column_definitions=None,
182-
row_edit_conditions=None):
183+
row_edit_callback=None):
183184
"""
184185
Renders a DataFrame or Series as an interactive qgrid, represented by
185186
an instance of the ``QgridWidget`` class. The ``QgridWidget`` instance
@@ -234,15 +235,14 @@ def show_grid(data_frame, show_toolbar=None,
234235
"data_frame must be DataFrame or Series, not %s" % type(data_frame)
235236
)
236237

237-
row_edit_conditions = (row_edit_conditions or {})
238238
column_definitions = (column_definitions or {})
239239

240240
# create a visualization for the dataframe
241241
return QgridWidget(df=data_frame, precision=precision,
242242
grid_options=grid_options,
243243
column_options=column_options,
244244
column_definitions=column_definitions,
245-
row_edit_conditions=row_edit_conditions,
245+
row_edit_callback=row_edit_callback,
246246
show_toolbar=show_toolbar)
247247

248248

@@ -366,6 +366,7 @@ class QgridWidget(widgets.DOMWidget):
366366
_df_json = Unicode('', sync=True)
367367
_primary_key = List()
368368
_columns = Dict({}, sync=True)
369+
_editable_rows = Dict({}, sync=True)
369370
_filter_tables = Dict({})
370371
_sorted_column_cache = Dict({})
371372
_interval_columns = List([], sync=True)
@@ -391,7 +392,7 @@ class QgridWidget(widgets.DOMWidget):
391392
grid_options = Dict(sync=True)
392393
column_options = Dict(sync=True)
393394
column_definitions = Dict({})
394-
row_edit_conditions = Dict(sync=True)
395+
row_edit_callback = Instance(FunctionType, sync=False, allow_none=True)
395396
show_toolbar = Bool(False, sync=True)
396397

397398
def __init__(self, *args, **kwargs):
@@ -573,6 +574,13 @@ def _update_table(self,
573574
double_precision=self.precision)
574575

575576
self._df_json = df_json
577+
578+
if self.row_edit_callback is not None:
579+
editable_rows = {}
580+
for index, row in df.iterrows():
581+
editable_rows[int(row[self._index_col_name])] = self.row_edit_callback(row)
582+
self._editable_rows = editable_rows
583+
576584
if fire_data_change_event:
577585
data_to_send = {
578586
'type': 'update_data_view',

qgrid/tests/test_grid.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,29 @@ def test_nans():
131131
'search_val': None
132132
})
133133

134+
def test_row_edit_callback():
135+
sample_df = create_df()
136+
137+
def can_edit_row(row):
138+
return row['E'] == 'train' and row['F'] == 'bar'
139+
140+
view = QgridWidget(df=sample_df, row_edit_callback=can_edit_row)
141+
142+
view._handle_qgrid_msg_helper({
143+
'type': 'sort_changed',
144+
'sort_field': 'index',
145+
'sort_ascending': True
146+
})
147+
148+
expected_dict = {
149+
0: False,
150+
1: True,
151+
2: False,
152+
3: False
153+
}
154+
155+
assert expected_dict == view._editable_rows
156+
134157
def test_period_object_column():
135158
range_index = pd.period_range(start='2000', periods=10, freq='B')
136159
df = pd.DataFrame({'a': 5, 'b': range_index}, index=range_index)

0 commit comments

Comments
 (0)