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

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