Events

Events are generated by the windowing back-end whenever user interaction occurs. The nature of the events will vary a little according to which windowing system is in use, but they generally follow the following pattern. An Event instance is created for each event and is passed down a cascade of handlers attached to the World. Handlers can be attached either using the SetEventHandler() method:

def handler( self, event ):
    print( event )

w = Shady.World().SetEventHandler( handler, slot=-1 )

or using the decorator version of the same operation, simply called EventHandler():

w = Shady.World()

@w.EventHandler( slot=-1 )
def handler( self, event ):
    print( event )

In either case, note the slot argument: a given slot number can be associated with only one handler, and handlers are run in increasing numeric order of their slot. The default handler—the Shady.World.HandleEvent method that closes the World when the user presses either q or the escape key—occupies the default slot=0 and can be replaced by specifying a different function in that slot.

If a handler returns a truthy return value, the cascade is halted (“I handled that event so you don’t have to”).

The best way to understand how to match events is to install the print(event) handler as in the examples above, and observe what is printed to the console. You can also explore the demo script examples/events.py. You will see that every Event instance has a type string, a timestamp t in seconds, and an abbrev string. It may also have other attributes that depend on the type. In Shady’s default windowing back-end (the GLFW-based binary accelerator library), possible type values include:

==================  =========================  ===========
.type               other relevant attributes  .abbrev
==================  =========================  ===========
'window_focus'                                 wf
'window_unfocus'                               wu
'mouse_enter'                                  me
'mouse_leave'                                  ml
'mouse_motion'      .x .y .dx .dy .modifiers   mm
'mouse_press'       .x .y .button .modifiers   mp[button]
'mouse_release'     .x .y .button .modifiers   mr[button]
'key_press'               .key    .modifiers   kp[key]
'key_auto'                .key    .modifiers   ka[key]
'key_release'             .key    .modifiers   kr[key]
'text'                    .text                t[text]
==================  =========================  ===========

The modifiers attribute is a space-delimited string containing zero or more of the words 'shift ctrl super alt num' (in arbitrary order) showing which modifier keys, if any, were held down when the event occurred.

If a key-press (or key auto-repeat) would normally result in a character being typed, then an Event with type='text' is generated separately from, and immediately after, the corresponding Event with type='key_press' or type='key_auto'. The text attribute of the second event will contain the character or characters typed (including case inflection), whereas the prior event will have a key attribute denoting (always in lower case) the label on the cap of the key that produced it. If modifier keys were used, there will also be separate key_press and key_release events corresponding to each modifier key—these events will not produce text events of their own. Note that (in contrast to the modifier substrings, which may simply denote that a logical shift or ctrl modifier applies) key-related events for the modifier keys themselves may tell you which physical modifier key was pressed, for example as key='lshift' or `key='rshift'.

The abbrev attribute is not used directly but it provides a hint for how you can match the event more concisely. Your event-matching code does not need to use this—it can contain code like:

if event.type == 'key_press' and event.key == ' ' and ....

but this can get very long-winded. You can often perform the same task more quickly using abbrev codes in conjunction with the >> operator:

def handler(world, e):
    if e >> 'kp[ ]':      print('you pressed the space bar!')
    if e >> 'kp[return]': print('you pressed the return key!')

An event-matching string can have modifiers, in any order you like and this time delimited by +, prepended to it. So, while 'kp[ ]' on its own will match any pressing of the space-bar regardless of modifiers, you can be more specific as to the modifier combination you are looking for (including, if desired, the specifier 'none'):

e >> 'ctrl+kp[c]'       # matches only ctrl-c
e >> 'ctrl+shift+kp[c]' # matches only ctrl-shift-c
e >> 'none+kp[c]'       # matches only a `c` key-press WITHOUT mods
e >> 'kp[c]'            # matches any of the above (and other mods)

You can match multiple possibilities at once by delimiting them with whitespace in the matching string:

e >> 'ctrl+kp[c]  shift+kp[c]'   # matches ctrl-c OR shift-c (only)

(This means you should not use whitespace inside each matching code, with the exception of a space-inside-square-brackets for matching the space bar, as in 'kp[ ]', or space character, as in 't[ ]'.)

See the demo script examples/events.py for more.