Preparation

a. Create a folder called lab-10-05 in your StuWork directory.

b. Go to the website Resources page and download the graphics.py file and save it in your lab-10-05 folder.

c. Open up an extra tab to the Online Graphics Reference. You can also find this on the Resources page.

d. Open a Terminal and navigate to your lab-10-05 directory.

Exercise 1

Let’s play around with detecting key presses from the user. Create a new file called move-circle.py and place the following code into it.


from graphics import *

def main():
    win = GraphWin("Moving a Circle", 500, 500)
    win.setCoords(0, 0, 1, 1)

    circ = Circle(Point(0.25, 0.25), 0.1)
    circ.setFill("red")
    circ.draw(win)

    while win.isOpen():
        key = win.checkKey()

        if key == 'Up':
            circ.move(0, 0.01)
        elif key == 'Down':
            circ.move(0, -0.01)
        elif key == 'Right':
            circ.move(0.01, 0)
        elif key == 'Left':
            circ.move(-0.01, 0)

        update(60) # slows down the loop to only 60 times per second

main()

a. Run the code and use the arrow keys on your keyboard to watch the circle move.

b. Modify the code so that if you press the space bar key the color of the circle changes to blue. Note that the checkKey method returns 'space' if the space bar is pressed.

c. Add the following line at the top of the while loop.

mouse = win.checkMouse()

Next add the following code right before the update(60) but after the if-elif code block.

if mouse != None:
    circ.setFill("green")

d. Run the code again and verify that clicking anywhere in the window causes the circle to turn to green.

e. Write a function called distance(p1, p2) that takes two Point objects and computes the Euclidean distance between the two points. If your two points have coordinates (x1, y1) and (x2, y2), then you can compute the distance between them by:

  1. Computing the differences dx = x1 - x2 and dy = y1 - y2.
  2. Squaring the differences.
  3. Adding the squares of the differences.
  4. Finally, taking the square root of the sum.

Remember that the getX() and the getY() methods get the coordinates out of a Point object.

f. Using your distance function, change the code above so that the circle only turns green if the user clicks inside the circle. (You will need to compare the point the user clicked to the current location of the circle and see if it is within its radius.)

g. Change the references to the colors red, green, and blue to three different colors of your choice. Try experimenting with the color_rgb function by trying colors such as color_rgb(128, 128, 128), color_rgb(0, 255, 255), and various other colors.

Exercise 2

Create a new file called plot.py and add the following code to it.

from graphics import *
import math

def myFunction(x):
    return math.sin(x)

# Generates a list of points using myFunction
def generate(start, stop, num_points):
    points = []

    step = (stop - start) / num_points

    cur_x = start

    for i in range(0, num_points):
        cur_y = myFunction(cur_x)
        cur_point = Point(cur_x, cur_y)
        points.append(cur_point)
        cur_x = cur_x + step

    return points

def drawPoints(pointsList, win):
    for point in pointsList:
        circ = Circle(point, 0.01)
        circ.setFill("black")
        circ.draw(win)

def main():
    win = GraphWin("Plot", 640, 480)
    win.setCoords(0, -1, 2*math.pi, 1)

    sinFun = generate(0, 2*math.pi, 100)

    drawPoints(sinFun, win)

    # Wait to end program until user clicks
    win.getMouse()

main()

a. Try running the file to see what it does.

b. Change the generate(0, 2*math.pi, 100) line of code so that it has different values of start, end, and step. What do each of these parameters do?

c. Change the drawPoints function so that instead of just plotting circles at each point, it also draws straight lines between adjacent points. To do this, you’ll need to change the for loop so that it is for i in range(len(pointsList)-1) so that you can draw lines between the points pointsList[i] and pointsList[i+1].

d. Change myFunction so that instead of computing f(x) = sin(x) it computes f(x) = 1/2 x - 1 or some other linear function.

e. Experiment with your own function ideas and watch them plot.

Exercise 3

Create a new file called gradient.py and add the following code into it.

from graphics import *

def main():
    win = GraphWin("Gradient", autoflush=False)

    for row in range(win.getHeight()):
        # converts the current row into the range of 0-255
        red = int(row / win.getHeight() * 255)
        color = color_rgb(red, 0, 0)
        for column in range(win.getWidth()):
            win.plotPixel(column, row, color)

    win.flush()

    # Wait to end program until user clicks
    win.getMouse()

main()

a. Run the program to see what it does. (Note that win.plotPixel(x, y, color) will change the color of the pixel at (x,y) of win to be colored according to color.)

b. Try removing the autoflush=False parameter to GraphWin and comment out the win.flush() command near the end of the main() function. What happened?

The flush method causes all pending draw commands to happen simultaneously. By default, GraphWin automatically flushes every time a draw instruction is given. Since drawing is expensive, sometimes it is nice to group multiple draw commands into a single flush.

c. Restore the code to how it was originally.

d. Change the code so that the gradient goes from left to right instead of top down.

e. Change the code so that the gradient goes diagonally from the upper left to the lower right.

f. Change the code so that the gradient goes from blue to green instead of black to red.

Exercise 4

Write a program called follow-mouse.py that shows a GraphWin window, draws a circle on the canvas, and whenever the user clicks on the window the circle moves to the location of where the mouse was clicked.

Exercise 5

Change the program from Exercise 1 so that there are several rectangles on the canvas initially. Change the way the circle is moved so that it cannot move through any of the rectangles. To do this, I recommend writing a function called checkCollision(circle, rectangle) that returns True if the circle and the rectangle are overlapping one another and returns False otherwise. Once this function is written, you can change the code to move the circle so that it only moves if it doesn’t collide with any of the rectangles.