By this point, you have learned enough Python to start building interactive apps. If you are set on the larger projects such as building a video game, making a visualization, or making a web app, you can skip this section. But if you would like to start building some simpler apps that run directly in your terminal, check out this notebook.
Terminal apps are a great way to build the core functionality of a program you are interested in, which can then be extended into a more accessible format.
A terminal application is simply an application that runs inside the terminal. By now, you have seen that most of the output from your programs is printed to the terminal. By learning a few more techniques such as clearing the terminal screen and "pickling" data, you can create full-fledged standalone applications that run in the terminal.
A terminal app starts just like any other program, but it finishes when the user selects an action that causes the program to quit. In terminal applications, this often means something like entering 'q' or 'quit'.
If you are on a linux system, you can run some terminal apps right now. Many of these are not written in Python, but that doesn't matter. They are applications that run in the terminal, and you can write your own apps in Python. If you are on Linux, try the following:
top
.nano test_file.txt
. vi
.:q
to exit.There are quite a few other terminal apps. Let's figure out how to write some of our own.
Unless you see people working in technical fields on a regular basis, it's quite possible you have not seen many people using terminal applications. Even so, there are a number of reasons to consider writing a few terminal apps of your own.
input("Please tell me some information: ")
. When you want some information in a graphical program, you have to build text boxes and buttons for submitting information. That has gotten pretty simple these days, but it is still more complicated than what you will do when writing terminal applications. Terminal applications let you focus on getting the logic right, rather than building a graphical interface.Let's define a simple terminal app that we can make in this notebook. Greeter will:
Now we will go over a few things we need to know in order to build this app, and then we will build it.
You may have noticed that your programs which produce a lot of output scroll down the terminal. This keeps your program from looking like a running application. It's fairly easy to clear the terminal screen any time you want to, though.
Let's make a really simple program to show this:
In [12]:
# Show a simple message.
print("I like climbing mountains.")
Now we will modify the program so that the screen is cleared right after the message is displayed:
In [ ]:
###highlight=[2,7,8]
import os
# Show a simple message.
print("I like climbing mountains.")
# Clear the screen.
os.system('clear')
There is no output to show here, because as soon as the message is displayed the screen is cleared. Run the program on your computer to see this.
The first line imports the module "os". This is a set of functions that let you interact with your operating system's commands. The line os.system('clear')
tells Python to talk to the operating system, and ask the system to run the clear
command. You can see this same thing by typing clear
in any open terminal window.
On a technical note, the screen is not actually erased when you enter the clear
command. Instead, the terminal is scrolled down one vertical window length. If you scroll the terminal window up, you can see the old output. This is fine, and it can actually be good to be able to scroll back up and look at some output you might have missed.
On Windows: The command to clear the terminal screen is different on Windows. The command os.system('cls')
should work.
Let's consider how to make a title bar that stays at the top of the screen. We can't make this yet, because as our program creates more and more output, any title bar we print will disappear up the top of the screen.
Now that we know how to clear the screen, we can rebuild the screen that our user sees any time we want. Let's start out by printing a title bar for Greeter. If you want to follow along, type out the following code and save it as greeter.py
:
In [16]:
# Greeter is a terminal application that greets old friends warmly,
# and remembers new friends.
# Display a title bar.
print("\t**********************************************")
print("\t*** Greeter - Hello old and new friends! ***")
print("\t**********************************************")
Everything is fine so far. Let's look at what happens when we have a bunch of output below the title bar. Copy the following code onto your machine and run it.
In [11]:
###highlight=[10,11,12]
# Greeter is a terminal application that greets old friends warmly,
# and remembers new friends.
# Display a title bar.
print("\t**********************************************")
print("\t*** Greeter - Hello old and new friends! ***")
print("\t**********************************************")
# Display a bunch of output, representing a long-running program.
for x in range(0,51):
print("\nWe have done %d fun and interesting things together!" % x)
I won't include the output here. All you should see are a number of lines about doing fun and interesting things together, with the title bar far up your terminal window, well above the visible portion.
In [7]:
# Import the sleep function.
from time import sleep
print("I'm going to sleep now.")
# Sleep for 3 seconds.
sleep(3)
print("I woke up!")
If you ran this code, you should have seen the message "I'm going to sleep now." Then you should have seen nothing happen for 3 seconds, and then you should have seen the message "I woke up."
The first line imports the sleep() function from the module time. You will be seeing more and more import statements. Basically, we are importing a function from Python's standard library of functions.
The sleep function can accept decimal inputs as well, so you can pause for as short or as long in your programs as you want.
In [ ]:
###highlight=[2,12,13,15,16,17,18,19,20,21,22]
from time import sleep
# Greeter is a terminal application that greets old friends warmly,
# and remembers new friends.
# Display a title bar.
print("\t**********************************************")
print("\t*** Greeter - Hello old and new friends! ***")
print("\t**********************************************")
# Print a bunch of information, in short intervals
names = ['aaron', 'brenda', 'cyrene', 'david', 'eric']
# Print each name 5 times.
for name in names:
# Pause for 1 second between batches, and then skip two lines.
sleep(1)
print("\n\n")
for x in range(0,5):
print(name.title())
You should have seen the title bar appear, and then groups of names appear every second for a while. At some point, the title bar probably disappeared. Now we are going to modify the code so that the screen is cleared each time through the loop.
In [ ]:
###highlight=[3,18,19,20,21,22,23,24,25,26,27,28,29,30,31]
from time import sleep
import os
# Greeter is a terminal application that greets old friends warmly,
# and remembers new friends.
# Display a title bar.
print("\t**********************************************")
print("\t*** Greeter - Hello old and new friends! ***")
print("\t**********************************************")
# Print a bunch of information, in short intervals
names = ['aaron', 'brenda', 'cyrene', 'david', 'eric']
# Print each name 5 times.
for name in names:
# Clear the screen before listing names.
os.system('clear')
# Display the title bar.
print("\t**********************************************")
print("\t*** Greeter - Hello old and new friends! ***")
print("\t**********************************************")
print("\n\n")
for x in range(0,5):
print(name.title())
# Pause for 1 second between batches.
sleep(1)
This time you should see the same output, but you should see a steady title bar, and the new information takes the place of the old information instead of being listed below the old output. We have a "running" application!
If you understood the section Introducing Functions, you may have noticed that we have some repeated code in the last program listing. Any time you see a significant amount of repetition, you can probably introduce a function.
The repeated code in this example involves displaying the title bar. Let's write a function to show the title bar.
In [ ]:
###highlight=[8,9,10,11,12,13,14,22]
from time import sleep
import os
# Greeter is a terminal application that greets old friends warmly,
# and remembers new friends.
def display_title_bar():
# Clears the terminal screen, and displays a title bar.
os.system('clear')
print("\t**********************************************")
print("\t*** Greeter - Hello old and new friends! ***")
print("\t**********************************************")
# Print a bunch of information, in short intervals
names = ['aaron', 'brenda', 'cyrene', 'david', 'eric']
# Print each name 5 times.
for name in names:
display_title_bar()
print("\n\n")
for x in range(0,5):
print(name.title())
# Pause for 1 second between batches.
sleep(1)
Again, you really need to copy this code onto your own machine and run it locally to see this in action. You should see the same behavior as before.
It's interesting to note that having good comments makes writing functions easier. The comment just before the code for displaying the title bar was "Display the title bar." This practically gives us a name for the function that will take over this job: display_title_bar
. If we use short, descriptive function names that tell us exactly what the function does, it becomes much easier to follow what is happening in the program. We can see that we don't even need a comment when the function is called, because the function name itself is so informative. The name display_title_bar
is much better than something like title
, although you don't want to go overboard with a name such as clear_screen_and_display_title_bar
.
This code gets a bit hard to read overall though, because we have several sections of code. You can use comments to visually break up your programs. Here's what that can look like in our current program:
In [ ]:
###highlight=[9,20]
from time import sleep
import os
# Greeter is a terminal application that greets old friends warmly,
# and remembers new friends.
### FUNCTIONS ###
def display_title_bar():
# Clears the terminal screen, and displays a title bar.
os.system('clear')
print("\t**********************************************")
print("\t*** Greeter - Hello old and new friends! ***")
print("\t**********************************************")
### MAIN PROGRAM ###
# Print a bunch of information, in short intervals
names = ['aaron', 'brenda', 'cyrene', 'david', 'eric']
# Print each name 5 times.
for name in names:
display_title_bar()
print("\n\n")
for x in range(0,5):
print(name.title())
# Pause for 1 second between batches.
sleep(1)
If this is more appealing visually to you, feel free to use these kind of "section header" comments in your longer programs.
Let's continue building Greeter. There are a couple more things we will introduce, but you should be able to follow everything we do.
We will start with the previous part where we have a persistent title bar, but we will remove the sleep functions. We will begin by offering users the choice to see a list of names, or enter a new name.
In [ ]:
###highlight=[2,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41]
import os
# Greeter is a terminal application that greets old friends warmly,
# and remembers new friends.
### FUNCTIONS ###
def display_title_bar():
# Clears the terminal screen, and displays a title bar.
os.system('clear')
print("\t**********************************************")
print("\t*** Greeter - Hello old and new friends! ***")
print("\t**********************************************")
### MAIN PROGRAM ###
# Set up a loop where users can choose what they'd like to do.
choice = ''
while choice != 'q':
display_title_bar()
# Let users know what they can do.
print("\n[1] See a list of friends.")
print("[2] Tell me about someone new.")
print("[q] Quit.")
choice = input("What would you like to do? ")
# Respond to the user's choice.
if choice == '1':
print("\nHere are the people I know.\n")
elif choice == '2':
print("\nI can't wait to meet this person!\n")
elif choice == 'q':
print("\nThanks for playing. Bye.")
else:
print("\nI didn't understand that choice.\n")
If you run this code, you will see that it doesn't quite work. There are no Python errors, but the logic is a little off. We can enter choices, but the call to display_title_bar()
at the beginning of the loop clears the screen as soon as the output is printed.
We can fix this by moving the call to display_title_bar()
, and putting it in two places. We call display_title_bar()
once just before we enter the loop, when the program starts running. But we also call it right after the user makes a choice, and before we respond to that choice:
In [ ]:
###highlight=[23,34]
import os
# Greeter is a terminal application that greets old friends warmly,
# and remembers new friends.
### FUNCTIONS ###
def display_title_bar():
# Clears the terminal screen, and displays a title bar.
os.system('clear')
print("\t**********************************************")
print("\t*** Greeter - Hello old and new friends! ***")
print("\t**********************************************")
### MAIN PROGRAM ###
# Set up a loop where users can choose what they'd like to do.
choice = ''
display_title_bar()
while choice != 'q':
# Let users know what they can do.
print("\n[1] See a list of friends.")
print("[2] Tell me about someone new.")
print("[q] Quit.")
choice = input("What would you like to do? ")
# Respond to the user's choice.
display_title_bar()
if choice == '1':
print("\nHere are the people I know.\n")
elif choice == '2':
print("\nI can't wait to meet this person!\n")
elif choice == 'q':
print("\nThanks for playing. Bye.")
else:
print("\nI didn't understand that choice.\n")
This works, so let's put the menu into a function:
In [ ]:
###highlight=[18,19,20,21,22,23,24,34]
import os
# Greeter is a terminal application that greets old friends warmly,
# and remembers new friends.
### FUNCTIONS ###
def display_title_bar():
# Clears the terminal screen, and displays a title bar.
os.system('clear')
print("\t**********************************************")
print("\t*** Greeter - Hello old and new friends! ***")
print("\t**********************************************")
def get_user_choice():
# Let users know what they can do.
print("\n[1] See a list of friends.")
print("[2] Tell me about someone new.")
print("[q] Quit.")
return input("What would you like to do? ")
### MAIN PROGRAM ###
# Set up a loop where users can choose what they'd like to do.
choice = ''
display_title_bar()
while choice != 'q':
choice = get_user_choice()
# Respond to the user's choice.
display_title_bar()
if choice == '1':
print("\nHere are the people I know.\n")
elif choice == '2':
print("\nI can't wait to meet this person!\n")
elif choice == 'q':
print("\nThanks for playing. Bye.")
else:
print("\nI didn't understand that choice.\n")
Now, let's make it so that the program actually does something.
In [ ]:
###highlight=[28,29,42,43,45,46,47]
import os
# Greeter is a terminal application that greets old friends warmly,
# and remembers new friends.
### FUNCTIONS ###
def display_title_bar():
# Clears the terminal screen, and displays a title bar.
os.system('clear')
print("\t**********************************************")
print("\t*** Greeter - Hello old and new friends! ***")
print("\t**********************************************")
def get_user_choice():
# Let users know what they can do.
print("\n[1] See a list of friends.")
print("[2] Tell me about someone new.")
print("[q] Quit.")
return input("What would you like to do? ")
### MAIN PROGRAM ###
# Set up a loop where users can choose what they'd like to do.
names = []
choice = ''
display_title_bar()
while choice != 'q':
choice = get_user_choice()
# Respond to the user's choice.
display_title_bar()
if choice == '1':
print("\nHere are the people I know.\n")
for name in names:
print(name.title())
elif choice == '2':
new_name = input("\nPlease tell me this person's name: ")
names.append(new_name)
print("\nI'm so happy to know %s!\n" % new_name.title())
elif choice == 'q':
print("\nThanks for playing. Bye.")
else:
print("\nI didn't understand that choice.\n")
If you run this program, you will see that it works mostly as its supposed to. But the code in lines 37-50, where we are responding to the user's choice, is starting to get crowded. Let's move the code for choice 1 and choice 2 into separate functions:
In [ ]:
###highlight=[26,27,28,29,30,32,33,34,35,36,52,54]
import os
# Greeter is a terminal application that greets old friends warmly,
# and remembers new friends.
### FUNCTIONS ###
def display_title_bar():
# Clears the terminal screen, and displays a title bar.
os.system('clear')
print("\t**********************************************")
print("\t*** Greeter - Hello old and new friends! ***")
print("\t**********************************************")
def get_user_choice():
# Let users know what they can do.
print("\n[1] See a list of friends.")
print("[2] Tell me about someone new.")
print("[q] Quit.")
return input("What would you like to do? ")
def show_names():
# Shows the names of everyone who is already in the list.
print("\nHere are the people I know.\n")
for name in names:
print(name.title())
def get_new_name():
# Asks the user for a new name, and stores the name.
new_name = input("\nPlease tell me this person's name: ")
names.append(new_name)
print("\nI'm so happy to know %s!\n" % new_name.title())
### MAIN PROGRAM ###
# Set up a loop where users can choose what they'd like to do.
names = []
choice = ''
display_title_bar()
while choice != 'q':
choice = get_user_choice()
# Respond to the user's choice.
display_title_bar()
if choice == '1':
show_names()
elif choice == '2':
get_new_name()
elif choice == 'q':
print("\nThanks for playing. Bye.")
else:
print("\nI didn't understand that choice.\n")
This code doesn't behave any differently, but the main program itself is much easier to read. Lines 48-57 are a clean series of if-elif-else statements, each of which has a clear action.
Putting each action into its own function also lets us focus on improving that action. If you look at the function get_new_name()
, you might notice that we are storing the name without checking anything about it. Let's put in a simple check to make sure we don't already know about this person.
In [ ]:
###highlight=[36,37,38,39,40]
import os
# Greeter is a terminal application that greets old friends warmly,
# and remembers new friends.
### FUNCTIONS ###
def display_title_bar():
# Clears the terminal screen, and displays a title bar.
os.system('clear')
print("\t**********************************************")
print("\t*** Greeter - Hello old and new friends! ***")
print("\t**********************************************")
def get_user_choice():
# Let users know what they can do.
print("\n[1] See a list of friends.")
print("[2] Tell me about someone new.")
print("[q] Quit.")
return input("What would you like to do? ")
def show_names():
# Shows the names of everyone who is already in the list.
print("\nHere are the people I know.\n")
for name in names:
print(name.title())
def get_new_name():
# Asks the user for a new name, and stores the name if we don't already
# know about this person.
new_name = input("\nPlease tell me this person's name: ")
if new_name in names:
print("\n%s is an old friend! Thank you, though." % new_name.title())
else:
names.append(new_name)
print("\nI'm so happy to know %s!\n" % new_name.title())
### MAIN PROGRAM ###
# Set up a loop where users can choose what they'd like to do.
names = []
choice = ''
display_title_bar()
while choice != 'q':
choice = get_user_choice()
# Respond to the user's choice.
display_title_bar()
if choice == '1':
show_names()
elif choice == '2':
get_new_name()
elif choice == 'q':
print("\nThanks for playing. Bye.")
else:
print("\nI didn't understand that choice.\n")
This works well. Now the program only stores new names, and names that are already in the list are greeted as old friends.
It's interesting to note that you can run this program through python tutor, and step through each line of the code. It's pretty enlightening to see exactly how the Python interpreter steps through a program like this. You can see all of the jumps from the main program to each of the functions, and the change in flow when the user makes a particular choice. It's informative to see which lines are skipped when certain if tests pass or fail.
There is one last thing we'd like Greeter to do, to consider it a basic terminal app. Let's make it remember the list of names after the program closes. To do this, let's learn about pickling using a simpler example.
When we "pickle" something in the physical world, we soak it in salt and vinegar so it won't rot. "Pickling" an object in Python packages it up and stores it on disk in a way that we can get it back in its original form later. You don't want to use pickle
for imporant data, but it's a good way to get started with storing data after your program closes.
Here is a simple program that asks the user for some input, and then stores the input in a list. The program dumps the list to a file using pickle
, and the next time the program runs it loads that data back in. Run this program on your computer, and see if it works for you.
In [ ]:
import pickle
# This program asks the user for some animals, and stores them.
# Make an empty list to store new animals in.
animals = []
# Create a loop that lets users store new animals.
new_animal = ''
while new_animal != 'quit':
print("\nPlease tell me a new animal to remember.")
new_animal = input("Enter 'quit' to quit: ")
if new_animal != 'quit':
animals.append(new_animal)
# Try to save the animals to the file 'animals.pydata'.
try:
file_object = open('animals.pydata', 'wb')
pickle.dump(animals, file_object)
file_object.close()
print("\nI will remember the following animals: ")
for animal in animals:
print(animal)
except Exception as e:
print(e)
print("\nI couldn't figure out how to store the animals. Sorry.")
This program uses three new things:
try-except
block.except
block.pickle.dump()
.Note: This may work slightly differently in Python 2, and on Windows or Mac. This notebook will be updated to include those specific differences.
When you run this program on your computer, look for the file animals.pydata in the same directory as the program file. If that worked, we need to modify the program so that the next time it runs it loads the data from animals.pydata back in.
We do that with a try-except block at the beginning of the program, which tries to open the file and read the data back into the animals
list. If that doesn't work, which will happen when the program is run for the first time or if you delete animals.pydata, we make the same empty list we had before.
In [ ]:
###highlight=[9,10,11,12,13,14]
import pickle
# This program asks the user for some animals, and stores them.
# It loads animals if they exist.
# Try to load animals. If they don't exist, make an empty list
# to store new animals in.
try:
file_object = open('animals.pydata', 'rb')
animals = pickle.load(file_object)
file_object.close()
except:
animals = []
# Show the animals that are stored so far.
if len(animals) > 0:
print("I know the following animals: ")
for animal in animals:
print(animal)
else:
print("I don't know any animals yet.")
# Create a loop that lets users store new animals.
new_animal = ''
while new_animal != 'quit':
print("\nPlease tell me a new animal to remember.")
new_animal = input("Enter 'quit' to quit: ")
if new_animal != 'quit':
animals.append(new_animal)
# Try to save the animals to the file 'animals.pydata'.
try:
file_object = open('animals.pydata', 'wb')
pickle.dump(animals, file_object)
file_object.close()
print("\nI will remember the following animals: ")
for animal in animals:
print(animal)
except Exception as e:
print(e)
print("\nI couldn't figure out how to store the animals. Sorry.")
The new try-except block at the beginning of the file works like this:
- It tries to open a file to read in a list of animals.
- If the file exists, animals will contain the previously stored list.
- If the file does not exist, line 9 will create an error, which would normally end the program.
- Instead, the program drops to the except block, where an empty list is created.
Now the program should just build a cumulative list of animals every time it is run. This is our first example of persistent data, data that lasts even after our program stops running.
Now let's use these concepts to finish Greeter.
We really just have a little left to do in order to finish Greeter. We need to dump the list of names before the program ends, and we need to load that list of names when the program starts. Let's do this by creating two new functions:
load_names()
will load the names from a file when the program first starts. This function will be called before the main loop begins.quit()
will dump the names into a file just before the program ends.
In [ ]:
###highlight=[43,44,45,46,47,48,49,50,51,52,53,55,56,57,58,59,60,61,62,63,64,69,84]
import os
import pickle
# Greeter is a terminal application that greets old friends warmly,
# and remembers new friends.
### FUNCTIONS ###
def display_title_bar():
# Clears the terminal screen, and displays a title bar.
os.system('clear')
print("\t**********************************************")
print("\t*** Greeter - Hello old and new friends! ***")
print("\t**********************************************")
def get_user_choice():
# Let users know what they can do.
print("\n[1] See a list of friends.")
print("[2] Tell me about someone new.")
print("[q] Quit.")
return input("What would you like to do? ")
def show_names():
# Shows the names of everyone who is already in the list.
print("\nHere are the people I know.\n")
for name in names:
print(name.title())
def get_new_name():
# Asks the user for a new name, and stores the name if we don't already
# know about this person.
new_name = input("\nPlease tell me this person's name: ")
if new_name in names:
print("\n%s is an old friend! Thank you, though." % new_name.title())
else:
names.append(new_name)
print("\nI'm so happy to know %s!\n" % new_name.title())
def load_names():
# This function loads names from a file, and puts them in the list 'names'.
# If the file doesn't exist, it creates an empty list.
try:
file_object = open('names.pydata', 'rb')
names = pickle.load(file_object)
file_object.close()
return names
except Exception as e:
print(e)
return []
def quit():
# This function dumps the names into a file, and prints a quit message.
try:
file_object = open('names.pydata', 'wb')
pickle.dump(names, file_object)
file_object.close()
print("\nThanks for playing. I will remember these good friends.")
except Exception as e:
print("\nThanks for playing. I won't be able to remember these names.")
print(e)
### MAIN PROGRAM ###
# Set up a loop where users can choose what they'd like to do.
names = load_names()
choice = ''
display_title_bar()
while choice != 'q':
choice = get_user_choice()
# Respond to the user's choice.
display_title_bar()
if choice == '1':
show_names()
elif choice == '2':
get_new_name()
elif choice == 'q':
quit()
print("\nThanks for playing. Bye.")
else:
print("\nI didn't understand that choice.\n")
We now have a long-running, standalone terminal application. It doesn't do a whole lot, but it shows the basic structure for creating terminal apps of your own. If you expand the list of choices and keep your code organized into clean, simple functions, you now know enough to make some pretty interesting programs.
abacus_bar = ''
for...
abacus_bar = abacus_bar + 'x'
print(abacus_bar)