Introduction to Interactive Programming in Python - Week 4
This week we learn how to create a canvas in Python and how to draw on the canvas. This is the first week where we get to start really interacting with our programs and fully understand the usefulness of event-driven programming.
This week's mini project is building a digital stopwatch and a very simple game where the player tries to stop the stopwatch on a whole second to score points.
A clone of this week's mini project, Stopwatch: The Game!, that I built in JavaScript, HTML, CSS, and Bootstrap.
Canvas Drawing: Most of the projects in this specialization are interactive applications that allow a player to interact with the program through a general user interface (GUI). Therefore it's necessary to understand how to create a canvas where we can draw and display objects such as images, strings, and numbers. When creating a canvas, it's helpful to think about your computer monitor as what it really is: a 2-dimensional grid of pixels whose colors are stored in memory as a frame buffer. Your computer constantly updates the monitor at a frequency determined by its refresh rate. Typically, event-driven programs such as this week's mini project register a special function called a draw handler that tell the computer what to draw on the canvas each time it updates its buffer. Since computers can update their buffers tens or hundreds of times per second (for example, a 120 Hz monitor can handle up to 120 frames per second), humans perceive objects shown on our monitors as being fixed in place or in smooth motion, rather than being constantly updated.
String Processing: Modifying text is an extremely useful application of programming, and Python makes it really easy to do. This week's lecture just scratches the surface of all of the string manipulation that's possible in Python. (I'm a big fan of the Manipulating Strings chapter of Al Sweigart's Automate the Boring Stuff with Python and the Python docs for additional string manipulation techniques.) Specifically, this week's lecture covered these basic string processing techniques:
Combining Strings: In Python, one way you can concatenate two strings is using the + operator. For example, printing "Hello" + " world" would result in "Hello world".
Characters and Slices: You can get a specific character in a string by specifying its index. For example, if myVariable = "Hello world!", we could get the "H" character by calling myVariable[0]. Note that in Python indexing starts at 0, rather than 1 (this is because 0 is the most logical starting point if you consider that computers store all numbers as bits, and the number 0 is represented as 00000000 in memory). Calling myVariable[:] would return the entire "Hello world!" string, whereas myVariable[2:] would return the entire string starting from the index position 2. You can also use negative numbers as index positions, such as myVariable[-1], which would return the "!" character.
Converting Strings: To convert the string representation of a number, such as "7", into an integer, use Python's "int" built-in function. For example, int("7") would return the integer 7. On the other hand, use the "str" function to convert other types of data into a string. Thus, str(7) would return the string "7".
Mini Project 4
This week's mini project is to build a digital stopwatch. To make the application more interactive, we'll add a score that increases whenever the player successfully stops the stopwatch on a whole second (i.e., when the stopwatch shows 0 milliseconds). To build this project, we need to do the following:
Construct a timer
Write the event handler function for the canvas that draws the current time
Add "Start", "Stop", and "Reset" buttons to the canvas and write their corresponding event handlers
Write a helper function to format the time
Add two additional counters that keep track of the number of successful stops and total stops of the stopwatch.
Timer: To create a timer in CodeSkulptor, we use the simplegui.create_timer function, which takes as parameters a function and the time interval between calling that function in milliseconds. Since we want our timer to call the time function every 1/10th of a second, we'll use 100 milliseconds as the time interval. The time function itself is straightforward: every time the function is executed it increments the global variable COUNT by 1.
Draw Handler: To draw objects to the canvas, we first need to create a canvas frame using the simplegui.create_frame function. Then we need to register the draw handler with the frame's set_draw_handler method, which takes the draw handler function draw() as its only parameter. Note that the draw handler takes a canvas as a parameter, which logically makes sense because we need to tell the draw handler where to draw objects.
Score Counters: To turn our digital stopwatch into a simple, interactive game, we need to add two additional global variables, TOTAL_STOPS and SUCCESSFUL_STOPS, that will be incremented in the event handler for the "Stop" button.
Buttons: This project has three buttons: "Start", "Stop", and "Reset". The "Start" button changes the global variable IS_TIMER_STOPPED to the Boolean value False and starts the timer. By default, this global variable is True since the timer doesn't start immediately when the player runs the program; rather, it should start only when the player clicks the "Start" button. The "Stop" button stops the timer and changes IS_TIMER_STOPPED to False. It also updates the global variables TOTAL_STOPS and SUCCESSFUL_STOPS depending on whether the COUNT represents a whole second. It's easy to introduce a bug into this button handler if you don't use the IS_TIMER_STOPPED global variable. For example, if you omit this Boolean value, then every time the player clicks the "Stop" button, the TOTAL_STOPS variable will be incremented. The "Reset" button stops the timer and resets the global variables COUNT, SUCCESSFUL_STOPS, and TOTAL_STOPS.
Formatting Helper Functions: We'll want to draw the global variable COUNT on the canvas in a format that resembles a digital stopwatch. For example, rather than drawing the COUNT on the canvas as 621, we'll reformat it to be 1:02.1. In order to do this, we need to use division, modulular arithmetic, and string processing.