Enabled programmatic edits to grid and additional configuration options for columns and rows by vardaofthevalier · Pull Request #191 · quantopian/qgrid (original) (raw)

Hello! First of all I just want to say thanks for this great widget. I took it upon myself to add a few features that I thought would be useful for a few particular use cases for a project that I'm currently working on at my job, and was hoping to get some feedback on them and possibly contribute the changes back if they might useful to other qgrid users.

The rationale behind many of these features can be described in terms of two different needs: 1) the ability to programmatically update values in the grid while also avoiding completely resetting the entire dataframe (which causes some flickering of the UI when it is redrawn); and 2) conditionally and dynamically setting which parts of the grid can be edited at a given time. Below is a summary of the added functionality:

from qgrid import show_grid
import pandas as pd
from IPython.display import display

# row edit conditions grammar:
# ROW EDIT CONDITIONS => dict([ROW EDIT CONDITION])
# ROW EDIT CONDITION => (BOOLEAN OPERATOR, dict([(COLUMN NAME, COLUMN VALUE)]))
# BOOLEAN OPERATOR => AND | OR | NAND | NOR
# COLUMN NAME => <string>  
# COLUMN VALUE => <column dtype>
row_edit_conditions = {  # This set of conditions will cause any row where foo == 'hello' and bar == 'world' to have editing disabled
    'NAND': {
        'foo': 'hello',
        'bar': 'world'
    }
}

column_defs = {
    'foo': {
        'editable': True,
        'toolTip': "Editable"
    },
    'bar': {
        'editable': False,
        'toolTip': "Not editable"
    }
}

grid_options = {
    'boldIndex': False
}

df = pd.DataFrame({'foo': ['boo'], 'bar': ['world'], 'baz': [42], 'boo': [57]})
df.set_index('baz', inplace=True, drop=True)

q = show_grid(df, grid_options=grid_options, column_definitions=column_defs, row_edit_conditions=row_edit_conditions)
display(q)

# Verify that:
# - Tooltips for columns foo and bar are visible
# - Index column (baz) values are not bolded
# - Column bar is not editable
# - Column foo is editable
# - Row (index 42) is only editable until the value of foo is set to 'hello'

# A note on precedence of edit controls:
# - Toggle editable won't have an effect on columns or rows that have already had edits disabled via the column definitions or row edit conditions
# - Toggle editable also won't impact the ability to use the internal update methods (add_row_internally(), set_value_internally()) and only applies to manual edits

# Toggle editable -- running this once will disable edits to the grid, and running it again should reenable edits to the grid
q.send({'type': 'toggle_editable'})

# Add row internally
new_row = [
    ('baz', 43),
    ('bar', "new bar"),
    ('boo', 58), 
    ('foo', "new foo")
]
q.add_row_internally(new_row)

# Set value internally
q.set_value_internally(42, 'foo', 'hello')

This will be my first PR against an existing open-source project that I didn't create from scratch on my own, so please let me know if there are any glaring issues that need to be fixed before merging. I'm very interested in any feedback you may have and would be happy to make tweaks or to discuss improvements. Also, I have included unit tests for the internal update methods described above, but am a little stumped on how to write tests for the front-end parts that can only be observed through interaction. If anyone has any suggestions I will happily write more tests to cover those things as well.