S
Solara7mo ago
Cyrus

Setting height of `ipyaggrid` in Solara app gives error

Hi, how can I set the height of a ipyaggrid that is being cross-filtered by a solara component? I am using @MaartenBreddels 's snippet here https://discord.com/channels/1106593685241614489/1106593686223069309/1212429587867504730. However, if I pass an integer height to the grid element like this ipyaggrid.Grid.element(..., height=500) it renders fine but then leads to this error: trait of a Grid instance expected a unicode string, not the int 500, whenever the data is filtered. Could it be a bug in ipyaggrid where the widget expects height to be an int but the trait is defined as unicode, leading to data type issues? I found a workaround but wonder if I am missing something or if there is a better approach. Thanks!
from typing import cast
import ipyaggrid
import plotly.express as px
import solara

df = solara.reactive(px.data.iris())
species = solara.reactive('setosa')

@solara.component
def AgGrid(df, grid_options):

height = solara.use_reactive(500) # set as int

def update_df():
widget = cast(ipyaggrid.Grid, solara.get_widget(el))
widget.grid_options = grid_options
widget.update_grid_data(df)
print(widget.height)

# workaround where we recast height
if isinstance(widget.height, int):
height.set(f'{widget.height}px')
else:
height.set(widget.height)

el = ipyaggrid.Grid.element(grid_data=df, grid_options=grid_options, height=height.value)
solara.use_effect(update_df, [df, grid_options])

return el

@solara.component
def Page():
grid_options = {
'columnDefs': [
{'headerName': 'Sepal Length', 'field': 'sepal_length'},
{'headerName': 'Species', 'field': 'species'},
]
}
df_filtered = df.value.query(f'species == {species.value!r}')
solara.Select('Species', value=species, values=['setosa', 'versicolor', 'virginica'])
AgGrid(df=df_filtered, grid_options=grid_options)
from typing import cast
import ipyaggrid
import plotly.express as px
import solara

df = solara.reactive(px.data.iris())
species = solara.reactive('setosa')

@solara.component
def AgGrid(df, grid_options):

height = solara.use_reactive(500) # set as int

def update_df():
widget = cast(ipyaggrid.Grid, solara.get_widget(el))
widget.grid_options = grid_options
widget.update_grid_data(df)
print(widget.height)

# workaround where we recast height
if isinstance(widget.height, int):
height.set(f'{widget.height}px')
else:
height.set(widget.height)

el = ipyaggrid.Grid.element(grid_data=df, grid_options=grid_options, height=height.value)
solara.use_effect(update_df, [df, grid_options])

return el

@solara.component
def Page():
grid_options = {
'columnDefs': [
{'headerName': 'Sepal Length', 'field': 'sepal_length'},
{'headerName': 'Species', 'field': 'species'},
]
}
df_filtered = df.value.query(f'species == {species.value!r}')
solara.Select('Species', value=species, values=['setosa', 'versicolor', 'virginica'])
AgGrid(df=df_filtered, grid_options=grid_options)
6 Replies
mariobuikhuizen
mariobuikhuizen7mo ago
Hi @Cyrus, ipyaggrid uses ipywidgets in a non standard way. It has a constructor that takes some arguments named the same as the traits and does some conversions on them. This indeed doesn't work so well with Solara. Your workaround looks good. We may at some point make a Solara component for ipyaggrid to deal with these issues.
Cyrus
Cyrus7mo ago
@mariobuikhuizen thanks for the info. A Solara AgGrid component would be great to have indeed. I can add that in the feature-requests section if you'd like to track it
MaartenBreddels
MaartenBreddels7mo ago
see also https://github.com/widgetti/solara/pull/531 - which explicitly mentions ipyaggrid, I think we should merge and publish this soon
Monty Python
Monty Python7mo ago
maartenbreddels
<:pull_open:882464248721182842> [widgetti/solara] docs: document better how to use classical ipywidgets in components
We also give specific examples for ipyaggrid and ipydatagrid which are quite popular with solara. Based on discussion on discord and: https://github.com/widgetti/solara/issues/512 https://github.com/widgetti/solara/issues/511 TODO: - ◻️ examples in markdown don't always work well.
Created
MaartenBreddels
MaartenBreddels7mo ago
Or do you think we should extend this a bit @Cyrus ?
Cyrus
Cyrus6mo ago
@MaartenBreddels These docs are very helpful so merging soon would be ideal. You could mention the height issue above but I feel like maybe it is best for that to come in a future release where you'd create a Solara component for ipyaggrid to fix that, as Mario suggested. Hi, I extended the above so that AgGrid can also take in menu as kwarg
import ipyaggrid
import plotly.express as px
import solara

df = px.data.iris()

@solara.component
def AgGrid(grid_data, grid_options, **kwargs):

height = kwargs.pop("height", 0)
menu = kwargs.pop("menu", {}) # need for menu to work
grid_height = solara.use_reactive(height)
grid_menu = solara.use_reactive(menu) # need for menu to work

def update_data():
widget = solara.get_widget(el)
widget.grid_options = grid_options
widget.update_grid_data(grid_data)

if isinstance(widget.height, int):
grid_height.set(f"{widget.height}px")
else:
grid_height.set(widget.height)

grid_menu.set(widget.menu) # need for menu to work

el = ipyaggrid.Grid.element(
grid_data=grid_data,
grid_options=grid_options,
height=grid_height.value,
menu=grid_menu.value, # need for menu to work
**kwargs,
)

solara.use_effect(update_data, [grid_data, grid_options])

@solara.component
def Page():

grid_options = {
'columnDefs': [
{'headerName': 'Species', 'field': 'species', 'enableRowGroup': True},
{'headerName': 'Sepal Length', 'field': 'sepal_length'},
]
}

menu = {
'buttons': [
{'name': 'Expand All', 'action': 'gridOptions.api.expandAll()'},
{'name': 'Collapse All', 'action': 'gridOptions.api.collapseAll()'}
]
}

AgGrid(
grid_data=df,
grid_options=grid_options,
menu=menu,
export_excel=True,
quick_filter=True,
height=200,
width='80%'
)

Page()
import ipyaggrid
import plotly.express as px
import solara

df = px.data.iris()

@solara.component
def AgGrid(grid_data, grid_options, **kwargs):

height = kwargs.pop("height", 0)
menu = kwargs.pop("menu", {}) # need for menu to work
grid_height = solara.use_reactive(height)
grid_menu = solara.use_reactive(menu) # need for menu to work

def update_data():
widget = solara.get_widget(el)
widget.grid_options = grid_options
widget.update_grid_data(grid_data)

if isinstance(widget.height, int):
grid_height.set(f"{widget.height}px")
else:
grid_height.set(widget.height)

grid_menu.set(widget.menu) # need for menu to work

el = ipyaggrid.Grid.element(
grid_data=grid_data,
grid_options=grid_options,
height=grid_height.value,
menu=grid_menu.value, # need for menu to work
**kwargs,
)

solara.use_effect(update_data, [grid_data, grid_options])

@solara.component
def Page():

grid_options = {
'columnDefs': [
{'headerName': 'Species', 'field': 'species', 'enableRowGroup': True},
{'headerName': 'Sepal Length', 'field': 'sepal_length'},
]
}

menu = {
'buttons': [
{'name': 'Expand All', 'action': 'gridOptions.api.expandAll()'},
{'name': 'Collapse All', 'action': 'gridOptions.api.collapseAll()'}
]
}

AgGrid(
grid_data=df,
grid_options=grid_options,
menu=menu,
export_excel=True,
quick_filter=True,
height=200,
width='80%'
)

Page()
I assume this workaround generalizes to every trait that gets converted? Let me know if you can think of a better approach! Thanks
Want results from more Discord servers?
Add your server