Spaces:
Runtime error
Runtime error
| import gradio as gr | |
| from typing import List, Dict, Tuple | |
| import time | |
| class TodoApp: | |
| def __init__(self): | |
| self.todos = [] | |
| def add_todo(self, todo_text: str, current_todos: List[Dict]) -> Tuple[List[Dict], str]: | |
| """Add a new todo item to the list.""" | |
| if not todo_text.strip(): | |
| raise gr.Warning("Please enter a todo item!") | |
| new_todo = { | |
| "id": len(current_todos) + 1, | |
| "text": todo_text.strip(), | |
| "completed": False, | |
| "created_at": time.strftime("%Y-%m-%d %H:%M") | |
| } | |
| updated_todos = current_todos + [new_todo] | |
| return updated_todos, "" | |
| def toggle_todo(self, todo_indices: List[int], current_todos: List[Dict]) -> List[Dict]: | |
| """Toggle the completion status of selected todos.""" | |
| if not todo_indices: | |
| return current_todos | |
| updated_todos = current_todos.copy() | |
| for idx in todo_indices: | |
| if 0 <= idx < len(updated_todos): | |
| updated_todos[idx]["completed"] = not updated_todos[idx]["completed"] | |
| return updated_todos | |
| def delete_todos(self, todo_indices: List[int], current_todos: List[Dict]) -> List[Dict]: | |
| """Delete selected todos from the list.""" | |
| if not todo_indices: | |
| return current_todos | |
| # Sort indices in reverse to avoid index shifting issues | |
| todo_indices_sorted = sorted(todo_indices, reverse=True) | |
| updated_todos = current_todos.copy() | |
| for idx in todo_indices_sorted: | |
| if 0 <= idx < len(updated_todos): | |
| del updated_todos[idx] | |
| # Reassign IDs | |
| for i, todo in enumerate(updated_todos): | |
| todo["id"] = i + 1 | |
| return updated_todos | |
| def clear_all(self) -> List[Dict]: | |
| """Clear all todos from the list.""" | |
| return [] | |
| def get_todo_display(self, todos: List[Dict]) -> List[str]: | |
| """Format todos for display in CheckboxGroup.""" | |
| display_list = [] | |
| for todo in todos: | |
| status = "β " if todo["completed"] else "β" | |
| display_text = f"{status} {todo['text']} (Created: {todo['created_at']})" | |
| display_list.append(display_text) | |
| return display_list | |
| # Initialize the TodoApp | |
| todo_manager = TodoApp() | |
| def create_todo_app(): | |
| """Create and configure the Gradio Todo App interface.""" | |
| with gr.Blocks( | |
| title="Todo App", | |
| theme=gr.themes.Soft(), | |
| css=""" | |
| .todo-container { | |
| border: 2px solid #e5e7eb; | |
| border-radius: 8px; | |
| padding: 16px; | |
| margin: 8px 0; | |
| background: #f9fafb; | |
| } | |
| .header-text { | |
| text-align: center; | |
| margin-bottom: 20px; | |
| } | |
| """ | |
| ) as demo: | |
| # Header with attribution | |
| gr.HTML(""" | |
| <div class="header-text"> | |
| <h1>π Todo App</h1> | |
| <p>Manage your tasks efficiently with this simple todo application!</p> | |
| <p style="font-size: 0.9em; color: #666;"> | |
| Built with <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank">anycoder</a> | |
| </p> | |
| </div> | |
| """) | |
| # State to manage the todo list | |
| todo_state = gr.State(value=[]) | |
| with gr.Row(): | |
| with gr.Column(scale=3): | |
| # Input section | |
| with gr.Group(): | |
| gr.Markdown("### Add New Todo") | |
| todo_input = gr.Textbox( | |
| placeholder="Enter your todo item here...", | |
| label="Todo Item", | |
| lines=1, | |
| max_lines=1, | |
| autofocus=True, | |
| show_label=False | |
| ) | |
| with gr.Row(): | |
| add_btn = gr.Button( | |
| "β Add Todo", | |
| variant="primary", | |
| size="sm" | |
| ) | |
| clear_input_btn = gr.Button( | |
| "ποΈ Clear Input", | |
| variant="secondary", | |
| size="sm" | |
| ) | |
| with gr.Column(scale=2): | |
| # Statistics | |
| with gr.Group(): | |
| gr.Markdown("### Statistics") | |
| total_count = gr.Number( | |
| label="Total Todos", | |
| value=0, | |
| interactive=False | |
| ) | |
| completed_count = gr.Number( | |
| label="Completed", | |
| value=0, | |
| interactive=False | |
| ) | |
| pending_count = gr.Number( | |
| label="Pending", | |
| value=0, | |
| interactive=False | |
| ) | |
| # Todo list display and actions | |
| with gr.Group(): | |
| gr.Markdown("### Todo List") | |
| todo_display = gr.CheckboxGroup( | |
| choices=[], | |
| label="Select todos to toggle or delete", | |
| info="Check items to mark as complete/incomplete or delete them", | |
| interactive=True | |
| ) | |
| with gr.Row(): | |
| toggle_btn = gr.Button( | |
| "π Toggle Selected", | |
| variant="secondary", | |
| size="sm" | |
| ) | |
| delete_btn = gr.Button( | |
| "ποΈ Delete Selected", | |
| variant="stop", | |
| size="sm" | |
| ) | |
| clear_all_btn = gr.Button( | |
| "π§Ή Clear All", | |
| variant="stop", | |
| size="sm" | |
| ) | |
| # Hidden component to trigger updates | |
| update_trigger = gr.Number(visible=False) | |
| # Functions | |
| def add_todo_handler(todo_text, current_todos): | |
| """Handle adding a new todo.""" | |
| updated_todos, cleared_input = todo_manager.add_todo(todo_text, current_todos) | |
| display_choices = todo_manager.get_todo_display(updated_todos) | |
| # Calculate statistics | |
| total = len(updated_todos) | |
| completed = sum(1 for todo in updated_todos if todo["completed"]) | |
| pending = total - completed | |
| return ( | |
| updated_todos, | |
| display_choices, | |
| cleared_input, | |
| total, | |
| completed, | |
| pending, | |
| time.time() # Trigger update | |
| ) | |
| def toggle_todo_handler(selected_indices, current_todos): | |
| """Handle toggling todo completion status.""" | |
| if not selected_indices: | |
| return current_todos, todo_manager.get_todo_display(current_todos) | |
| updated_todos = todo_manager.toggle_todo(selected_indices, current_todos) | |
| display_choices = todo_manager.get_todo_display(updated_todos) | |
| # Calculate statistics | |
| total = len(updated_todos) | |
| completed = sum(1 for todo in updated_todos if todo["completed"]) | |
| pending = total - completed | |
| return ( | |
| updated_todos, | |
| display_choices, | |
| total, | |
| completed, | |
| pending, | |
| time.time() # Trigger update | |
| ) | |
| def delete_todo_handler(selected_indices, current_todos): | |
| """Handle deleting selected todos.""" | |
| if not selected_indices: | |
| return current_todos, todo_manager.get_todo_display(current_todos) | |
| updated_todos = todo_manager.delete_todos(selected_indices, current_todos) | |
| display_choices = todo_manager.get_todo_display(updated_todos) | |
| # Calculate statistics | |
| total = len(updated_todos) | |
| completed = sum(1 for todo in updated_todos if todo["completed"]) | |
| pending = total - completed | |
| return ( | |
| updated_todos, | |
| display_choices, | |
| total, | |
| completed, | |
| pending, | |
| time.time() # Trigger update | |
| ) | |
| def clear_all_handler(): | |
| """Handle clearing all todos.""" | |
| updated_todos = todo_manager.clear_all() | |
| display_choices = todo_manager.get_todo_display(updated_todos) | |
| return ( | |
| updated_todos, | |
| display_choices, | |
| 0, | |
| 0, | |
| 0, | |
| time.time() # Trigger update | |
| ) | |
| def clear_input_handler(): | |
| """Clear the input textbox.""" | |
| return "" | |
| def update_display(current_todos): | |
| """Update the display when todos change.""" | |
| display_choices = todo_manager.get_todo_display(current_todos) | |
| # Calculate statistics | |
| total = len(current_todos) | |
| completed = sum(1 for todo in current_todos if todo["completed"]) | |
| pending = total - completed | |
| return display_choices, total, completed, pending | |
| # Event handlers | |
| add_btn.click( | |
| fn=add_todo_handler, | |
| inputs=[todo_input, todo_state], | |
| outputs=[todo_state, todo_display, todo_input, total_count, completed_count, pending_count, update_trigger] | |
| ) | |
| todo_input.submit( | |
| fn=add_todo_handler, | |
| inputs=[todo_input, todo_state], | |
| outputs=[todo_state, todo_display, todo_input, total_count, completed_count, pending_count, update_trigger] | |
| ) | |
| clear_input_btn.click( | |
| fn=clear_input_handler, | |
| outputs=[todo_input] | |
| ) | |
| toggle_btn.click( | |
| fn=toggle_todo_handler, | |
| inputs=[todo_display, todo_state], | |
| outputs=[todo_state, todo_display, total_count, completed_count, pending_count, update_trigger] | |
| ) | |
| delete_btn.click( | |
| fn=delete_todo_handler, | |
| inputs=[todo_display, todo_state], | |
| outputs=[todo_state, todo_display, total_count, completed_count, pending_count, update_trigger] | |
| ) | |
| clear_all_btn.click( | |
| fn=clear_all_handler, | |
| outputs=[todo_state, todo_display, total_count, completed_count, pending_count, update_trigger] | |
| ) | |
| # Update display when state changes | |
| update_trigger.change( | |
| fn=update_display, | |
| inputs=[todo_state], | |
| outputs=[todo_display, total_count, completed_count, pending_count] | |
| ) | |
| # Examples | |
| gr.Examples( | |
| examples=[ | |
| ["Buy groceries"], | |
| ["Finish project report"], | |
| ["Call mom"], | |
| ["Schedule dentist appointment"], | |
| ["Learn Gradio"] | |
| ], | |
| inputs=[todo_input], | |
| examples_per_page=3, | |
| label="Example todos (click to use)" | |
| ) | |
| # Instructions | |
| with gr.Accordion("π How to Use", open=False): | |
| gr.Markdown(""" | |
| ### Instructions: | |
| 1. **Add Todo**: Type your task in the input box and press Enter or click "Add Todo" | |
| 2. **Mark Complete**: Check the box next to a todo and click "Toggle Selected" | |
| 3. **Delete Todos**: Select todos and click "Delete Selected" | |
| 4. **Clear All**: Click "Clear All" to remove all todos at once | |
| 5. **Statistics**: View real-time counts of total, completed, and pending todos | |
| ### Tips: | |
| - β indicates completed tasks | |
| - β indicates pending tasks | |
| - Each todo shows its creation time | |
| - Select multiple todos to perform bulk actions | |
| """) | |
| return demo | |
| # Create and launch the app | |
| if __name__ == "__main__": | |
| demo = create_todo_app() | |
| demo.launch( | |
| share=False, | |
| show_error=True, | |
| show_tips=True, | |
| height=600, | |
| width="100%" | |
| ) |