Urwid Tutorial

Urwid Home Page / Example Screenshots / Wide Character Screenshots / Tutorial / Reference

Note: This tutorial requires Urwid 1.8.5 or later.
1. Hello World
2. Conversation

1. Hello World

1.1. Minimal Urwid Application

This program displays the string "Hello World" in the top left corner of the screen and waits for a keypress before exiting.
import urwid.curses_display
import urwid

ui = urwid.curses_display.Screen()

def run():
	canvas = urwid.Canvas( ["Hello World"] )
	ui.draw_screen( (20, 1), canvas )
	
	while not ui.get_input():
		pass

ui.run_wrapper( run )
Creating canvases directly is generally only done when writing custom widget classes. Note that the draw_screen function must be passed a canvas and a screen size that matches it.
Hello World         


1.2. Text and Filler Widgets

This program displays the string "Hello World" in the center of the screen and waits for a keypress before exiting.
import urwid.curses_display
import urwid

ui = urwid.curses_display.Screen()

def run():
	cols, rows = ui.get_cols_rows()

	txt = urwid.Text("Hello World", align="center")
	fill = urwid.Filler( txt )

	canvas = fill.render( (cols, rows) )
	ui.draw_screen( (cols, rows), canvas )

	while not ui.get_input():
		pass

ui.run_wrapper( run )
Flow widgets and box widgets are not interchangeable. The first parameter of the render function of a box widget is a two-element tuple (columns, rows) and the first parameter of the render function of a flow widget is a one-element tuple (columns, ). This difference makes sure that when the wrong type of widget is used, such as a box widget inside a filler widget, a ValueError exception will be thrown.
                     
                     
                     
     Hello World     
                     
                     
                     


1.3. AttrWrap Widgets and Text Attributes

This program displays the string "Hello World" in the center of the screen. It uses different attributes used for the text, the space on either side of the text and the space above and below the text. It then and waits for a keypress before exiting.
import urwid.curses_display
import urwid

ui = urwid.curses_display.Screen()

ui.register_palette( [
	('banner', 'black', 'light gray', ('standout', 'underline')),
	('streak', 'black', 'dark red', 'standout'),
	('bg', 'black', 'dark blue'),
	] )

def run():
	cols, rows = ui.get_cols_rows()

	txt = urwid.Text(('banner', " Hello World "), align="center")
	wrap1 = urwid.AttrWrap( txt, 'streak' )
	fill = urwid.Filler( wrap1 )
	wrap2 = urwid.AttrWrap( fill, 'bg' )

	canvas = wrap2.render( (cols, rows) )
	ui.draw_screen( (cols, rows), canvas )

	while not ui.get_input():
		pass

ui.run_wrapper( run )
AttrWrap widgets will behave like flow widgets or box widgets depending on how they are called. The filler widget treats the first AttrWrap widget as a flow widget when calling its render function, so the AttrWrap widget calls the text widget's render function the same way. The second AttrWrap is used as the topmost widget and treated as a box widget, so it calls the filler render function in the same way.
                     
                     
                     
     Hello World     
                     
                     
                     


1.4. Live Resizing

This program displays the string "Hello World" in the center of the screen. It uses different attributes used for the text, the space on either side of the text and the space above and below the text. When the window is resized it will repaint the screen, and it will exit when "Q" is pressed.
import urwid.curses_display
import urwid

ui = urwid.curses_display.Screen()

ui.register_palette( [
	('banner', 'black', 'light gray', ('standout', 'underline')),
	('streak', 'black', 'dark red', 'standout'),
	('bg', 'black', 'dark blue'),
	] )

def run():
	cols, rows = ui.get_cols_rows()

	txt = urwid.Text(('banner', " Hello World "), align="center")
	wrap1 = urwid.AttrWrap( txt, 'streak' )
	fill = urwid.Filler( wrap1 )
	wrap2 = urwid.AttrWrap( fill, 'bg' )

	while True:
		canvas = wrap2.render( (cols, rows) )
		ui.draw_screen( (cols, rows), canvas )

		keys = ui.get_input()
		if "q" in keys or "Q" in keys:
			break
		if "window resize" in keys:
			cols, rows = ui.get_cols_rows()	

ui.run_wrapper( run )
The get_input function will return "window resize" among keys pressed when the window is resized. It is a good idea to check for uppercase and lowercase letters on input because it is easy to users to forget that Caps Lock is on.
                     
                     
                     
     Hello World     
                     
                     
                     
          
          
          
   Hello  
  World   
          
          
          
          
                              
          Hello World         
                              
  Hello World  
               


2. Conversation

2.1. Edit Widgets

This program asks for your name then responds "Nice to meet you, (your name)."
import urwid.curses_display
import urwid

ui = urwid.curses_display.Screen()

def run():
	cols, rows = ui.get_cols_rows()

	ask = urwid.Edit("What is your name?\n")
	fill = urwid.Filler( ask )
	reply = None

	while True:
		canvas = fill.render( (cols, rows), focus=True )
		ui.draw_screen( (cols, rows), canvas )

		keys = ui.get_input()
		for k in keys:
			if k == "window resize":
				cols, rows = ui.get_cols_rows()
				continue
			if reply is not None:
				return
			if k == "enter": 
				reply = urwid.Text( "Nice to meet you,\n"+
					ask.edit_text+"." )
				fill.body = reply
			fill.keypress( (cols, rows), k )

ui.run_wrapper( run )
The Edit widget has many capabilities. It lets you make corrections and move the cursor around with the HOME, END and arrow keys. It is based on the Text widget so it supports the same wrapping and alignment modes.
                     
                     
What is your name?   
                     
                     
                     
                     
                     
                     
What is your name?   
Arthur, King of the  
Britons              
                     
                     
                     
                     
Nice to meet you,    
Arthur, King of the  
Britons.             
                     
                     


2.2. Frame and ListBox Widgets

This program asks for your name and responds "Nice to meet you, (your name)" while you type your name. F1 exits.
import urwid.curses_display
import urwid

class Conversation:
	def __init__(self):
		self.items = [ self.new_question() ]
		self.listbox = urwid.ListBox( self.items )
		instruct = urwid.Text("Press F1 to exit.")
		header = urwid.AttrWrap( instruct, 'header' )
		self.top = urwid.Frame(self.listbox, header)

	def main(self):
		self.ui = urwid.curses_display.Screen()
		self.ui.register_palette([
			('header', 'black', 'dark cyan', 'standout'),
			('I say', 'dark blue', 'default', 'bold'),
			])
		self.ui.run_wrapper( self.run )

	def run(self):
		size = self.ui.get_cols_rows()

		while True:
			self.draw_screen( size )
			keys = self.ui.get_input()
			if "f1" in keys: 
				break
			for k in keys:
				if k == "window resize":
					size = self.ui.get_cols_rows()
					continue
				self.top.keypress( size, k )
			if keys:
				name = self.items[0].edit_text
				self.items[1:2] = [self.new_answer(name)]
					
	def draw_screen(self, size):
		canvas = self.top.render( size, focus=True )
		self.ui.draw_screen( size, canvas )
	
	def new_question(self):
		return urwid.Edit(('I say',"What is your name?\n"))
	
	def new_answer(self, name):
		return urwid.Text(('I say',"Nice to meet you, "+name+"\n"))
			

Conversation().main()
When changing the contents of ListBox widgets remember to use in-place editing operations on the list, eg. "list = list + [something]" will not work, use "list += [something]" instead.
Press F1 to exit.    
What is your name?   
                     
                     
                     
                     
                     
Press F1 to exit.    
What is your name?   
Tim t                
Nice to meet you, Tim
t                    
                     
                     
Press F1 to exit.    
What is your name?   
Tim the Ench         
Nice to meet you, Tim
the Ench             
                     
                     
Press F1 to exit.    
What is your name?   
Tim the Enchanter    
Nice to meet you, Tim
the Enchanter        
                     
                     


2.3. ListBox Widgets Continued

This program asks for your name and responds "Nice to meet you, (your name)." It then asks again, and again. Old values may be changed and the responses will be updated when you press ENTER. F1 exits.

Update the 2.2 program with this code:
	def run(self):
		size = self.ui.get_cols_rows()

		while True:
			self.draw_screen( size )
			keys = self.ui.get_input()
			if "f1" in keys: 
				break
			for k in keys:
				if k == "window resize":
					size = self.ui.get_cols_rows()
					continue
				self.keypress( size, k )
					
	def keypress(self, size, k):
		if k == "enter":
			widget, pos = self.listbox.get_focus()
			if not hasattr(widget,'edit_text'):
				return
			
			answer = self.new_answer( widget.edit_text )
			
			if pos == len(self.items)-1:
				self.items.append( answer )
				self.items.append( self.new_question() )
			else:
				self.items[pos+1:pos+2] = [answer]

			self.listbox.set_focus( pos+2, coming_from='above' )
			widget, pos = self.listbox.get_focus()
			widget.set_edit_pos(0)
		else:
			self.top.keypress( size, k )
The ListBox widget tries to do the most sensible thing when scrolling and changing focus. When the widgets displayed are all unselectable the ListBox widget will always scroll. When some widgets are selectable it will try changing focus before scrolling, possibly scrolling a few lines to bring in a full selectable widget. When all the widgets are selectable it will only scroll when the cursor reaches the top or bottom edge.
Press F1 to exit.      
What is your name?     
Abe                    
Nice to meet you, Abe  
                       
What is your name?     
Bob                    
                       
                       
                       
                       
                       
                       
Press F1 to exit.      
Nice to meet you, Abe  
                       
What is your name?     
Bob                    
Nice to meet you, Bob  
                       
What is your name?     
Carl                   
Nice to meet you, Carl 
                       
What is your name?     
                       
Press F1 to exit.      
Nice to meet you, Bob  
                       
What is your name?     
Carl                   
Nice to meet you, Carl 
                       
What is your name?     
Dave                   
Nice to meet you, Dave 
                       
What is your name?