Homework 6: GUIs and Recursion
This assignment will include defining classes, user interfaces using Tkinter and recursion.
Testing GUIS: It is hard to test a GUI program using a testing function. Instead, you should
thoroughly exercise your code by running it repeatedly, and maybe even trying random inputs.
What happens if you just start typing random things, or clicking random buttons? Where
possible, make a list of things to test, and be sure you have checked them all.
Preparing and turning in the assignment
Download the hw6Code.py file to use with this assignment. Put all your programming answers
into hw6code.py. Be sure that each function or method you write is preceded by a
hash-marked comment that indicates the problem number, and inside it a triple quoted
comment that describes the function/method’s purpose, and its inputs and outputs. All code
should be inside a function or inside the if __name__ == “__main__” statement.
1. Building a GUI! Create a GUI program using the tkinter module, and use a
style similar to those you learned in the in-class activity and reading. There is an outline
of your program in the hw6Code.py file, including some helper methods. Your task is to
fill in the missing methods.
This program was inspired by a recent article about Game of Thrones, talking about
how the actors do not get paper scripts, in an attempt to keep the plot a secret. Instead,
the actors work with a tablet app to learn their lines. The purpose of this GUI program is
to read in a (slightly formatted) text file containing a script, and to display one stage
direction or character’s line at a time, allowing the user to move forward or backward
through the script.
The program should include two Label widgets for the filename of the script file, plus a
Label widget to display the text, and 4 Button widgets: to load a script file, to go to the
previous or next line, and to quit.
Step 1: Fill in the __init__ method for the class definition for LineGUI, such that it:
(1) creates a Tk window object (setting its title to be “Line Presenter”)
(2) creates and places the 7 required widgets on the grid to implement a GUI
that looks like the picture below
(3) defines and initializes the instance variables that will hold the name of the
file, the text of the file, and the index into the file text (see notes below)
Notes about the __init__ method:
● You can use whatever font you’d like. If you want to match this color scheme, the
code used "dark green", "light green", and "floral white". You may use
something else if your computer doesn’t support those colors..
● Use external padding by setting padx and pady values of 10 or 5 in the call to
the grid method for each widget.
● I placed widgets in rows 0 and 1, and in columns 0, 1, 2, and 3, with the big label
box spanning 2 columns.
● Set the labels to have fixed width and height (experiment!).
● Each button should have its own callback function assigned to it: loadFile,
doQuit, previousLine, and nextLine. You will implement some of those later.
● Set up instance variables self.filename, self.lines, and self.lineIndex, to
hold the script information. The first will eventually hold the filename the user
selects; in __init__ set it to be an empty string. The second will be a list of
strings, where each string is a multiline string holding one line of dialogue or
stage direction. That will be done later, in __init__ set it to be an empty list.
The third will be the index of the currently-displayed line in the script. Set this to
be None initially, so we can distinguish when no script has been loaded, versus
when one is in place.
Before moving on to implement callbacks for the Prev and Next buttons (and their
helper), test your program and see if it looks right and displays values where you want
Step two: Complete the displayLine helper method. This method is used by three of
the Button callback methods (loadFile, previousLine, nextLine). This method should
do the following:
● Check to see if the line index (self.lineIndex) is a number.
● If it is, set the big label’s text to be the line at the current index.
● If it is not a number, then set the big label’s text to the empty string.
To test this function, you can use the Load File button, which calls it. See what happens
when the user cancels out of the file-loading dialog box without selecting a file.
Step three: Complete the previousLine and nextLine methods. These are callback
methods bound to the Prev and Next buttons. As button callbacks, they should take no
inputs other than the required self input all methods have. The previousLine method
should do this:
● Check if the line index instance variable is None
○ If so, do nothing
● If the line index instance variable is zero, then there is no previous line,
○ do nothing
○ update the index variable to be one less than before
○ call self.displayLine
The nextLine callback method is similar
Test these functions on more than one of the text files provided to you: the program
should now be complete.
2. More GUIs – this time with a Canvas! For this question you are going to debug
two methods that are part of another GUI program. This program has a class, WallGUI,
which creates a window with a title label, a quit button, a “reset game” button, and a
big canvas. On the canvas is drawn a simple maze, and a blue square that the user
controls. There is a goal rectangle in the maze as well. The user must move the square
from its starting location to the goal without touching any walls. If the square touches a
wall, the user loses the game. If the square touches the goal rectangle, the user wins! At
any point the user may click the quit button to end the program, or the “reset game”
button to reset the game back to the beginning.
First of all, uncomment the entire class declaration.
There are seven methods in this class/program: __init__, go, drawMaze, moveToward,
checkForEndOfGame, resetGame, and doQuit. The __init__ method and the
moveToward method are buggy, the rest are not.
● First, debug the __init__ method. This sets up the window and the four
widgets. When it is working correctly, it should look produce something like this:
There are two bugs in this method. More than one bug per line is possible. Find
the two bugs, mark them and explain them, and correct them!
● Next, debug the moveToward callback method. This method is called when the
user clicks on the canvas widget. We can get the location in the canvas where
the mouse was clicked from the method’s input, event. The (x, y) coordinates in
the canvas are in event.x and event.y. This method then determines whether
the user clicked mostly to the right, above, to the left, or below the rectangle, and
it moves the rectangle 10 pixels in that direction. Finally, it checks to see whether
the game is over, win or loss.
There are three bugs in moveToward. More than one bug per line is possible.
Find the three bugs, mark them, explain them, and fix them. When it is working,
you should be able to play the game as shown in the demo video.
interleave. This function takes in two lists, and it combines them by alternating the
elements of the two lists. Any extra elements are tacked on to the end. Below are some
calls to the interleave function, and what they should return.
>>> interleave([10, 20], [4, 8])
[10, 4, 20, 8]
>>> interleave(['a', 'e', 'i', 'o', 'u'], ['x', 'y'])
['a', 'x', 'e', 'y', 'i', 'o', 'u']
>>> interleave(, )
>>> interleave(, ['one'])
The base case for this function occurs when one or the other input list is empty. In that
case, it returns a copy of the other input list. When both lists are non-empty, the function
calls itself recursively, removing the first element from each list. It then adds those
elements to the answer from the recursion to complete the work required.
Your task is to draw a diagram that shows the sequence of recursive calls that would be
made, and partial results returned, for a call to this function Be sure to indicate the
values of the input parameters in each call, and the returned value passed back to the
previous call. Here is the call:
interleave([6, 8, 10], [-10, -20, -30, -40])
4. I have written a recursive function to determine whether a string is a palindrome.
The base case is when the input string is empty. The empty string is definitely a
palindrome. Otherwise, the function compares the first and last elements of the string. If
they are different, then the string isn't a palindrome. Otherwise, it removes first and last,
and repeats for the remaining elements of the string.
Note that this function, to keep it simple, it does not work with varying capitalization, or
with punctuation or spaces that complicate the strings. Here are some examples of what
the function should do, further examples are in the testing file.
The isPalindrome function I've given you has three bugs in it. Find the bugs, label the
lines they are on, and fix them. Use the testing function in hw6Tests.py to check for
These solutions may offer step-by-step problem-solving explanations or good writing examples that include modern styles of formatting and construction
of bibliographies out of text citations and references. Students may use these solutions for personal skill-building and practice.
Unethical use is strictly forbidden.
# Put import statements here
from tkinter import *
import tkinter.filedialog as tkFileDialog
# Question 1
# Add code for the LineGUI class below
"""Sets up the instance variables that will hold the lines of text, and also the GUI elements.
All instance variables are initialized in this method."""
self.filename = ""
self.lines = 
self.lineIndex = None
self.mainWin = Tk()
self.loadButton = Button(self.mainWin, text="Load File", command=self.loadFile, font="Arial 10 bold")
self.loadButton.grid(row=0, column=0, padx=10, pady=10)
self.fileLabel = Label(self.mainWin, text="File:", font="Arial 10 bold")
self.fileLabel.grid(row=0, column=1, padx=10, pady=10)
self.fileNameLabel = Label(self.mainWin, text=self.filename, width=50, font="Arial 10 bold")
self.fileNameLabel.grid(row=0, column=2, columnspan=2, padx=10, pady=10)
self.quitButton = Button(self.mainWin, text="Quit", command=self.doQuit, font="Arial 10 bold")
self.quitButton.grid(row=0, column=4, padx=10, pady=10)
self.prevButton = Button(self.mainWin, text="Prev", command=self.previousLine, font="Arial 10 bold")
self.prevButton.grid(row=1, column=0, padx=10, pady=10)
self.lineLabel = Label(self.mainWin, text="", width=60, height=20, wraplength=500, font="Arial 12 bold")