-
Notifications
You must be signed in to change notification settings - Fork 2.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add event listener support to render blocks #8243
Conversation
* state changes * changes --------- Co-authored-by: Ali Abid <aliabid94@gmail.com> Co-authored-by: Abubakar Abid <abubakar@huggingface.co>
Actually 2 small things I noticed:
E.g. the slider here is not interactive automatically; import gradio as gr
with gr.Blocks() as demo:
s = gr.Slider(1, 4, step=1)
@gr.render(inputs=s, triggers=[s.change])
def inference(num):
with gr.Row():
for i in range(num):
gr.Textbox()
demo.launch() |
One other thing I noticed was that a I.e. this does not work as expected: import gradio as gr
with gr.Blocks() as demo:
b = gr.Button("Add Textbox")
num = gr.State(0)
b.click(lambda x:x+1, num, num)
with gr.Row():
@gr.render(inputs=num)
def show_textbox(n):
for i in range(n):
gr.Textbox()
demo.launch() (it renders the textboxes in a column). However, this works as expected: import gradio as gr
with gr.Blocks() as demo:
b = gr.Button("Add Textbox")
num = gr.State(0)
b.click(lambda x:x+1, num, num)
@gr.render(inputs=num)
def show_textbox(n):
with gr.Row():
for i in range(n):
gr.Textbox()
demo.launch() |
And one other thing that I noticed is that the import gradio as gr
with gr.Blocks() as demo:
gr.Markdown("# Train a Text Classifier with Synthetic Data")
labels = gr.Dropdown(choices=[], value=[], label="Classes", allow_custom_value=True, multiselect=True)
@gr.render(inputs=[labels])
def show_textbox(labels_):
with gr.Row():
for label in labels_:
gr.TextArea(label=f"Samples for class: {label}")
if len(labels_)>=2:
with gr.Row():
gr.Button("TRAIN!", variant="primary")
demo.launch() If I quickly change the value of the dropdown by backspacing and deleting the choices, you can see that a couple of the textareas are still visible at the end: Screen.Recording.2024-05-21.at.11.33.06.PM.mov |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tested and works well as far as I can tell, frontend looks good to me too! Thanks for this @aliabid94 and thanks for going through it with me!
Thanks for the reviews! Will add docs, and some more tests and cleanup in follow up PR |
Awesome feature! I'm just wondering if there's a way to define the event listener outside gr.Blocks? This is crucial for readability in large applications. |
Hi @acylam great point. One thing that you can do is import Blocks from other files and Say this is your import gradio as gr
with gr.Blocks() as func_blocks:
labels = gr.Dropdown(choices=[], value=[], label="Classes", allow_custom_value=True, multiselect=True)
@gr.render(inputs=[labels])
def show_textbox(labels_):
with gr.Row():
for label in labels_:
gr.TextArea(label=f"Samples for class: {label}")
if len(labels_)>=2:
with gr.Row():
gr.Button("TRAIN!", variant="primary")
and in your main import gradio as gr
from utils import func_blocks
with gr.Blocks() as demo:
gr.Markdown("# Train a Text Classifier with Synthetic Data")
func_blocks.render()
demo.launch() Does this work for you? cc @aliabid94 if he has any other recommendations |
@abidlabs Thanks for the quick reply. This works, but I was hoping for a way to separate event handling from UI elements. Kind of like how you'd define the structure of the UI in HTML and the event handling logic in JavaScript in traditional web development? So ideally, cc @aliabid94 |
I've searched high and low and I can't find the issue or notes anywhere. I did find this message in slack that I wrote a long time ago:
This one seems relevant here:
The idea being that you could somehow contain a component within a function or block and wrap the events (or just forward them) with a new interface for better abstraction. The original use case was:
Imo it is very valuable for complex apps and probably the last missing piece in terms of making gradio a really expressive way of building complex UI. |
This PR allows adding event listeners inside render blocks. Take a look at the code from demo/render_merge below:
This is done by removing previous event listeners from a render block and attaching the new event listeners on every re-render. A previous assumption is no longer true: that
dependencies
is a static list of event listeners for an app, and event listener can be identified by it'sfn_index
which is it's index within this list of listeners. We've replaced this with a concept of an id for each dependency instead of using indices, and different sessions are able to extend the default set of event listeners by adding new dependencies with different ids.Closes: #4689
Closes: #7739
Closes: #2570
Closes: #2107 (once #8297 is merged in)