Dev Explorer
Advice, tutorials and tips for beginner and experienced software/web application developers
Developing Console Interfaces Using Python and Curses
Date Published: 18/05/2009 14:39In general I spend quite a lot of time building python applications and scripts for home and office use. These vary from scripts at home for cleaning up my music library to decent sized applications implementing business logic. One thing that can be quite difficult when carrying out these tasks is interacting with the code I am writing without building a fully fledged interface. When programming this way I use the python curses library to allow me to build a quick temporary command line interface to use and test my application/script.
When I first started using curses I found it difficult understand how it worked. As someone experienced in using generic x window GUI builders I struggled to get to grips with curses and how I could use it in a similar way. Curses is much more basic than these GUI packages but can be just as powerful. It can also be much quicker for building small testing interfaces while still in development to avoid the toil of building and rebuilding GUI modules.
In this article I am going to document the basics of python curses programming to help readers understand the concept and curses' benefits. There isn't much demand for command line interfaces but they can be extremely useful just to assist you in the early build stages of coding projects. I have also built a class library which implements a pseudo window system using curses, making building proper interfaces in curses much simpler but for now I'll stick to the basics. If you're interested in getting hold of my class library give me a buzz in the comment section below and I'll tidy it up and post it.
Note
Just a quick note to say this is a basic introduction to curses designed to familiarise people who have never used the library before. It covers all areas to get you going with your curses project but to create a really good usable interface you will have to combine the lessons here with other curses features (such as windows which aren't really covered here) and your own ingenuity.
About Curses
For those who don't know what curses is here's a quick overview. Curses is a programming library which allows developers to create text based interfaces in a terminal environment. As opposed to positioning and sizing objects using pixels like the average GUI curses uses characters and lines and curses colour support is very basic. Curses is available for many different programming languages such as python, c++, c and perl. Curses has been ported to various platforms but since I am a python programmer using Linux this example will be in context of that environment. It is possible to monitor key press events from your user which provides the interaction between them and your script/application. There are many established applications available which make use of the curses library such as Nano (text editor), Rtorrent (torrent client) and Lynx (web browser).
The Basics
To use curses you must first import the curses library itself.
import curses
Now we have included the library we can start working with the curses by instantiating a screen.
screen = curses.initscr()
This will give you control of your terminal window and allow you to monitor events such as key presses from your user. The following options are the default I use to ensure the screen acts the way I want it to.
curses.noecho()
curses.curs_set(0)
screen.keypad(1)
The first line stops curses from outputting key presses from the user onto the screen. The second option removes the cursor from the screen and the third sets the mode which the screen uses when capturing key presses (this is important later).
Now we can output strings onto this new screen we have created using the addstr() method of the screen. These strings can be styled and positioned depending on what it is you want to do with them. The following is a basic addstr() command which just places “This is a String” in plain text in the default style (the style of the terminal window) in the default position (top left).
screen.addstr("This is a String")
To position this string somewhere specific we can use two coordinate parameters (in terms of lines) before the string in the method call like so. Be careful when positioning strings in curses as whereas in most technologies the left position comes first before the top position, in curses this is the other way around and can cause confusion.
top_pos = 12
left_pos = 12
screen.addstr(top_pos, left_pos, "Positioned String")
To style a string there are some curses constants which are available. There are also some more detailed options which you can use but I shall not be covering them here. Here is a string with reverse style (reverse of the terminal default). There are a list of these styles available in the python documentation for the curses library (http://docs.python.org/library/curses.html).
screen.addstr("Reverse Styled String", curses.A_REVERSE)
Next we are going to deal with event handling in curses. This is done using an endless while loop and the getch() method of the screen.
while True:
event = screen.getch()
if event == ord("q"): break
The code above loops infinitely capturing key presses and if that key press is a q it will break the loop and continue executing the script. To react to key presses simply add entries in the if statement catching the correct key. For letters and numbers you can use the ord() function to turn a string into a hexadecimal value which is what the getch() method of the screen returns. Here is an example of a script catching various key presses.
while True:
event = screen.getch()
if event == ord("q"): break
elif event == ord("p"):
screen.clear()
screen.addstr("The User Pressed Lower Case p")
elif event == ord("P"):
screen.clear()
screen.addstr("The User Pressed Upper Case P")
elif event == ord("3"):
screen.clear()
screen.addstr("The User Pressed 3")
elif event == ord(" "):
screen.clear()
screen.addstr("The User Pressed The Space Bar")
The curses library has specific constants defined for special key presses such as enter (curses.KEY_ENTER), backspace (curses.KEY_BACKSPACE) and the arrow keys (curses.KEY_UP, curses.KEY_DOWN, curses.KEY_LEFT, curses.KEY_RIGHT). There is a list of constants in the official python documentation (http://docs.python.org/library/curses.html) which is generally very helpful and I would recommend keeping this bookmarked whilst working with curses. Here is a piece of code using curses to capture special key presses.
while True:
event = screen.getch()
if event == ord("q"): break
elif event == curses.KEY_UP:
screen.clear()
screen.addstr("The User Pressed UP")
elif event == curses.KEY_DOWN:
screen.clear()
screen.addstr("The User Pressed DOWN")
Now we have handled the screen and events we can end the application without breaking the terminal session. Make sure you include the following command at the end of any script you use curses with to make sure that when execution finishes you can continue using the terminal.
curses.endwin()
If you forget to use the above command or your program crashes without reaching it, just type reset and hit enter. You won't be able to see the letters come up but the terminal will still take commands therefore by entering reset it should clear the command line.
The Full Script
Here is the above points put together into one simple usable piece of code which you can customise to meet your needs.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import curses
screen = curses.initscr()
curses.noecho()
curses.curs_set(0)
screen.keypad(1)
screen.addstr("This is a Sample Curses Script\n\n")
while True:
event = screen.getch()
if event == ord("q"): break
curses.endwin()
Conclusion
So there we have a basic introduction to the curses library in python. This should be enough to help beginners understand how it works and how it can be used. When used properly curses is a very powerful tool and can easily be used to create very nice detailed interfaces within the command line. This is just the tip of the iceberg so if you feel you want some more information on how to achieve certain results feel free to comment below, or drop me an email from the contact me page and I'll try my best to answer your questions.
5 Most Recent Articles
A quick guide for ASP.NET developers on how to manually trigger ASP.NET events from JavaScript.
An article for users of MySQL databases describing how they can use advanced stored procedures to improve efficiently in their applications.
A guide for LAMP developers to using stored procedures in MySQL and extending the MySQLi class.
An introduction to using the xlwt and xlrd modules for python to interact with Microsoft Excel spreadsheets.
This is an introduction to making HTTP requests from a python script/application using httplib.
Liorithiel
18/05/2009 23:55
A comment should be added that python curses support lacks proper unicode handling. Although you can put unicode characters on screen, you have no full control, f.e. on how big they are (some of them can take two cells).
Dev Explorer
19/05/2009 08:05
That is indeed correct liorithiel. I had a problem with that when using a special character like the copyright symbol as it displayed a strange character before it in some instances depending on the encoding of the script file. Common letters, numbers and punctuation are all properly supported. I've heard this should be all sorted out in Python 3 but I couldn't confirm this as I'm not moving over until all my extra libraries are ported.
Jozef Sevcik
19/05/2009 22:01
Great article, thanks.
Anon
25/10/2009 05:23
You can cut out several lines and make the script more robust against bugs and errors by using curses.wrapper: import curses import curses.wrapper def main(screen): screen.addstr("This is a Sample Curses Script") while True: event = screen.getch() if event == ord("q"): break if __name__ == '__main__': curses.wrapper(main)
anon
25/10/2009 05:24
well that didn't format properly.
Leandrosf
21/11/2009 23:28
Where i can find advanced examples about python and curses? Thank you
JAvier
14/01/2010 20:27
two questions. I know nothing about curses. I like call a program. I want background appears python program. It is a print of characters. How do I call? On the other hand ... ¿Call do a single print of python? ¿as ? Sorry for my inglish. Speak spanish. Good Bye and Thank You!!!
JAvier
14/01/2010 20:27
two questions. I know nothing about curses. I like call a program. I want background appears python program. It is a print of characters. How do I call? On the other hand ... ¿Call do a single print of python? ¿as ? Sorry for my inglish. Speak spanish. Good Bye and Thank You!!!
alex
08/02/2010 16:34
this is really cool. I work in robotics and use command line interfaces heavily. In a search for the up arrow key code is stumbled upon this. I could really do some great stuff with this. The robots i use are running linux and do have python 2.6. However when i try import the curses library it throws an error, it can not find it. I have been looking for the package itself but have been unable to find it. I would like to put it on my robots. DO you know where I can get the library?
Felix
25/02/2010 07:31
I agree with Anon. Use curses.wrapper! http://docs.python.org/library/curses.html#module-curses.wrapper