"""A wrapper for SaccadePursuit to track eye movements in addition Author - Michael Tan (mtan30@uic.edu) Adapted from: https://github.com/colinquirk/ChangeDetectionEyeTracking Note: this code relies on the Colin Quirk templateexperiments module. You can get it from https://github.com/colinquirk/templateexperiments and either put it in the same folder as this code or give the path to psychopy in the preferences. """ from __future__ import division from __future__ import print_function import os import random import sys import traceback import subprocess import math import numpy # Necesssary to access psychopy paths import psychopy # noqa:F401 import eyelinker import SaccadePursuit # Experimental Parameters monitor_name = 'testMonitor' monitor_width = 41 # 25.8 height distance_to_monitor = 74 monitor_px = [1440, 900] window_screen = 1 disableTracker = False # For Debugging isi_time = 2 # Interstimulus Interval data_directory = os.path.join( os.path.expanduser('~'), 'Desktop', 'ExperimentalData', 'SaccadePursuitEyeTracking') # image_directory = os.path.join(os.getcwd(),'Images') image_directory = os.path.join( os.path.expanduser('~'), 'Desktop', 'SaccadePursuitExperiment', 'Images') new_trial_sound = 'A' instHeight = 6 # Saccade / Antisaccade Parameters number_of_saccade_trials = 1 number_of_saccade_blocks = 1 saccade_distance = 15 # Degrees per direction number_of_saccade_lights = 7 # Number of active lights per direction saccade_time = 3 # Maximum Time stimulus_size = 0.3 stim_color = [1, -1, -1] saccade_fixation_color = [255, 255, 255] saccade_dot_color = [50, 50, 50] antisaccade_instruct_file = os.path.join( image_directory, 'AntiSaccadeInstruct2.tif') antisaccade_file_scale = 1 # Pursuit Parameters number_of_pursuit_trials = 1 number_of_pursuit_blocks = 1 pursuit_distance = 15 pursuit_frequencies = [0.1, 0.2, 0.3] pursuit_time = [40, 20, 13.33] # Necker Cube Parameters number_of_necker_trials = 1 number_of_necker_blocks = 3 necker_time = 60 necker_color = [190, 190, 190] necker_bg_color = [30, 30, 30] necker_scale = 0.5 necker_file = os.path.join(image_directory, 'Necker1.tif') necker_response_box_file = os.path.join(image_directory, 'ResponseBox5.tif') necker_response_box_scale = 0.22 # Fixation Parameters number_of_fixation_trials = 1 number_of_fixation_blocks = 1 fixation_size = stimulus_size*2 fixation_trial_time = 15 # Binocular Rivalry Parameters number_of_rivalry_trials = 1 number_of_rivalry_blocks = 1 rivalry_time = 90 #rivalry_scale = 2.5 rivalry_height = 1.5 rivalry_width = 1 rivalry_distance = 3 rivalry_file1 = os.path.join(image_directory, 'house4n_11-160.tif') rivalry_file2 = os.path.join(image_directory, 'face2nS_11-160.tif') rivalry_border_color = [190, 190, 190] rivalry_border_width = 5 response_box_file = os.path.join(image_directory, 'ResponseBox3.tif') response_box_scale = 0.22 data_fields = [ 'Subject', 'Condition', 'Block', 'Trial', 'Timestamp', 'TrialType', 'Duration', 'Frequency', 'Locations', ] # Add additional questions here questionaire_dict = { 'Run Smooth Pursuit': True, 'Run Saccade': True, 'Run Anti-Saccade': True, # 'Run Necker Cube': False, 'Run Binocular Rivalry': True, } questionaire_order = [ 'Subject ID', 'Session', 'Timepoint', 'Experimenter Initials', 'Gender', 'Run Smooth Pursuit', 'Run Saccade', 'Run Anti-Saccade', # 'Run Necker Cube', 'Run Binocular Rivalry', ] instruct_text = [ ('Welcome to the experiment'), ( # 'In this experiment you will be following targets.\n\n' # 'Each trial will start with a fixation cross. ' # 'Focus on the fixation cross until a stimulus appears.\n\n' # 'In the first three phases, you will follow the stimuli. ' # 'In the fourth phase, you will focus on the ' # 'opposite side of center cross at an ' # 'equal distance as the stimulus. In the fifth ' # 'phase, you will see a cube, and are to respond as ' # 'indicated.' # '\n\n' 'Do not move your head during the trials of this ' 'experiment.\n\nMove only your eyes.\n'), # 'You will be offered rests between sections.\n\n' # 'Press any key to continue.'), ] saccade_instruct_text = ( 'For this experiment, we want to know how accurately your ' 'eyes can move from one position to another. You will see ' 'many gray dots and a cross on the screen. Please focus on ' 'the fixation cross in the beginning. When the red dot ' 'appears, move your eyes to the red dot as quickly as ' 'possible and fixate on the red dot. When you hear a sound, ' 'move your eyes back to the fixation cross.\n\n' 'Try not to blink while a red dot is displayed.\n\n' 'You can then blink or close your eyes to rest for a few seconds.\n\n' # 'Press any key to continue.' ) antisaccade_instruct_text = ( 'For this experiment, we also want to know how accurately your ' 'eyes can move from one point to another. Please focus on the ' 'fixation cross. When the red dot appears, move your eyes to ' 'the OPPOSITE direction of the target at approximately the ' 'same distance from the fixation cross as the target as shown ' 'by the arrow in the image below this text. The arrow will ' 'not be present during the experiment. Move your eyes back ' 'to the fixation point after each target disappears.\n\n' 'Try not to blink while a red dot is displayed.\n\n' 'You can then blink or close your eyes to rest for a few seconds.\n\n' # 'Press any key to continue.' ) pursuit_instruct_text = ( 'For this experiment, we want to know how well your eyes can ' 'follow a target at different speeds. Please fixate the cross. ' 'When the dot appears, follow the target with your eyes. You ' 'will hear a sound when the trial is done. You may blink ' 'until the next trial begins. A new trial will begin after ' 'two seconds.\n\n' 'Try not to blink while the dot is moving.\n\n' 'You can then blink or close your eyes to rest for a few seconds.\n\n' # 'Press any key to continue.' ) necker_instruct_text = ( 'For this experiment, focus on the square. When you see the ' 'square is pointing down and to the left, press the left ' 'button. As soon as it switches to up and to the right, ' 'press the right button. Refer to the image below this ' 'text. The center button is not used during this experiment.\n\n' 'Respond at any time during the stimulus.\n\n' 'Try not to blink after the cube appears.\n\n' 'You can blink or close your eyes to rest after the cube ' 'disappears.\n\n' # 'Press any key to continue.' ) rivalry_instruct_text = ( 'For this experiment, you will see different images in your ' 'left and right eyes through the mirrors.\n\n' 'If you see a face, press the right button. If you see a ' 'house, press the left button. If you perceive a mixture of ' 'the face and house, press the center button.\n\n' 'You can blink or close your eyes to rest for a few seconds ' 'after the pictures disappear.\n\n' # 'Press any key to continue.' ) def convert_color_value(color): """Converts a list of 3 values from 0 to 255 to -1 to 1. Parameters: color -- A list of 3 ints between 0 and 255 to be converted. """ return [round(((n/127.5)-1), 2) for n in color] class EyeTrackingSaccadePursuit(SaccadePursuit.SPtask): def __init__(self, **kwargs): self.quit = False # Needed because eyetracker must shut down self.tracker = None self.disableTracker = disableTracker self.window_screen = window_screen self.monitor_px = monitor_px self.number_of_saccade_trials = number_of_saccade_trials self.number_of_saccade_blocks = number_of_saccade_blocks self.number_of_saccade_lights = number_of_saccade_lights self.number_of_pursuit_trials = number_of_pursuit_trials self.number_of_pursuit_blocks = number_of_pursuit_blocks self.number_of_necker_trials = number_of_necker_trials self.number_of_necker_blocks = number_of_necker_blocks self.number_of_fixation_trials = number_of_fixation_trials self.number_of_fixation_blocks = number_of_fixation_blocks self.number_of_rivalry_trials = number_of_rivalry_trials self.number_of_rivalry_blocks = number_of_rivalry_blocks self.response_box_file = response_box_file self.necker_response_box_file = necker_response_box_file self.necker_response_box_scale = necker_response_box_scale self.response_box_scale = response_box_scale self.antisaccade_instruct_file = antisaccade_instruct_file self.antisaccade_file_scale = antisaccade_file_scale self.instHeight = instHeight super(EyeTrackingSaccadePursuit, self).__init__(**kwargs) def quit_experiment(self): self.quit = True if self.experiment_window: self.display_text_screen('Quiting...', wait_for_input=False) if self.tracker: self.tracker.set_offline_mode() self.tracker.close_edf() self.tracker.transfer_edf() self.tracker.close_connection() fName = os.path.join(self.data_directory, self.experiment_info['Subject ID'] + self.experiment_info['Timepoint'] + '.edf') if os.path.exists(fName): baseName = os.path.join(self.data_directory, 'ETSP_' + self.experiment_info['Subject ID'] + self.experiment_info['Session'] + self.experiment_info['Timepoint']) fName2 = baseName + '.edf' if os.path.exists(fName2): ii = 1 newName = baseName+'('+str(ii)+')'+'.edf' while os.path.exists(newName): ii+=1 newName = baseName+'('+str(ii)+')'+'.edf' fName2 = newName os.rename(fName, fName2) subprocess.call(['edf2asc', fName2]) super(EyeTrackingSaccadePursuit, self).quit_experiment() def run(self): self.chdir() print( 'Note: EDF file will be overwritten if identical subject identifiers are used!') ok = self.get_experiment_info_from_dialog( additional_fields_dict=questionaire_dict, field_order=questionaire_order) if not ok: print('Experiment has been terminated.') sys.exit(1) conditions = [] if self.experiment_info['Run Smooth Pursuit']: conditions.append('Pursuit') if self.experiment_info['Run Saccade']: conditions.append('Saccade') if self.experiment_info['Run Anti-Saccade']: conditions.append('AntiSaccade') # if self.experiment_info['Run Necker Cube']: # conditions.append('Necker') if self.experiment_info['Run Binocular Rivalry']: conditions.append('Rivalry') self.save_experiment_info() self.open_csv_data_file() self.open_window(screen=self.window_screen) self.display_text_screen('Loading...', wait_for_input=False) if not self.disableTracker: self.tracker = eyelinker.EyeLinker( self.experiment_window, self.experiment_info['Subject ID'] + self.experiment_info['Timepoint'] + '.edf', 'BOTH' ) self.tracker.initialize_graphics() self.tracker.open_edf() self.tracker.initialize_tracker() self.tracker.send_calibration_settings( settings={ 'preamble_text': 'Saccade Pursuit Experiment Plus Fixation and Necker Cube', } ) for instruction in self.instruct_text: self.display_text_screen(text=instruction) if not self.disableTracker: self.tracker.display_eyetracking_instructions() self.tracker.setup_tracker() self.tracker.send_command("track_search_limits=NO") # random.shuffle(conditions) condition_counter = 0 for condition in conditions: condition_counter += 1 numBlocks = 1 numTrials = 1 if condition == 'Saccade': self.display_saccade_instructions() numBlocks = self.number_of_saccade_blocks numTrials = self.number_of_saccade_trials elif condition == 'AntiSaccade': self.display_saccade_instructions(anti=True) numBlocks = self.number_of_saccade_blocks numTrials = self.number_of_saccade_trials elif condition == 'Fixation': numBlocks = self.number_of_fixation_blocks numTrials = self.number_of_fixation_trials elif condition == 'Pursuit': self.display_pursuit_instructions() numBlocks = self.number_of_pursuit_blocks numTrials = self.number_of_pursuit_trials elif condition == 'Necker': self.display_text_screen(text=necker_instruct_text, image_file=self.necker_response_box_file, image_scale=self.necker_response_box_scale) numBlocks = self.number_of_necker_blocks numTrials = self.number_of_necker_trials elif condition == 'Rivalry': self.display_rivalry_instructions( image_file=self.response_box_file, image_scale=self.response_box_scale) numBlocks = self.number_of_rivalry_blocks numTrials = self.number_of_rivalry_trials else: continue for block_num in range(numBlocks): block = self.make_block(condition, numTrials) self.display_text_screen( text='Get ready...', wait_for_input=False) psychopy.core.wait(2) if condition == 'Saccade' or condition == 'AntiSaccade': self.display_saccade_fixation(1) else: self.display_fixation(0.5, []) for trial_num, trial in enumerate(block): printString = ("Condition: %s (%d/%d) Block: %d/%d Trial %d/%d" % (condition, condition_counter, len(conditions), block_num+1, numBlocks, trial_num+1, len(block))) if condition == 'Saccade' or condition == 'AntiSaccade': if trial['locations'][0] > 0: printString = printString + " (Right)" else: printString = printString + " (Left)" print(printString) if not self.disableTracker: self.tracker.send_message('CONDITION %s' % condition) self.tracker.send_message('BLOCK %d' % block_num) self.tracker.send_message('TRIAL %d' % trial_num) self.tracker.send_message('Location: %d,%d' % ( trial['locations'][0], trial['locations'][1])) status = '%s: Block %d, Trial %d' % ( condition, block_num, trial_num) self.tracker.send_status(status) # psychopy.sound.Sound('C').play() self.tracker.start_recording() if condition == 'Pursuit' and trial_num == 0: self.display_fixation( self.fixation_trial_time, self.tracker) data = self.run_trial( trial, block_num, trial_num, self.tracker) self.tracker.stop_recording() # psychopy.sound.Sound('C').play() else: if condition == 'Pursuit' and trial_num == 0: self.display_fixation( self.fixation_trial_time, self.tracker) data = self.run_trial(trial, block_num, trial_num) data.update({'Condition': condition}) self.send_data(data) if condition == 'Saccade' or condition == 'AntiSaccade': self.display_saccade_fixation(self.isi_time) else: self.display_fixation(self.isi_time, []) if condition == 'Saccade' or condition == 'AntiSaccade': self.display_saccade_fixation(1) else: self.display_fixation(0.5, []) self.save_data_to_csv() if block_num + 1 != numBlocks: self.display_break() if condition == 'Saccade': self.display_saccade_instructions() elif condition == 'AntiSaccade': self.display_saccade_instructions(anti=True) elif condition == 'Fixation': self.display_text_screen( text='Prepare for next trial.') elif condition == 'Pursuit': self.display_pursuit_instructions() elif condition == 'Necker': self.display_text_screen(text='Remember:\n\n' + necker_instruct_text, image_file=self.necker_response_box_file, image_scale=self.necker_response_box_scale) elif condition == 'Rivalry': self.display_rivalry_instructions( image_file=self.response_box_file, image_scale=self.response_box_scale) self.display_text_screen( 'The experiment is now over.', bg_color=[50, 150, 50], text_color=[255, 255, 255], wait_for_input=False) psychopy.core.wait(5) self.quit_experiment() def display_pursuit_instructions( self, bg_color=[0, 0, 0], wait_for_input=True, **kwargs): bg_color = convert_color_value(bg_color) fg_color = convert_color_value([255, 255, 255]) backgroundRect = psychopy.visual.Rect( self.experiment_window, fillColor=bg_color, units='norm', width=2, height=2) backgroundRect.draw() borderFrame = psychopy.visual.Rect( self.experiment_window, lineColor=fg_color, units='deg', width=self.pursuit_distance*2.1, height=1.5, pos=(0, self.instHeight)) textObject = psychopy.visual.TextStim( self.experiment_window, text='** Smooth Pursuit Example **', color=fg_color, units='deg', height=1, pos=(0, self.instHeight+2), **kwargs) color = self.stim_color Xposition = [0] num_frames_per_second = 60 counter = 0 stim_frequency = [0.1] stim_time = [10] for freq in stim_frequency: stim_frames = int(round(stim_time[counter]*num_frames_per_second)) for time in range(stim_frames): Xposition.append(math.sin( freq*math.radians((time+1)*360/num_frames_per_second))*self.pursuit_distance) counter += 1 stim = psychopy.visual.Circle( self.experiment_window, radius=self.stimulus_size/2, pos=(0, 0), fillColor=color, lineColor=color, units='deg') keys = None while keys != ['space']: for Xpos in Xposition: textObject.draw() borderFrame.draw() stim.pos = (Xpos, self.instHeight) stim.draw() self.experiment_window.flip() keys = psychopy.event.getKeys() if keys == ['space']: break return keys def display_saccade_instructions( self, anti=False, bg_color=[0, 0, 0], wait_for_input=True, **kwargs): bg_color = convert_color_value(bg_color) fg_color = convert_color_value([255, 255, 255]) backgroundRect = psychopy.visual.Rect( self.experiment_window, fillColor=bg_color, units='norm', width=2, height=2) backgroundRect.draw() borderFrame = psychopy.visual.Rect( self.experiment_window, lineColor=fg_color, units='deg', width=self.pursuit_distance*2.1, height=3, pos=(0, self.instHeight)) if anti: dispText = '** Anti-Saccade Example **' else: dispText = '** Saccade Example **' textObject = psychopy.visual.TextStim( self.experiment_window, text=dispText, color=fg_color, units='deg', height=1, pos=(0, self.instHeight+3), **kwargs) color = self.stim_color stim = psychopy.visual.Circle( self.experiment_window, radius=self.stimulus_size/2, pos=(1, 0), fillColor=color, lineColor=color, units='deg') #arrowVert = [(-0.8,0.1),(-0.8,-0.1),(-0.4,-0.1),(-0.4,-0.2),(0,0),(-0.4,0.2),(-0.4,0.1)] arrowVert = numpy.array([(-.25, -2), (.25, -2), (.25, -.75), (.5, -.75), (0, 0), (-.5, -.75), (-.25, -.75), (-.25, -2)]) arrowVert = arrowVert + (0, self.instHeight + 5.25) arrowStim = psychopy.visual.ShapeStim( self.experiment_window, vertices=arrowVert, fillColor=color, size=0.5, lineColor=color, units='deg') fixation = psychopy.visual.TextStim( self.experiment_window, text='+', color=self.saccade_fixation_color, height=self.fixation_size, units='deg', pos=[0, self.instHeight]) stimList = [] temp = list(range(-self.saccade_distance, self.saccade_distance, 1)) for circleN in temp: if circleN < 0: tempCoord = circleN else: tempCoord = circleN+1 tempStim = psychopy.visual.Circle( self.experiment_window, radius=self.stimulus_size/2, pos=[tempCoord, self.instHeight], fillColor=self.saccade_dot_color, lineColor=self.saccade_dot_color, units='deg') stimList.append(tempStim) temp1 = random.sample( range(1, self.saccade_distance+1), self.number_of_saccade_lights) temp2 = random.sample( range(1, self.saccade_distance+1), self.number_of_saccade_lights) saccade_locations = [x*-1 for x in temp1] + temp2 keys = None while keys != ['space']: for ii in range(4): if anti: stim_time = 5 else: stim_time = round(random.uniform(1, self.saccade_time), 3) for frameN in range(90): textObject.draw() borderFrame.draw() for s in stimList: s.draw() fixation.color = color fixation.draw() self.experiment_window.flip() keys = psychopy.event.getKeys() if keys == ['space']: break if keys == ['space']: break Xpos = random.randint(1, 15)*random.choice([-1, 1]) for frameN in range(int(round(stim_time*60))): textObject.draw() borderFrame.draw() for s in stimList: s.draw() stim.pos = (Xpos, self.instHeight) stim.draw() fixation.color = self.saccade_fixation_color fixation.draw() if anti and frameN > stim_time*30: arrowVert2 = arrowVert + (-Xpos*2, 0) arrowStim.vertices = arrowVert2 arrowStim.draw() self.experiment_window.flip() keys = psychopy.event.getKeys() if keys == ['space']: break if keys == ['space']: break if keys == ['space']: break if anti: textObject.draw() borderFrame.draw() for s in stimList: s.draw() fixation.color = color fixation.draw() self.experiment_window.flip() keys = psychopy.event.waitKeys() return keys def display_rivalry_instructions( self, anti=False, bg_color=[0, 0, 0], wait_for_input=True, image_file=[], image_scale=0.25, **kwargs): fg_color = convert_color_value([255, 255, 255]) bg_color = convert_color_value(bg_color) backgroundRect = psychopy.visual.Rect( self.experiment_window, fillColor=bg_color, units='norm', width=2, height=2) backgroundRect.draw() textObject = psychopy.visual.TextStim( self.experiment_window, text='** Binocular Rivalry Example **', color=fg_color, units='deg', height=1, pos=(0, self.instHeight+2), **kwargs) color = self.rivalry_border_color lineSize = self.rivalry_border_width stimPos = self.experiment_window.size[0]/4 stimPos = psychopy.tools.monitorunittools.pix2deg( stimPos, self.experiment_monitor) stim1 = psychopy.visual.ImageStim( self.experiment_window, image=self.rivalry_file1, pos=(-stimPos, 0)) stim1.size = (self.rivalry_width, self.rivalry_height) stim2 = psychopy.visual.ImageStim( self.experiment_window, image=self.rivalry_file2, pos=(stimPos, 0)) stim2.size = (self.rivalry_width, self.rivalry_height) borderScale = stim1.size[0]*0.4 borderWidth1 = stim1.size[0]+borderScale borderHeight1 = stim1.size[1]+borderScale borderWidth2 = stim2.size[0]+borderScale borderHeight2 = stim2.size[1]+borderScale stimBorder1 = psychopy.visual.Rect( self.experiment_window, width=borderWidth1, height=borderHeight1, pos=stim1.pos, lineWidth=lineSize, lineColor=color, units='deg') stimBorder2 = psychopy.visual.Rect( self.experiment_window, width=borderWidth2, height=borderHeight2, pos=stim2.pos, lineWidth=lineSize, lineColor=color, units='deg') stimFix1_top = psychopy.visual.Line( self.experiment_window, start=( stim1.pos[0], stim1.pos[1]+borderHeight1/2), end=(stim1.pos[0], stim1.pos[1]+stim1.size[1]/1.9), units='deg', lineColor=color, lineWidth=lineSize) stimFix1_left = psychopy.visual.Line( self.experiment_window, start=( stim1.pos[0]-borderWidth1/2, stim1.pos[1]), end=(stim1.pos[0]-stim1.size[0]/1.9, stim1.pos[1]), units='deg', lineColor=color, lineWidth=lineSize) stimFix2_bottom = psychopy.visual.Line( self.experiment_window, start=( stim2.pos[0], stim2.pos[1]-borderHeight2/2), end=(stim2.pos[0], stim2.pos[1]-stim2.size[1]/1.9), units='deg', lineColor=color, lineWidth=lineSize) stimFix2_right = psychopy.visual.Line( self.experiment_window, start=( stim2.pos[0]+borderWidth2/2, stim2.pos[1]), end=(stim2.pos[0]+stim2.size[0]/1.9, stim2.pos[1]), units='deg', lineColor=color, lineWidth=lineSize) textObject.draw() stim1.draw() stim2.draw() stimBorder1.draw() stimBorder2.draw() stimFix1_top.draw() stimFix1_left.draw() stimFix2_bottom.draw() stimFix2_right.draw() imageObject = psychopy.visual.ImageStim( self.experiment_window, units='pix', image=image_file) sizex = int(round(imageObject.size[0])*image_scale) sizey = int(round(imageObject.size[1])*image_scale) bottomOfScreen = int( math.floor(-self.experiment_window.size[1]/2+sizey/2))+5 imageObject.size = [sizex, sizey] imageObject.pos = (0, bottomOfScreen) imageObject.draw() self.experiment_window.flip() keys = None if wait_for_input: psychopy.core.wait(.2) # Prevents accidental key presses keys = psychopy.event.waitKeys() while keys != ['space']: keys = psychopy.event.waitKeys() self.experiment_window.flip() experiment = EyeTrackingSaccadePursuit( experiment_name='ETSP', data_fields=data_fields, data_directory=data_directory, instruct_text=instruct_text, monitor_name=monitor_name, monitor_width=monitor_width, monitor_px=monitor_px, monitor_distance=distance_to_monitor, pursuit_time=pursuit_time, stim_color=stim_color, pursuit_frequencies=pursuit_frequencies, saccade_distance=saccade_distance, saccade_time=saccade_time, number_of_saccade_lights=number_of_saccade_lights, saccade_fixation_color=convert_color_value(saccade_fixation_color), saccade_dot_color=convert_color_value(saccade_dot_color), isi_time=isi_time, stimulus_size=stimulus_size, fixation_size=fixation_size, pursuit_distance=pursuit_distance, necker_time=necker_time, necker_color=necker_color, necker_scale=necker_scale, necker_bg_color=convert_color_value(necker_bg_color), fixation_trial_time=fixation_trial_time, rivalry_time=rivalry_time, rivalry_height=rivalry_height, rivalry_width=rivalry_width, necker_file=necker_file, rivalry_file1=rivalry_file1, rivalry_file2=rivalry_file2, rivalry_border_color=convert_color_value(rivalry_border_color), disableTracker=disableTracker, rivalry_border_width=rivalry_border_width, rivalry_distance=rivalry_distance, new_trial_sound=new_trial_sound ) if __name__ == '__main__': try: experiment.run() except Exception: print(traceback.format_exc()) if not experiment.quit: experiment.quit_experiment()