Closed mwouts closed 1 month ago
Thank you for making this pull request.
Did you know? You can try it on Binder: .
Also, the version of ITables developed in this PR can be installed with pip
:
pip install git+https://github.com/mwouts/itables.git@try_anywidget
(this requires nodejs
, see more at Developing ITables)
Attention: Patch coverage is 80.43478%
with 36 lines
in your changes missing coverage. Please review.
Project coverage is 93.65%. Comparing base (
20546b2
) to head (a177e4a
). Report is 1 commits behind head on main.
:umbrella: View full report in Codecov by Sentry.
:loudspeaker: Have feedback on the report? Share it here.
just tried to run through the docs (on binder and locally) and got this error:
import ipywidgets as widgets
from itables import show
from itables.sample_dfs import get_dict_of_test_dfs
sample_dfs = get_dict_of_test_dfs()
def use_show_in_interactive_output(table_name: str):
show(
sample_dfs[table_name],
caption=table_name,
style="table-layout:auto;width:auto;float:left;caption-side:bottom",
)
table_selector = widgets.Dropdown(options=sample_dfs.keys(), value="int_float_str")
out = widgets.interactive_output(
use_show_in_interactive_output, {"table_name": table_selector}
)
widgets.VBox([table_selector, out])
just tried to run through the docs (on binder and locally) and got this error:
(...) FileNotFoundError: [Errno 2] No such file or directory: '/home/jovyan/miniforge3/envs/complexapps-2024/lib/python3.11/site-packages/itables/samples/countries.csv'
Thanks for giving it a go, and sorry about that - My attempt to simplify the pyproject.toml
didn't go as expected...
This should be fixed now, at least I have seen this notebook run on Binder: https://mybinder.org/v2/gh/mwouts/itables/try_anywidget?urlpath=lab/tree/docs/ipywidgets.md. Let me know what you think! Thanks
thanks @mwouts for the fix - generally looks and works great, I can see the ability to provide bits of interaction whilst keeping the look and feel of itables could be v useful.
just had a quick play and have a few comments to address as you see fit:
value
would be a better trait name that data
as it matches the ipywidgets lib. (that said, ipydatagrid uses data
for dataframes so there is already a precedent for that)data
trait cannot be updated from python by going table.data = [...]
, or table.dt_args = {...}
. This would feel natural for an ipywidget. Note, I developed the ipyautoui
library which is a pure python lib built on ipywidgets... I struggled a little with this and arrived at a solution where by trait is _value
and then I have a value setter and getter... enabling me to do stuff on set and get whilst the widget still feels like an ipywidget.selected_rows
is independent of search which is greatdata
as records (i.e. list of dicts) would be better? you can copy the markdown below into you ipywidget.md
file for more info
## JG Comments
```{code-cell} ipython3
import ipywidgets as widgets
from itables import show
from itables.sample_dfs import get_dict_of_test_dfs
from itables.widget import ITable
sample_dfs = get_dict_of_test_dfs()
name = "ordered_categories"
df = sample_dfs[name]
table = ITable()
table = ITable(
df,
caption=name,
select=True,
style="table-layout:auto;width:auto;float:left",
)
table
# view traits
table.traits()
columns = [c["title"] for c in table.dt_args["columns"]]
columns
# `data` is the trait name for the widget value
# as a big ipywidgets user, I'd advocate using `value` instead
# as that then becomes consistent with all the other ipywidgets
table.data # would a list of records be possible?
columns = [c["title"] for c in table.dt_args["columns"]]
[dict(zip(d, columns)) for d in table.data]
# not possible set table trait value... which would be nice
table.data = [['a', 0], ['b', 1], ['c', 2], ['d', 3]]
table = ITable(df)
table
# used this to check that the `selected_rows` doesn't care about search - it doesn't - which is great
name1 = "countries"
df1 = sample_dfs[name1]
table1 = ITable(
df1,
caption=name1,
select=True,
style="table-layout:auto;width:auto;float:left",
)
table1
table1.selected_rows
Hi @jgunstone , thank you so much for your feedback, that's really helpful!
I personally think that value would be a better trait name that data as it matches the ipywidgets lib. (that said, ipydatagrid uses data for dataframes so there is already a precedent for that)
Well interesting that you mention that! I was seeing data
and dt_args
as internal traits, an I don't really expect the users to modify them. Instead, I was thinking that you would use the update
method to update the data and the dt args by passing directly the dataframe and the usual options (if you don't mind, can you give it a try and let me know what you think?)
Internally the update
method transforms df
into the appropriate list of rows, defines the columns, and increases destroy_and_recreate
to refresh the table (refreshing on data
or dt_args
separately causes issues when the column definitions don't match the row length).
At the very least I should make that more explicit in the documentation. I can also move the traits that I don't think people should use to underscore names as you suggest, that's a good point! Actually, the traits that I would like to expose are the following:
selected_rows
, to set or retrieve a selection (I think that's the most useful one!)caption
, style
and classes
At some point I plan to make the tables editable (https://github.com/mwouts/itables/issues/243, will require a subscription to datatables' editor), but until then I don't want to expose data
directly (it's not a one to one conversion of df, etc). I'd be curious to give a look at how you can defined setters and getters - setting df
for instance would be ideal and possibly more idiomatic than update
.
Also thanks for pointing out at ipydatagrid
! At first sight we seem to have the same approach re passing the data as a DataFrame through the first argument of the widget. Do you see people interacting directly with the data
attribute maybe?
Hi hi - this is how they do the setter / getter in ipydatagrid: https://github.com/jupyter-widgets/ipydatagrid/blob/f7fab2945d89063eaa647fb7e9f94cc1c140d7bb/ipydatagrid/datagrid.py#L465-L492
and then the trait
is _data
- maybe you could do a similar thing by putting the code in your update
method into the setter? I think it would be nice to interact with data
in this way.
just playing with dt_args
and getting a little confused (though tbh I haven't done loads of customisation stuff with itables so not super familiar generally) -
what vars would typically be passed to dt_args
, and how are they distinct from what would just be passed as **kwargs
?
import pandas as pd
import itables.options as opt
from itables import init_notebook_mode, show
from itables.sample_dfs import get_countries
df = get_countries(html=False)
init_notebook_mode(all_interactive=True)
show(df, classes="display nowrap compact")
ITable(df, classes="display nowrap compact")
# ^ this works the same as show which is nice from a user perspective.
ITable(df, dt_args=dict(classes="display nowrap compact"))
# ^ this doesn't do anything....
The Jupyter Widget is now part of ITables v2.2. See https://mwouts.github.io/itables/ipywidgets.html for the documentation.
Thank you @jgunstone for your feedback on the widget, it has been very helpful. Since our last chat I have made sure that only the traits that the user can modify directly are public. I have also added a df
property and setter to let the user modify the underlying dataframe more easily - examples are available in the documentation.
Re your last question re dt_args
, that's an internal distinction that I make between the arguments that are passed to the JavaScript DataTable
constructor, and the other ones (e.g caption, style, classes, selected rows...). As a user you don't need to make that distinction.
I have used AnyWidget to provide the widget, as suggested at https://github.com/mwouts/itables/issues/267#issuecomment-2343712189
Closes #267 Closes #250
TODO
update
is called withselected_rows=None
(the default)