Developing Console Interfaces Using Python and Curses

Date Published: 18/05/2009 14:39 Image of the Python Logo

In 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

Screenshot of Curses Text Editor Nano

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.

Comments

Sorry comments are currently disabled for maintenence

5 Most Recent Articles

Manually Triggering Events in ASP.NET from JavaScript

A quick guide for ASP.NET developers on how to manually trigger ASP.NET events from JavaScript.

Advanced Use of MySQL Stored Procedures

An article for users of MySQL databases describing how they can use advanced stored procedures to improve efficiently in their applications.

Using MySQL Stored Procedures and Extending MySQLi in PHP

A guide for LAMP developers to using stored procedures in MySQL and extending the MySQLi class.

Reading and Writing to Excel Spreadsheets in Python

An introduction to using the xlwt and xlrd modules for python to interact with Microsoft Excel spreadsheets.

Interact with the Web Using Python and the HTTP Library

This is an introduction to making HTTP requests from a python script/application using httplib.

Sponsors