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.key variable and that enables things like: 53 if (event.key == '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 L{KeyEvent.key} attribute. For example the 'ENTER' key will call key_ENTER 204 with the associated L{KeyDown} event as its parameter. 205 206 - L{update}: This method is called every loop. It is passed a single 207 parameter detailing the time in seconds since the last update 208 (often known as deltaTime.) 209 210 You may want to call drawing routines in this method followed by 211 L{tdl.flush}. 212 """ 213 __slots__ = ('__running', '__prevTime') 214 __running = False 215 __prevTime = None 216
217 - def ev_QUIT(self, event):
218 """Unless overridden this method raises a SystemExit exception closing 219 the program.""" 220 raise SystemExit()
221
222 - def ev_KEYDOWN(self, event):
223 """Override this method to handle a L{KeyDown} event."""
224
225 - def ev_KEYUP(self, event):
226 """Override this method to handle a L{KeyUp} event."""
227
228 - def ev_MOUSEDOWN(self, event):
229 """Override this method to handle a L{MouseDown} event."""
230
231 - def ev_MOUSEUP(self, event):
232 """Override this method to handle a L{MouseUp} event."""
233
234 - def ev_MOUSEMOTION(self, event):
235 """Override this method to handle a L{MouseMotion} event."""
236
237 - def update(self, deltaTime):
238 """Override this method to handle per frame logic and drawing. 239 240 @type deltaTime: float 241 @param deltaTime: This parameter tells the amount of time passed since 242 the last call measured in seconds as a floating point 243 number. 244 245 You can use this variable to make your program 246 frame rate independent. 247 Use this parameter to adjust the speed of motion, 248 timers, and other game logic. 249 """ 250 pass
251
252 - def suspend(self):
253 """When called the App will begin to return control to where 254 L{App.run} was called. 255 256 Some further events are processed and the L{App.update} method will be 257 called one last time before exiting 258 (unless suspended during a call to L{App.update}.) 259 """ 260 self.__running = False
261
262 - def run(self):
263 """Delegate control over to this App instance. This function will 264 process all events and send them to the special methods ev_* and key_*. 265 266 A call to L{App.suspend} will return the control flow back to where 267 this function is called. And then the App can be run again. 268 But a single App instance can not be run multiple times simultaneously. 269 """ 270 if self.__running: 271 raise _tdl.TDLError('An App can not be run multiple times simultaneously') 272 self.__running = True 273 while self.__running: 274 self.runOnce()
275
276 - def runOnce(self):
277 """Pump events to this App instance and then return. 278 279 This works in the way described in L{App.run} except it immediately 280 returns after the first L{update} call. 281 282 Having multiple L{App} instances and selectively calling runOnce on 283 them is a decent way to create a state machine. 284 """ 285 if self.__prevTime is None: 286 self.__prevTime = time.clock() # initiate __prevTime 287 for event in get(): 288 if event.type: # exclude custom events with a blank type variable 289 # call the ev_* methods 290 method = 'ev_%s' % event.type # ev_TYPE 291 getattr(self, method)(event) 292 if event.type == 'KEYDOWN': 293 # call the key_* methods 294 method = 'key_%s' % event.key # key_KEYNAME 295 if hasattr(self, method): # silently exclude undefined methods 296 getattr(self, method)(event) 297 newTime = time.clock() 298 self.update(newTime - self.__prevTime) 299 self.__prevTime = newTime
300 #_tdl.flush() 301
302 -def _processEvents():
303 """Flushes the event queue from libtcod into the global list _eventQueue""" 304 global _mousel, _mousem, _mouser, _eventsflushed, _pushedEvents 305 _eventsflushed = True 306 events = _pushedEvents # get events from event.push 307 _pushedEvents = [] # then clear the pushed events queue 308 309 mouse = _Mouse() 310 libkey = _Key() 311 while 1: 312 libevent = _lib.TCOD_sys_check_for_event(_tcod.TCOD_EVENT_ANY, libkey, mouse) 313 if not libevent: # no more events from libtcod 314 break 315 316 #if mouse.dx or mouse.dy: 317 if libevent & _tcod.TCOD_EVENT_MOUSE_MOVE: 318 events.append(MouseMotion(*mouse.motion)) 319 320 mousepos = ((mouse.x, mouse.y), (mouse.cx, mouse.cy)) 321 322 for oldstate, newstate, released, button in zip((_mousel, _mousem, _mouser), 323 mouse.button, mouse.button_pressed, (1, 2, 3)): 324 if released: 325 if not oldstate: 326 events.append(MouseDown(button, *mousepos)) 327 events.append(MouseUp(button, *mousepos)) 328 if newstate: 329 events.append(MouseDown(button, *mousepos)) 330 elif newstate and not oldstate: 331 events.append(MouseDown(button, *mousepos)) 332 333 if mouse.wheel_up: 334 events.append(MouseDown(4, *mousepos)) 335 if mouse.wheel_down: 336 events.append(MouseDown(5, *mousepos)) 337 338 _mousel = mouse.lbutton 339 _mousem = mouse.mbutton 340 _mouser = mouse.rbutton 341 342 if libkey.vk == _tcod.K_NONE: 343 break 344 if libkey.pressed: 345 keyevent = KeyDown 346 else: 347 keyevent = KeyUp 348 events.append(keyevent(*tuple(libkey))) 349 350 if _lib.TCOD_console_is_window_closed(): 351 events.append(Quit()) 352 353 _eventQueue.extend(events)
354
355 -def get():
356 """Flushes the event queue and returns the list of events. 357 358 This function returns L{Event} objects that can be indentified by their 359 type attribute or their class. 360 361 @rtype: iterator 362 @return: Returns an iterable of objects derived from L{Event} or anything 363 put in a L{push} call. If the iterator is deleted or otherwise 364 interrupted before finishing the excess items are preserved for the 365 next call. 366 """ 367 _processEvents() 368 while _eventQueue: 369 # if there is an interruption the rest of the events stay untouched 370 # this means you can break out of a event.get loop without losing 371 # the leftover events 372 yield(_eventQueue.pop(0)) 373 raise StopIteration()
374
375 -def push(event):
376 """Push an event into the event buffer. 377 378 @type event: L{Event}-like object 379 @param event: The event will be available on the next call to L{event.get}. 380 An event pushed in the middle of a L{get} will not show until 381 the next time L{get} called preventing push related 382 infinite loops. 383 """ 384 _pushedEvents.append(event)
385
386 -def keyWait():
387 """Waits until the user presses a key. Then returns a L{KeyDown} event. 388 389 @rtype: L{KeyDown} 390 """ 391 global _eventsflushed 392 _eventsflushed = True 393 flush = False 394 libkey = _Key() 395 _lib.TCOD_console_wait_for_keypress_wrapper(libkey, flush) 396 return KeyDown(*libkey)
397
398 -def isWindowClosed():
399 """Returns True if the exit button on the window has been clicked and 400 stays True afterwards. 401 402 @rtype: boolean 403 """ 404 return _lib.TCOD_console_is_window_closed()
405 406 __all__ = [_var for _var in locals().keys() if _var[0] != '_' and _var not in ['time', 'ctypes']] 407