Add logic to handle non-interactive or hidden tabs (#7107)

* Refactor change_tab function to handle non-interactive or hidden tabs

* add changeset

* Refactor flashcards app UI and modify test

* Fix formatting

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>
This commit is contained in:
Hannah 2024-01-23 08:31:59 +01:00 committed by GitHub
parent 13cb6af8b2
commit 80f8fbf0e8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 126 additions and 78 deletions

View File

@ -0,0 +1,6 @@
---
"@gradio/tabs": patch
"gradio": patch
---
fix:Add logic to handle non-interactive or hidden tabs

File diff suppressed because one or more lines are too long

View File

@ -4,90 +4,113 @@ import gradio as gr
demo = gr.Blocks() demo = gr.Blocks()
def is_data_empty(flashcards):
if isinstance(flashcards, dict):
return all(item == '' for sublist in flashcards['data'] for item in sublist)
elif isinstance(flashcards, list):
return all(all(item == '' for item in sublist) for sublist in flashcards)
else:
return True
with demo: with demo:
gr.Markdown( gr.Markdown(
"Load the flashcards in the table below, then use the Practice tab to practice." "Load the flashcards in the table below, then use the Practice tab to practice."
) )
with gr.Tab("Word Bank"): with gr.Tabs() as tabs:
flashcards_table = gr.Dataframe(headers=["front", "back"], type="array") with gr.Tab("Word Bank"):
with gr.Tab("Practice"): flashcards_table = gr.Dataframe(headers=["front", "back"], type="array")
with gr.Row(): flashcards_table.change(fn=lambda: print(flashcards_table.value))
with gr.Column(): practice_btn = gr.Button("Start Practice")
front = gr.Textbox(label="Prompt") with gr.Tab("Practice", interactive=False, id=1) as practice_tab:
with gr.Row(): with gr.Row():
new_btn = gr.Button("New Card") with gr.Column():
flip_btn = gr.Button("Flip Card") front = gr.Textbox(label="Prompt")
with gr.Column(visible=False) as answer_col: with gr.Row():
back = gr.Textbox(label="Answer") new_btn = gr.Button("New Card")
selected_card = gr.State() flip_btn = gr.Button("Flip Card")
with gr.Column(visible=False) as answer_col:
back = gr.Textbox(label="Answer")
selected_card = gr.State()
with gr.Row(): with gr.Row():
correct_btn = gr.Button("Correct") correct_btn = gr.Button("Correct")
incorrect_btn = gr.Button("Incorrect") incorrect_btn = gr.Button("Incorrect")
with gr.Tab("Results", visible=False) as results_tab: def start_practice(flashcards):
results = gr.State(value={}) # if no cards entered into dataframe yet, return
correct_field = gr.Markdown("# Correct: 0") if is_data_empty(flashcards):
incorrect_field = gr.Markdown("# Incorrect: 0") practice_tab = gr.Tab("Practice", interactive=False, id=1)
gr.Markdown("Card Statistics: ") raise gr.Error("Please enter word prompts into the table.")
results_table = gr.Dataframe(headers=["Card", "Correct", "Incorrect"]) return [practice_tab, tabs]
else:
practice_tab = gr.Tab("Practice", interactive=True, id=1)
new_tabs = gr.Tabs(selected=1)
return [practice_tab, new_tabs]
def load_new_card(flashcards): with gr.Tab("Results", visible=False) as results_tab:
card = random.choice(flashcards) results = gr.State(value={})
return ( correct_field = gr.Markdown("# Correct: 0")
card, incorrect_field = gr.Markdown("# Incorrect: 0")
card[0], gr.Markdown("Card Statistics: ")
gr.Column(visible=False), results_table = gr.Dataframe(headers=["Card", "Correct", "Incorrect"])
practice_btn.click(start_practice, inputs=[flashcards_table], outputs=[practice_tab, tabs])
def load_new_card(flashcards):
card = random.choice(flashcards)
return (
card,
card[0],
gr.Column(visible=False),
)
new_btn.click(
load_new_card,
[flashcards_table],
[selected_card, front, answer_col],
) )
new_btn.click( def flip_card(card):
load_new_card, return card[1], gr.Column(visible=True)
[flashcards_table],
[selected_card, front, answer_col],
)
def flip_card(card): flip_btn.click(flip_card, [selected_card], [back, answer_col])
return card[1], gr.Column(visible=True)
flip_btn.click(flip_card, [selected_card], [back, answer_col]) def mark_correct(card, results):
if card[0] not in results:
results[card[0]] = [0, 0]
results[card[0]][0] += 1
correct_count = sum(result[0] for result in results.values())
return (
results,
f"# Correct: {correct_count}",
[[front, scores[0], scores[1]] for front, scores in results.items()],
)
def mark_correct(card, results): def mark_incorrect(card, results):
if card[0] not in results: if card[0] not in results:
results[card[0]] = [0, 0] results[card[0]] = [
results[card[0]][0] += 1 0, 0]
correct_count = sum(result[0] for result in results.values()) results[card[0]][1] += 1
return ( incorrect_count = sum(result[1] for result in results.values())
results, return (
f"# Correct: {correct_count}", results,
[[front, scores[0], scores[1]] for front, scores in results.items()], f"# Inorrect: {incorrect_count}",
[[front, scores[0], scores[1]] for front, scores in results.items()],
)
def toggle_results_tab():
return gr.Tab("Results", visible=True)
correct_btn.click(
mark_correct,
[selected_card, results],
[results, correct_field, results_table],
) )
def mark_incorrect(card, results): incorrect_btn.click(mark_incorrect, [selected_card, results], [results, incorrect_field, results_table])
if card[0] not in results:
results[card[0]] = [
0, 0]
results[card[0]][1] += 1
incorrect_count = sum(result[1] for result in results.values())
return (
results,
f"# Inorrect: {incorrect_count}",
[[front, scores[0], scores[1]] for front, scores in results.items()],
)
def toggle_results_tab(): # set results tab to visible when correct or incorrect button is clicked
return gr.Tab("Results", visible=True) correct_btn.click(fn=toggle_results_tab, outputs=[results_tab])
incorrect_btn.click(fn=toggle_results_tab, outputs=[results_tab])
correct_btn.click(
mark_correct,
[selected_card, results],
[results, correct_field, results_table],
)
incorrect_btn.click(mark_incorrect, [selected_card, results], [results, incorrect_field, results_table])
# set results tab to visible when correct or incorrect button is clicked
correct_btn.click(fn=toggle_results_tab, outputs=[results_tab])
incorrect_btn.click(fn=toggle_results_tab, outputs=[results_tab])
if __name__ == "__main__": if __name__ == "__main__":
demo.launch() demo.launch()

View File

@ -1,6 +1,12 @@
import { test } from "@gradio/tootils"; import { test, expect } from "@gradio/tootils";
test("shows the results tab when results > 0", async ({ page }) => { test("shows the results tab when results > 0", async ({ page }) => {
await page.getByRole("button", { name: "Start Practice" }).click();
await expect(
page.getByText("Please enter word prompts into the table.")
).toBeAttached();
await page.getByLabel("Close").click();
await page await page
.getByRole("button", { name: "front back" }) .getByRole("button", { name: "front back" })
.getByRole("button") .getByRole("button")
@ -35,10 +41,14 @@ test("shows the results tab when results > 0", async ({ page }) => {
await page.getByText("New row").click(); await page.getByText("New row").click();
await page.getByRole("tab", { name: "Practice" }).click();
await page.getByRole("button", { name: "New Card" }).click();
await page.waitForTimeout(1000); await page.waitForTimeout(1000);
await page.getByRole("button", { name: "Flip Card" }).click(); await page.getByText("Start Practice").dblclick();
await page.getByRole("button", { name: "Correct", exact: true }).click();
await page.getByRole("tab", { name: "Results" }).click(); await page.waitForTimeout(5000);
// await page.getByRole("button", { name: "New Card" }).click();
// await page.waitForTimeout(1000);
// await page.getByRole("button", { name: "Flip Card" }).click();
// await page.getByRole("button", { name: "Correct", exact: true }).click();
// await page.getByRole("tab", { name: "Results" }).click();
}); });

View File

@ -68,13 +68,22 @@
}); });
function change_tab(id: object | string | number): void { function change_tab(id: object | string | number): void {
selected = id; const tab_to_activate = tabs.find((t) => t.id === id);
$selected_tab = id; if (
$selected_tab_index = tabs.findIndex((t) => t.id === id); tab_to_activate &&
dispatch("change"); tab_to_activate.interactive &&
tab_to_activate.visible
) {
selected = id;
$selected_tab = id;
$selected_tab_index = tabs.findIndex((t) => t.id === id);
dispatch("change");
} else {
console.warn("Attempted to select a non-interactive or hidden tab.");
}
} }
$: selected !== null && change_tab(selected); $: tabs, selected !== null && change_tab(selected);
</script> </script>
<div class="tabs {elem_classes.join(' ')}" class:hide={!visible} id={elem_id}> <div class="tabs {elem_classes.join(' ')}" class:hide={!visible} id={elem_id}>