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