Scoping Functions
Jed Rembold & Fred Agbo
February 19, 2024
Announcements
- Project Wordle is due today at 10 pm.
- Grading for PS3 will be returned this week! Apologies for the
delay
- We are in Ch6 of the text this week, which is interactive
graphics
- Remember the 1st midterm exam is this Friday Feb 23
in this hall
- I will provide more information about the exam right this
morning
- Those with accommodations with Testing Center
should contact them to make arrangement NOW! and cc me in the email
- Polling continues https://www.polleverywhere.com/agbofred203
Review Question!
What would be the printed value of z at the end of the code to the
right?
- 19
- 25
- 27
- None of the above
def f(x,y=0):
z = (x + 3) ** 2
return y + z
x = 1
z = x + f(y=x,x=2)
print(z)
The Python Event Model
- Graphical applications usually make it possible for the user to
control the action of a program by using an input device such a mouse.
- Programs supporting this type of control are called interactive
programs.
- User actions such as clicking the mouse are called
events.
- Programs that respond to events are said to be event
driven.
- User input does not generally occur at predictable times. As the
events are not controlled by the program, they are said to be
asynchronous.
- In Python, you write a function that acts as a listener for
a particular event type. When the event happens, the listener is
called.
First Class Functions
- Functions in Python are treated as data values just like anything
else!
- We will need to take advantage of this to write listener
functions.
- You can assign a function to a variable, pass it as a parameter,
return it as a result, etc
- Functions treated like any other data value are called
first-class functions
- To work with a function itself, you leave off the
()
. Including the parentheses is how you
call the function!
A First Class Example
import math
def evaluate_numbers(func):
print(func)
print(func(0))
print(func(2))
print(func(10))
A = evaluate_numbers
A(math.sqrt)
A(math.exp)
Closures
Consider the code to the right.
- Why does the line 12 not error?
- Nothing named
a
should
still exist when it is called!
- Python
Tutor
f2
must also keep track
of any local variables!
- The local variables that are included as part of a
function are called its closure
b = 1
def f1(a):
print(a)
print(b)
def f2():
c = a + b
return c * 3
return f2
f2 = f1(10)
c = f2()
Our First Interactive Example
Consider the simple program below, where we’ve imported the
basics and some of our helper functions
def draw_dots():
def click_action(event):
c = create_filled_rect(
event.get_x(), event.get_y(),
10,10, random_color())
gw.add(c)
gw = GWindow(500, 500)
gw.add_event_listener("click", click_action)
The click_action
function specifies
what to do when the mouse is clicked
- Note that it has access to the
gw
variable since it is in the enclosing function and thus in the
closure.
Registering a Listener
The last line of our example function:
gw.add_event_listener("click", click_action)
tells the graphics window (gw
) to call
the click_action
function whenever a mouse
“click” occurs within the window.
When the user clicks the mouse, the graphics window, in essense,
calls the client back to let them know that a click has occured. Thus,
functions such as click_action
are known as
callback functions.
The parameter event
given to the
callback function is a special data structure called a mouse
event, which contains details about the specifics of the event that
triggered the action.
Mouse Events
- We have a fairly comprehensive list of mouse-events that we can
trigger callbacks on:
"click" |
The user clicks the mouse in the
window |
"dblclk" |
The user double-clicks the mouse in the
window |
"mousedown" |
The user presses the mouse button
down |
"mouseup" |
The user releases the mouse button |
"mousemove" |
The user moves the mouse |
"drag" |
The user moves the mouse with the button
down |
Event Details
- Certain actions can trigger more than one event
- Clicking generates a “mousedown”, “mouseup”, and then “click” event,
in that order
- Events trigger no action unless the window is listening for that
event
- If I drag my mouse in the
draw_dots()
function, you’ll notice that nothing happens
- You can setup however many listeners you feel you need in order to
make your program behave as desired
gw.add_event_listener("click", click_action)
gw.add_event_listener("dblclk", dblclk_action)
Line Drawing
- Say we wanted to write a simple program that allows the user to draw
lines by clicking and dragging the mouse
- Using two event listeners would be useful:
- “mousedown” could start drawing a zero-length line at the current
mouse position (and add it to the window)
- “drag” could update the end-point of that line
- The strategy would allow the user to have visual feedback as they
drag around, helping them to position the line
- Since the line stretches and contracts as you move the cursor
around, the technique is commonly called rubber-banding
Attempt #1
from pgl import GWindow, GLine
WIDTH = 500
HEIGHT = 500
def draw_lines():
def mousedown_event(e):
x = e.get_x()
y = e.get_y()
line = GLine(x,y,x,y)
gw.add(line)
def drag_action(e):
line.set_end_point(e.get_x(), e.get_y())
gw = GWindow(WIDTH, HEIGHT)
line = None
gw.add_event_listener("mousedown", mousedown_event)
gw.add_event_listener("drag", drag_action)
if __name__ == '__main__':
draw_lines()
What Happened?
- Remember that if you define a variable in a function, that variable
is assumed to be local!
- Keeps you from accidentally overwriting variables you may not have
meant to
- It works against us here, since we WANT to override the original
value
- We can’t pass in the info as a parameter, since it is not part of
the event information
- Python does have a
nonlocal
keyword, which allows you to state that a specific variable is
not local, but it tends to just confuse students