Getting Connected

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.

The ioHubConnection Class

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.

class psychopy.iohub.client.ioHubConnection(ioHubConfig=None, ioHubConfigAbsPath=None)[source]

Bases: object

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]
getDevice(deviceName)[source]

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.

Args:
deviceName (str): Name given to the ioHub Device to be returned
Returns:
device (ioHubDeviceView) : the PsychoPy Process represention for the device that matches the name provided.
getSessionMetaData()[source]

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.

Args:
None
Returns:
dict: Experiment Session metadata saved to the ioHub DataStore. None if the ioHub DataStore is not enabled.
getExperimentMetaData()[source]

Returns a dict representing the experiment data that is being used for the current ioHub Experiment.

Args:
None
Returns:
dict: Experiment metadata saved to the ioHub DataStore. None if the ioHub DataStore is not enabled.
getEvents(device_label=None, as_type='namedtuple')[source]

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.
Args:

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’.
Returns:
tuple: A tuple of event objects, where the event object type is defined by the ‘as_type’ parameter.
clearEvents(device_label=None)[source]

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.

Note

To clear all events from both the ioHub Global Event Buffer and all Device Event Buffer’s, set the device_label argument to ‘all’.

Args:
device_label (str): name of the device to clear events from, or None (the default) to clear all events from the “Global Event Buffer”, or ‘all’, to clear events from both the Global Event Buffer and all device level Device Event Buffer’s.
Returns:
int: The number of events cleared by the request on the ioHub Server.
wait(delay, check_hub_interval=0.02)[source]

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.

Args:

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 ).
Returns:
float/double: The actual duration of the delay in sec.msec format.
sendMessageEvent(text, prefix='', offset=0.0, sec_time=None)[source]

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.

Note

MessageEvents can be thought of as DeviceEvents from the virtual PsychoPy Process “Device”.

Args:

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.

Returns:
bool: True
initializeConditionVariableTable(condition_variable_provider)[source]

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.

Args:
condition_variable_provider: The ExperimentVariableProvider class instance that you have created for use during the experiment.
Returns:
None
addRowToConditionVariableTable(data)[source]

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.

Args:
data: A Condition Variable Set object, as received from the ExperimentVariableProvider.getNextConditionSet() method. ANy changes to the values of the condition variables within the object are reflected in the data saved to the ioDataStore.
Returns:
None
enableHighPriority(disable_gc=False)[source]

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.

Args:
disable_gc(bool): True = Turn off the Python Garbage Collector. False (Default) = Leave the Garbage Collector running.
disableHighPriority()[source]

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.

Args:
None
Returns:
None
getProcessAffinity()[source]

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.

Args:
None
Returns:
list: A list of integer values between 0 and Computer.getSystemProcessorCount()-1, where values in the list indicate processing unit indexes that the ioHub process is able to run on.
setProcessAffinity(processor_list)[source]

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.

Args:
processor_list (list): A list of integer values between 0 and Computer.processingUnitCount-1, where values in the list indicate processing unit indexes that the ioHub process is able to run on.
Returns:
None
addDeviceToMonitor(device_class, device_config={})[source]

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.

Args:

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.

Returns:
DeviceView Instance: The PsychoPy Process’s view of the ioHub Device created that was created, as would be returned if a device was accessed using the .devices attribute or the .getDeviceByLabel() method.
flushDataStoreFile()[source]

Manually tell the ioDataStore to flush any events it has buffered in memory to disk.”

Args:
None
Returns:
None
shutdown()[source]

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.

Args:
None
Returns:
None
quit()[source]

Same as the shutdown() method, but has same name as PsychoPy core.quit() so maybe easier to remember.