Running an experiment using the ioHub Event Framework utilizes two Python processes. The first is for running the traditional PsychoPy coder experiment logic called the PsychoPy Process. The second is a separate process for device monitoring, event bundling, and data storage called the ioHub Process.
The PsychoPy Process is established by the Python interpreter executing the experiment script. Therefore, the first thing that needs to be done when using the ioHub Event Framework is to have the PsychoPy Process establish and connect to a new ioHub Server process.
The functionality for establishing and connecting to a new ioHub Process is provided in the psychopy.iohub.ioHubConnection class. The experiment script should indirectly create one instance of the ioHubConnection class using one of the two methods discussed in the next section. That is, an instance of ioHubConnection should never be created directly by the experiment script.
There are two ways to create an instance of the ioHubConnection class to use with a PsychoPy experiment: #. Calling the psychopy.iohub.client.quickStartHubServer function. #. Extending the psychopy.iohub.client.ioHubExperimentRuntime class.
Each approach to creating the ioHubConnection instance has strengths and weaknesses, and the most appropriate approach for a given experiment depends primarily on the ioHub Device types used in the experiment.
ioHubConnection is responsible for creating, sending requests to, and reading replies from the ioHub Process. This class can also shut down and disconnect the ioHub Process.
The ioHubConnection class is also used as the interface to any ioHub Device instances that have been created so that events from the device can be monitored. These device objects can be accessed via the ioHubConnection .devices attribute, providing ‘dot name’ attribute access, or by using the .deviceByLabel dictionary attribute; which stores the device names as keys, and the PsychoPy Process represention of the ioHub Device as the values.
For example, to print the available methods for each device registered with the ioHub Process, assuming hub refers to the ioHubConnection instance:
for device_name,device_access in hub.deviceByLabel.iteritems(): print 'Device Name: ',device_name print 'Device Interface:' for method in device_access.getDeviceInterface(): print ' ',method print "--------------" # Will return the following assuming only default devices were created: Device Name: keyboard Device Interface: clearEvents enableEventReporting getConfiguration getEvents isReportingEvents -------------- Device Name: mouse Device Interface: clearEvents enableEventReporting getConfiguration getCurrentButtonStates getDisplayIndex getDisplayIndexForMousePosition getEvents getPosition getPositionAndDelta getScroll getSystemCursorVisibility getlockedMouseDisplayID isReportingEvents lockMouseToDisplayID setPosition setScroll setSystemCursorVisibility -------------- Device Name: display Device Interface: clearEvents enableEventReporting getDisplayCount getConfiguration getCoordBounds getCoordinateType getDefaultEyeDistance getDeviceNumber getDisplayIndexForNativePixelPosition getEvents getIndex getOrigin getPhysicalDimensions getPixelResolution getPixelsPerDegree getPsychopyMonitorName getRetraceInterval getRuntimeInfo isReportingEvents -------------- Device Name: experiment Device Interface: clearEvents critical data debug enableEventReporting error exp fatal getConfiguration getEvents info isReportingEvents log warn warning --------------
Using the .devices attribute is handy if you know the name of the device to be accessed and you are sure it is actually enabled on the ioHub Process. The following is an example of accessing a device using the .devices attribute:
# get the Mouse device, named mouse mouse=hub.devices.mouse current_mouse_position = mouse.getPosition() print 'current mouse position: ', current_mouse_position # Returns something like: # >> current mouse position: [-211.0, 371.0]
Returns the ioHubDeviceView that has a matching name (based on the device : name property specified in the ioHub_config.yaml for the experiment). If no device with the given name is found, None is returned. Example, accessing a Keyboard device that was named ‘kb’
keyboard = self.getDevice('kb') kb_events= keyboard.getEvent()
This is the same as using the ‘natural naming’ approach supported by the .devices attribute, i.e:
keyboard = self.devices.kb kb_events= keyboard.getEvent()
However the advantage of using getDevice(device_name) is that an exception is not created if you provide an invalid device name, or if the device is not enabled on the ioHub server (for example if the device hardware was not connected when the ioHub server started). Instead None is returned by this method. This allows for conditional checking for the existance of a requested device within the experiment script, which can be useful in some cases.
Returns a dict representing the experiment session data that is being used for the current ioHub Experiment Session. Changing values in the dict has no effect on the session data that has already been saved to the ioHub DataStore.
Returns a dict representing the experiment data that is being used for the current ioHub Experiment.
Retrieve any events that have been collected by the ioHub Process from monitored devices since the last call to getEvents() or clearEvents().
By default all events for all monitored devices are returned, with each event being represented as a namedtuple of all event attributes.
When events are retrieved from an event buffer, they are removed from that buffer as well.
If events are only needed from one device instead of all devices, providing a valid device name as the device_label argument will result in only events from that device being returned.
Events can be received in one of several object types by providing the optional as_type property to the method. Valid values for as_type are the following str values:
- ‘list’: Each event is sent from the ioHub Process as a list of ordered attributes. This is the most efficient for data transmission, but not for human readability or usability. However, if you do want events to be kept in list form, set as_type = ‘list’.
- ‘astuple’: Each event is converted to a namedtuple object. Event attributes are accessed using natural naming style (dot name style), or by the index of the event attribute for the event type. The namedtuple class definition is created once for each Event type at the start of the experiment, so memory overhead is almost the same as the event value list, and conversion from the event list to the namedtuple is very fast. This is the default, and normally most useful, event representation type.
- ‘dict’: Each event converted to a dict object, keys equaling the event attribute names, values being, well the attribute values for the event.
- ‘object’: Each event is converted into an instance of the ioHub DeviceEvent subclass based on the event’s type. This conversion process can take a bit of time if the number of events returned is large, and currently there is no real benefit converting events into DeviceEvent Class instances vs. the default namedtuple object type. Therefore this option should be used rarely.
device_label (str): Indicates what device to retrieve events for. If None ( the default ) returns device events from all devices.
as_type (str): Indicates how events should be represented when they are returned to the user. Default: ‘namedtuple’.
Clears events from the ioHub Process’s Global Event Buffer (by default) so that uneeded events are not sent to the PsychoPy Process the next time iohub.getEvents() is called.
If device_label is None ( the default ), then all events in the ioHub Global Event Buffer are cleared, which leaves the Device Event Buffers unaffected.
If device_label is a str giving a valid device name, then events that were received from that device are returned and the Device Event Buffer for that device is cleared on the ioHub Server, but the Global Event Buffer is not affected.
To clear all events from both the ioHub Global Event Buffer and all Device Event Buffer’s, set the device_label argument to ‘all’.
Pause the experiment script execution for a duration equal to the delay (in sec.msec format). time.sleep() is used to make the wait operation give time up to the operating system.
During the wait period, events are received from the ioHub Process by calling getEvents() every ‘check_hub_interval’ sec.msec. Any events that are gathered during the delay period will be handed to the experiment script the next time getEvents() is called, unless clearEvents() is called prior to getEvents().
This is done for two reasons:
- The ioHub Process and ioHub Device buffers do not reach their specified limits and start descarding old events when new events arrive.
- So that a very large build up of events does not occur on the ioHub Process, resulting in a very large number of events being returned if getEvents() is called after a long wait period. If a large number of events needs to be returned by the ioHub, that will result in multiple UDP packets being sent to the PsychoPy Process to return all the events. This would slow event retrieval down for that request unnecessarily.
Calling clearEvents(‘all’) after any long delays between calls to getEvents() or clearEvents() will clear events from the ioHub Process as well. If you know that during a period of time the experiment does not need online event information, call clearEvents() at the end of that period so events that did occur are not uncessarily sent to the PsychoPy Process.
delay (float/double): The sec.msec period that the PsychoPy Process should wait before returning from the function call.
check_hub_interval (float/double): The sec.msec interval after which a call to getEvents() will be made by the wait() function. Any returned events are stored in a local buffer. This is repeated every check_hub_interval sec.msec until the delay time has passed. Default is every 0.02 sec ( 20.0 msec ).
Create and send an Experiment MessageEvent to the ioHub Server Process for storage with the rest of the event data being recorded in the ioDataStore.
MessageEvents can be thought of as DeviceEvents from the virtual PsychoPy Process “Device”.
text (str): The text message for the message event. Can be up to 128 characters in length.
prefix (str): A 0 - 3 character prefix for the message that can be used to sort or group messages by ‘types’ during post hoc analysis.
offset (float): The sec.msec offset to apply to the time stamp of the message event. If you send the event before or after the time the event actually occurred, and you know what the offset value is, you can provide it here and it will be applied to the ioHub time stamp for the MessageEvent.
sec_time (float): The time stamp to use for the message in sec.msec format. If not provided, or None, then the MessageEvent is time stamped when this method is called using the global timer.
Create a condition variable table in the ioDataStore (in the class_table_mapping group) when utilizing the iohub.util.ExperimentVariableProvider class in the experiment handling script. Each Dependent and Independent variable defined by the condition_variable_provider instance results in a column created in the ioDataStore for the experiment.
Column names match the condition variable names defined for the ExperimentVariableProvider.
Add a row to the condition variable table for the current experiment (created by calling the initializeConditionVariableTable() method) when using iohub.util.ExperimentVariableProvider class in the experiment handling script. Each row in a condition variable table contains the state of all the Dependent and Independent Variables that were defined for the ExperimentVariableProvider.
Sets the priority of the ioHub Process to high priority and optionally (default is False) disables the python GC if the disable_gc parameter is set to True.
This is method is useful for the duration of a trial, or relatively short periods of time where time critial processing is a priority. The normal usage pattern is to call enableHighPriority() at the start of a trial and disableHighPriority() is called at the end of a trial.
Improvements in timing and execution speed depend on computer load, hardware configuration, as well as the OS being used.
This method is not supported on OS X at this time.
Sets the priority of the ioHub Process to normal priority and enables the python GC if it had been disabled by a earlier call to enableHighPriority().
In general enableHighPriority() would be called at start of a trial where time critial processing is important, disableHighPriority() would be called at the end of thlaunchHubProcesse trial or time critical period.
This method is not supported on OS X at this time.
Returns the current ioHub Process Affinity setting, as a list of ‘processor’ id’s (from 0 to getSystemProcessorCount()-1). A Process’s Affinity determines which CPU’s or CPU cores a process can run on. By default the ioHub Process can run on any CPU or CPU core.
This method is not supported on OS X at this time.
Sets the ioHub Process Affinity based on the value of processor_list. A Process’s Affinity determines which CPU’s or CPU cores a process can run on. By default the ioHub Process can run on any CPU or CPU core.
The processor_list argument must be a list of ‘processor’ id’s; integers in the range of 0 to Computer.processingUnitCount-1, representing the processing unit indexes that the ioHub Server should be allowed to run on. If processor_list is given as an empty list, the ioHub Process will be able to run on any processing unit on the computer.
This method is not supported on OS X at this time.
Adds a device to the ioHub Process for event monitoring after the ioHub Process has been started. Normally all devices should be specified to the function or class that is having the ioHubConnection class instance created, and therefore the ioHub Process started. This is due to the fact that ‘adding’ a device to be monitored can take several, to tens, or even a couple hundred msec to perform on the ioHub server (depending on the device type). When this is occurring, events from existing devices can not be monitored.
Therefore it is best to define all devices the experiment will use during runtime at the time the ioHub Process is being created. If a device does need to be added during the experiment runtime, using this method will do so.
device_class (str): The class name of the device type to be created.
device_config (dict): The device configuartion settings that you want to set and override the default values that are used for any settings not provided.
Manually tell the ioDataStore to flush any events it has buffered in memory to disk.”
Tells the ioHub Process to close all ioHub Devices, the ioDataStore, and the connection monitor between the PsychoPy and ioHub Processes. Then exit the Server Process itself.
Same as the shutdown() method, but has same name as PsychoPy core.quit() so maybe easier to remember.