1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315
|
Global Event Keys
=================
Global event keys are single keys (or combinations of a single key and one or
more "modifier" keys such as Ctrl, Alt, etc.) with an associated Python
callback function. This function will be executed if the key (or
key/modifiers combination) was pressed.
.. note::
Global event keys only work with the `pyglet` backend, which is the default.
|PsychoPy| fully automatically monitors and processes key presses during most
portions of the experimental run, for example during
`core.wait()` periods, or when calling `win.flip()`. If a global
event key press is detected, the specified function will be run
immediately. You are not required to manually poll and check for key
presses. This can be particularly useful to implement a global
"shutdown" key, or to trigger laboratory equipment on a key press
when testing your experimental script -- without cluttering the code.
But of course the application is not limited to these two scenarios.
In fact, you can associate any Python function with a global event key.
All active global event keys are stored in `event.globalKeys`.
Adding a global event key (simple)
----------------------------------
First, let's ensure no global event keys are currently set by calling
func:`event.globalKeys.clear`.
::
>>> from psychopy import event
>>> event.globalKeys.clear()
To add a new global event key, you need to invoke
func:`event.globalKeys.add`. This function has two required arguments: the
key name, and the function to associate with that key.
::
>>> key = 'a'
>>> def myfunc():
... pass
...
>>> event.globalKeys.add(key=key, func=myfunc)
Look at `event.globalKeys`, we can see that the global event key has indeed
been created.
::
>>> event.globalKeys
<_GlobalEventKeys :
[A] -> 'myfunc' <function myfunc at 0x10669ba28>
>
Your output should look similar. You may happen to spot
We can take a closer look at the specific global key event we added.
::
>>> event.globalKeys['a']
_GlobalEvent(func=<function myfunc at 0x10669ba28>, func_args=(), func_kwargs={}, name='myfunc')
This output tells us that
- our key `a` is associated with our function `myfunc`
- `myfunc` will be called without passing any positional or keyword
arguments (`func_args` and `func_kwargs`, respectively)
- the event name was automatically set to the name of the function.
.. note::
Pressing the key won't do anything unless a :class:`psychopy.visual.Window`
is created and and its :func:~`psychopy.visual.Window.flip` method or
:func:`psychopy.core.wait` are called.
Adding a global event key (advanced)
------------------------------------
We are going to associate a function with a more complex calling signature
(with positional and keyword arguments) with a global event key. First, let's
create the dummy function:
::
>>> def myfunc2(*args, **kwargs):
... pass
...
Next, compile some positional and keyword arguments and a custom name for this
event. Positional arguments must be passed as tists or uples, and keyword
arguments as dictionaries.
::
>>> args = (1, 2)
>>> kwargs = dict(foo=3, bar=4)
>>> name = 'my name'
.. note::
Even when intending to pass only a single positional argument, `args` must be
a list or tuple, e.g., `args = [1]` or `args = (1,)`.
Finally, specify the key and a combination of modifiers. While key names are
just strings, modifiers are lists or tuples of modifier names.
::
>>> key = 'b'
>>> modifiers = ['ctrl', 'alt']
.. note::
Even when specifying only a single modifier key, `modifiers` must be a list
or tuple, e.g., `modifiers = ['ctrl']` or `modifiers = ('ctrl',)`.
We are now ready to create the global event key.
::
>>> event.globalKeys.add(key=key, modifiers=modifiers,
... func=myfunc2, func_args=args, func_kwargs=kwargs,
... name=name)
Check that the global event key was successfully added.
::
>>> event.globalKeys
<_GlobalEventKeys :
[A] -> 'myfunc' <function myfunc at 0x10669ba28>
[CTRL] + [ALT] + [B] -> 'my name' <function myfunc2 at 0x112eecb90>
>
The key combination `[CTRL] + [ALT] + [B]` is now associated with the function
`myfunc2`, which will be called in the following way:
::
myfunc2(1, 2, foo=2, bar=4)
.. _indexing:
Indexing
--------
`event.globalKeys` can be accessed like an ordinary dictionary. The index keys
are `(key, modifiers)` namedtuples.
::
>>> event.globalKeys.keys()
[_IndexKey(key='a', modifiers=()), _IndexKey(key='b', modifiers=('ctrl', 'alt'))]
To access the global event associated with the key combination
`[CTRL] + [ALT] + [B]`, we can do
>>> event.globalKeys['b', ['ctrl', 'alt']]
_GlobalEvent(func=<function myfunc2 at 0x112eecb90>, func_args=(1, 2), func_kwargs={'foo': 3, 'bar': 4}, name='my name')
To make access more convenient, specifying the modifiers is optional in case
none were passed to :func:`psychopy.event.globalKeys.add` when the global
event key was added, meaning the following commands are identical.
::
>>> event.globalKeys['a', ()]
_GlobalEvent(func=<function myfunc at 0x10669ba28>, func_args=(), func_kwargs={}, name='myfunc')
>>> event.globalKeys['a']
_GlobalEvent(func=<function myfunc at 0x10669ba28>, func_args=(), func_kwargs={}, name='myfunc')
All elements of a global event can be accessed directly.
::
>>> index = ('b', ['ctrl', 'alt'])
>>> event.globalKeys[index].func
<function myfunc2 at 0x112eecb90>
>>> event.globalKeys[index].func_args
(1, 2)
>>> event.globalKeys[index].func_kwargs
{'foo': 3, 'bar': 4}
>>> event.globalKeys[index].name
'my name'
Number of active event keys
---------------------------
The number of currently active event keys can be retrieved by passing
`event.globalKeys` to the `len()` function.
::
>>> len(event.globalKeys)
2
Removing global event keys
--------------------------
There are three ways to remove global event keys:
- using :func:`psychopy.event.globalKeys.remove`,
- using `del`, and
- using :func:`psychopy.event.globalKeys.pop`.
:func:`psychopy.event.globalKeys.remove`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
To remove a single key, pass the key name and modifiers (if any) to
:func:`psychopy.event.globalKeys.remove`.
::
>>> event.globalKeys.remove(key='a')
A convenience method to quickly delete *all* global event keys is to pass
`key='all'`
::
>>> event.globalKeys.remove(key='all')
`del`
~~~~~
Like with other dictionaries, items can be removed from `event.globalKeys`
by using the `del` statement. The provided index key must be specified as
described in :ref:`indexing`.
::
>>> index = ('b', ['ctrl', 'alt'])
>>> del event.globalKeys[index]
:func:`psychopy.event.globalKeys.pop`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Again, as other dictionaries, `event.globalKeys` provides a `pop` method to
retrieve an item and remove it from the dict. The first argument to `pop` is the
index key, specified as described in :ref:`indexing`. The second argument is
optional. Its value will be returned in case no item with the matching indexing
key could be found, for example if the item had already been removed previously.
::
>>> r = event.globalKeys.pop('a', None)
>>> print(r)
_GlobalEvent(func=<function myfunc at 0x10669ba28>, func_args=(), func_kwargs={}, name='myfunc')
>>> r = event.globalKeys.pop('a', None)
>>> print(r)
None
Global shutdown key
-------------------
The |PsychoPy| preferences for `shutdownKey` and `shutdownKeyModifiers`
(both unset by default) will be used to automatically create a global
shutdown key. To demonstrate this automated behavior, let us first change
the preferences programmatically (these changes will be lost when quitting the
current Python session).
::
>>> from psychopy.preferences import prefs
>>> prefs.general['shutdownKey'] = 'q'
We can now check if a global shutdown key has been automatically created.
::
>>> from psychopy import event
>>> event.globalKeys
<_GlobalEventKeys :
[Q] -> 'shutdown (auto-created from prefs)' <function quit at 0x10c171938>
>
And indeed, it worked!
What happened behind the scenes? When importing the `psychopy.event`
module, the initialization of `event.globalKeys` checked for valid shutdown key
preferences and automatically initialized a shutdown key accordingly.
This key is associated with the :func:~`psychopy.core.quit` function, which will
shut down |PsychoPy|.
::
>>> from psychopy.core import quit
>>> event.globalKeys['q'].func == quit
True
Of course you can very easily add a global shutdown key manually, too. You
simply have to associate a key with :func:~`psychopy.core.quit`.
::
>>> from psychopy import core, event
>>> event.globalKeys.add(key='q', func=core.quit, name='shutdown')
That's it!
A working example
-----------------
In the above code snippets, our global event keys were not actually functional,
as we didn't create a window, which is required to actually collect the key
presses. Our working example will thus first create a window and then add
global event keys to change the window color and quit the experiment,
respectively.
::
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import print_function
from psychopy import core, event, visual
def change_color(win, log=False):
win.color = 'blue' if win.color == 'gray' else 'gray'
if log:
print('Changed color to %s' % win.color)
win = visual.Window(color='gray')
text = visual.TextStim(win,
text='Press C to change color,\n CTRL + Q to quit.')
# Global event key to change window background color.
event.globalKeys.add(key='c',
func=change_color,
func_args=[win],
func_kwargs=dict(log=True),
name='change window color')
# Global event key (with modifier) to quit the experiment ("shutdown key").
event.globalKeys.add(key='q', modifiers=['ctrl'], func=core.quit)
while True:
text.draw()
win.flip()
|