Mateja
Mateja6mo ago

I'm having a lot of trouble with placing

I'm having a lot of trouble with placing a button in an accordion element. For example,
mo.accordion(
{
"Reveal a Button": mo.ui.button(label="My Awesome Button", on_click=lambda _: print("Button Pushed"))
},
lazy=True
)
mo.accordion(
{
"Reveal a Button": mo.ui.button(label="My Awesome Button", on_click=lambda _: print("Button Pushed"))
},
lazy=True
)
works, however,
def an_expensive_function():
return mo.ui.button(label="My Awesome Button", on_click=lambda _: print("Button Pushed"))

mo.accordion(
{
"Reveal a Button": an_expensive_function
},
lazy=True
)
def an_expensive_function():
return mo.ui.button(label="My Awesome Button", on_click=lambda _: print("Button Pushed"))

mo.accordion(
{
"Reveal a Button": an_expensive_function
},
lazy=True
)
does not. Can anyone please explain to me the difference, and how to make a function that returns a button, and is lazy-evaluated?
16 Replies
Myles Scolnick
Myles Scolnick6mo ago
This seems like a bug with the button not being registered as UI element when being lazily rendered is it possible to move the button out of the expensive evaluation?
button = mo.ui.button(label="My Awesome Button", on_click=lambda _: print("Button Pushed"))
def an_expensive_function():
return button

mo.accordion(
{
"Reveal a Button": an_expensive_function
},
lazy=True
)
button = mo.ui.button(label="My Awesome Button", on_click=lambda _: print("Button Pushed"))
def an_expensive_function():
return button

mo.accordion(
{
"Reveal a Button": an_expensive_function
},
lazy=True
)
Mateja
MatejaOP6mo ago
So my actual code looks more like:
import marimo as mo
from functools import partial


def an_expensive_function(idx):
return mo.ui.button(
label="My Awesome Button", on_click=lambda _: print(f"Button {idx} Pushed")
)


mo.accordion(
{
"Reveal Button {idx}": partial(an_expensive_function, idx=idx)
for idx in range(10)
},
lazy=True,
)
import marimo as mo
from functools import partial


def an_expensive_function(idx):
return mo.ui.button(
label="My Awesome Button", on_click=lambda _: print(f"Button {idx} Pushed")
)


mo.accordion(
{
"Reveal Button {idx}": partial(an_expensive_function, idx=idx)
for idx in range(10)
},
lazy=True,
)
because I don't know how many buttons I'll need before the cell runs (10 for the sake of example, but it's variable) Would this work in the mean time?
import marimo as mo
from functools import partial

buttons = []


def an_expensive_function(idx):
global buttons
button = mo.ui.button(
label="My Awesome Button", on_click=lambda _: print(f"Button {idx} Pushed")
)
buttons.append(button)
return button


mo.accordion(
{
"Reveal Button {idx}": partial(an_expensive_function, idx=idx)
for idx in range(10)
},
lazy=True,
)
import marimo as mo
from functools import partial

buttons = []


def an_expensive_function(idx):
global buttons
button = mo.ui.button(
label="My Awesome Button", on_click=lambda _: print(f"Button {idx} Pushed")
)
buttons.append(button)
return button


mo.accordion(
{
"Reveal Button {idx}": partial(an_expensive_function, idx=idx)
for idx in range(10)
},
lazy=True,
)
Myles Scolnick
Myles Scolnick6mo ago
yea that seems totally fine - (if you update to f"Reveal Button {idx}"). if its not buttons (i.e checkbox) and you need to depend on the actual value (instead of the using callback) you can also do buttons = mo.ui.array([mo.ui.button() for idx in range(10)]) and then index into the buttons like you would a normal array
Mateja
MatejaOP6mo ago
Thank you @Myles Scolnick ! Is this an issue with _clone?
Myles Scolnick
Myles Scolnick6mo ago
I’m not entirely sure the issue - @Akshay would know better. I would’ve guess it’s because the ui element is not registered as a global, but I’m pretty sure it’s the same case in your working example
Mateja
MatejaOP6mo ago
Thanks, still trying to wrap my head around the globals thing. I keep repeating, "think Excel, think Excel ..."
Myles Scolnick
Myles Scolnick6mo ago
I still get tripped up from time to time
Akshay
Akshay6mo ago
UI elements get registered when they’re instantiated, and I wouldn’t have guessed that lazy evaluation would interfere with that. But it could! I’m out today but can check tomorrow.
Mateja
MatejaOP6mo ago
Curious to know if it only affects buttons or all UIElements, I haven't tried
Myles Scolnick
Myles Scolnick6mo ago
It’d be all ui elements
Mateja
MatejaOP6mo ago
Yeah, makes sense
Myles Scolnick
Myles Scolnick6mo ago
Initially I made the mo.lazy to be FE lazy. Then when I made it BE lazy, I probably didn’t add support for UI elements (hence needing to create the buttons beforehand)
Mateja
MatejaOP6mo ago
One more thing about accordion: The docs say. [lazy] is a convenience that wraps each accordion in a mo.lazy component. mo.lazy takes an arg show_loading_indicator. Would it make sense to add the show_loading_indicator to accordion have have it pass through to mo.lazy? Now that I think about it, if mo.lazy is a wrapper around accordion, then accordion can't pass args to its own wrapper ...
Myles Scolnick
Myles Scolnick6mo ago
we could add the show_loading_indicator to mo.accordion args as well (to pass when it uses lazy under the hood. you can probably roll it yourself at the cost of some extra code
mo.accordion(
{
"Reveal Button {idx}": mo.lazy(partial(an_expensive_function, idx=idx), show_loading_indicator=True)
for idx in range(10)
},
)
mo.accordion(
{
"Reveal Button {idx}": mo.lazy(partial(an_expensive_function, idx=idx), show_loading_indicator=True)
for idx in range(10)
},
)
Mateja
MatejaOP6mo ago
Oh I see, lazy wraps the callable, not the accordion, got it. Thanks! Would you like me to submit an issue for this in the repo?
Myles Scolnick
Myles Scolnick6mo ago
yes, please! that would be appreciated