Way to edit the datatable directly? #627
amks1
started this conversation in
Feature request
Replies: 2 comments
-
Actually I planned to add a from typing import Union, List, Callable, Any, Sequence, Mapping, Tuple
from pywebio.output import put_datatable, JSFunction, output_register_callback
from pywebio.utils import random_str
def editable_datatable_inplace_update_callback(records: Sequence[Mapping]):
"""
Return a callback function that can be used in `put_editable_datatable()` to update the table data inplace.
All new data will be stored as string.
"""
def callback(row_id, field_path, new_value):
row = records[row_id]
for key in field_path[:-1]:
row = row[key]
row[field_path[-1]] = new_value
return callback
def put_editable_datatable(
records: Sequence[Mapping],
edit_callback: Callable[[Union[str, int], List[str], 'Any'], None],
editable_fields: List[Tuple[str]] = None,
**kwargs
):
"""
Put a editable datatable.
:param records: the data to display in the table
:param edit_callback: callback function to be called when cell of the table is edited.
The callback function should accept three parameters: `row_id`, `field_path`, `new_value`,
- `row_id`: the index of the row being edited in the `records` list.
- `field_path`: the field being edited, since the table may be nested, this is a list of field names.
- `new_value`: the new value of the cell.
:param editable_fields: list of field path that can be edited. e.g. [('name',), ('info', 'age')].
Leave it to None to make all fields editable
:param kwargs: other arguments passed to `put_datatable()`
"""
assert kwargs.get(
'id_field') is None, 'id_field is not allowed in put_editable_datatable()'
def on_edit(info):
row_id = int(info['row'])
field_path = info['path']
new_value = info['value']
edit_callback(row_id, field_path, new_value)
instance_id = kwargs.get('instance_id')
if instance_id is None:
instance_id = random_str(10)
kwargs['instance_id'] = instance_id
callback_id = output_register_callback(on_edit)
edit_value_parser = JSFunction('params', """
if (params.newValue === params.oldValue)
return params.newValue;
WebIO.pushData({
row: params.node.id,
path: ag_grid_%(instance_id)s.field2path(params.colDef.field),
value: params.newValue
}, %(callback_id)r);
return params.newValue;
""" % dict(callback_id=callback_id, instance_id=instance_id))
if editable_fields is None:
grid_args = kwargs.get('grid_args') or {}
grid_args.setdefault('defaultColDef', {})
grid_args['defaultColDef']['editable'] = True
grid_args['defaultColDef']['valueParser'] = edit_value_parser
kwargs['grid_args'] = grid_args
else:
column_args = kwargs.get('column_args') or {}
for field_path in editable_fields:
column_args.setdefault(field_path, {})
column_args[field_path]['editable'] = True
column_args[field_path]['valueParser'] = edit_value_parser
kwargs['column_args'] = column_args
return put_datatable(records, **kwargs)
def main():
import urllib.request
import json
from pywebio.output import put_button, put_markdown, popup, put_code
put_markdown("""
# Editable Datatable Demo
The table below is editable. The edit will be synced to the original data.
""")
with urllib.request.urlopen('https://fakerapi.it/api/v1/persons?_quantity=10') as f:
data = json.load(f)['data']
put_button("Show Table Data", lambda: popup("data of table", put_code(json.dumps(data, indent=2)), size='large'))
put_editable_datatable(
data,
edit_callback=editable_datatable_inplace_update_callback(data),
)
if __name__ == '__main__':
from pywebio import start_server
start_server(main, port=8080, cdn=False) Live demo: https://s.pywebio.online/37e54603585dde6edb36811011add288 |
Beta Was this translation helpful? Give feedback.
0 replies
-
In case this is useful for anyone, based on this, I created the same functionality for import pandas as pd
from typing import Union, List, Callable, Any, Sequence, Mapping, Tuple
from pywebio.output import put_datatable, JSFunction, output_register_callback
from pywebio.utils import random_str
def editable_datatable_inplace_update_callback(df: pd.DataFrame):
"""
Return a callback function that can be used in `put_editable_datatable()` to update the DataFrame inplace.
All new data will be stored as string.
"""
def callback(row_id, column, new_value):
if len(column) == 1:
df.at[row_id, column[0]] = new_value
else:
raise ValueError("Nested fields are not supported with DataFrame")
return callback
def put_dataframe(
df: pd.DataFrame,
edit_callback: Callable[[Union[str, int], List[str], 'Any'], None],
editable_columns: List[str] = None,
**kwargs
):
"""
Put an editable datatable.
:param df: the DataFrame to display in the table
:param edit_callback: callback function to be called when cell of the table is edited.
The callback function should accept three parameters: `row_id`, `field_path`, `new_value`,
- `row_id`: the index of the row being edited in the DataFrame.
- `field_path`: the field being edited, since the table may be nested, this is a list of field names.
- `new_value`: the new value of the cell.
:param editable_columns: list of column names that can be edited. e.g. ['name', 'info', 'age'].
Leave it to None to make all columns editable
:param kwargs: other arguments passed to `put_datatable()`
"""
assert kwargs.get('id_field') is None, \
'id_field is not allowed in put_editable_datatable()'
def on_edit(info):
row_id = int(info['row'])
field_path = info['path']
new_value = info['value']
edit_callback(row_id, field_path, new_value)
instance_id = kwargs.get('instance_id')
if instance_id is None:
instance_id = random_str(10)
kwargs['instance_id'] = instance_id
callback_id = output_register_callback(on_edit)
edit_value_parser = JSFunction('params', """
if (params.newValue === params.oldValue)
return params.newValue;
WebIO.pushData({
row: params.node.id,
path: ag_grid_%(instance_id)s.field2path(params.colDef.field),
value: params.newValue
}, %(callback_id)r);
return params.newValue;
""" % dict(callback_id=callback_id, instance_id=instance_id))
if editable_columns is None:
grid_args = kwargs.get('grid_args') or {}
grid_args.setdefault('defaultColDef', {})
grid_args['defaultColDef']['editable'] = True
grid_args['defaultColDef']['valueParser'] = edit_value_parser
kwargs['grid_args'] = grid_args
else:
column_args = kwargs.get('column_args') or {}
for column in editable_columns:
column_args.setdefault(column, {})
column_args[column]['editable'] = True
column_args[column]['valueParser'] = edit_value_parser
kwargs['column_args'] = column_args
return put_datatable(df.to_dict(orient='records'), **kwargs)
def main():
import urllib.request
import json
from pywebio.output import put_button, put_markdown, popup, put_code
def flatten_json(data):
def flatten(item, parent_key='', sep='_'):
flattened = {}
for k, v in item.items():
new_key = f"{parent_key}{sep}{k}" if parent_key else k
if isinstance(v, dict):
flattened.update(flatten(v, new_key, sep=sep))
else:
flattened[new_key] = v
return flattened
return [flatten(item) for item in data]
put_markdown("""
# Editable Datatable Demo
The table below is editable. The edit will be synced to the original data.
""")
with urllib.request.urlopen('https://fakerapi.it/api/v1/persons?_quantity=10') as f:
data = flatten_json(json.load(f)['data'])
df = pd.DataFrame(data)
del data
put_button("Show Table Data",
lambda: popup("data of table", put_code(df.to_json(orient='records', indent=2)), size='large'))
put_dataframe(
df,
editable_columns=['firstname', 'lastname', 'email'],
edit_callback=editable_datatable_inplace_update_callback(df),
)
if __name__ == '__main__':
from pywebio import start_server
start_server(main, port=8080, cdn=False) It works well for my purposes, but I haven't tested it thoroughly. Use at your own risk. |
Beta Was this translation helpful? Give feedback.
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
-
Hi,
Not sure if this is already implemented. Is there a way to edit the datatable cells directly in the table?
Beta Was this translation helpful? Give feedback.
All reactions