widgetti / ipyaggrid

Using ag-Grid in Jupyter notebooks.
MIT License
57 stars 15 forks source link

Reliable user selection real time listener #76

Open petkoivanov1 opened 1 month ago

petkoivanov1 commented 1 month ago

Hi, I used the notebook linked below to test a real-time selection listener in different modes (table grouped or not). In the process , I think I fixed 1 typo and a couple of bugs in grid.py

I still have one big issue : I cant get it to work with aggregated rows. Nothing fires at all. image Clicking anywhere on the Grouped columns (A in the screenshot) fires this: {'data': [[]], 'index_rows': {'names': ['Index'], 'values': [[None]]}, 'index_columns': []} Clicking on any other columns does not fire anything. ( Expanding the grouped row and clicking on leaf rows, works. ) Has any one been able to get that working?

https://github.com/petkoivanov1/ipyaggrid/blob/master/docs/notebooks/demo-ipyaggrid-selection-listener.ipynb

Thanks in advance,

P.S. here is a description of the fixes/ enhancements (from inside the notebook):

  1. ipyaggrid/ipyaggrid/grid.py : 241 should be 'range' not 'cols' ? I think so.
  2. to_df['columns'] = pd.MultiIndex.from_tuples(*[data_up['index_columns']]) does not handle one corner case well (selecting a grouped column) and is copy-pasted about 5 times over in grid.py

image

  1. additional .get_selected_columns() call after processing a 'rows' fire . With rangeSelectionEnabled, i get a 'range' fire, then a rows fire but never a 'cols' fire. I trigger it programatically after 'rows'. Seems to work well in ungrouped mode.
  2. refactored the monolithic 'export' method to smaller functions so it can be clearer what each piece does and can be overridden if needed.
  3. added grid_range_selection method for easy hook-up (in my test examples I just display on a separate Output widget).
petkoivanov1 commented 1 month ago

OK. After debugging all this python and JS code and some reverse engineering, you dont need most of the 'export' code in this project. That does not handle groups at all. Here is my super short code that works in ALL situations:

The screenshots dont capture well where I clicked, I have highlighted that manually in red. As you can see, you get the column, all row values, the node and path to the root if applicable. Its also fast, no errors are logged in JS or python in all aggrid variations I tested.

enjoy.

image

image

image

petkoivanov1 commented 1 month ago

Here is the jist of the code for convenience: `` js_post = """ function myRowClickedHandler(event) { //console.log('The row was clicked:', event);
let n = event.node; let tmp1 = {'column':event.column.colId,'value':event.value,'rowIndex':event.rowIndex, 'rowData':event.data,'node.aggData':n.aggData, 'node.expanded':n.expanded, 'node.field':n.field, 'node.group':n.group,'node.key': n.key, 'node.leafGroup':n.leafGroup,'node.level':n.level};
view.model.set('_grid_data_up', tmp1); for (p = n.parent; p.rowIndex !=null ;p = p.parent){ tmp1['parent'] = {'rowIndex': p.rowIndex, 'key':p.key, 'field':p.field} tmp1 = tmp1['parent'] }
view.touch(); } gridOptions.onCellClicked = myRowClickedHandler; """

def foo1(grid_options): out1 = widgets.Output() g1 = Grid(grid_data = data, grid_options=grid_options, theme='ag-theme-balham', js_post_grid=js_post) g1.export_mode = 'auto' def display1(grid1):
if 'grid' not in g1._grid_data_up.keys(): # i dont need the grid here and its a lot out1.clear_output() with out1: print (datetime.datetime.now())
print (g1._grid_data_up)

g1.observe(display1) return widgets.VBox([g1, out1]),g1

columnDefs = [ {'headerName': "Country", 'field': "country", 'rowGroup':True, 'hide':True}, {'headerName': "Sport", 'field': "sport", 'rowGroup':True, 'hide':True}, {'headerName': "Athlete", 'field': "athlete"}, {'headerName': "Age", 'field': "age", 'aggFunc':'min'}, {'headerName': "Total", 'field': "total", 'aggFunc':'sum'},
] # dont use enableRangeSelection. js/src/widget_export code does not work with groups and logs a lot of JS and python exceptions w1,g1 = foo1({'columnDefs': columnDefs,'defaultColDef': {'sortable': 'true', 'filter': 'true', 'resizable': 'true'}}) w1 ``