Urwid
A console user interface library for Python. A well-established TUI library providing a rich widget set and event-driven programming model.
GitHub Overview
urwid/urwid
Console user interface library for Python (official repo)
Repository:https://github.com/urwid/urwid
Homepage:urwid.org
Stars2,930
Watchers56
Forks323
Created:February 25, 2010
Language:Python
License:GNU Lesser General Public License v2.1
Topics
None
Star History
Data as of: 7/25/2025, 06:23 AM
Library
Urwid
Overview
Urwid is a mature library for building console user interfaces in Python. It provides a rich widget set and event-driven programming model, enabling the development of complex TUI applications.
Details
Urwid was developed by Ian Ward in 2004 and has been a pioneering presence in Python TUI libraries, used in many projects over the years. It's highly regarded for its stability and maturity, trusted even for enterprise-level application development.
Key Features
- Rich Widget Set: 50+ widgets including buttons, text boxes, list boxes, menus
- Layout System: Flexible layout management
- Event-Driven: Handling keyboard, mouse, and timer events
- Custom Widgets: Easy creation of custom widgets
- Theme System: Color palette configuration
- Asynchronous Processing: Support for non-blocking operations
- Unicode Support: Multi-language text display
- Mouse Support: Mouse event handling
Architecture
Urwid's design consists of the following main components:
- Widget: Base class for UI elements
- MainLoop: Event loop and application control
- Pile/Columns: Layout containers
- Frame: Layout with header, footer, and body
- Palette: Color theme management
- Canvas: Drawing surface
Pros and Cons
Pros
- Long track record and stability
- Rich widget library
- Detailed documentation
- Flexible layout system
- Full mouse and keyboard support
- Relatively lightweight
- Mature community
- Enterprise adoption history
Cons
- Somewhat old-fashioned API design
- High learning cost
- Lack of modern UI elements
- Limited styling options
- Slow addition of new features
Key Links
Code Example
import urwid
class TodoApp:
def __init__(self):
self.todos = []
self.current_id = 0
# Header
header = urwid.Text("📝 Todo App")
header = urwid.AttrMap(header, 'header')
# Input field
self.edit = urwid.Edit("New todo: ")
# Buttons
add_btn = urwid.Button("Add")
urwid.connect_signal(add_btn, 'click', self.add_todo)
clear_btn = urwid.Button("Clear All")
urwid.connect_signal(clear_btn, 'click', self.clear_todos)
# Button layout
buttons = urwid.Columns([
('weight', 1, add_btn),
('weight', 1, clear_btn)
])
# Todo list
self.todo_list = urwid.SimpleFocusListWalker([])
self.listbox = urwid.ListBox(self.todo_list)
# Layout
pile = urwid.Pile([
('pack', self.edit),
('pack', buttons),
urwid.Divider(),
self.listbox
])
# Frame
self.frame = urwid.Frame(
pile,
header=header,
footer=urwid.Text("Enter: Add | Space: Toggle | d: Delete")
)
def add_todo(self, button):
text = self.edit.get_edit_text().strip()
if text:
todo_item = TodoItem(self.current_id, text, self.on_delete)
self.todo_list.append(todo_item)
self.todos.append({'id': self.current_id, 'text': text, 'done': False})
self.current_id += 1
self.edit.set_edit_text("")
def clear_todos(self, button):
self.todo_list.clear()
self.todos.clear()
def on_delete(self, todo_id):
# Remove corresponding Todo from list
for i, item in enumerate(self.todo_list):
if hasattr(item, 'todo_id') and item.todo_id == todo_id:
del self.todo_list[i]
break
# Remove from data as well
self.todos = [t for t in self.todos if t['id'] != todo_id]
def unhandled_input(self, key):
if key in ('q', 'Q'):
raise urwid.ExitMainLoop()
class TodoItem(urwid.WidgetWrap):
def __init__(self, todo_id, text, delete_callback):
self.todo_id = todo_id
self.done = False
self.delete_callback = delete_callback
self.checkbox = urwid.CheckBox("", state=False)
self.text = urwid.Text(text)
# Layout
columns = urwid.Columns([
('fixed', 4, self.checkbox),
self.text
])
super().__init__(columns)
def keypress(self, size, key):
if key == ' ':
self.checkbox.toggle_state()
self.done = not self.done
style = 'done' if self.done else None
self.text.set_text(('done' if self.done else None, self.text.text))
return None
elif key == 'd':
self.delete_callback(self.todo_id)
return None
return super().keypress(size, key)
def main():
# Color palette
palette = [
('header', 'white', 'dark blue', 'bold'),
('done', 'dark gray', 'default', 'strikethrough'),
]
app = TodoApp()
# Main loop
loop = urwid.MainLoop(
app.frame,
palette=palette,
unhandled_input=app.unhandled_input
)
loop.run()
if __name__ == "__main__":
main()