ud036 ยป

Building a User Interface in Python

One of the most exciting parts of this course is the final project, where you get to design and create your own simple app in Python.  Good programs usually involve some kind of user interaction.  In order to help you focus on making your awesome program to provide whatever service or solve whatever program you're interested in, here's a quick tutorial on some simple ways to create a user interface in Python.  Being able to quickly "hack" together a user interface is a useful skill for testing and prototyping software, and it's a great way to impress your friends with how well you can program.  The three methods we'll learn here are using the input() and raw_input() built in procedures to create a basic text interface, using the easygui library to create a basic graphical user interface using simple functions, and using the more powerful Tkinter module.

input() and raw_input()

These two procedures make your program wait for a user to type something, then return whatever the user typed.  input() will interpret the user's text as Python code, while raw_input() will always return a string.  Unless your user knows Python, you're usually better of using raw_input() to get a string, and then examining the string at your leisure in the rest of the program to determine what to do with that input.  input() and raw_input() take a string as an argument, which they will display to the user as a prompt.

A simple example

Here's a basic example of how you might use raw_input().

user_name = raw_input("Enter your name: ")
print "Hello " + user_name + "!"

This code will create a variable named user_name, display "Enter your name: " and give the user a cursor.  It will then store everything they write before pressing enter as a string in the variable user_name.  Finally, we print a response.

A more complex example

Here we will write an entire simple program using this feature.  Feel free to copy this into an .py file and run it to see it in action!

#A demo on how to use the built in procedure raw_input()
#to make a simple text-based user interface.  This program
#will accept a bunch of values and print the average of them.

print "Mr. Matrix's Mathemagical Mean Machine"
print "--------------------------------------"
print "Enter any number of integers to take their average!"
print "Enter q to quit or t to get the average of what you've entered so far."
total = 0
count = 0
#This loop will continue prompting users for input until
#we get a command to stop.
while True:
    #The raw_input() procedure takes a prompt string as an
    #argument and displays it to the user, and then the program
    #stops and waits for the user to write something and press
    #enter.  The text they entered is then returned as a string.
    text = raw_input("Enter a number: ")

    #If we see a quit or total command, stop the loop.
    if text == "q" or text == "t":
        break

    #try / except blocks are a way for us to try to do something
    #that we think might produce an error.  We try to use the
    #int operator on the user input which will turn the string
    #into a number if they have written an integer.  If the input
    #is not an integer in string form, the int operator will give
    #a ValueError, and Python will skip to the except block.
    try:
        number = int(text)
        total += number
        count += 1
    except ValueError:
        print "I'm sorry, I didn't understand that."

#Once the loop is done, if the user gave a total command, print
#the results.  Then the program ends!
if text == "t":
    print "Total sum: " + str(total)
    print "Total count: " + str(count)
    print "Average: " + str(1.0 * total / count)

A GUI using easygui

easygui is a great module that does exactly what it says: allows you to easily make a GUI without worrying about all the details.  If you have pip installed you can install easygui by opening your terminal or command prompt and typing:

pip install easygui

If you don't have pip, you can download the source code for easygui here.  See this tutorial on manually installing a Python package.

easygui operates entirely using functions.  Each function creates a user interface object for the user with a message, a title, and a variety of buttons and response options.  For most easygui functions, the first two arguments are the message and the title.  For example, the message box:

msgbox("This is the message that will appear!", "Window Title")

That line of code creates a UI box displaying the message "This is the message that will appear!" with the title "Window Title" at the top of the box.  The box will have an OK button, and the program will not continue until the user clicks OK.

Of course, we want to do more that display messages to a user.  We want to get user input as well.  Read through this short program demonstrating the basic easygui functions and how to use them.  Feel free to copy + paste this code into a .py file and run it to see how it looks!

#easygui is exactly what it sounds like: a module for easily making a simply
#graphical user interface.  You can do a lot in easgui.  To learn more, see
#the documentation here:

#http://easygui.sourceforge.net/tutorial.html#introduction

#We will cover a few basic ideas: displaying messages and getting user input.

#Start by importing everything from the easygui module.
from easygui import *

#Most easygui functions take a title as a second argument.  We're going to use
#the same string for every one, so let's save it in a variable.
title = "GUI DEMO"

#msgbox displays the string you give as an argument and gives the user an OK
#button.
msgbox("Welcome to the demo GUI!", title)

#You can specify the text of the OK button using the ok_button parameter.
msgbox("Are you read for some UI magic?", title, ok_button="I'm ready!")

#The ccbox asks gives the user "Contiue" and "Cancel" options, and returns
#true if they click continue and false if they click cancel.  You can put
#the function straight into a condition and decide what to do next.  In this case,
#if the function returns false, we display a quit message.
if not ccbox("Alright.  This is a ccbox.  Hit continue to continue or cancel to quit!", title):
    msgbox("Alright.  Later!", title, ok_button="Quit.")
    quit()

#By the way, the ynbox() function works the same as the ccbox() function except it
#displays Yes and No as choices instead of Continue and Cancel.

#But what if you want to give the user a custom set of choices?  Save the choices
#in a list and use the buttonbox() function!  It returns the choice they make.
choices = ["Blue", "Green", "Yellow", "Something else"]
choice = buttonbox("What is your favorite color?", title, choices)

if choice == "Something else":
    msgbox("Huh.  I guess you like a more interesting color!", title, ok_button="Yup.")
else:
    msgbox("Cool.  I like " + choice + " too!", title, ok_button="Wow, we're like twins!")

#By the way, the choicebox() function works the same as buttonbox(), but it will display the
#choices in a list rather than as buttons.  This is a good idea if you have lots of choices,
#or if the choice strings are long.

#Buttonboxes are all very well, but what if you want the user to be able to input text?
#There's a function for that! Use enterbox() to get a string from a user.  It returns the
#string.
band = enterbox("What's your favorite band?", title)
msgbox(band + "?  Never heard of them.", title, ok_button="Yeah right.")

#By the way, integerbox() works the same as enterbox() except it only lets the user input
#an integer, and returns an integer value instead of a string.

#Lastly, for super in-depth user-input gathering, you can use the multenterbox() to get
#a bunch of strings at once.  You need to give it a third argument which is a list of
#prompts for the fields the user will write in, and it returns a list of strings that
#the user types in.
fields = ["Place I want to go:", "Person I want to meet:", "Thing I want to do:"]
answers = multenterbox("If you could go anywhere, meet anyone, or do anything, where would you go, who would you meet, and what would you do?", title, fields)

message = answers[0] + "?  I bet it's really pretty there.  "
message += answers[1] + " sounds like a cool person.  I hope you get to meet them someday!  And "
message += answers[2] + "--that sounds amazing.  I may just do that myself!"
msgbox(message, title, ok_button="Quit.")

#That's it for the GUI demo!  Try copy+pasting this code into a python file and running
#it to get a feel for how easygui works.  You might find that there are some issues.
#For example, you can leave fields blank in the boxes where the user types.  And
#what happens when someone presses cancel?  For your project you will need to validate
#user input, and provide user friendly error messages when the user enters the wrong
#thing.  Hopefully this gets you started!

A Tkinter Application

Tkinter is the most advanced GUI module we will cover but it is also the most powerful.  Tkinter is part of the Python Standard Library, so it should be included with your Python distribution.  Note that in Python 3, the module is renamed to tkinter.

Below is an example of how to create a simple Tkinter application by creating an extension of the Frame class.  More information on the Tkinter module is available here.  In this example, we do not make use of many of the features of the Frame class, but it is a better design pattern than placing Buttons and Labels into a Tkinter window directly.  We also use the .pack() method to place widgets into our UI.  The pack geometry manager is a simple approach that arranges UI objects into a column or row.  For a more nuanced approach to create a visually pleasing application, see the grid geometry manager.

Feel free to copy + paste this code into a .py file and try running it for yourself!

#Tkinter is the module we will use to create the GUI.
import Tkinter

#Your app is a subclass of the Tkinter class Frame.
class MyApp(Tkinter.Frame):

    #Constructor for our app.  The master parameter represents the root object
    #that will hold out application.  It is basically a window, with our application
    #existing in a frame inside that window.
    def __init__(self, master):
        #Call the constructor of the Frame superclass.  The pad options create padding
        #between the edge of the Frame and the Widgits inside.
        Tkinter.Frame.__init__(self, master, padx=10, pady=10)
        #Give our window a title by calling the .title() method on the master object.
        master.title("Sample Application")
        #Set a minimum size through the master object, just to make our UI a little
        #nicer to look at.
        master.minsize(width=250, height=100)
        #.pack() is a necessary method to get our app ready to be displayed.  Packing
        #objects puts them in a simple column.  For a more complex way to arrange your
        #widgets, see .grid():
        #http://effbot.org/tkinterbook/grid.htm
        self.pack()

        #Now let's make some buttons using the Tkinter class Button.  The "text" parameter
        #indicates the text to be displayed in the button and the "command" parameter
        #specifies a procedure to execute if the button is clicked.  In this app, we will
        #have buttons that increase and decrease a variable.
        self.upButton = Tkinter.Button(self, text="Up", command=self.increment)
        self.upButton.pack() #Individual objects also must be packed to appear.
        self.downButton = Tkinter.Button(self, text="Down", command=self.decrement)
        self.downButton.pack()

        #The variable that we'll be incrementing and decrementing.
        self.value = 0
        #When you want to integrate a variable with your Widgets (eg buttons, labels, etc),
        #you make it a special type of Tkinter variable.  In this case, a StringVar.  There
        #is also IntVar, DoubleVar, and BooleanVar.  These are essentially mutable versions
        #of primitive types.  If we assign a normal string varaible to be the text of a button,
        #then change that string variable, the text of the button would be unchanged.  If we
        #instead use a StringVar, the button text will update automatically.  You'll see!
        self.value_str = Tkinter.StringVar()
        self.value_str.set("0") #You set all Tkinter variable objects with the .set() method.

        #A Label to display our value.  Labels are like buttons except with no click effect.
        #Note that we use the textvariable parameter instead of text so that the text on
        #this label will automatically update with our StringVar.
        self.valueLabel = Tkinter.Label(self, textvariable=self.value_str)
        self.valueLabel.pack()

        #Lastly, a quit button, which will call the .create_quit_window() method defined below,
        #which displays a new window asking whether the user want's to quit.
        self.quitButton = Tkinter.Button(self, text="Quit", command=self.create_quit_window)
        self.quitButton.pack()

    #Methods that will be called when the up and down buttons are pressed.
    def increment(self):
        self.value += 1
        #When we reset the value of the StringVar, the text on valueLabel will change!
        self.value_str.set(str(self.value))

    def decrement(self):
        self.value -= 1
        self.value_str.set(str(self.value))

    #This method creates a new window (which will be a child of the master of our frame,
    #not of our frame itself).  The quit window will ask the user if they really want to quit.
    #If the user clicks yes, the application will close.  If they say no, the quit window
    #will close.
    def create_quit_window(self):
        #The Toplevel class makes a window.  It's simpler than the Frame class.  We will make
        #it a child of our application's master object, but since it is a Toplevel object, it 
        #will create a whole new window rather than one that is part of the application window.
        quit_window = Tkinter.Toplevel(self.master)
        #Give our quit window a title and minimum size.
        quit_window.title("Quit?")
        quit_window.minsize(width=150, height=50)
        #Display a message to the user asking if they want to quit.
        quit_label = Tkinter.Label(quit_window, text="Are you sure you want to quit?")
        quit_label.pack()
        #We give our window a yes and no button.  One quits the application and one quits
        #the window.
        yes_button = Tkinter.Button(quit_window, text="Yes", command=self.quit)
        yes_button.pack()
        no_button = Tkinter.Button(quit_window, text="No", command=quit_window.destroy)
        no_button.pack()


#We make a Tk object to serve as the root of our interface.  We're making 
#something to use as an argument for the master parameter, to be the "parent"
#of our frame.  We can use it to do things like set the size.  Other than
#that, you don't have to worry too much about this step.
root = Tkinter.Tk()
#Initialize our app object and run the Frame method .mainloop() to begin!
#You should see a small window with up and down buttons, a label displaying
#a number, and a quit button when you run this program.
app = MyApp(root)
app.mainloop()