0
# Widget Library
1
2
Complete collection of built-in UI components for creating rich terminal interfaces, including input controls, display widgets, navigation components, data widgets, and utility components.
3
4
## Capabilities
5
6
### Input and Form Controls
7
8
Interactive widgets for user input and form creation.
9
10
```python { .api }
11
class Button(Widget):
12
"""A clickable button widget."""
13
14
class Pressed(Message):
15
"""Sent when the button is pressed."""
16
def __init__(self, button: Button): ...
17
18
def __init__(
19
self,
20
label: RenderableType = "",
21
*,
22
variant: str = "default",
23
disabled: bool = False,
24
**kwargs
25
):
26
"""
27
Initialize a button.
28
29
Parameters:
30
- label: Button text or renderable content
31
- variant: Button style ("default", "primary", "success", "warning", "error")
32
- disabled: Whether the button is disabled
33
"""
34
35
def press(self) -> None:
36
"""Programmatically press the button."""
37
38
class Input(Widget):
39
"""Single-line text input widget."""
40
41
class Changed(Message):
42
"""Sent when input value changes."""
43
def __init__(self, input: Input, value: str): ...
44
45
class Submitted(Message):
46
"""Sent when input is submitted (Enter key)."""
47
def __init__(self, input: Input, value: str): ...
48
49
def __init__(
50
self,
51
value: str = "",
52
placeholder: str = "",
53
*,
54
password: bool = False,
55
restrict: str | None = None,
56
type: str = "text",
57
max_length: int = 0,
58
**kwargs
59
):
60
"""
61
Initialize an input widget.
62
63
Parameters:
64
- value: Initial input value
65
- placeholder: Placeholder text when empty
66
- password: Whether to hide input characters
67
- restrict: Regex pattern to restrict input
68
- type: Input type ("text", "integer", "number")
69
- max_length: Maximum input length (0 for unlimited)
70
"""
71
72
# Properties
73
value: str
74
placeholder: str
75
cursor_position: int
76
77
class TextArea(Widget):
78
"""Multi-line text editor with syntax highlighting."""
79
80
class Changed(Message):
81
"""Sent when text content changes."""
82
83
def __init__(
84
self,
85
text: str = "",
86
*,
87
language: str | None = None,
88
theme: str = "monokai",
89
soft_wrap: bool = True,
90
tab_size: int = 4,
91
**kwargs
92
):
93
"""
94
Initialize a text area.
95
96
Parameters:
97
- text: Initial text content
98
- language: Programming language for syntax highlighting
99
- theme: Color theme for highlighting
100
- soft_wrap: Whether to wrap long lines
101
- tab_size: Number of spaces per tab
102
"""
103
104
def load_text(self, text: str) -> None:
105
"""Load new text content."""
106
107
# Properties
108
text: str
109
cursor_position: tuple[int, int]
110
111
class Checkbox(Widget):
112
"""Boolean checkbox input."""
113
114
class Changed(Message):
115
"""Sent when checkbox state changes."""
116
def __init__(self, checkbox: Checkbox, value: bool): ...
117
118
def __init__(self, label: str = "", *, value: bool = False, **kwargs):
119
"""
120
Initialize a checkbox.
121
122
Parameters:
123
- label: Checkbox label text
124
- value: Initial checked state
125
"""
126
127
def toggle(self) -> None:
128
"""Toggle the checkbox state."""
129
130
# Properties
131
value: bool
132
133
class Switch(Widget):
134
"""Toggle switch control."""
135
136
class Changed(Message):
137
"""Sent when switch state changes."""
138
139
def __init__(self, *, value: bool = False, **kwargs):
140
"""
141
Initialize a switch.
142
143
Parameters:
144
- value: Initial switch state
145
"""
146
147
def toggle(self) -> None:
148
"""Toggle the switch state."""
149
150
# Properties
151
value: bool
152
153
class RadioButton(Widget):
154
"""Individual radio button."""
155
156
class Changed(Message):
157
"""Sent when radio button selection changes."""
158
159
def __init__(self, label: str = "", *, value: bool = False, **kwargs):
160
"""
161
Initialize a radio button.
162
163
Parameters:
164
- label: Radio button label
165
- value: Whether this button is selected
166
"""
167
168
# Properties
169
value: bool
170
171
class RadioSet(Widget):
172
"""Group of radio buttons."""
173
174
class Changed(Message):
175
"""Sent when selection changes."""
176
def __init__(self, radio_set: RadioSet, pressed: RadioButton): ...
177
178
def __init__(self, *labels: str, **kwargs):
179
"""
180
Initialize a radio set.
181
182
Parameters:
183
- *labels: Radio button labels
184
"""
185
186
# Properties
187
pressed_button: RadioButton | None
188
pressed_index: int
189
190
class Select(Widget):
191
"""Dropdown selection widget."""
192
193
class Changed(Message):
194
"""Sent when selection changes."""
195
196
def __init__(
197
self,
198
options: Iterable[tuple[str, Any]] | Iterable[str],
199
*,
200
value: Any = None,
201
allow_blank: bool = True,
202
**kwargs
203
):
204
"""
205
Initialize a select widget.
206
207
Parameters:
208
- options: Selection options as (label, value) tuples or strings
209
- value: Initial selected value
210
- allow_blank: Whether to allow no selection
211
"""
212
213
# Properties
214
value: Any
215
options: list[tuple[str, Any]]
216
```
217
218
### Display and Content Widgets
219
220
Widgets for displaying static and dynamic content.
221
222
```python { .api }
223
class Static(Widget):
224
"""Display static rich content."""
225
226
def __init__(self, renderable: RenderableType = "", **kwargs):
227
"""
228
Initialize a static widget.
229
230
Parameters:
231
- renderable: Content to display (string, Rich object, etc.)
232
"""
233
234
def update(self, renderable: RenderableType) -> None:
235
"""Update the displayed content."""
236
237
class Label(Widget):
238
"""Simple text label."""
239
240
def __init__(self, text: str = "", **kwargs):
241
"""
242
Initialize a label.
243
244
Parameters:
245
- text: Label text content
246
"""
247
248
# Properties
249
text: str
250
251
class Pretty(Widget):
252
"""Pretty-printed Python objects using Rich."""
253
254
def __init__(self, obj: Any, **kwargs):
255
"""
256
Initialize a pretty widget.
257
258
Parameters:
259
- obj: Python object to pretty-print
260
"""
261
262
def update(self, obj: Any) -> None:
263
"""Update the displayed object."""
264
265
class Markdown(Widget):
266
"""Markdown content renderer."""
267
268
class LinkClicked(Message):
269
"""Sent when a markdown link is clicked."""
270
271
def __init__(self, markdown: str = "", **kwargs):
272
"""
273
Initialize a markdown widget.
274
275
Parameters:
276
- markdown: Markdown text content
277
"""
278
279
# Properties
280
markdown: str
281
282
class MarkdownViewer(Widget):
283
"""Scrollable markdown viewer with navigation."""
284
285
def __init__(self, markdown: str = "", **kwargs):
286
"""
287
Initialize a markdown viewer.
288
289
Parameters:
290
- markdown: Markdown content to display
291
"""
292
293
def go(self, location: str) -> None:
294
"""Navigate to a specific location in the document."""
295
296
class RichLog(Widget):
297
"""Rich text logging display."""
298
299
def __init__(self, *, max_lines: int | None = None, **kwargs):
300
"""
301
Initialize a rich log.
302
303
Parameters:
304
- max_lines: Maximum number of log lines to keep
305
"""
306
307
def write(self, content: RenderableType) -> None:
308
"""Write content to the log."""
309
310
def clear(self) -> None:
311
"""Clear all log content."""
312
313
class Log(Widget):
314
"""Simple text logging widget."""
315
316
def write_line(self, line: str) -> None:
317
"""Write a line to the log."""
318
319
def clear(self) -> None:
320
"""Clear the log."""
321
322
class Digits(Widget):
323
"""Large digital display for numbers."""
324
325
def __init__(self, value: str = "", **kwargs):
326
"""
327
Initialize a digits display.
328
329
Parameters:
330
- value: Number to display as string
331
"""
332
333
def update(self, value: str) -> None:
334
"""Update the displayed number."""
335
336
class ProgressBar(Widget):
337
"""Progress indicator bar."""
338
339
def __init__(
340
self,
341
total: float = 100,
342
*,
343
show_eta: bool = True,
344
show_percentage: bool = True,
345
**kwargs
346
):
347
"""
348
Initialize a progress bar.
349
350
Parameters:
351
- total: Total progress value
352
- show_eta: Whether to show estimated time remaining
353
- show_percentage: Whether to show percentage
354
"""
355
356
def advance(self, amount: float = 1) -> None:
357
"""Advance progress by amount."""
358
359
def update(self, *, completed: float = None, total: float = None) -> None:
360
"""Update progress values."""
361
362
# Properties
363
progress: float
364
percentage: float
365
```
366
367
### Navigation and Layout Widgets
368
369
Widgets for organizing content and navigation.
370
371
```python { .api }
372
class Header(Widget):
373
"""Application header bar."""
374
375
def __init__(self, *, show_clock: bool = False, **kwargs):
376
"""
377
Initialize a header.
378
379
Parameters:
380
- show_clock: Whether to display a clock
381
"""
382
383
class Footer(Widget):
384
"""Application footer bar."""
385
386
def __init__(self, **kwargs):
387
"""Initialize a footer."""
388
389
class Tabs(Widget):
390
"""Tab navigation container."""
391
392
class TabActivated(Message):
393
"""Sent when a tab is activated."""
394
def __init__(self, tabs: Tabs, tab: Tab): ...
395
396
def add_tab(self, tab: str | Tab, **kwargs) -> Tab:
397
"""
398
Add a new tab.
399
400
Parameters:
401
- tab: Tab instance or tab label
402
403
Returns:
404
The added Tab instance
405
"""
406
407
def remove_tab(self, tab_id: str) -> None:
408
"""Remove a tab by ID."""
409
410
# Properties
411
active_tab: Tab | None
412
413
class Tab(Widget):
414
"""Individual tab component."""
415
416
def __init__(self, label: str, **kwargs):
417
"""
418
Initialize a tab.
419
420
Parameters:
421
- label: Tab display label
422
"""
423
424
# Properties
425
label: str
426
427
class TabbedContent(Widget):
428
"""Tabbed content container."""
429
430
def add_pane(self, pane: TabPane) -> None:
431
"""Add a content pane."""
432
433
def remove_pane(self, pane_id: str) -> None:
434
"""Remove a content pane."""
435
436
class TabPane(Widget):
437
"""Individual tab content pane."""
438
439
def __init__(self, title: str, **kwargs):
440
"""
441
Initialize a tab pane.
442
443
Parameters:
444
- title: Pane title for the tab
445
"""
446
447
class Tree(Widget):
448
"""Hierarchical tree view."""
449
450
class NodeSelected(Message):
451
"""Sent when a tree node is selected."""
452
453
class NodeExpanded(Message):
454
"""Sent when a tree node is expanded."""
455
456
def __init__(self, label: str, **kwargs):
457
"""
458
Initialize a tree.
459
460
Parameters:
461
- label: Root node label
462
"""
463
464
def add(self, label: str, *, data: Any = None) -> TreeNode:
465
"""
466
Add a child node.
467
468
Parameters:
469
- label: Node display label
470
- data: Associated node data
471
472
Returns:
473
The created TreeNode
474
"""
475
476
def clear(self) -> None:
477
"""Clear all tree nodes."""
478
479
class DirectoryTree(Widget):
480
"""File system directory browser."""
481
482
class DirectorySelected(Message):
483
"""Sent when a directory is selected."""
484
485
class FileSelected(Message):
486
"""Sent when a file is selected."""
487
488
def __init__(self, path: str | Path, **kwargs):
489
"""
490
Initialize a directory tree.
491
492
Parameters:
493
- path: Root directory path
494
"""
495
496
def reload(self) -> None:
497
"""Reload the directory tree."""
498
499
class Collapsible(Widget):
500
"""Expandable/collapsible sections."""
501
502
class Toggled(Message):
503
"""Sent when collapsed state changes."""
504
505
def __init__(
506
self,
507
title: str = "",
508
*,
509
collapsed: bool = True,
510
**kwargs
511
):
512
"""
513
Initialize a collapsible.
514
515
Parameters:
516
- title: Section title
517
- collapsed: Initial collapsed state
518
"""
519
520
def toggle(self) -> None:
521
"""Toggle collapsed/expanded state."""
522
523
# Properties
524
collapsed: bool
525
526
class Rule(Widget):
527
"""Horizontal or vertical separator line."""
528
529
def __init__(
530
self,
531
orientation: str = "horizontal",
532
*,
533
line_style: str = "solid",
534
**kwargs
535
):
536
"""
537
Initialize a rule.
538
539
Parameters:
540
- orientation: "horizontal" or "vertical"
541
- line_style: Line drawing style
542
"""
543
```
544
545
### Data and List Widgets
546
547
Widgets for displaying and interacting with structured data.
548
549
```python { .api }
550
class DataTable(Widget):
551
"""Spreadsheet-like data grid."""
552
553
class RowSelected(Message):
554
"""Sent when a row is selected."""
555
556
class CellSelected(Message):
557
"""Sent when a cell is selected."""
558
559
def __init__(self, *, zebra_stripes: bool = False, **kwargs):
560
"""
561
Initialize a data table.
562
563
Parameters:
564
- zebra_stripes: Whether to alternate row colors
565
"""
566
567
def add_column(
568
self,
569
key: str,
570
*,
571
label: str | None = None,
572
width: int | None = None,
573
**kwargs
574
) -> None:
575
"""
576
Add a table column.
577
578
Parameters:
579
- key: Unique column identifier
580
- label: Column header label
581
- width: Column width in characters
582
"""
583
584
def add_row(self, *cells: Any, **kwargs) -> RowKey:
585
"""
586
Add a data row.
587
588
Parameters:
589
- *cells: Cell values for the row
590
591
Returns:
592
Key identifying the added row
593
"""
594
595
def remove_row(self, row_key: RowKey) -> None:
596
"""Remove a row by key."""
597
598
def clear(self, columns: bool = False) -> None:
599
"""
600
Clear table data.
601
602
Parameters:
603
- columns: Whether to also remove columns
604
"""
605
606
# Properties
607
row_count: int
608
column_count: int
609
610
class ListView(Widget):
611
"""Scrollable list container."""
612
613
class Selected(Message):
614
"""Sent when list item is selected."""
615
616
class Highlighted(Message):
617
"""Sent when list item is highlighted."""
618
619
def __init__(self, *children: ListItem, **kwargs):
620
"""
621
Initialize a list view.
622
623
Parameters:
624
- *children: Initial list items
625
"""
626
627
def append(self, item: ListItem) -> None:
628
"""Add item to end of list."""
629
630
def extend(self, items: Iterable[ListItem]) -> None:
631
"""Add multiple items to list."""
632
633
def clear(self) -> None:
634
"""Remove all list items."""
635
636
# Properties
637
index: int | None
638
639
class ListItem(Widget):
640
"""Individual list item."""
641
642
def __init__(self, child: Widget, **kwargs):
643
"""
644
Initialize a list item.
645
646
Parameters:
647
- child: Widget to display in the item
648
"""
649
650
class OptionList(Widget):
651
"""Selectable option list."""
652
653
class OptionSelected(Message):
654
"""Sent when an option is selected."""
655
656
def __init__(self, *options: str | Option, **kwargs):
657
"""
658
Initialize an option list.
659
660
Parameters:
661
- *options: Initial options
662
"""
663
664
def add_option(self, option: str | Option) -> None:
665
"""Add a new option."""
666
667
def remove_option(self, option_id: str) -> None:
668
"""Remove an option by ID."""
669
670
def clear_options(self) -> None:
671
"""Remove all options."""
672
673
# Properties
674
highlighted: int | None
675
676
class SelectionList(Widget):
677
"""Multi-select list widget."""
678
679
class SelectionToggled(Message):
680
"""Sent when selection changes."""
681
682
def __init__(self, *selections: Selection, **kwargs):
683
"""
684
Initialize a selection list.
685
686
Parameters:
687
- *selections: Initial selections
688
"""
689
690
def select_all(self) -> None:
691
"""Select all items."""
692
693
def deselect_all(self) -> None:
694
"""Deselect all items."""
695
696
# Properties
697
selected: list[int]
698
```
699
700
### Utility and System Widgets
701
702
Specialized widgets for development, loading states, and system functions.
703
704
```python { .api }
705
class Placeholder(Widget):
706
"""Development placeholder widget."""
707
708
def __init__(self, label: str = "Placeholder", **kwargs):
709
"""
710
Initialize a placeholder.
711
712
Parameters:
713
- label: Placeholder display text
714
"""
715
716
class LoadingIndicator(Widget):
717
"""Loading animation indicator."""
718
719
def __init__(self, **kwargs):
720
"""Initialize a loading indicator."""
721
722
class Tooltip(Widget):
723
"""Hover help text."""
724
725
def __init__(self, text: str, **kwargs):
726
"""
727
Initialize a tooltip.
728
729
Parameters:
730
- text: Tooltip text content
731
"""
732
733
class ContentSwitcher(Widget):
734
"""Dynamic content switching widget."""
735
736
def __init__(self, **kwargs):
737
"""Initialize a content switcher."""
738
739
def current(self) -> str | None:
740
"""Get the current content identifier."""
741
742
class Welcome(Widget):
743
"""Welcome screen component."""
744
745
def __init__(self, **kwargs):
746
"""Initialize a welcome screen."""
747
```
748
749
## Usage Examples
750
751
### Form with Input Controls
752
753
```python
754
from textual.app import App
755
from textual.containers import Container, Horizontal
756
from textual.widgets import Button, Input, Checkbox, Select
757
758
class FormApp(App):
759
def compose(self):
760
yield Container(
761
Input(placeholder="Enter your name", id="name"),
762
Input(placeholder="Enter email", id="email"),
763
Select([("Option 1", 1), ("Option 2", 2)], id="choice"),
764
Checkbox("Subscribe to newsletter", id="subscribe"),
765
Horizontal(
766
Button("Submit", variant="primary", id="submit"),
767
Button("Cancel", id="cancel"),
768
),
769
id="form"
770
)
771
772
def on_button_pressed(self, event: Button.Pressed):
773
if event.button.id == "submit":
774
name = self.query_one("#name", Input).value
775
email = self.query_one("#email", Input).value
776
choice = self.query_one("#choice", Select).value
777
newsletter = self.query_one("#subscribe", Checkbox).value
778
779
self.log(f"Form submitted: {name}, {email}, {choice}, {newsletter}")
780
```
781
782
### Data Table
783
784
```python
785
from textual.app import App
786
from textual.widgets import DataTable
787
788
class TableApp(App):
789
def compose(self):
790
yield DataTable(zebra_stripes=True)
791
792
def on_mount(self):
793
table = self.query_one(DataTable)
794
795
# Add columns
796
table.add_column("Name", key="name")
797
table.add_column("Age", key="age")
798
table.add_column("City", key="city")
799
800
# Add rows
801
table.add_row("Alice", 30, "New York")
802
table.add_row("Bob", 25, "London")
803
table.add_row("Carol", 35, "Tokyo")
804
```