Package tdl :: Module event
[frames] | no frames]

Source Code for Module tdl.event

  1  """ 
  2      This module handles user input. 
  3       
  4      To handle user input you will likely want to use the L{event.get} function 
  5      or create a subclass of L{event.App}. 
  6       - L{event.get} iterates over recent events. 
  7       - L{event.App} passes events to the overridable methods: ev_* and key_*. 
  8       
  9      But there are other options such as L{event.keyWait} and L{event.isWindowClosed}. 
 10       
 11      A few event attributes are actually string constants. 
 12      Here's a reference for those: 
 13       - L{Event.type} 
 14        
 15         'QUIT', 'KEYDOWN', 'KEYUP', 'MOUSEDOWN', 'MOUSEUP', or 'MOUSEMOTION.' 
 16          
 17       - L{MouseButtonEvent.button} (found in L{MouseDown} and L{MouseUp} events) 
 18        
 19         'LEFT', 'MIDDLE', 'RIGHT', 'SCROLLUP', 'SCROLLDOWN' 
 20          
 21       - L{KeyEvent.key} (found in L{KeyDown} and L{KeyUp} events) 
 22        
 23         'NONE', 'ESCAPE', 'BACKSPACE', 'TAB', 'ENTER', 'SHIFT', 'CONTROL', 
 24         'ALT', 'PAUSE', 'CAPSLOCK', 'PAGEUP', 'PAGEDOWN', 'END', 'HOME', 'UP', 
 25         'LEFT', 'RIGHT', 'DOWN', 'PRINTSCREEN', 'INSERT', 'DELETE', 'LWIN', 
 26         'RWIN', 'APPS', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 
 27         'KP0', 'KP1', 'KP2', 'KP3', 'KP4', 'KP5', 'KP6', 'KP7', 'KP8', 'KP9', 
 28         'KPADD', 'KPSUB', 'KPDIV', 'KPMUL', 'KPDEC', 'KPENTER', 'F1', 'F2', 
 29         'F3', 'F4', 'F5', 'F6', 'F7', 'F8', 'F9', 'F10', 'F11', 'F12', 
 30         'NUMLOCK', 'SCROLLLOCK', 'SPACE', 'CHAR' 
 31  """ 
 32   
 33  import time 
 34  import ctypes 
 35   
 36  from .__tcod import _lib, _Mouse, _Key 
 37  from . import __tcod as _tcod 
 38  import tdl as _tdl 
 39   
 40  _eventQueue = [] 
 41  _pushedEvents = [] 
 42   
 43  _mousel = 0 
 44  _mousem = 0 
 45  _mouser = 0 
 46   
 47  # this interpets the constants from libtcod and makes a key -> keyname dictionary 
48 -def _parseKeyNames(module):
49 """ 50 returns a dictionary mapping of human readable key names to their keycodes 51 this parses constants with the names of K_* and makes code=name pairs 52 this is for KeyEvent.keyname variable and that enables things like: 53 if (event.keyname == 'PAGEUP'): 54 """ 55 _keyNames = {} 56 for attr in dir(module): # from the modules variables 57 if attr[:2] == 'K_': # get the K_* constants 58 _keyNames[getattr(_tcod, attr)] = attr[2:] # and make CODE=NAME pairs 59 return _keyNames
60 61 _keyNames = _parseKeyNames(_tcod) 62
63 -class Event(object):
64 """Base Event class. 65 66 You can easily subclass this to make your own events. Be sure to set 67 the class attribute L{Event.type} for it to be passed to a custom L{App} 68 ev_* method.""" 69 __slots__ = ('__weakref__',) 70 type = None 71 """String constant representing the type of event. 72 73 The L{App} ev_* methods depend on this attribute. 74 75 Can be: 'QUIT', 'KEYDOWN', 'KEYUP', 'MOUSEDOWN', 'MOUSEUP', or 'MOUSEMOTION.' 76 """ 77
78 - def __repr__(self):
79 """List an events public attributes in print calls. 80 """ 81 attrdict = {} 82 for varname in dir(self): 83 if '_' == varname[0]: 84 continue 85 attrdict[varname] = self.__getattribute__(varname) 86 return '%s Event %s' % (self.__class__.__name__, repr(attrdict))
87
88 -class Quit(Event):
89 """Fired when the window is closed by the user. 90 """ 91 __slots__ = () 92 type = 'QUIT'
93
94 -class KeyEvent(Event):
95 __slots__ = ('key', 'char', 'shift', 'alt', 'control', 96 'leftAlt', 'leftCtrl', 'rightAlt', 'rightCtrl') 97
98 - def __init__(self, key, char, lalt, lctrl, ralt, rctrl, shift):
99 self.key = _keyNames[key] 100 """Human readable name of the key pressed. 101 102 Can be one of 103 'NONE', 'ESCAPE', 'BACKSPACE', 'TAB', 'ENTER', 'SHIFT', 'CONTROL', 104 'ALT', 'PAUSE', 'CAPSLOCK', 'PAGEUP', 'PAGEDOWN', 'END', 'HOME', 'UP', 105 'LEFT', 'RIGHT', 'DOWN', 'PRINTSCREEN', 'INSERT', 'DELETE', 'LWIN', 106 'RWIN', 'APPS', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 107 'KP0', 'KP1', 'KP2', 'KP3', 'KP4', 'KP5', 'KP6', 'KP7', 'KP8', 'KP9', 108 'KPADD', 'KPSUB', 'KPDIV', 'KPMUL', 'KPDEC', 'KPENTER', 'F1', 'F2', 109 'F3', 'F4', 'F5', 'F6', 'F7', 'F8', 'F9', 'F10', 'F11', 'F12', 110 'NUMLOCK', 'SCROLLLOCK', 'SPACE', 'CHAR' 111 @type: string""" 112 char = char if isinstance(char, str) else char.decode() 113 self.char = char.replace('\x00', '') # change null to empty string 114 """A single character string of the letter or symbol pressed. 115 116 Special characters like delete and return are not cross-platform. 117 L{key} should be used instead for special keys. 118 @type: string""" 119 self.leftAlt = bool(lalt) 120 """@type: boolean""" 121 self.rightAlt = bool(ralt) 122 """@type: boolean""" 123 self.leftCtrl = bool(lctrl) 124 """@type: boolean""" 125 self.rightCtrl = bool(rctrl) 126 """@type: boolean""" 127 self.shift = bool(shift) 128 """True if shift was held down during this event. 129 @type: boolean""" 130 self.alt = bool(lalt or ralt) 131 """True if alt was held down during this event. 132 @type: boolean""" 133 self.control = bool(lctrl or rctrl) 134 """True if control was held down during this event. 135 @type: boolean"""
136
137 -class KeyDown(KeyEvent):
138 """Fired when the user presses a key on the keyboard or a key repeats. 139 """ 140 __slots__ = () 141 type = 'KEYDOWN'
142
143 -class KeyUp(KeyEvent):
144 """Fired when the user releases a key on the keyboard. 145 """ 146 __slots__ = () 147 type = 'KEYUP'
148 149 _mouseNames = {1: 'LEFT', 2: 'MIDDLE', 3: 'RIGHT', 4: 'SCROLLUP', 5: 'SCROLLDOWN'}
150 -class MouseButtonEvent(Event):
151 __slots__ = ('button', 'pos', 'cell') 152
153 - def __init__(self, button, pos, cell):
154 self.button = _mouseNames[button] 155 """Can be one of 156 'LEFT', 'MIDDLE', 'RIGHT', 'SCROLLUP', 'SCROLLDOWN' 157 @type: string""" 158 self.pos = pos 159 """(x, y) position of the mouse on the screen 160 @type: (int, int)""" 161 self.cell = cell 162 """(x, y) position of the mouse snapped to a cell on the root console 163 @type: (int, int)"""
164
165 -class MouseDown(MouseButtonEvent):
166 """Fired when a mouse button is pressed.""" 167 __slots__ = () 168 type = 'MOUSEDOWN'
169
170 -class MouseUp(MouseButtonEvent):
171 """Fired when a mouse button is released.""" 172 __slots__ = () 173 type = 'MOUSEUP'
174
175 -class MouseMotion(Event):
176 """Fired when the mouse is moved.""" 177 __slots__ = ('pos', 'motion', 'cell', 'cellmotion') 178 type = 'MOUSEMOTION' 179
180 - def __init__(self, pos, cell, motion, cellmotion):
181 self.pos = pos 182 """(x, y) position of the mouse on the screen. 183 type: (int, int)""" 184 self.cell = cell 185 """(x, y) position of the mouse snapped to a cell on the root console. 186 type: (int, int)""" 187 self.motion = motion 188 """(x, y) motion of the mouse on the screen. 189 type: (int, int)""" 190 self.cellmotion = cellmotion 191 """(x, y) mostion of the mouse moving over cells on the root console. 192 type: (int, int)"""
193
194 -class App(object):
195 """ 196 Application framework. 197 198 - ev_*: Events are passed to methods based on their L{Event.type} attribute. 199 If an event type is 'KEYDOWN' the ev_KEYDOWN method will be called 200 with the event instance as a parameter. 201 202 - key_*: When a key is pressed another method will be called based on the 203 keyname attribute. For example the 'ENTER' keyname will call key_ENTER 204 with the associated L{KeyDown} event as its parameter. 205 206 - L{update}: This method is called every loop before making a call to 207 L{tdl.flush}. It is passed a single parameter detailing the time in 208 seconds since the last update (often known as deltaTime.) 209 """ 210 __slots__ = ('__running', '__prevTime') 211 __running = False 212
213 - def ev_QUIT(self, event):
214 """Unless overridden this method raises a SystemExit exception closing 215 the program.""" 216 raise SystemExit()
217
218 - def ev_KEYDOWN(self, event):
219 """Override this method to handle a L{KeyDown} event."""
220
221 - def ev_KEYUP(self, event):
222 """Override this method to handle a L{KeyUp} event."""
223
224 - def ev_MOUSEDOWN(self, event):
225 """Override this method to handle a L{MouseDown} event."""
226
227 - def ev_MOUSEUP(self, event):
228 """Override this method to handle a L{MouseUp} event."""
229
230 - def ev_MOUSEMOTION(self, event):
231 """Override this method to handle a L{MouseMotion} event."""
232
233 - def update(self, deltaTime):
234 """Override this method to handle per frame logic and drawing. 235 236 @type deltaTime: float 237 @param deltaTime: This parameter tells the amount of time passed since 238 the last call measured in seconds as a floating point 239 number. 240 241 You can use this variable to make your program 242 frame rate independent. Use this parameter to adjust 243 the speed of physics, special visual effects, 244 and other game logic. 245 """ 246 pass
247
248 - def suspend(self):
249 """When called the App will begin to return control to where 250 L{App.run} was called. 251 252 Some further events are processed and the L{App.update} method will be 253 called one last time before exiting 254 (unless suspended during a call to L{App.update}.) 255 """ 256 self.__running = False
257
258 - def run(self):
259 """Delegate control over to this App instance. This function will 260 process all events and send them to the special methods ev_* and key_*. 261 262 A call to L{App.suspend} will return the control flow back to where 263 this function is called. And then the App can be run again. 264 But a single App instance can not be run multiple times simultaneously. 265 """ 266 if self.__running: 267 raise _tdl.TDLError('An App can not be run multiple times simultaneously') 268 self.__running = True 269 while self.__running: 270 self.runOnce()
271
272 - def runOnce(self):
273 """Pump events to this App instance and then return. 274 275 This works in the way described in L{App.run} except it immediately 276 returns after the first L{update} call. 277 278 Having multiple L{App} instances and selectively calling runOnce on 279 them is a decent way to create a state machine. 280 """ 281 if not hasattr(self, '__prevTime'): 282 self.__prevTime = time.clock() # initiate __prevTime 283 for event in get(): 284 if event.type: # exclude custom events with a blank type variable 285 # call the ev_* methods 286 method = 'ev_%s' % event.type # ev_TYPE 287 getattr(self, method)(event) 288 if event.type == 'KEYDOWN': 289 # call the key_* methods 290 method = 'key_%s' % event.key # key_KEYNAME 291 if hasattr(self, method): # silently exclude undefined methods 292 getattr(self, method)(event) 293 newTime = time.clock() 294 self.update(newTime - self.__prevTime) 295 self.__prevTime = newTime 296 _tdl.flush()
297
298 -def _processEvents():
299 """Flushes the event queue from libtcod into the global list _eventQueue""" 300 global _mousel, _mousem, _mouser, _eventsflushed, _pushedEvents 301 _eventsflushed = True 302 events = _pushedEvents # get events from event.push 303 _pushedEvents = [] # then clear the pushed events queue 304 305 mouse = _Mouse() 306 libkey = _Key() 307 while 1: 308 libevent = _lib.TCOD_sys_check_for_event(_tcod.TCOD_EVENT_ANY, libkey, mouse) 309 if not libevent: # no more events from libtcod 310 break 311 312 #if mouse.dx or mouse.dy: 313 if libevent & _tcod.TCOD_EVENT_MOUSE_MOVE: 314 events.append(MouseMotion(*mouse.motion)) 315 316 mousepos = ((mouse.x, mouse.y), (mouse.cx, mouse.cy)) 317 318 for oldstate, newstate, released, button in zip((_mousel, _mousem, _mouser), 319 mouse.button, mouse.button_pressed, (1, 2, 3)): 320 if released: 321 if not oldstate: 322 events.append(MouseDown(button, *mousepos)) 323 events.append(MouseUp(button, *mousepos)) 324 if newstate: 325 events.append(MouseDown(button, *mousepos)) 326 elif newstate and not oldstate: 327 events.append(MouseDown(button, *mousepos)) 328 329 if mouse.wheel_up: 330 events.append(MouseDown(4, *mousepos)) 331 if mouse.wheel_down: 332 events.append(MouseDown(5, *mousepos)) 333 334 _mousel = mouse.lbutton 335 _mousem = mouse.mbutton 336 _mouser = mouse.rbutton 337 338 if libkey.vk == _tcod.K_NONE: 339 break 340 if libkey.pressed: 341 keyevent = KeyDown 342 else: 343 keyevent = KeyUp 344 events.append(keyevent(*tuple(libkey))) 345 346 if _lib.TCOD_console_is_window_closed(): 347 events.append(Quit()) 348 349 _eventQueue.extend(events)
350
351 -def get():
352 """Flushes the event queue and returns the list of events. 353 354 This function returns L{Event} objects that can be indentified by their 355 type attribute or their class. 356 357 @rtype: iterator 358 @return: Returns an iterable of objects derived from L{Event} or anything 359 put in a L{push} call. If the iterator is deleted or otherwise 360 interrupted before finishing the excess items are preserved for the 361 next call. 362 """ 363 _processEvents() 364 while _eventQueue: 365 # if there is an interruption the rest of the events stay untouched 366 # this means you can break out of a event.get loop without losing 367 # the leftover events 368 yield(_eventQueue.pop(0)) 369 raise StopIteration()
370
371 -def push(event):
372 """Push an event into the event buffer. 373 374 @type event: L{Event}-like object 375 @param event: The event will be available on the next call to L{event.get}. 376 An event pushed in the middle of a L{get} will not show until 377 the next time L{get} called preventing push related 378 infinite loops. 379 """ 380 _pushedEvents.append(event)
381
382 -def keyWait():
383 """Waits until the user presses a key. Then returns a L{KeyDown} event. 384 385 @rtype: L{KeyDown} 386 """ 387 global _eventsflushed 388 _eventsflushed = True 389 flush = False 390 libkey = _Key() 391 _lib.TCOD_console_wait_for_keypress_wrapper(libkey, flush) 392 return KeyDown(*libkey)
393
394 -def isWindowClosed():
395 """Returns True if the exit button on the window has been clicked and 396 stays True afterwards. 397 398 @rtype: boolean 399 """ 400 return _lib.TCOD_console_is_window_closed()
401 402 __all__ = [_var for _var in locals().keys() if _var[0] != '_' and _var not in ['time', 'ctypes']] 403