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("""

๐Ÿ“ Todo App

Manage your tasks efficiently with this simple todo application!

Built with anycoder

""") # 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%" )