Due: Friday, March 17, 2017, 11:59 pm
The purpose of this lab is to get you working with the Zelle graphics objects, which are a somewhat higher level graphics package than the turtle. Using the Zelle graphics primitives you can create more complex objects, move them, and change their color. A scene is a collection of complex objects.
We'll continue to use the graphics package, but we'll start using features other than Pixmaps. You probably want to bring up the documentation in a separate tab in the browser.
For this project, we have a modified version of the graphics module
(graphicsPlus.py) that adds two new
methods to the shape objects. They are
getFill() and they return the outline and fill colors,
respectively. And for the particularly adventurous, we have added a
parameter to the GraphWin creator function that allows you to
have a specific function called whenever a key is pressed. We demo
these functions in demoGraphicsPlus.py
import time import random import graphicsPlus as gr
Note how we are importing the graphicsPlus package. What that means is,
instead of typing something like graphicsPlus.GraphWin, we can
gr.GraphWin. It simply assigns the symbol gr to mean the
def main(): # assign to win a GraphWin object made with title, width, height, and the value False # (note the final parameter is not one we have used before. It makes it so the # window doesn't attempt to redraw after every change). # assign to shapes an empty list # assign to c a new Circle object at (250, 250) with radius 10 # call the draw function of the circle object stored in c # append the variable c to the list shapes # while True # call time.sleep with a half-second delay (0.5) # for each thing in shapes # assign to dx a random integer between -10 and 10 # assign to dy a random integer between -10 and 10 # call the move method of the object referred to by thing, passing in dx and dy # tell the window to update its display (call win.update()) # if win.checkMouse() is not None # break out of the while loop # close the window
Set up a call to your main function inside the conditional that runs only if the file is run on the command line.
if __name__ == "__main__": main()
Then run your test.py program. The shape should bounce around the screen in Brownian motion, which is another way of saying it should move randomly around the window.
# assign to r a random value in [0, 255] # assign to g a random value in [0, 255] # assign to b a random value in [0, 255] # assign to color the result of calling color_rgb( r, g, b) # call the setFill method of the object in thing, passing in color
# if a call to random.random is less than 0.2 # assign to oldthing the result of calling the function random.choice with shapes as its input # assign to newthing the result of calling the clone method on oldthing # call the draw method of newthing, passing in the window object in win # append newthing to the shapes list
Try out your test.py program again and see what happens.
steam_init(x, y, scale). The function should have the following overall structure.
def steam_init(x, y, scale): """ Create the list of objects needed to draw a steam plant at position (x,y) with the given scale """ # assign to shapes an empty list # assign to r a rectangle to represent the steam plant that has opposite corners at (x, y) and (x+scale*100, y-scale*30) # use the setFill method to set the rectangle to a light grey (185, 185, 185) # append r to shapes # assign to r a rectangle for the roof that has opposite corners at (x-scale*1, y-scale*30) # (continued) and (x+scale*101, y-scale*40) # use the setFill method to set the rectangle to a light brown (176, 133, 85) # append r to shapes # assign to r a rectangle for the smokestack that has opposite corners at (x, y) and (x+scale*10, y-scale*100) # use the setFill method to set the rectangle to a rusty color (136, 96, 90) # add r to the front of shapes # return the shapes list
Note the structure of the above function. First, create an empty list. Second, create all of the basic shape objects needed to create the complex shape. For each of the basic shape objects, append it to the list. Third, return the list. All of the primitive objects in your complex shape will be contained in the list returned by the corresponding init function.
def test_steam(): """ Create a window and plot a scene with a picture of a steam plant in it. """ win = gr.GraphWin( 'title', 400, 400, False ) steamplant = steam_init( 100, 300, 1.0 ) for thing in steamplant: thing.draw( win ) win.update() win.getMouse() win.close() if __name__ == "__main__": test_steam()
If all of your complex objects have the same structure--they are all lists of primitive objects--then we should be able to write some functions to draw them, move them, undraw them, and clone them. Each of these functions needs to loop over the elements in the object list and call the appropriate methods.
To encapsulate this functionality, create functions at the top of your shape_group.py file for draw, move, and undraw. The skeletons are below.
def draw( objlist, win ): """ Draw all of the objects in objlist in the window (win) """ # for each thing in objlist # call the draw method on thing with win as the argument def move( objlist, dx, dy ): """ Draw all of the objects in objlist by dx in the x-direction and dy in the y-direction """ # for each item in objlist # call the move method on item with dx and dy as arguments def undraw( objlist ): """ Undraw all of the objects in objlist """ # for each thing in objlist # call the undraw method on thing
When you are done with that, go back to the test_steam function and replace
the for loop that calls thing.draw with a call to
win ). Test it.
Now we're going to animate the steam plant. In shape_group.py, create a function steam_animation_frame immediately after the steam_init function, with the first argument being the shape list, and the second argument being a frame number. Then we want to make puffs of smoke (circles) come out of the smokestack and start moving upwards. Once a puff reaches the top of the screen, it will be "recycled" by being moved back down to the top of the smokestack.
The steam_animation_frame function draws one frame of the animation. It will be called over and over, with different frame numbers, to construct the animation
def steam_animation_frame( shapes, frame_num, win ): """ Draw one frame of a steam plant animation. The animation will involve smoke rising out of the chimney. shapes is a list containing the graphics objects needed to draw the steam plant. frame_num is a number indicating which frame of the animation it is. win is the GraphWin object containing the scene. This animates by creating up to 20 steam circles. Each circle creeps up the screen until it gets to the top, when it is brought back down to the smokestack so it can be used again. (Wouldn't it be great if the actual steam plant could do that?) """ # assign to p1 the result of calling the getP1 method on the first item in shapes # assign to p2 the result of calling the getP2 method on the first item in shapes # assign to dx the width of the smokestack p2.getX() - p1.getX() # assign to newx the middle of the smokestack (p1.getX() + p2.getX())*0.5 # assign to newy a location above the top of the smokestack p2.getY() - dx*0.5 # if the frame number is even and the length of the shapes list is < 20 # assign to c a new circle located at newx, newy with a radius of 0.4*dx # use the setFill method of c to color the circle grey (150, 150, 150) # use the draw method of c to draw the circle into the window # append c to the shapes list # for each item in shapes, excluding the first three: shapes[3:] # use the move method of item to move the smoke up (-y direction) # assign to center the result of calling the getCenter method of item # if center.y is less than 0 (it's off the screen) # assign to mx the value newx - center.getX() # assign to my the value newy - center.getY() # call the move method of item with (mx, my) as arguments
Add the following code to your test function in shape_group.py and try it out.
for frame_num in range(100): time.sleep( 0.25 ) steam_animation_frame( steamplant, frame_num, win ) win.update() if win.checkMouse(): break
As a final test, download and run lab6test.py. It will import your shape_group.py file and create a simple animated scene.
At the end of this lab, you will have the file shape_group.py, which will contain your complex object (shape group) functions. The steam_init and steam_animation_frame functions are a template for you to design other complex scene elements. If you make the steam plant more interesting, you may use it as part of your project.
When you are done with the lab exercises, you may begin Project 6.
© 2017 Ying Li. Page last modified: .