Today: Canvas drawing, draw functions, drawing a grid, function black-box if we have time
Expand the .zip to get a "draw1" folder with .py files in it. Run PyCharm. Use Open... menu to open the ***folder***
Then use the "Terminal" tab at lower left to run by command line as shown below. Command line is the "pro" way to control the computer.
For more detail see guide: Command Line.
bit.paint('blue')def draw_oval(canvas, x, y, width, height):We'll use the CS106A "DrawCanvas" which supports these functions:
def draw_line(x1, y1, x2, y2):
"""
Draws a black line between points x1,y1 and x2,y2
Optional color='red' parameter can specify a color.
"""
def draw_rect(x, y, width, height):
"""
Draws a 1 pixel rectangle frame with its upper left at x,y
and covering width, height pixels.
Takes optional color='red' parameter.
"""
def fill_rect( x, y, width, height):
"""
Draws a solid black rectangle with its upper left at x,y
and covering width, height pixels.
Takes optional color='red' parameter.
"""
def draw_oval(x, y, width, height):
"""
Draws a 1 pixel oval frame with its upper left bounding rect at x,y
and covering width, height pixels.
Takes optional color='red' parameter.
"""
def fill_oval(x, y, width, height):
"""
Draws a solid black oval with its upper left bounding rect at x,y
and covering width, height pixels.
Takes optional color='red' parameter.
"""
def draw_string(x, y, text):
"""
Draws a black text string with its upper left at x,y
Takes optional color='red' parameter.
"""
These are noun.verb functions on a Canvas like this:
# Create a 500x300 canvas, draw a red line on it canvas = DrawCanvas(500, 300) canvas.draw_line(0, 0, 100, 100, color='red') ...
The "oval" figure has a black rectangle at its outer boundary. Then a yellow oval inset by 20 pixels on 4 sides. Then 2 red lines making a big X. Running the program draws 2 copies of the figure - one upper-left, one lower-right, which helps with testing.
Thinking about math to position the oval:
def draw_oval(canvas, x, y, width, height):
"""
Given a canvas and x,y and width,height
Draw the "oval" figure inside these bounds.
(this code is complete)
"""
# Draw outer rectangle
canvas.draw_rect(x, y, width, height)
# Draw oval set in by 20 pixels all around
canvas.fill_oval(x + 20, y + 20, width - 40, height - 40, color='yellow')
# Draw red lines
# upper-left to lower-right
canvas.draw_line(x, y, x + width - 1, y + height - 1, color='red')
# lower-left to upper-right
canvas.draw_line(x, y + height - 1, x + width - 1, y, color='red')
# Note: better style would be to have a MARGIN constant = 20, and the oval line
# is as below. We used 20 above to keep the example simple. We'll use constants
# like this on a later homework.
# canvas.fill_oval(x + MARGIN, y + MARGIN, width - 2 * MARGIN, height - 2 * MARGIN, color='yellow')
The main() in this case calls draw_oval() twice, once upper left and once lower right. This tests that the x,y are handled correctly. Open the "terminal" at the lower left of PyCharm. The "$" below is the prompt, the rest you type (on Windows its "python" or "py" instead of "python3".) Close the drawing window when done, this exits the program.
$ python3 draw1.py -oval 300 200
In the terminal, hit the up arrow. Edit the old command and run it again easily. This is a very productive and fast way to run and vary your program.
for i in range(n):
y_add = (i / (n - 1)) * (height - 1)
def draw_lines1(canvas, x, y, width, height, n):
"""
Draw the lines1 figure within x,y width,height
"""
canvas.draw_rect(x, y, width, height)
# Figure y_add for each i in the loop
for i in range(n):
y_add = (i / (n - 1)) * (height - 1) # formula: 0..1 fraction * max
canvas.draw_line(x, y, x + width - 1, y + y_add, color='red')
Run from command line
$ python3 draw1.py -lines1 300 200 12
Run from the command line:
$ python3 draw1.py -lines2 300 200 12
def draw_lines2(canvas, x, y, width, height, n):
"""
Draw the lines2 figure within x,y width,height
The code for lines1 is already here
"""
canvas.draw_rect(x, y, width, height)
for i in range(n):
y_add = (i / (n - 1)) * (height - 1) # formula: 0..1 fraction * max
canvas.draw_line(x, y, x + width - 1, y + y_add, color='red')
# loop to draw green "lines2" lines
for i in range(n):
pass
y_add = (i / (n - 1)) * (height - 1)
x_add = (i / (n - 1)) * (width - 1)
canvas.draw_line(x, y + y_add, x + x_add, y + height - 1, color='green')
>>> 500 / 11 45.45454545454545 # float division with fraction >>> 500 // 11 # int division - what we want here 45
Solution code
def draw_grid1(width, height, n):
"""
Creates a canvas.
Draws a grid1 of n-by-n black rectangles
(this code is complete)
"""
canvas = DrawCanvas(width, height, fast_draw=True)
# Figure sizes for all sub rects
sub_width = width // n
sub_height = height // n
# Loop over row/col
for row in range(n):
for col in range(n):
# Figure upper left of this sub rect
x = col * sub_width
y = row * sub_height
canvas.draw_rect(x, y, sub_width, sub_height)
Run from command line:
$ python3 draw1.py -grid1 600 400 12
Here is the draw_lines2() def first line:
def draw_lines2(canvas, x, y, width, height, n):
"""
Draw the lines2 figure within x,y width,height
The code for lines1 is already here
"""
def draw_grid2(width, height, n):
"""
Creates a canvas.
Add code to draw the lines2 figure in each grid sub rect.
"""
canvas = DrawCanvas(width, height, fast_draw=False)
sub_width = width // n
sub_height = height // n
for row in range(n):
for col in range(n):
pass
# Your code here - draw a lines2 figure in each grid rect
x = col * sub_width
y = row * sub_height
# key line: call the function to draw the figure here
draw_lines2(canvas, x, y, sub_width, sub_height, n)
Looks neat - loops + function-call = power!
Here is the "function black box" material, which can look at today if we have time, or start it next time...
Black-box model of a function: function runs with parameters as input, returns a value. All the complexity of its code is sealed inside. Calling it is relatively simple - provide parameters, get back an answer.
(had a tech problem in lecture, now fixed)
return yields the result of a functionInside the body of a function, like this:
def foo(n):
...
return n * 10
...
Suppose the caller code looks like
# in caller function a = foo(6) b = foo(7)
in out 0 0 1 10 2 20 3 30 4 40 5 60 6 72 7 84 8 96 9 108 10 120
def winnings1(score):
"""Compute winnings1 described above."""
if score >= 5:
return score * 12
# Run gets here: we know score < 5
return score * 10