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 as _time 
 34   
 35  from .__tcod import _lib, _Mouse, _Key 
 36  from . import __tcod as _tcod 
 37  from . import __style as _style 
 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 when printed. 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', 'keychar', 'shift', 'alt', 'control', 96 'leftAlt', 'leftCtrl', 'rightAlt', 'rightCtrl') 97
98 - def __init__(self, key, char, lalt, lctrl, ralt, rctrl, shift):
99 # Convert keycodes into string, but use string if passed 100 self.key = key if isinstance(key, str) else _keyNames[key] 101 """Human readable names of the key pressed. 102 Non special characters will show up as 'CHAR'. 103 104 Can be one of 105 'NONE', 'ESCAPE', 'BACKSPACE', 'TAB', 'ENTER', 'SHIFT', 'CONTROL', 106 'ALT', 'PAUSE', 'CAPSLOCK', 'PAGEUP', 'PAGEDOWN', 'END', 'HOME', 'UP', 107 'LEFT', 'RIGHT', 'DOWN', 'PRINTSCREEN', 'INSERT', 'DELETE', 'LWIN', 108 'RWIN', 'APPS', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 109 'KP0', 'KP1', 'KP2', 'KP3', 'KP4', 'KP5', 'KP6', 'KP7', 'KP8', 'KP9', 110 'KPADD', 'KPSUB', 'KPDIV', 'KPMUL', 'KPDEC', 'KPENTER', 'F1', 'F2', 111 'F3', 'F4', 'F5', 'F6', 'F7', 'F8', 'F9', 'F10', 'F11', 'F12', 112 'NUMLOCK', 'SCROLLLOCK', 'SPACE', 'CHAR' 113 114 For the actual character instead of 'CHAR' use L{keychar}. 115 @type: string""" 116 char = char if isinstance(char, str) else char.decode() 117 self.char = char.replace('\x00', '') # change null to empty string 118 """A single character string of the letter or symbol pressed. 119 120 Special characters like delete and return are not cross-platform. 121 L{key} or L{keychar} should be used instead for special keys. 122 Characters are also case sensitive. 123 @type: string""" 124 # get the best out of self.key and self.char 125 self.keychar = self.char if self.key == 'CHAR' else self.key 126 """Similar to L{key} but returns a case sensitive letter or symbol 127 instead of 'CHAR'. 128 129 This variable makes available the widest variety of symbols and should 130 be used for key-mappings or anywhere where a narrower sample of keys 131 isn't needed. 132 """ 133 self.leftAlt = bool(lalt) 134 """@type: boolean""" 135 self.rightAlt = bool(ralt) 136 """@type: boolean""" 137 self.leftCtrl = bool(lctrl) 138 """@type: boolean""" 139 self.rightCtrl = bool(rctrl) 140 """@type: boolean""" 141 self.shift = bool(shift) 142 """True if shift was held down during this event. 143 @type: boolean""" 144 self.alt = bool(lalt or ralt) 145 """True if alt was held down during this event. 146 @type: boolean""" 147 self.control = bool(lctrl or rctrl) 148 """True if control was held down during this event. 149 @type: boolean"""
150
151 -class KeyDown(KeyEvent):
152 """Fired when the user presses a key on the keyboard or a key repeats. 153 """ 154 __slots__ = () 155 type = 'KEYDOWN'
156
157 -class KeyUp(KeyEvent):
158 """Fired when the user releases a key on the keyboard. 159 """ 160 __slots__ = () 161 type = 'KEYUP'
162 163 _mouseNames = {1: 'LEFT', 2: 'MIDDLE', 3: 'RIGHT', 4: 'SCROLLUP', 5: 'SCROLLDOWN'}
164 -class MouseButtonEvent(Event):
165 __slots__ = ('button', 'pos', 'cell') 166
167 - def __init__(self, button, pos, cell):
168 self.button = _mouseNames[button] 169 """Can be one of 170 'LEFT', 'MIDDLE', 'RIGHT', 'SCROLLUP', 'SCROLLDOWN' 171 @type: string""" 172 self.pos = pos 173 """(x, y) position of the mouse on the screen 174 @type: (int, int)""" 175 self.cell = cell 176 """(x, y) position of the mouse snapped to a cell on the root console 177 @type: (int, int)"""
178
179 -class MouseDown(MouseButtonEvent):
180 """Fired when a mouse button is pressed.""" 181 __slots__ = () 182 type = 'MOUSEDOWN'
183
184 -class MouseUp(MouseButtonEvent):
185 """Fired when a mouse button is released.""" 186 __slots__ = () 187 type = 'MOUSEUP'
188
189 -class MouseMotion(Event):
190 """Fired when the mouse is moved.""" 191 __slots__ = ('pos', 'motion', 'cell', 'cellmotion') 192 type = 'MOUSEMOTION' 193
194 - def __init__(self, pos, cell, motion, cellmotion):
195 self.pos = pos 196 """(x, y) position of the mouse on the screen. 197 type: (int, int)""" 198 self.cell = cell 199 """(x, y) position of the mouse snapped to a cell on the root console. 200 type: (int, int)""" 201 self.motion = motion 202 """(x, y) motion of the mouse on the screen. 203 type: (int, int)""" 204 self.cellmotion = cellmotion 205 """(x, y) mostion of the mouse moving over cells on the root console. 206 type: (int, int)"""
207
208 -class App(object):
209 """ 210 Application framework. 211 212 - ev_*: Events are passed to methods based on their L{Event.type} attribute. 213 If an event type is 'KEYDOWN' the ev_KEYDOWN method will be called 214 with the event instance as a parameter. 215 216 - key_*: When a key is pressed another method will be called based on the 217 L{KeyEvent.key} attribute. For example the 'ENTER' key will call key_ENTER 218 with the associated L{KeyDown} event as its parameter. 219 220 - L{update}: This method is called every loop. It is passed a single 221 parameter detailing the time in seconds since the last update 222 (often known as deltaTime.) 223 224 You may want to call drawing routines in this method followed by 225 L{tdl.flush}. 226 """ 227 __slots__ = ('__running', '__prevTime') 228
229 - def ev_QUIT(self, event):
230 """Unless overridden this method raises a SystemExit exception closing 231 the program.""" 232 raise SystemExit()
233
234 - def ev_KEYDOWN(self, event):
235 """Override this method to handle a L{KeyDown} event."""
236
237 - def ev_KEYUP(self, event):
238 """Override this method to handle a L{KeyUp} event."""
239
240 - def ev_MOUSEDOWN(self, event):
241 """Override this method to handle a L{MouseDown} event."""
242
243 - def ev_MOUSEUP(self, event):
244 """Override this method to handle a L{MouseUp} event."""
245
246 - def ev_MOUSEMOTION(self, event):
247 """Override this method to handle a L{MouseMotion} event."""
248
249 - def update(self, deltaTime):
250 """Override this method to handle per frame logic and drawing. 251 252 @type deltaTime: float 253 @param deltaTime: This parameter tells the amount of time passed since 254 the last call measured in seconds as a floating point 255 number. 256 257 You can use this variable to make your program 258 frame rate independent. 259 Use this parameter to adjust the speed of motion, 260 timers, and other game logic. 261 """ 262 pass
263
264 - def suspend(self):
265 """When called the App will begin to return control to where 266 L{App.run} was called. 267 268 Some further events are processed and the L{App.update} method will be 269 called one last time before exiting 270 (unless suspended during a call to L{App.update}.) 271 """ 272 self.__running = False
273
274 - def run(self):
275 """Delegate control over to this App instance. This function will 276 process all events and send them to the special methods ev_* and key_*. 277 278 A call to L{App.suspend} will return the control flow back to where 279 this function is called. And then the App can be run again. 280 But a single App instance can not be run multiple times simultaneously. 281 """ 282 if getattr(self, '_App__running', False): 283 raise _tdl.TDLError('An App can not be run multiple times simultaneously') 284 self.__running = True 285 while self.__running: 286 self.runOnce()
287
288 - def run_once(self):
289 """Pump events to this App instance and then return. 290 291 This works in the way described in L{App.run} except it immediately 292 returns after the first L{update} call. 293 294 Having multiple L{App} instances and selectively calling runOnce on 295 them is a decent way to create a state machine. 296 """ 297 if not hasattr(self, '_App__prevTime'): 298 self.__prevTime = _time.clock() # initiate __prevTime 299 for event in get(): 300 if event.type: # exclude custom events with a blank type variable 301 # call the ev_* methods 302 method = 'ev_%s' % event.type # ev_TYPE 303 getattr(self, method)(event) 304 if event.type == 'KEYDOWN': 305 # call the key_* methods 306 method = 'key_%s' % event.key # key_KEYNAME 307 if hasattr(self, method): # silently exclude undefined methods 308 getattr(self, method)(event) 309 newTime = _time.clock() 310 self.update(newTime - self.__prevTime) 311 self.__prevTime = newTime
312 #_tdl.flush() 313
314 -def _processEvents():
315 """Flushes the event queue from libtcod into the global list _eventQueue""" 316 global _mousel, _mousem, _mouser, _eventsflushed, _pushedEvents 317 _eventsflushed = True 318 events = _pushedEvents # get events from event.push 319 _pushedEvents = [] # then clear the pushed events queue 320 321 mouse = _Mouse() 322 libkey = _Key() 323 while 1: 324 libevent = _lib.TCOD_sys_check_for_event(_tcod.TCOD_EVENT_ANY, libkey, mouse) 325 if not libevent: # no more events from libtcod 326 break 327 328 #if mouse.dx or mouse.dy: 329 if libevent & _tcod.TCOD_EVENT_MOUSE_MOVE: 330 events.append(MouseMotion(*mouse.motion)) 331 332 mousepos = ((mouse.x, mouse.y), (mouse.cx, mouse.cy)) 333 334 for oldstate, newstate, released, button in zip((_mousel, _mousem, _mouser), 335 mouse.button, mouse.button_pressed, (1, 2, 3)): 336 if released: 337 if not oldstate: 338 events.append(MouseDown(button, *mousepos)) 339 events.append(MouseUp(button, *mousepos)) 340 if newstate: 341 events.append(MouseDown(button, *mousepos)) 342 elif newstate and not oldstate: 343 events.append(MouseDown(button, *mousepos)) 344 345 if mouse.wheel_up: 346 events.append(MouseDown(4, *mousepos)) 347 if mouse.wheel_down: 348 events.append(MouseDown(5, *mousepos)) 349 350 _mousel = mouse.lbutton 351 _mousem = mouse.mbutton 352 _mouser = mouse.rbutton 353 354 if libkey.vk == _tcod.K_NONE: 355 break 356 if libkey.pressed: 357 keyevent = KeyDown 358 else: 359 keyevent = KeyUp 360 events.append(keyevent(*tuple(libkey))) 361 362 if _lib.TCOD_console_is_window_closed(): 363 events.append(Quit()) 364 365 _eventQueue.extend(events)
366
367 -def get():
368 """Flushes the event queue and returns the list of events. 369 370 This function returns L{Event} objects that can be indentified by their 371 type attribute or their class. 372 373 @rtype: iterator 374 @return: Returns an iterable of objects derived from L{Event} or anything 375 put in a L{push} call. If the iterator is deleted or otherwise 376 interrupted before finishing the excess items are preserved for the 377 next call. 378 """ 379 def eventGenerator(): 380 while _eventQueue: 381 # if there is an interruption the rest of the events stay untouched 382 # this means you can break out of a event.get loop without losing 383 # the leftover events 384 yield(_eventQueue.pop(0)) 385 raise StopIteration()
386 _processEvents() 387 return eventGenerator() 388
389 -def push(event):
390 """Push an event into the event buffer. 391 392 @type event: L{Event}-like object 393 @param event: The event will be available on the next call to L{event.get}. 394 An event pushed in the middle of a L{get} will not show until 395 the next time L{get} called preventing push related 396 infinite loops. 397 """ 398 _pushedEvents.append(event)
399
400 -def key_wait():
401 """Waits until the user presses a key. 402 Then returns a L{KeyDown} event. 403 404 Key events will repeat if held down. 405 406 A click to close the window will be converted into an Alt+F4 KeyDown event. 407 408 @rtype: L{KeyDown} 409 """ 410 while 1: 411 for event in get(): 412 if event.type == 'KEYDOWN': 413 return event 414 if event.type == 'QUIT': 415 # convert QUIT into alt+F4 416 return KeyDown('F4', '', True, False, True, False, False) 417 _time.sleep(.001)
418
419 -def set_key_repeat(delay=500, interval=0):
420 """Change or disable key repeat. 421 422 @type delay: int 423 @param delay: Milliseconds before a held key begins to repeat. 424 425 Key repeat can be disabled entirely by setting a delay of zero. 426 427 @type interval: int 428 @param interval: Milliseconds between key repeats. 429 430 An interval of zero will repeat every frame. 431 """ 432 _lib.TCOD_console_set_keyboard_repeat(delay, interval)
433
434 -def is_window_closed():
435 """Returns True if the exit button on the window has been clicked and 436 stays True afterwards. 437 438 @rtype: boolean 439 """ 440 return _lib.TCOD_console_is_window_closed()
441 442 __all__ = [_var for _var in locals().keys() if _var[0] != '_'] 443 444 App.runOnce = _style.backport(App.run_once) 445 keyWait = _style.backport(key_wait) 446 setKeyRepeat = _style.backport(set_key_repeat) 447 isWindowClosed = _style.backport(is_window_closed) 448