460 likes | 777 Views
Object-Oriented Programming in Python Goldwasser and Letscher Chapter 15 Event-Driven Programming. Terry Scott University of Northern Colorado 2007 Prentice Hall. Introduction: What is Covered in Chapter 15. Basics of event-driven programming. Event Handling in cs1graphics. Event class.
E N D
Object-Oriented Programming in PythonGoldwasser and LetscherChapter 15Event-Driven Programming Terry Scott University of Northern Colorado 2007 Prentice Hall
Introduction: What is Covered in Chapter 15 • Basics of event-driven programming. • Event Handling in cs1graphics. • Event class. • Programming using events. • Case Study: a full GUI for mastermind. Terry Scott University of Northern Colorado 2007 Prentice Hall
Programming Styles • Programming style so far might be described as flow control: • Program flows in a preplanned way. • It may wait for input from the user. • Event-driven programming: • program decides what to do next based on events that occur (mouse clicks, keyboard clicks). • Graphical user interface (GUI) pronounced gooey is one kind of event-driven programming. • GUI is what most of us are used to when using an operating system whether it is Windows, a Mac, or X-windows on Linux. Terry Scott University of Northern Colorado 2007 Prentice Hall
Basics of Event-Driven Programming • name = raw_input() • The program waits for the user to enter a response. • The entering of the response is an event. • The following code waits for the user to click the mouse on the paper. This is also an event. paper = Canvas() cue = paper.wait() ball = Circle(10, cue.getMouseLocation()) paper.add(ball) Terry Scott University of Northern Colorado 2007 Prentice Hall
Basics of Event-Driven Programming • Both previous examples are event driven. • However they are still both sequential. • Program advances to the spot where it waits until the event occurs and then it continues. • For true event handling the program is described by what is to be done when various events occur. Terry Scott University of Northern Colorado 2007 Prentice Hall
Basics of Event-Driven Programming • Event handlers – code that is called when an event occurs. • A stand-alone event handler is referred to as a callback function. • Event loop is a code loop that waits for various prescribed events to occur. This is something like an infinite while loop but it is part of the class and is hidden from the user of the class. • A thread is a way for a single processor to execute different pieces of code. Terry Scott University of Northern Colorado 2007 Prentice Hall
cs1graphics Event Programming • When cs1graphics is started the event loop is started as well. • Included in the cs1graphics module is an EventHandler class. • Basic handler that prints Event Triggered when an event occurs is shown below. class BasicHandler(EventHandler): def handle(self, event): print 'Event Triggered' Terry Scott University of Northern Colorado 2007 Prentice Hall
Registering a Handler with a Graphics Object #The following code will print 'Event Triggered' #every time the mouse is clicked on the Canvas. simple = BasicHandler() #create handler object paper = Canvas() paper.addHandler(simple) #adds handler to paper # Could just as well have added a circle to # the paper and then added the handler to the # circle and then would respond only when the # circle was clicked. Terry Scott University of Northern Colorado 2007 Prentice Hall
A Handler with State Information #Handler keeps track of how many times it. #has been clicked. Displays results on shell. class CountingHandler(EventHandler): def __init__(self): EventHandler.__init__(self) #call parent self.__count = 0 #constructor def handle(self, event): self._count += 1 print 'Event Triggered. Count: ', self._count Terry Scott University of Northern Colorado 2007 Prentice Hall
Event Handler with State Info. #Displays number of clicks on the canvas. class TallyHandler(EventHandler): def __init__(self, textObj): EventHandler.__init__(self) self.__count = 0 self._text = textObj self._textObj.setMessage(str(self._count)) #reset to 0 def handle(self, event): self._count += 1 self._textObj.setMessage(str(self._count)) paper = Canvas(100,100) score = Text(' ', 12, Point(40,40)) paper.add(score) referee = TallyHandler(score) #create handler paper.addHandler(referee) #activate handler Terry Scott University of Northern Colorado 2007 Prentice Hall
Event Handler That Closes After One Event class HandleOnce(EventHandler): def __init__(self, eventTrigger): EventHandler.__init__(self) self._trigger = eventTrigger def handle(self, event): print "That's all folks!!!" self._trigger.removeHandler(self) paper = Canvas( ) oneTime = HandleOnce(paper) paper.addHandler(oneTime) Terry Scott University of Northern Colorado 2007 Prentice Hall
The Event Class • So far the signature of the handler has been: def handle(self, event): • Have not done anything with event. • One method in the event class is getTrigger(). This returns a reference to the object where the event occurred. • Can use this to write the HandleOnce class with fewer lines of code. Terry Scott University of Northern Colorado 2007 Prentice Hall
Using getTrigger Method class HandleOnce(EventHandler): def handle(self, event): print "That's all folks!!!" event.getTrigger().removeHandler(self) paper = Canvas( ) oneTime = HandleOnce(paper) paper.addHandler(oneTime) Terry Scott University of Northern Colorado 2007 Prentice Hall
Mouse Events • Event object method: getDescription(). This returns the event type. For the mouse this can be: • 'mouse click' – mouse button clicked • 'mouse release' – mouse button released • 'mouse drag' – mouse moved • Event object method: getMouseLocation(). This returns the mouse coordinates when the mouse is clicked. Terry Scott University of Northern Colorado 2007 Prentice Hall
Mouse Events #Draws circle when the mouse is clicked. class CircleDrawHandler(EventHandler): def handle(self, event): if event.getDescription() == 'mouse click': c = Circle(5, event.getMouseLocation()) event.getTrigger().add(c) #adds circle to #canvas paper = Canvas(100,100) handler = CircleDrawHandler( ) paper.addHandler(handler) Terry Scott University of Northern Colorado 2007 Prentice Hall
Mouse Events • In a 'mouse drag' event the getOldMouseLocation() method remembers where the mouse was originally. • A program might want different behavior for stationary click than one where the mouse was dragged and then clicked. • See code on next page for an example of detecting whether the mouse was dragged before it was clicked. • _mouseDragged keeps track of whether mouse was dragged since the last mouse click. Terry Scott University of Northern Colorado 2007 Prentice Hall
class ClickAndReleaseHandler(EventHandler): def __init__(self): EventHandler.__init__(self) self._mouseDragged = False def handle(self, event): if event.getDescription( ) = 'mouse click': self._mouseDragged = False elif event.getDescription() = 'mouse drag' self._mouseDragged = True elif event.getDescription() == 'mouse release': if self._mouseDragged: print 'Mouse was dragged' else: print 'Mouse was clicked without dragging' paper = Canvas( ) dragDetector = ClickAndReleaseHandler() paper.addHandler(dragDetector) Terry Scott University of Northern Colorado 2007 Prentice Hall
Keyboard Events • Keyboard causes an event. • Method getKey() determines which key was depressed. • Code on next slide allows keyboard presses to be displayed on the Canvas Text widget. Terry Scott University of Northern Colorado 2007 Prentice Hall
Keyboard Events Code class KeyHandler(EventHandler): def __init__(self, textObj): EventHandler.__init__(self) self._textobj = textObj def handle(self, event): if event.getDescription( ) = 'keyboard': self._textObj.setMessage(self._textObj.getMessage() + event.getKey()) elif event.getDescription() = 'mouse click' self._textObj.setMessage(' ') #clears the text paper = Canvas( ) textDisplay = Text(' ', 12, Point(100,100)) paper.add(textDisplay) echo = KeyHandler(textDisplay) paper.addHandler(echo) Terry Scott University of Northern Colorado 2007 Prentice Hall
Timers • alarm = timer(10, True) • 10: The timer generates an event after 10 seconds. • True: timer repeats. If set to False or left off, then is a one time event. • alarm.start() – starts timer. • Second slide rotates a circle around a point outside the circle. Terry Scott University of Northern Colorado 2007 Prentice Hall
Timer Code alarm = Timer(1, True) stopwatch = CountingHandler() alarm.addHandler(stopwatch) print 'Ready . . .' alarm.start( ) #displays Ready … every sec. Terry Scott University of Northern Colorado 2007 Prentice Hall
Rotate a Circle class RotationHandler(EventHandler): def __init__(self, shape): self._shape = shape def handle(self, event): self._shape.rotate paper = Canvas(100,100) sampleCircle = Circle(20, Point(50,20)) sampleCircle.adjustReference(0,30) paper.add(sampleCircle) alarm = Timer(0.1, True) rotator = RotationHandler(sampleCircle) alarm.addHandler(rotator) alarm.start() Terry Scott University of Northern Colorado 2007 Prentice Hall
Monitor • Class with two methods: • wait( ) : forces a wait for an event. • release( ): event occurs causes the wait to be released. • Monitor like wait() except it is released on any event but the monitor can be set to only release on certain events. Terry Scott University of Northern Colorado 2007 Prentice Hall
Code Using a Monitor class ShapeHandler(EventHandler): def __init__(self, monitor): EventHandler.__init__(self) self._monitor = monitor def handle(self,event): if event.getDescription( ) == 'mouse drag': self._monitor.release( ) #release on mouse #drag #code using this class on next slide Terry Scott University of Northern Colorado 2007 Prentice Hall
Coding Using a Monitor (continue) paper = Canvas() checkpoint = Monitor() handler = ShapeHandler(checkpoint) cir = Circle(10, Point(50,50)) cir.setFillColor('blue') cir.addHandler(handler) paper.add(cir) checkpoint.wait() #forced to wait until mouse is #dragged inside circle paper.setBackgroundColor('green') Terry Scott University of Northern Colorado 2007 Prentice Hall
Adding and Moving Shapes on a Canvas • Code on the next slides does the following: • when mouse is clicked on canvas it creates a new shape. Cycles through circle, square, rectangle, and a triangle. • dragging mouse on existing shape moves it. • when mouse clicked it enlarges object. • typing on keyboard changes color of current in-focus shape. Terry Scott University of Northern Colorado 2007 Prentice Hall
Adding and Moving Shapes Code # class ShapeHandler(EventHandler): def __init__(self): EventHandler.__init__(self): self._mouseDragged= False Terry Scott University of Northern Colorado 2007 Prentice Hall
Adding and Moving Shapes Code (continued) def handle(self, event): shape = event.getTrigger( ) if event.getDescription( ) == 'mouse drag': old = event.getOldMouseLocation( ) new = event.getMouseLocation( ) shape.move(new.getX()–old.getX(),new.getY() - old.getY( ) self._mouseDragged = True elif even.getDescription( ) == 'mouse click': self._mouseDragged = False elif event.getDescription() == 'mouse release': if not self._mouseDragged: shape.scale(1.5) elif event.getDescription() == 'keyboard': shape.setFillColor(Color.randomColor()) Terry Scott University of Northern Colorado 2007 Prentice Hall
Adding and Moving Shapes Code (continued) class NewShapeHandler(EventHandler): def __init__(self): EventHandler.__init__(self) self._shapeCode = 0 self._handler = ShapeHandler() #single instance handles all shapes Terry Scott University of Northern Colorado 2007 Prentice Hall
Adding and Moving Shapes Code (continued) def handle(self, event): if event.getDescription() == 'mouse click': s = Circle(10) elif self._shapeCode == 1: s = Square(10) elif self._shAapeCode == 2 s = Rectangle(15, 5) elif self._shapeCode == 3: s = Polygon(Point(5,5), Point(0,-5),Point(-5,5)) Terry Scott University of Northern Colorado 2007 Prentice Hall
Adding and Moving Shapes Code (continued) self._shapeCode = (self._shapeCode+1)% 4 #advance cyclically s.move(event.getMouseLocation().getX(), event.getMouseLocation().getY()) s.setFillColor('white') event.getTrigger().add(s) #add shape to canvas s.addHandler(self._handler) #register the #ShapeHandler with new shape paper = Canvas(400, 300, 'white', 'Click me!') paper.addHandler(self._handler) #instantiate handler and #register all at once Terry Scott University of Northern Colorado 2007 Prentice Hall
Dialog Box Class • Dialog is not a full fledged window. Can not change its size. • Used for asking a question such as: Do you wish to continue? • The dialog class constructor is called with: Dialog(prompt, options, title, width, height). Terry Scott University of Northern Colorado 2007 Prentice Hall
Dialog Box Code survey = Dialog('How would you rate this', 'interface', 'good','so-so','poor'), 'User Survey') answer = survey.display() if answer != 'good' print "Let's see you do better" Terry Scott University of Northern Colorado 2007 Prentice Hall
Dialog Box Survey Output Terry Scott University of Northern Colorado 2007 Prentice Hall
Stopwatch Widget Class • Unlike the dialog box this creates an actual component to go on the window. • Called a widget. • This uses multiple inheritance: • Layer. • EventHandler. • See code on pages 511-512. Terry Scott University of Northern Colorado 2007 Prentice Hall
Stopwatch Widget Terry Scott University of Northern Colorado 2007 Prentice Hall
Stopwatch Widget Code class Stopwatch(Layer, EventHandler): def __init__(self): #create stopwatch instance Layer.__init__(self) border = Rectangle(200,100) border.setFillColor('white') border.setDepth(52) self._display = Text('0:00', 36, Point(-25,-40) self._start = Square(40,Point(-60,25)) self._reset = Square(40, Point(60,26)) buttons=[self._start, self._stop, self._reset) Terry Scott University of Northern Colorado 2007 Prentice Hall
Stopwatch Widget Code (continued) for b in buttons: b.setFillColor('lightgray') b.setDepth(51) #in front of border, behind icons starIcon = Polygon(Point(-70,15),Point(-70,35), Point(-50,25)) startIcon.setFillColor('black') stopIcon =Square(20,Point(0,25)) stopIcon.setFillColor('black') resetIcon = Text('00',24,Point(47,15)) for obj in buttons+[self._display,border,startIcon, stopIcon,resetIcon]: self.add(obj) #add to the layer Terry Scott University of Northern Colorado 2007 Prentice Hall
Stopwatch Widget Code (continued) self._clock = 0 #measured in seconds self._timer = Timer(1, True) for active in (self._timer,self._start,self._stop,self._reset): active.addHandle(self) #handle all events def getTime(self): """Convert clock's time to a string with minutes and seconds""" min = str(self._clock //60) sec = str(self._clock %60) if (len(sec) == 1: sec = '0' + sec #pad with leading 0 return min + ':' +sec Terry Scott University of Northern Colorado 2007 Prentice Hall
Stopwatch Widget Code (continued) def handle(self, event): #deal with each event if event.getDescription() == 'timer': self._clock += 1 self._display.setText(self.getTime()) elif event.getDescription() == 'mouse click': if event.getTrigger() == self._start: self._timer.start() elif event.getTrigger() == self.stop: self._time.stop() else: self._clock = 0 self._display.setText(self.getTime()) Terry Scott University of Northern Colorado 2007 Prentice Hall
Stopwatch Widget Code (continued) if __name__ == '__main__': paper = Canvas(400,400) clock = Stopwatch() paper.add(clock) clock.move(200,200) Terry Scott University of Northern Colorado 2007 Prentice Hall
Mastermind GUI • MastermindGUI must perform all tasks of both the input and output classes. • Full code is on pages 515 – 518. Terry Scott University of Northern Colorado 2007 Prentice Hall
A Full GUI for Mastermind Terry Scott University of Northern Colorado 2007 Prentice Hall