def __init__(self, display): """Initiates an eyetracker dummy object, that simulates gaze position using the mouse arguments display -- a pygaze display.Display instance keyword arguments None """ # try to copy docstrings (but ignore it if it fails, as we do # not need it for actual functioning of the code) try: copy_docstr(BaseEyeTracker, Dummy) except: # we're not even going to show a warning, since the copied # docstring is useful for code editors; these load the docs # in a non-verbose manner, so warning messages would be lost pass self.recording = False self.blinking = False self.bbpos = (settings.DISPSIZE[0]/2, settings.DISPSIZE[1]/2) self.resolution = settings.DISPSIZE[:] self.simulator = Mouse(disptype=settings.DISPTYPE, mousebuttonlist=None, timeout=2, visible=False) self.kb = Keyboard(disptype=settings.DISPTYPE, keylist=None, timeout=None) self.angrybeep = Sound(osc='saw',freq=100, length=100, attack=0, decay=0, soundfile=None) self.display = display self.screen = Screen(disptype=settings.DISPTYPE, mousevisible=False)
def __init__(self, display): """Initiates a 'dumb dummy' object, that doesn't do a thing arguments display -- a pygaze display.Display instance keyword arguments None """ # try to copy docstrings (but ignore it if it fails, as we do # not need it for actual functioning of the code) try: copy_docsafe_decode(BaseEyeTracker, DumbDummy) except: # we're not even going to show a warning, since the copied # docstring is useful for code editors; these load the docs # in a non-verbose manner, so warning messages would be lost pass self.recording = False self.blinking = False self.bbpos = (settings.DISPSIZE[0] / 2, settings.DISPSIZE[1] / 2) self.display = display self.screen = Screen(disptype=settings.DISPTYPE, mousevisible=False)
def confirm_abort_experiment(self): """ Asks for confirmation before aborting the experiment. Displays a confirmation screen, collects the response, and acts accordingly. Exceptions: Raises a response_error upon confirmation. Returns: False if no confirmation was given. """ # Display the confirmation screen scr = Screen(disptype=DISPTYPE) kb = Keyboard(timeout=5000) yc = DISPSIZE[1] / 2 xc = DISPSIZE[0] / 2 ld = 40 # Line height scr.draw_text(u'Really abort experiment?', pos=(xc, yc - 3 * ld)) scr.draw_text(u'Press \'Y\' to abort', pos=(xc, yc - 0.5 * ld)) scr.draw_text(u'Press any other key or wait 5s to go to setup', \ pos=(xc, yc+0.5*ld)) self.display.fill(scr) self.display.show() # process the response: try: key, time = kb.get_key() except: return False # if confirmation, close experiment if key == u'y': raise Exception(u'The experiment was aborted') self.eyelink_graphics.esc_pressed = False return False
multicast_ip = 0 _print("No multicast IP found.") # GENERAL # Total number of points. total = 0.0 # Create a colour wheel to select colours from for the stimuli. cw = create_colourwheel(STIML, STIMR, savefile='colourwheel.png') # PYGAZE # Initialise a new Display instance disp = Display() # Initialise a Screen instance for arbitrary drawing. scr = Screen() scr.draw_text(text="Initialising the experiment...", fontsize=FONTSIZE) disp.fill(scr) disp.show() # Initialise the ka-ching sound. kaching = Sound(soundfile=KACHING) # Initialise a Keyboard and a Mouse instance for participant interaction. kb = Keyboard() mouse = Mouse() # COMMUNICATIONS timer.pause(5000) _print("Initialising Client.") # Initialise a new Client instance.
mouse = Mouse(mousebuttonlist=None, timeout=None) # Initialise a log. log = Logfile() header = ['trialnr', 'nstim', 'fixonset', 'stimonset', 'maintenanceonset', \ 'probeonset', 'RT', 'response'] for i in range(max(NSTIM)): header.extend(['stimx%d' % (i), 'stimy%d' % (i), 'stimori%d' % (i), \ 'stimerror%d' % (i)]) header.extend(['E', 'X', 'T']) for i in range(max(NSTIM)-1): header.append('NT%d' % i) log.write(header) # Initialise a blank Screen for ad-hoc drawing. scr = Screen() # Initialise a blank Screen. blankscr = Screen() # Initialise a fixation Screen. fixscr = Screen() fixscr.draw_fixation(fixtype=FIXTYPE, diameter=FIXSIZE, pw=FIXPW) # Initialise stimulus and probe Screens. stimscr = {} probescr = {} for nstim in NSTIM: locs = nstim * [DISPCENTRE] oris = nstim * [0] stimscr[nstim] = StimScreen(nstim, locs, oris, \
def __init__(self, display, logfile=settings.LOGFILE, \ alea_key=settings.ALEAKEY, \ animated_calibration=settings.ALEAANIMATEDCALIBRATION, \ eventdetection=settings.EVENTDETECTION, \ saccade_velocity_threshold=35, \ saccade_acceleration_threshold=9500, \ blink_threshold=settings.BLINKTHRESH, \ **args): """Initializes the AleaTracker object arguments display -- a pygaze.display.Display instance keyword arguments logfile -- logfile name (string value); note that this is the name for the eye data log file (default = LOGFILE) """ # try to copy docstrings (but ignore it if it fails, as we do # not need it for actual functioning of the code) try: copy_docstr(BaseEyeTracker, AleaTracker) except: # we're not even going to show a warning, since the copied # docstring is useful for code editors; these load the docs # in a non-verbose manner, so warning messages would be lost pass # object properties self.disp = display self.screen = Screen() self.dispsize = self.disp.dispsize # display size in pixels self.screensize = settings.SCREENSIZE # display size in cm self.kb = Keyboard(keylist=['space', 'escape', 'q'], timeout=1) self.errorbeep = Sound(osc='saw', freq=100, length=100) # show a message self.screen.clear() self.screen.draw_text( text="Initialising the eye tracker, please wait...", fontsize=20) self.disp.fill(self.screen) self.disp.show() # output file properties self.outputfile = logfile + '.tsv' # calibration properties self.animated_calibration = animated_calibration == True # eye tracker properties self.connected = False self.recording = False self.errdist = 2 # degrees; maximal error for drift correction self.pxerrdist = 30 # initial error in pixels self.maxtries = 100 # number of samples obtained before giving up (for obtaining accuracy and tracker distance information, as well as starting or stopping recording) self.prevsample = (-1, -1) self.prevps = -1 # event detection properties self.fixtresh = 1.5 # degrees; maximal distance from fixation start (if gaze wanders beyond this, fixation has stopped) self.fixtimetresh = 100 # milliseconds; amount of time gaze has to linger within self.fixtresh to be marked as a fixation self.spdtresh = saccade_velocity_threshold # degrees per second; saccade velocity threshold self.accthresh = saccade_acceleration_threshold # degrees per second**2; saccade acceleration threshold self.blinkthresh = blink_threshold # milliseconds; blink detection threshold used in PyGaze method self.eventdetection = eventdetection self.set_detection_type(self.eventdetection) self.weightdist = 10 # weighted distance, used for determining whether a movement is due to measurement error (1 is ok, higher is more conservative and will result in only larger saccades to be detected) # connect to the tracker self.alea = OGAleaTracker(alea_key, file_path=self.outputfile) # get info on the sample rate # TODO: Compute after streaming some samples? self.samplerate = 60.0 self.sampletime = 1000.0 / self.samplerate # initiation report self.log("pygaze initiation report start") self.log("display resolution: {}x{}".format( \ self.dispsize[0], self.dispsize[1])) self.log("display size in cm: {}x{}".format( \ self.screensize[0], self.screensize[1])) self.log("samplerate: {} Hz".format(self.samplerate)) self.log("sampletime: {} ms".format(self.sampletime)) self.log("fixation threshold: {} degrees".format(self.fixtresh)) self.log("speed threshold: {} degrees/second".format(self.spdtresh)) self.log("acceleration threshold: {} degrees/second**2".format( \ self.accthresh)) self.log("pygaze initiation report end")
from pygaze.display import Display import pygaze.libtime as timer from pygaze.screen import Screen from constants import * fixscreen = Screen() fixscreen.draw_fixation(fixtype='dot') disp = Display() timer.pause(2000) disp.close()
kb = Keyboard() # Initialise the EyeTracker and let it know which Display instance to use by # passing it to the EyeTracker. tracker = EyeTracker(disp) # Create a Logfile instance that keeps track of when videos start. log = Logfile() # Write a header to the log file. log.write(['date', 'time', 'trialnr', 'video', 'timestamp']) # # # # # # SCREENS # Create a screen to show instructions on. textscr = Screen() textscr.draw_text("Press any key to start the next video.", fontsize=24) # Create a screen to show images on. This will be the screen that we will use # to display each video frame. stimscr = Screen() # # # # # # PLAY VIDEOS # Calibrate the eye tracker. tracker.calibrate() # Randomise the list of videos. Remove this line if you want the videos to be # displayed in alphabetical order. random.shuffle(VIDEOS)
def __init__(self, libeyelink, tracker): """ Constructor. Arguments: libeyelink -- A libeyelink object. tracker -- An tracker object as returned by pylink.EyeLink(). """ pylink.EyeLinkCustomDisplay.__init__(self) # objects self.libeyelink = libeyelink self.display = libeyelink.display self.screen = Screen(disptype=DISPTYPE, mousevisible=False) self.kb = Keyboard(keylist=None, timeout=0) self.mouse = Mouse(timeout=0) if DISPTYPE == 'pygame': self.kb.set_timeout(timeout=0.001) # If we are using a DISPTYPE that cannot be used directly, we have to # save the camera image to a temporary file on each frame. #if DISPTYPE not in ('pygame', 'psychopy'): import tempfile import os self.tmp_file = os.path.join(tempfile.gettempdir(), '__eyelink__.jpg') # drawing properties self.xc = self.display.dispsize[0]/2 self.yc = self.display.dispsize[1]/2 self.extra_info = True self.ld = 40 # line distance self.fontsize = libeyelink.fontsize self.title = "" self.display_open = True # menu self.menuscreen = Screen(disptype=DISPTYPE, mousevisible=False) self.menuscreen.draw_text(text="Eyelink calibration menu", pos=(self.xc,self.yc-6*self.ld), center=True, font='mono', fontsize=int(2*self.fontsize), antialias=True) self.menuscreen.draw_text(text="%s (pygaze %s, pylink %s)" \ % (libeyelink.eyelink_model, pygaze.version, pylink.__version__), pos=(self.xc,self.yc-5*self.ld), center=True, font='mono', fontsize=int(.8*self.fontsize), antialias=True) self.menuscreen.draw_text(text="Press C to calibrate", pos=(self.xc, self.yc-3*self.ld), center=True, font='mono', fontsize=self.fontsize, antialias=True) self.menuscreen.draw_text(text="Press V to validate", pos=(self.xc, self.yc-2*self.ld), center=True, font='mono', fontsize=self.fontsize, antialias=True) self.menuscreen.draw_text(text="Press A to auto-threshold", pos=(self.xc,self.yc-1*self.ld), center=True, font='mono', fontsize=self.fontsize, antialias=True) self.menuscreen.draw_text(text="Press I to toggle extra info in camera image", pos=(self.xc,self.yc-0*self.ld), center=True, font='mono', fontsize=self.fontsize, antialias=True) self.menuscreen.draw_text(text="Press Enter to show camera image", pos=(self.xc,self.yc+1*self.ld), center=True, font='mono', fontsize=self.fontsize, antialias=True) self.menuscreen.draw_text( text="(then change between images using the arrow keys)", pos=(self.xc, self.yc+2*self.ld), center=True, font='mono', fontsize=self.fontsize, antialias=True) self.menuscreen.draw_text(text="Press Escape to abort experiment", pos=(self.xc, self.yc+4*self.ld), center=True, font='mono', fontsize=self.fontsize, antialias=True) self.menuscreen.draw_text(text="Press Q to exit menu", pos=(self.xc, self.yc+5*self.ld), center=True, font='mono', fontsize=self.fontsize, antialias=True) # beeps self.__target_beep__ = Sound(osc='sine', freq=440, length=50, attack=0, decay=0, soundfile=None) self.__target_beep__done__ = Sound(osc='sine', freq=880, length=200, attack=0, decay=0, soundfile=None) self.__target_beep__error__ = Sound(osc='sine', freq=220, length=200, attack=0, decay=0, soundfile=None) # Colors self.color = { pylink.CR_HAIR_COLOR: pygame.Color('white'), pylink.PUPIL_HAIR_COLOR: pygame.Color('white'), pylink.PUPIL_BOX_COLOR: pygame.Color('green'), pylink.SEARCH_LIMIT_BOX_COLOR: pygame.Color('red'), pylink.MOUSE_CURSOR_COLOR: pygame.Color('red'), 'font': pygame.Color('white'), } # Font pygame.font.init() self.font = pygame.font.SysFont('Courier New', 11) # further properties self.state = None self.pal = None self.size = (0,0) self.set_tracker(tracker) self.last_mouse_state = -1 self.bit64 = '64bit' in platform.architecture() self.imagebuffer = self.new_array()
# -*- coding: utf-8 -*- """ Created on Thu Nov 10 13:29:48 2016 @author: adam """ from pygaze.display import Display from pygaze.screen import Screen import pygaze.libtime as timer # disp = Window(size=DISPSIZE, units='pix', fullscr=True) disp = Display() fixscreen = Screen() fixscreen.draw_fixation(fixtype='dot') imgscreen = Screen() imgscreen.draw_image('/home/adam/Desktop/experiment0/Example.png') disp.fill(fixscreen) disp.show() timer.pause(1000) disp.fill(imgscreen) disp.show() timer.pause(2000) disp.close()
from pygaze.logfile import Logfile from pygaze.eyetracker import EyeTracker import pygaze.libtime as timer from libmeg import * #%% # # # # # # INITIALISE # Initialise a new Display instance. disp = Display() # Present a start-up screen. scr = Screen() scr.draw_text("Loading, please wait...", fontsize=MAIN_FONTSIZE) disp.fill(scr) disp.show() # Open a new log file. log = Logfile(filename = LOGFILE) log_det = Logfile(filename = DETAILED_LOGFILE) log_events = Logfile(filename = EVENT_LOGFILE) # TODO: Write header. log.write(["trialnr","left_ang","right_ang", "cue_dir", "targ", "targ_ang", "resp_ang", "perc_diff", "resp_onset", "resp_duration", "iti", "iti_onset", "stim_onset","delay_onset", "cue_onset", "postcue_onset","probe_onset", "prac"]) log_det.write(["trialnr", "timestamp", "angle", "event", "targ_ang", "cue_dir"]) log_events.write(["Trigger", "Timestamp"]) # Initialise the eye tracker.
kb = Keyboard() # Após kb - Criando o logfile para armazenar e salvar os dados coletados log = Logfile() # Tela de instruções do experimento instructions = 'Seja bem vindo(a)!\n' \ 'Neste experimento as letras F e J apareceraão em ambos os lados da tela.\n' \ 'Se você ver a letra F, então pressione a tecla F.\n' \ 'Se você ver a letra J, então pressione a tecla J.\n' \ 'Tente ser tão rápido e preciso quanto possível durante os ensaios.\n\n' \ 'Aperte a barra de espaço do teclado para começar.\n\n' \ 'Boa sorte!' instscr = Screen() instscr.draw_text(text=instructions, fontsize=22) # Criando a tela de fixação fixscr = Screen() fixscr.draw_fixation(fixtype='cross', diameter=15) # draw the left box fixscr.draw_rect(x=BOXCORS['left'][0], y=BOXCORS['left'][1], w=BOXSIZE, h=BOXSIZE, pw=3, fill=False) # draw the right box
cy = yax / 2 cx = xax / 2 ### Set size of game matrix relative to screen with 'margin'. margin = 0.7 side = yax * margin t_side = side / p1 tloc = t_side / p1 owncol = (255, 255, 255) othercol = (255, 255, 255) disp = Display(dispsize=DISPSIZE, screennr=2) mse = Mouse(visible=False, timeout=10) gamescreen = Screen(dispsize=DISPSIZE) #tracker = EyeTracker(disp) fixscr = Screen(dispsize=DISPSIZE) fixscr.draw_fixation(fixtype='cross', diameter=8) introscreen = Screen(dispsize=DISPSIZE) introscreen.draw_text("Welcome to the experiment", fontsize=30) introscreen.draw_text("\n\n\n (To proceed, press any key)", fontsize=25) istrscreen = Screen(dispsize=DISPSIZE) istrscreen.draw_text( "In this experiment, you will \n play a sequence of games \n like the one displayed on the right", fontsize=30) istrscreen.draw_text("You", fontsize=25)
def __init__(self, nstim, locs, oris, linewidth=3, \ stimtypes='gabor', showcolourwheel=False): """Initialises a new StimScreen instance. Arguments nstim - Integer that determines the number of stimuli on this screen. locs - List of (x,y) tuples that determine the positions of all stimuli. The list's length should be equal to the number of stimuli as defined by nstim. oris - List of integers that determine the orientations of all stimuli. The list's length should be equal to the number of stimuli as defined by nstim. Keyword Arguments linewidth - Integer or a list of integers that determines the width of the lines around stimuli (in pixels). Default value is 3. stimtypes - String or a list of strings that determines the type of stimulus. Options are 'gabor' and 'noise'. Default is 'gabor'. showcolourwheel- Boolean that determines whether a central colour wheel should be drawn of not. Default is False. """ # Settings from the constants. self._sf = float(STIMSF) / float(STIMSIZE) self._alpha = STIMALPHA self._contrast = STIMCONTRAST # Noise texture. self._noisetex = numpy.random.rand(STIMNOISERES, STIMNOISERES) # Convert the linewidth to a list (if necessary). if type(linewidth) in [int, float]: linewidth = nstim * [int(linewidth)] # Convert the stimulus types to a list (if necessary). if type(stimtypes) in [str, unicode]: stimtypes = nstim * [stimtypes] # Create a Screen to use its wonderful drawing functions. self.scr = Screen() # Draw the fixation cross. self.scr.draw_fixation(fixtype=FIXTYPE, diameter=FIXSIZE) # Draw the colouw wheel if showcolourwheel: # Load the image. self.scr.draw_image(CWIMG, scale=CWIMGSCALE) # Create an internal list of stimuli (=PsychoPy stimulus instances) by # copying it from the internal screen. self.screen = self.scr.screen # Keep a list of the index numbers of all stimuli. The indices refer # to positions within the self.scr.screen list of PsychoPy stimuli. self._stimindexnrs = [] self._outlineindexnrs = [] # Draw the stimuli. for i in range(nstim): # Add the stimulus' index number to the list of indices. self._stimindexnrs.append(len(self.screen)) # # Create a new Rect stimulus instance. # stim = Rect(pygaze.expdisplay, \ # pos=pos2psychopos(locs[i]), \ # fillColor=rgb2psychorgb(list(oris[i])), \ # lineColor=rgb2psychorgb(list(linecols[i])), \ # lineWidth=linewidth[i], \ # width=STIMSIZE, \ # height=STIMSIZE) # Create a Gabor-ish GratingStim. if stimtypes[i] == 'gabor': tex = 'sin' else: tex = self._noisetex stim = GratingStim(pygaze.expdisplay, \ pos=pos2psychopos(locs[i]), \ ori=oris[i], \ size=STIMSIZE, \ sf=self._sf, \ opacity=self._alpha, \ contrast=self._contrast, \ tex=tex, \ mask='circle', \ color=(1,1,1) ) # Add the new stimulus to our list of stimuli. self.screen.append(stim) # Add an outline for the stimulus. self._outlineindexnrs.append(len(self.screen)) stim = Circle(pygaze.expdisplay, \ pos=pos2psychopos(locs[i]), \ lineWidth=linewidth[i], \ radius=STIMSIZE//2, \ edges=32, \ closeShape=False, \ fillColor=None, \ lineColor=(0,0,0) ) # Add the new stimulus to our list of stimuli. self.screen.append(stim)
kb = Keyboard() # Create a new Logfile instance. log = Logfile() log.write(["trialnr", "trial_type", "stimulus", \ "fix_onset", "stim_onset", "response", "RT", \ "correct"]) # Create a BAD sound. bad_sound = Sound(osc="whitenoise", length=200) bad_sound.set_volume(1) good_sound = Sound(osc="sine", freq=440, length=200) good_sound.set_volume(0.5) # Create a new Screen instance. scr = Screen() scr.draw_text("Welcome!", fontsize=100, \ colour=(255,100,100)) # Pass the screen to the display. disp.fill(scr) disp.show() timer.pause(3000) # Create a list of all trials. trials = [] # Add all the words. for word in WORDS: t = {} t["type"] = "word" t["stimulus"] = word
def __init__(self, display, logfile=LOGFILE, eventdetection=EVENTDETECTION, \ saccade_velocity_threshold=35, saccade_acceleration_threshold=9500, \ **args): """Initializes the EyeTribeTracker object arguments display -- a pygaze.display.Display instance keyword arguments logfile -- logfile name (string value); note that this is the name for the eye data log file (default = LOGFILE) """ # try to copy docstrings (but ignore it if it fails, as we do # not need it for actual functioning of the code) try: copy_docstr(BaseEyeTracker, EyeTribeTracker) except: # we're not even going to show a warning, since the copied # docstring is useful for code editors; these load the docs # in a non-verbose manner, so warning messages would be lost pass # object properties self.disp = display self.screen = Screen() self.dispsize = DISPSIZE # display size in pixels self.screensize = SCREENSIZE # display size in cm self.kb = Keyboard(keylist=['space', 'escape', 'q'], timeout=1) self.errorbeep = Sound(osc='saw',freq=100, length=100) # output file properties self.outputfile = logfile # eye tracker properties self.connected = False self.recording = False self.errdist = 2 # degrees; maximal error for drift correction self.pxerrdist = 30 # initial error in pixels self.maxtries = 100 # number of samples obtained before giving up (for obtaining accuracy and tracker distance information, as well as starting or stopping recording) self.prevsample = (-1,-1) self.prevps = -1 # event detection properties self.fixtresh = 1.5 # degrees; maximal distance from fixation start (if gaze wanders beyond this, fixation has stopped) self.fixtimetresh = 100 # milliseconds; amount of time gaze has to linger within self.fixtresh to be marked as a fixation self.spdtresh = saccade_velocity_threshold # degrees per second; saccade velocity threshold self.accthresh = saccade_acceleration_threshold # degrees per second**2; saccade acceleration threshold self.eventdetection = eventdetection self.set_detection_type(self.eventdetection) self.weightdist = 10 # weighted distance, used for determining whether a movement is due to measurement error (1 is ok, higher is more conservative and will result in only larger saccades to be detected) # connect to the tracker self.eyetribe = EyeTribe(logfilename=logfile) # get info on the sample rate self.samplerate = self.eyetribe._samplefreq self.sampletime = 1000.0 * self.eyetribe._intsampletime # initiation report self.log("pygaze initiation report start") self.log("display resolution: %sx%s" % (self.dispsize[0],self.dispsize[1])) self.log("display size in cm: %sx%s" % (self.screensize[0],self.screensize[1])) self.log("samplerate: %.2f Hz" % self.samplerate) self.log("sampletime: %.2f ms" % self.sampletime) self.log("fixation threshold: %s degrees" % self.fixtresh) self.log("speed threshold: %s degrees/second" % self.spdtresh) self.log("acceleration threshold: %s degrees/second**2" % self.accthresh) self.log("pygaze initiation report end")
def draw_menu_screen(self): """ desc: Draws the menu screen. """ self.menuscreen = Screen(disptype=settings.DISPTYPE, mousevisible=False) self.menuscreen.draw_text(text="Eyelink calibration menu", pos=(self.xc, self.yc - 6 * self.ld), center=True, font='mono', fontsize=int(2 * self.fontsize), antialias=True) self.menuscreen.draw_text(text="%s (pygaze %s, pylink %s)" \ % (self.libeyelink.eyelink_model, pygaze.version, pylink.__version__), pos=(self.xc,self.yc-5*self.ld), center=True, font='mono', fontsize=int(.8*self.fontsize), antialias=True) self.menuscreen.draw_text(text="Press C to calibrate", pos=(self.xc, self.yc - 3 * self.ld), center=True, font='mono', fontsize=self.fontsize, antialias=True) self.menuscreen.draw_text(text="Press V to validate", pos=(self.xc, self.yc - 2 * self.ld), center=True, font='mono', fontsize=self.fontsize, antialias=True) self.menuscreen.draw_text(text="Press A to auto-threshold", pos=(self.xc, self.yc - 1 * self.ld), center=True, font='mono', fontsize=self.fontsize, antialias=True) self.menuscreen.draw_text( text="Press I to toggle extra info in camera image", pos=(self.xc, self.yc - 0 * self.ld), center=True, font='mono', fontsize=self.fontsize, antialias=True) self.menuscreen.draw_text(text="Press Enter to show camera image", pos=(self.xc, self.yc + 1 * self.ld), center=True, font='mono', fontsize=self.fontsize, antialias=True) self.menuscreen.draw_text( text="(then change between images using the arrow keys)", pos=(self.xc, self.yc + 2 * self.ld), center=True, font='mono', fontsize=self.fontsize, antialias=True) self.menuscreen.draw_text(text="Press Escape to abort experiment", pos=(self.xc, self.yc + 4 * self.ld), center=True, font='mono', fontsize=self.fontsize, antialias=True) self.menuscreen.draw_text(text="Press Q to exit menu", pos=(self.xc, self.yc + 5 * self.ld), center=True, font='mono', fontsize=self.fontsize, antialias=True)
def __init__(self, display, logfile=settings.LOGFILE, \ eventdetection=settings.EVENTDETECTION, \ saccade_velocity_threshold=35, \ saccade_acceleration_threshold=9500, \ blink_threshold=settings.BLINKTHRESH, \ **args): # try to copy docstrings (but ignore it if it fails, as we do # not need it for actual functioning of the code) try: copy_docstr(BaseEyeTracker, EyeLogicTracker) except: # we're not even going to show a warning, since the copied # docstring is useful for code editors; these load the docs # in a non-verbose manner, so warning messages would be lost pass self.disp = display self.screen = Screen() self.dispsize = self.disp.dispsize # display size in pixels self.screensize = settings.SCREENSIZE # display size in cm self.kb = Keyboard(keylist=['space', 'escape', 'q'], timeout=1) self.errorbeep = Sound(osc='saw', freq=100, length=100) # show a message self.screen.clear() self.screen.draw_text( text="Initialising the eye tracker, please wait...", fontsize=20) self.disp.fill(self.screen) self.disp.show() # output file properties self.logfile = logfile # eye tracker properties self._recording = Event() self._recording.clear() self._calibrated = Event() self._calibrated.clear() self.eye_used = 2 # 0=left, 1=right, 2=binocular self.sampleLock = Lock() self.lastSample = None self.maxtries = 100 # number of samples obtained before giving up (for obtaining accuracy and tracker distance information, as well as starting or stopping recording) # event detection properties self.pxfixtresh = 50; self.fixtresh = 1.5 # degrees; maximal distance from fixation start (if gaze wanders beyond this, fixation has stopped) self.fixtimetresh = 100 # milliseconds; amount of time gaze has to linger within self.fixtresh to be marked as a fixation self.spdtresh = saccade_velocity_threshold # degrees per second; saccade velocity threshold self.accthresh = saccade_acceleration_threshold # degrees per second**2; saccade acceleration threshold self.blinkthresh = blink_threshold # milliseconds; blink detection threshold used in PyGaze method self.eventdetection = eventdetection self._log_vars = [ \ "timestampMicroSec", \ "index", \ "porFilteredX", \ "porFilteredY", \ "porLeftX", \ "porLeftY", \ "pupilRadiusLeft", \ "porRightX", \ "porRightY", \ "pupilRadiusRight", \ ] # Open a new log file. dir_name = os.path.dirname(logfile) file_name = os.path.basename(logfile) name, ext = os.path.splitext(file_name) self._data_file_path = os.path.join(dir_name, name+".eyelogic.csv") self._log_file = open(self._data_file_path, "w") # Write a header to the log. header = ["TYPE"] header.extend(self._log_vars) self._sep = ";" self._log_file.write("Sep="+self._sep+"\n") self._log_file.write(self._sep.join(map(str, header))) # Create a lock to prevent simultaneous access to the log file. self._logging_queue = Queue() self._logging_queue_empty = Event() self._logging_queue_empty.set() self._connected = Event() self._connected.set() self._log_counter = 0 self._log_consolidation_freq = 60 self._logging_thread = Thread( target=self.loggingThread, \ name='PyGaze_EyeLogic_Logging', args=[]) global g_api g_api = self # log self.log("pygaze initiation") #self.log("experiment = {}".format(self.description)) #self.log("participant = {}".format(self.participant)) self.log("display resolution = {}x{}".format(self.dispsize[0], \ self.dispsize[1])) self.log("display size in cm = {}x{}".format(self.screensize[0], \ self.screensize[1])) self.log("fixation threshold = {} degrees".format(self.fixtresh)) self.log("speed threshold = {} degrees/second".format(self.spdtresh)) self.log("acceleration threshold = {} degrees/second**2".format( \ self.accthresh)) # connect self.api = ELApi( "PyGaze" ) self.api.registerGazeSampleCallback( gazeSampleCallback ) self.api.registerEventCallback( eventCallback ) resultConnect = self.api.connect() if (resultConnect != ELApi.ReturnConnect.SUCCESS): self._connected.clear() raise Exception("Cannot connect to EyeLogic server = {}".format(errorstringConnect(resultConnect))) self._connected.set() screenConfig = self.api.getScreenConfig() self.log("eye tracker is mounted on screen {}".format(screenConfig.id)) self.rawResolution = (screenConfig.resolutionX, screenConfig.resolutionY) self.log("raw screen resolution = {}x{}".format( self.rawResolution[0], self.rawResolution[1])) self.log("end pygaze initiation") deviceConfig = self.api.getDeviceConfig() if (deviceConfig.deviceSerial == 0): raise Exception("no eye tracking device connected") if (len(deviceConfig.frameRates) == 0): raise Exception("failed to read out device configuration") g_api.sampleRate = deviceConfig.frameRates[0] g_api.sampleTime = 1000.0 / g_api.sampleRate g_api.log("samplerate = {} Hz".format(g_api.sampleRate)) g_api.log("sampletime = {} ms".format(g_api.sampleTime)) self._logging_thread.start() self.screen.clear() self.disp.fill(self.screen) self.disp.show()
def __init__(self, libeyelink, tracker): """ Constructor. Arguments: libeyelink -- A libeyelink object. tracker -- An tracker object as returned by pylink.EyeLink(). """ pylink.EyeLinkCustomDisplay.__init__(self) # objects self.libeyelink = libeyelink self.display = libeyelink.display self.screen = Screen(disptype=settings.DISPTYPE, mousevisible=False) self.kb = Keyboard(keylist=None, timeout=0) self.mouse = Mouse(timeout=0) if settings.DISPTYPE == 'pygame': self.kb.set_timeout(timeout=0.001) # If we are using a DISPTYPE that cannot be used directly, we have to # save the camera image to a temporary file on each frame. #if DISPTYPE not in ('pygame', 'psychopy'): import tempfile import os self.tmp_file = os.path.join(tempfile.gettempdir(), '__eyelink__.jpg') # drawing properties self.xc = self.display.dispsize[0] / 2 self.yc = self.display.dispsize[1] / 2 self.extra_info = True self.ld = 40 # line distance self.fontsize = libeyelink.fontsize self.title = "" self.display_open = True self.draw_menu_screen() # beeps self.__target_beep__ = Sound(osc='sine', freq=440, length=50, attack=0, decay=0, soundfile=None) self.__target_beep__done__ = Sound(osc='sine', freq=880, length=200, attack=0, decay=0, soundfile=None) self.__target_beep__error__ = Sound(osc='sine', freq=220, length=200, attack=0, decay=0, soundfile=None) # Colors self.color = { pylink.CR_HAIR_COLOR: pygame.Color('white'), pylink.PUPIL_HAIR_COLOR: pygame.Color('white'), pylink.PUPIL_BOX_COLOR: pygame.Color('green'), pylink.SEARCH_LIMIT_BOX_COLOR: pygame.Color('red'), pylink.MOUSE_CURSOR_COLOR: pygame.Color('red'), 'font': pygame.Color('white'), } # Font pygame.font.init() self.font = pygame.font.SysFont('Courier New', 11) # further properties self.state = None self.pal = None self.size = (0, 0) self.set_tracker(tracker) self.last_mouse_state = -1 self.bit64 = '64bit' in platform.architecture() self.imagebuffer = self.new_array()
from pygaze.logfile import Logfile from pygaze.eyetracker import EyeTracker import pygaze.libtime as timer from scansync.mri import MRITriggerBox ############## # INITIALISE # ############## # Initialise a new Display instance. disp = Display() # Present a start-up screen. scr = Screen() scr.draw_text("Loading, please wait...", fontsize=24) disp.fill(scr) disp.show() # Open a new log file. log = Logfile() # TODO: Write header. log.write(["trialnr", "block", "run","stim", "keypress", "go_nogo", "face_onset", "signal_onset","resp_onset", "RT", "accuracy", "respmap", "block_type"]) # Open a new log file to log events. event_log = Logfile(filename=EVENT_LOG) event_log.write(["time", "event"]) # Initialise the eye tracker. tracker = EyeTracker(disp)
import numpy from psychopy.visual import GratingStim # Initialize a new Display instance (specifications are in # constants.py). disp = Display() # Create a new GratingStim. The sinusoidal texture and the # Gaussian mask will make it into a Gabor. The spatial # frequency of 0.05 cycles per pixel will make it have # 1 cycle every 20 pixels. gabor = GratingStim(pygaze.expdisplay, tex='sin', mask='gauss', \ sf=0.05, size=200) # Initialize a new Screen instance for the Gabor. gaborscreen = Screen() # Add the GratingStim to the gaborscreen's screen property # (a list of all PsychoPy stimuli in the screen). gaborscreen.screen.append(gabor) # Create random numbers between 0 and 1 (numpy.random.rand), # converted to numbers between 0 and 2 (*2), and # then converted to numbers between -1 and 1 (-1). The size # must be a power of 2! noise = (numpy.random.rand(64, 64) * 2) - 1 # Create a new GratingStim. Using the noise array as # texture will result in visual snow. The mask is Gaussian # (to match the Gabor). noisepatch = GratingStim(pygaze.expdisplay, tex=noise, \ mask='gauss', size=200)
# set up function argument types and return type XInputSetState = xinput.XInputSetState XInputSetState.argtypes = [ctypes.c_uint, ctypes.POINTER(XINPUT_VIBRATION)] XInputSetState.restype = ctypes.c_uint # define helper function def set_vibration(controller, left_motor, right_motor): vibration = XINPUT_VIBRATION(int(left_motor * 65535), int(right_motor * 65535)) XInputSetState(controller, ctypes.byref(vibration)) # # # # # # PYGAZE INSTANCES # visual disp = Display() scr = Screen() # input js = Joystick() # # # # # # RUN # run until a minute has passed t0 = timer.get_time() t1 = timer.get_time() text = "Test the joystick!" while t1 - t0 < 60000: # get joystick input event, value, t1 = js.get_joyinput(timeout=10) # update text
t = threading.Thread(target=run_server) t.daemon = True eye_lock = threading.Lock() eye_event = threading.Event() eye_thread = threading.Thread(name='eye_tracking', target=eye_tracker, args=( eye_lock, eye_event, )) eye_thread.daemon = True disp = Display(disptype='psychopy') scr = Screen(disptype='psychopy') disp.close() tracker = EyeTracker(disp, trackertype='eyetribe') EYE_DATA = Queue.Queue(500) try: t.start() eye_thread.start() with Listener(on_click=on_click) as listener: listener.join() # listener.start()
def __init__(self, display, ip='127.0.0.1', sendport=4444, receiveport=5555, logfile=settings.LOGFILE, eventdetection=settings.EVENTDETECTION, saccade_velocity_threshold=35, saccade_acceleration_threshold=9500, **args): """Initializes the SMItracker object arguments display -- a pygaze.display.Display instance keyword arguments ip -- internal ip address for iViewX (default = '127.0.0.1') sendport -- port number for iViewX sending (default = 4444) receiveport -- port number for iViewX receiving (default = 5555) logfile -- logfile name (string value); note that this is the name for the SMI logfile, NOT the .idf file (default = LOGFILE) """ # try to copy docstrings (but ignore it if it fails, as we do # not need it for actual functioning of the code) try: copy_docstr(BaseEyeTracker, SMITracker) except: # we're not even going to show a warning, since the copied # docstring is useful for code editors; these load the docs # in a non-verbose manner, so warning messages would be lost pass # object properties self.disp = display self.screen = Screen() self.dispsize = settings.DISPSIZE # display size in pixels self.screensize = settings.SCREENSIZE # display size in cm self.kb = Keyboard(keylist=['space', 'escape', 'q'], timeout=1) self.errorbeep = Sound(osc='saw', freq=100, length=100) # output file properties self.outputfile = logfile self.description = "experiment" # TODO: EXPERIMENT NAME self.participant = "participant" # TODO: PP NAME # eye tracker properties self.connected = False self.recording = False self.eye_used = 0 # 0=left, 1=right, 2=binocular self.left_eye = 0 self.right_eye = 1 self.binocular = 2 self.errdist = 2 # degrees; maximal error for drift correction self.maxtries = 100 # number of samples obtained before giving up (for obtaining accuracy and tracker distance information, as well as starting or stopping recording) self.prevsample = (-1, -1) self.prevps = -1 # event detection properties self.fixtresh = 1.5 # degrees; maximal distance from fixation start (if gaze wanders beyond this, fixation has stopped) self.fixtimetresh = 100 # milliseconds; amount of time gaze has to linger within self.fixtresh to be marked as a fixation self.spdtresh = saccade_velocity_threshold # degrees per second; saccade velocity threshold self.accthresh = saccade_acceleration_threshold # degrees per second**2; saccade acceleration threshold self.eventdetection = eventdetection self.set_detection_type(self.eventdetection) self.weightdist = 10 # weighted distance, used for determining whether a movement is due to measurement error (1 is ok, higher is more conservative and will result in only larger saccades to be detected) # set logger res = iViewXAPI.iV_SetLogger(c_int(1), c_char_p(logfile + '_SMILOG.txt')) if res != 1: err = errorstring(res) raise Exception( "Error in libsmi.SMItracker.__init__: failed to set logger; %s" % err) # first logger argument is for logging type (I'm guessing these are decimal bit codes) # LOG status bitcode # 1 = LOG_LEVEL_BUG 00001 # 2 = LOG_LEVEL_iV_FCT 00010 # 4 = LOG_LEVEL_ETCOM 00100 # 8 = LOG_LEVEL_ALL 01000 # 16 = LOG_LEVEL_IV_COMMAND 10000 # these can be used together, using a bitwise or, e.g.: 1|2|4 (bitcode 00111) # connect to iViewX res = iViewXAPI.iV_Connect(c_char_p(ip), c_int(sendport), c_char_p(ip), c_int(receiveport)) if res == 1: res = iViewXAPI.iV_GetSystemInfo(byref(systemData)) self.samplerate = systemData.samplerate self.sampletime = 1000.0 / self.samplerate if res != 1: err = errorstring(res) raise Exception( "Error in libsmi.SMItracker.__init__: failed to get system information; %s" % err) # handle connection errors else: self.connected = False err = errorstring(res) raise Exception( "Error in libsmi.SMItracker.__init__: establishing connection failed; %s" % err) # initiation report self.log("pygaze initiation report start") self.log("experiment: %s" % self.description) self.log("participant: %s" % self.participant) self.log("display resolution: %sx%s" % (self.dispsize[0], self.dispsize[1])) self.log("display size in cm: %sx%s" % (self.screensize[0], self.screensize[1])) self.log("samplerate: %s Hz" % self.samplerate) self.log("sampletime: %s ms" % self.sampletime) self.log("fixation threshold: %s degrees" % self.fixtresh) self.log("speed threshold: %s degrees/second" % self.spdtresh) self.log("acceleration threshold: %s degrees/second**2" % self.accthresh) self.log("pygaze initiation report end")
def __init__(self, display, logfile=settings.LOGFILE, eventdetection=settings.EVENTDETECTION, saccade_velocity_threshold=35, saccade_acceleration_threshold=9500, blink_threshold=settings.BLINKTHRESH, **args): """Initializes a TobiiProTracker instance arguments display -- a pygaze.display.Display instance keyword arguments None """ self.gaze = [] self.disp = display # initialize a screen self.screen = Screen() # initialize keyboard self.kb = Keyboard(keylist=['space', 'escape', 'q','enter'], timeout=1) self.recording = False self.screendist = settings.SCREENDIST if hasattr(settings, 'TRACKERSERIALNUMBER'): # Search for a specific eye tracker self.eyetrackers = [t for t in tr.find_all_eyetrackers() if t.serial_number == settings.TRACKERSERIALNUMBER] else: # Search for all eye trackers (The first one found will be selected) self.eyetrackers = tr.find_all_eyetrackers() if self.eyetrackers: self.eyetracker = self.eyetrackers[0] else: print("WARNING! libtobii.TobiiProTracker.__init__: no eye trackers found!") self.LEFT_EYE = 0 self.RIGHT_EYE = 1 self.BINOCULAR = 2 self.eye_used = 0 # 0=left, 1=right, 2=binocular # calibration and validation points lb = 0.1 # left bound xc = 0.5 # horizontal center rb = 0.9 # right bound ub = 0.1 # upper bound yc = 0.5 # vertical center bb = 0.9 # bottom bound self.points_to_calibrate = [self._norm_2_px(p) for p in [(lb, ub), (xc,ub), (rb, ub), (lb,yc), (xc, yc),(rb,yc), (lb, bb),(xc,bb),(rb, bb)]] # event detection properties self.fixtresh = 1.5 # degrees; maximal distance from fixation start (if gaze wanders beyond this, fixation has stopped) self.fixtimetresh = 100 # milliseconds; amount of time gaze has to linger within self.fixtresh to be marked as a fixation self.spdtresh = saccade_velocity_threshold # degrees per second; saccade velocity threshold self.accthresh = saccade_acceleration_threshold # degrees per second**2; saccade acceleration threshold self.blinkthresh = blink_threshold # milliseconds; blink detection threshold used in PyGaze method self.eventdetection = eventdetection self.weightdist = 10 # weighted distance, used for determining whether a movement is due to measurement error (1 is ok, higher is more conservative and will result in only larger saccades to be detected) self.screensize = settings.SCREENSIZE # display size in cm self.pixpercm = (self.disp.dispsize[0] / float(self.screensize[0]) + self.disp.dispsize[1] / float(self.screensize[1])) / 2.0 self.errdist = 2 # degrees; maximal error for drift correction self.pxerrdist = self._deg2pix(self.screendist, self.errdist, self.pixpercm) self.event_data = [] self.t0 = None self._write_enabled = True self.datafile = open("{0}_TOBII_output.tsv".format(logfile), 'w') # initiation report self.datafile.write("pygaze initiation report start\n") self.datafile.write("display resolution: %sx%s\n" % (self.disp.dispsize[0], self.disp.dispsize[1])) self.datafile.write("display size in cm: %sx%s\n" % (self.screensize[0], self.screensize[1])) self.datafile.write("fixation threshold: %s degrees\n" % self.fixtresh) self.datafile.write("speed threshold: %s degrees/second\n" % self.spdtresh) self.datafile.write("acceleration threshold: %s degrees/second**2\n" % self.accthresh) self.datafile.write("pygaze initiation report end\n")
# PyGaze from constants import * from pygaze.display import Display from pygaze.screen import Screen from pygaze.eyetracker import EyeTracker from pygaze.keyboard import Keyboard from pygaze.libtime import clock # # # # # # SETUP # visuals disp = Display() scr = Screen() # input tracker = EyeTracker(disp) kb = Keyboard(keylist=None, timeout=None) # calibrate tracker.calibrate() # starting screen scr.clear() scr.draw_text(text="Press Space to start") disp.fill(scr) disp.show() kb.get_key(keylist=['space'], timeout=None, flush=True)
def __init__(self, display, address='192.168.71.50', udpport=49152, logfile=settings.LOGFILE, eventdetection=settings.EVENTDETECTION, saccade_velocity_threshold=35, saccade_acceleration_threshold=9500, blink_threshold=settings.BLINKTHRESH, **args): """Initializes a TobiiProGlassesTracker instance arguments display -- a pygaze.display.Display instance keyword arguments address -- internal ipv4/ipv6 address for Tobii Pro Glasses 2 (default = '192.168.71.50', for IpV6 address use square brackets [fe80::xxxx:xxxx:xxxx:xxxx]) udpport -- UDP port number for Tobii Pro Glasses data streaming (default = 49152) """ # try to copy docstrings (but ignore it if it fails, as we do # not need it for actual functioning of the code) try: copy_docstr(BaseEyeTracker, TobiiProGlassesTracker) except: # we're not even going to show a warning, since the copied # docstring is useful for code editors; these load the docs # in a non-verbose manner, so warning messages would be lost pass # object properties self.disp = display self.screen = Screen() self.dispsize = settings.DISPSIZE # display size in pixels self.screensize = settings.SCREENSIZE # display size in cm self.screendist = settings.SCREENDIST # distance between participant and screen in cm self.pixpercm = (self.dispsize[0] / float(self.screensize[0]) + self.dispsize[1] / float(self.screensize[1])) / 2.0 self.kb = Keyboard(keylist=['space', 'escape', 'q'], timeout=1) self.errorbeep = Sound(osc='saw', freq=100, length=100) # output file properties self.outputfile = logfile self.description = "experiment" # TODO: EXPERIMENT NAME self.participant = "participant" # TODO: PP NAME # eye tracker properties self.eye_used = 0 # 0=left, 1=right, 2=binocular self.left_eye = 0 self.right_eye = 1 self.binocular = 2 self.maxtries = 100 # number of samples obtained before giving up (for obtaining accuracy and tracker distance information, as well as starting or stopping recording) self.prevsample = (-1, -1) # validation properties self.nvalsamples = 1000 # samples for one validation point # event detection properties self.fixtresh = 1.5 # degrees; maximal distance from fixation start (if gaze wanders beyond this, fixation has stopped) self.fixtimetresh = 100 # milliseconds; amount of time gaze has to linger within self.fixtresh to be marked as a fixation self.spdtresh = saccade_velocity_threshold # degrees per second; saccade velocity threshold self.accthresh = saccade_acceleration_threshold # degrees per second**2; saccade acceleration threshold self.blinkthresh = blink_threshold # milliseconds; blink detection threshold used in PyGaze method self.eventdetection = eventdetection self.set_detection_type(self.eventdetection) self.weightdist = 10 # weighted distance, used for determining whether a movement is due to measurement error (1 is ok, higher is more conservative and will result in only larger saccades to be detected) self.tobiiglasses = TobiiGlassesController(udpport, address) self.triggers_values = {} self.logging = False self.current_recording_id = None self.current_participant_id = None self.current_project_id = None
def __init__(self, display, resolution=settings.DISPSIZE, data_file=settings.LOGFILENAME + ".edf", fg_color=settings.FGC, bg_color=settings.BGC, eventdetection=settings.EVENTDETECTION, saccade_velocity_threshold=35, saccade_acceleration_threshold=9500, blink_threshold=settings.BLINKTHRESH, force_drift_correct=True, pupil_size_mode=settings.EYELINKPUPILSIZEMODE, **args): """See pygaze._eyetracker.baseeyetracker.BaseEyeTracker""" # try to import copy docstring (but ignore it if it fails, as we do # not need it for actual functioning of the code) try: copy_docstr(BaseEyeTracker, libeyelink) except: # we're not even going to show a warning, since the copied # docstring is useful for code editors; these load the docs # in a non-verbose manner, so warning messages would be lost pass global _eyelink # Make sure that we have a valid data file. The local_data_file may # contain a folder. The eyelink_data_file is only a basename, i.e. # without folder. The eyelink_data_file must be at most eight characters # and end with a `.edf` extension. self.local_data_file = data_file self.eyelink_data_file = os.path.basename(data_file) stem, ext = os.path.splitext(self.eyelink_data_file) if len(stem) > 8 or ext.lower() != '.edf': raise Exception( "The EyeLink cannot handle filenames longer than eight " "characters (excluding '.edf' extension).") # properties self.display = display self.fontsize = 18 self.scr = Screen(disptype=settings.DISPTYPE, mousevisible=False) self.kb = Keyboard(keylist=["escape", "q"], timeout=1) self.resolution = resolution self.recording = False self.saccade_velocity_treshold = saccade_velocity_threshold self.saccade_acceleration_treshold = saccade_acceleration_threshold self.blink_threshold = blink_threshold self.eye_used = None self.left_eye = 0 self.right_eye = 1 self.binocular = 2 self.pupil_size_mode = pupil_size_mode self.prevsample = (-1, -1) self.prevps = -1 # event detection properties # degrees; maximal distance from fixation start (if gaze wanders beyond # this, fixation has stopped) self.fixtresh = 1.5 # milliseconds; amount of time gaze has to linger within self.fixtresh # to be marked as a fixation self.fixtimetresh = 100 # degrees per second; saccade velocity threshold self.spdtresh = self.saccade_velocity_treshold # degrees per second**2; saccade acceleration threshold self.accthresh = self.saccade_acceleration_treshold self.set_detection_type(eventdetection) # weighted distance, used for determining whether a movement is due to # measurement error (1 is ok, higher is more conservative and will # result in only larger saccades to be detected) self.weightdist = 10 # distance between participant and screen in cm self.screendist = settings.SCREENDIST # distance between participant and screen in cm self.screensize = settings.SCREENSIZE self.pixpercm = (self.resolution[0]/float(self.screensize[0]) + \ self.resolution[1]/float(self.screensize[1])) / 2.0 # only initialize eyelink once if _eyelink == None: try: _eyelink = pylink.EyeLink() except: raise Exception( "Error in libeyelink.libeyelink.__init__(): Failed to " "connect to the tracker!") # determine software version of tracker self.tracker_software_ver = 0 self.eyelink_ver = pylink.getEYELINK().getTrackerVersion() if self.eyelink_ver == 3: tvstr = pylink.getEYELINK().getTrackerVersionString() vindex = tvstr.find("EYELINK CL") self.tracker_software_ver = int(float(tvstr[(vindex + \ len("EYELINK CL")):].strip())) if self.eyelink_ver == 1: self.eyelink_model = 'EyeLink I' elif self.eyelink_ver == 2: self.eyelink_model = 'EyeLink II' elif self.eyelink_ver == 3: self.eyelink_model = 'EyeLink 1000' else: self.eyelink_model = 'EyeLink (model unknown)' # Open graphics self.eyelink_graphics = EyelinkGraphics(self, _eyelink) pylink.openGraphicsEx(self.eyelink_graphics) # Optionally force drift correction. For some reason this must be done # as (one of) the first things, otherwise a segmentation fault occurs. if force_drift_correct: try: self.send_command('driftcorrect_cr_disable = OFF') except: print('Failed to force drift correction (EyeLink 1000 only)') # Set pupil-size mode if self.pupil_size_mode == 'area': pylink.getEYELINK().setPupilSizeDiameter(False) elif self.pupil_size_mode == 'diameter': pylink.getEYELINK().setPupilSizeDiameter(True) else: raise Exception( "pupil_size_mode should be 'area' or 'diameter', not %s" \ % self.pupil_size_mode) pylink.getEYELINK().openDataFile(self.eyelink_data_file) pylink.flushGetkeyQueue() pylink.getEYELINK().setOfflineMode() # notify eyelink of display resolution self.send_command("screen_pixel_coords = 0 0 %d %d" % \ (self.resolution[0], self.resolution[1])) # get some configuration stuff if self.eyelink_ver >= 2: self.send_command("select_parser_configuration 0") if self.eyelink_ver == 2: # turn off scenelink camera stuff self.send_command("scene_camera_gazemap = NO") # set EDF file contents (this specifies which data is written to the EDF # file) self.send_command( "file_event_filter = LEFT,RIGHT,FIXATION,SACCADE,BLINK,MESSAGE,BUTTON" ) if self.tracker_software_ver >= 4: self.send_command( "file_sample_data = LEFT,RIGHT,GAZE,AREA,GAZERES,STATUS,HTARGET" ) else: self.send_command( "file_sample_data = LEFT,RIGHT,GAZE,AREA,GAZERES,STATUS") # set link data (this specifies which data is sent through the link and # thus can be used in gaze contingent displays) self.send_command( "link_event_filter = LEFT,RIGHT,FIXATION,SACCADE,BLINK,BUTTON") if self.tracker_software_ver >= 4: self.send_command( "link_sample_data = LEFT,RIGHT,GAZE,GAZERES,AREA,STATUS,HTARGET" ) else: self.send_command( "link_sample_data = LEFT,RIGHT,GAZE,GAZERES,AREA,STATUS") # not quite sure what this means (according to Sebastiaan Mathot, it # might be the button that is used to end drift correction?) self.send_command("button_function 5 'accept_target_fixation'") if not self.connected(): raise Exception( "Error in libeyelink.libeyelink.__init__(): Failed to connect " "to the eyetracker!")
def __init__(self, libeyelink, tracker): """ Constructor. Arguments: libeyelink -- A libeyelink object. tracker -- An tracker object as returned by pylink.EyeLink(). """ pylink.EyeLinkCustomDisplay.__init__(self) # objects self.libeyelink = libeyelink self.display = libeyelink.display self.screen = Screen(disptype=settings.DISPTYPE, mousevisible=False) self.kb = Keyboard(keylist=None, timeout=1) self.mouse = Mouse(timeout=1) # If we are using a DISPTYPE that cannot be used directly, we have to # save the camera image to a temporary file on each frame. #if DISPTYPE not in ('pygame', 'psychopy'): import tempfile import os self.tmp_file = os.path.join(tempfile.gettempdir(), "__eyelink__.jpg") # drawing properties self.xc = self.display.dispsize[0] / 2 self.yc = self.display.dispsize[1] / 2 self.extra_info = True self.ld = 40 # line distance self.fontsize = libeyelink.fontsize self.title = "" self.display_open = True self.draw_menu_screen() # A crosshair is drawn onto the eye image. This should be scaled in # pylink 1.1.0.5 (tested on Python 2.7) but not on pylink 1.11.0.0 # (tested on Python 3.6). I'm not sure when this change happened, so # it's quite likely we'll have to update the minor version used here. pl_version = pylink.__version__.split(".") if int(pl_version[0]) > 1 or int(pl_version[1]) >= 11: self.scale_lines_in_eye_image = False else: self.scale_lines_in_eye_image = True # Beeps self.__target_beep__ = Sound(osc="sine", freq=440, length=50, attack=0, decay=0, soundfile=None) self.__target_beep__done__ = Sound(osc="sine", freq=880, length=200, attack=0, decay=0, soundfile=None) self.__target_beep__error__ = Sound(osc="sine", freq=220, length=200, attack=0, decay=0, soundfile=None) # Colors self.color = { pylink.CR_HAIR_COLOR: pygame.Color("white"), pylink.PUPIL_HAIR_COLOR: pygame.Color("white"), pylink.PUPIL_BOX_COLOR: pygame.Color("green"), pylink.SEARCH_LIMIT_BOX_COLOR: pygame.Color("red"), pylink.MOUSE_CURSOR_COLOR: pygame.Color("red"), 'font': pygame.Color("white"), } # Font pygame.font.init() self.font = pygame.font.SysFont("Courier New", 11) # further properties self.state = None self.pal = None self.size = (0, 0) self.set_tracker(tracker) self.last_mouse_state = -1 self.bit64 = "64bit" in platform.architecture() self.imagebuffer = self.new_array()