class EyeLinkCoreGraphicsIOHubPsychopy(EyeLinkCustomDisplay): IOHUB_HEARTBEAT_INTERVAL=0.050 # seconds between forced run through of # micro threads, since one is blocking # on camera setup. IOHUB2PYLINK_KB_MAPPING={ KeyboardConstants._virtualKeyCodes.VK_F1: pylink.F1_KEY, KeyboardConstants._virtualKeyCodes.VK_F2: pylink.F2_KEY, KeyboardConstants._virtualKeyCodes.VK_F3: pylink.F3_KEY, KeyboardConstants._virtualKeyCodes.VK_F4: pylink.F4_KEY, KeyboardConstants._virtualKeyCodes.VK_F5: pylink.F5_KEY, KeyboardConstants._virtualKeyCodes.VK_F6: pylink.F6_KEY, KeyboardConstants._virtualKeyCodes.VK_F7: pylink.F7_KEY, KeyboardConstants._virtualKeyCodes.VK_F8: pylink.F8_KEY, KeyboardConstants._virtualKeyCodes.VK_F9: pylink.F9_KEY, KeyboardConstants._virtualKeyCodes.VK_F10: pylink.F10_KEY, KeyboardConstants._virtualKeyCodes.VK_PAGE_UP: pylink.PAGE_UP, KeyboardConstants._virtualKeyCodes.VK_PAGE_UP: pylink.PAGE_DOWN, KeyboardConstants._virtualKeyCodes.VK_UP: pylink.CURS_UP, KeyboardConstants._virtualKeyCodes.VK_DOWN: pylink.CURS_DOWN, KeyboardConstants._virtualKeyCodes.VK_LEFT: pylink.CURS_LEFT, KeyboardConstants._virtualKeyCodes.VK_RIGHT: pylink.CURS_RIGHT, KeyboardConstants._asciiKeyCodes.BACKSPACE: '\b', KeyboardConstants._asciiKeyCodes.RETURN: pylink.ENTER_KEY, KeyboardConstants._asciiKeyCodes.ESCAPE: pylink.ESC_KEY, KeyboardConstants._virtualKeyCodes.VK_F10: pylink.F10_KEY, KeyboardConstants._virtualKeyCodes.VK_F10: pylink.F10_KEY, } # SOUND_MAPPINGS={ # -1 : 'wav/error.wav', #cal error beep # -2 : 'wav/error.wav', # DC error beep # 0 : 'wav/qbeep.wav', # cal. good beep # 1: 'wav/type.wav', # cal target beep # 2: 'wav/qbeep.wav', # DC good beep # 3 : 'wav/type.wav' # dc target beep # #'wav/error.wav': None, # file name to Nsound buffer mapping # #'wav/qbeep.wav': None, # file name to Nsound buffer mapping # #'wav/type.wav': None, # file name to Nsound buffer mapping # } WINDOW_BACKGROUND_COLOR=(128,128,128) CALIBRATION_POINT_OUTER_RADIUS=15.0,15.0 CALIBRATION_POINT_OUTER_EDGE_COUNT=64 CALIBRATION_POINT_OUTER_COLOR=(255,255,255) CALIBRATION_POINT_INNER_RADIUS=3.0,3.0 CALIBRATION_POINT_INNER_EDGE_COUNT=32 CALIBRATION_POINT_INNER_COLOR=(25,25,25) def __init__(self, eyetrackerInterface, targetForegroundColor=None, targetBackgroundColor=None, screenColor=None, targetOuterDiameter=None, targetInnerDiameter=None, dc_sounds=["","",""], cal_sounds=["","",""]): EyeLinkCustomDisplay.__init__(self) self._eyetrackerinterface=eyetrackerInterface self.tracker = eyetrackerInterface._eyelink self._ioKeyboard=None self._ioMouse=None self.img_size=None self.imagebuffer = array.array('I') self.pal = None self.screenSize = self._eyetrackerinterface._display_device.getPixelResolution() self.width=self.screenSize[0] self.height=self.screenSize[1] self.keys=[] self.pos = [] self.state = 0 if sys.byteorder == 'little': self.byteorder = 1 else: self.byteorder = 0 EyeLinkCoreGraphicsIOHubPsychopy.CALIBRATION_POINT_OUTER_COLOR=targetForegroundColor EyeLinkCoreGraphicsIOHubPsychopy.CALIBRATION_POINT_INNER_COLOR=targetBackgroundColor EyeLinkCoreGraphicsIOHubPsychopy.WINDOW_BACKGROUND_COLOR=screenColor EyeLinkCoreGraphicsIOHubPsychopy.CALIBRATION_POINT_OUTER_RADIUS=targetOuterDiameter/2.0,targetOuterDiameter/2.0 EyeLinkCoreGraphicsIOHubPsychopy.CALIBRATION_POINT_INNER_RADIUS=targetInnerDiameter/2.0,targetInnerDiameter/2.0 self.tracker.setOfflineMode(); self.window = FullScreenWindow(self._eyetrackerinterface._display_device) self.window.setColor(color=self.WINDOW_BACKGROUND_COLOR,colorSpace='rgb255') self.window.flip(clearBuffer=True) self._createStim() self._registerEventMonitors() self._ioMouse.setSystemCursorVisibility(False) self._lastMsgPumpTime=currentSecTime() self.clearAllEventBuffers() def clearAllEventBuffers(self): pylink.flushGetkeyQueue(); self.tracker.resetData() self._iohub_server.eventBuffer.clear() for d in self._iohub_server.devices: d.clearEvents() def _registerEventMonitors(self): self._iohub_server=self._eyetrackerinterface._iohub_server if self._iohub_server: for dev in self._iohub_server.devices: #ioHub.print2err("dev: ",dev.__class__.__name__) if dev.__class__.__name__ == 'Keyboard': kbDevice=dev elif dev.__class__.__name__ == 'Mouse': mouseDevice=dev if kbDevice: eventIDs=[] for event_class_name in kbDevice.__class__.EVENT_CLASS_NAMES: eventIDs.append(getattr(EventConstants,convertCamelToSnake(event_class_name[:-5],False))) self._ioKeyboard=kbDevice self._ioKeyboard._addEventListener(self,eventIDs) else: ioHub.print2err("Warning: elCG could not connect to Keyboard device for events.") if mouseDevice: eventIDs=[] for event_class_name in mouseDevice.__class__.EVENT_CLASS_NAMES: eventIDs.append(getattr(EventConstants,convertCamelToSnake(event_class_name[:-5],False))) self._ioMouse=mouseDevice self._ioMouse._addEventListener(self,eventIDs) else: ioHub.print2err("Warning: elCG could not connect to Mouse device for events.") def _unregisterEventMonitors(self): # ioHub.print2err('_unregisterEventMonitors') if self._ioKeyboard: self._ioKeyboard._removeEventListener(self) if self._ioMouse: self._ioMouse._removeEventListener(self) def _handleEvent(self,ioe): #ioHub.print2err("Got Event: ",ioe) event_type_index=DeviceEvent.EVENT_TYPE_ID_INDEX if ioe[event_type_index] == EventConstants.KEYBOARD_PRESS: #ioHub.print2err('Should handle keyboard event for: ', ioe[-4], ' key_id: ',ioe[-5],' key_mods: ',ioe[-2]) #key pressed # ioHub.print2err('** KEY: ', ioe[-4]," ,key_id: ",ioe[-5]," ,ascii_code: ",ioe[-6]," ,scan_code: ",ioe[-7]) self.translate_key_message((ioe[-5],ioe[-2])) elif ioe[event_type_index] == EventConstants.MOUSE_BUTTON_PRESS: # ioHub.print2err('Should handle mouse pressed event for button id: ', ioe[-7]) self.state=1 elif ioe[event_type_index] == EventConstants.MOUSE_BUTTON_RELEASE: # ioHub.print2err('Should handle mouse release event for button id: ', ioe[-7]) self.state=0 elif ioe[event_type_index] == EventConstants.MOUSE_MOVE: self.pos=self._ioMouse.getPosition() # def _printKeyMapping(self): # ioHub.print2err("===========================================") # for iokey, pylkey in self.IOHUB2PYLINK_KB_MAPPING.iteritems(): # ioHub.print2err(iokey,' : ',pylkey) # ioHub.print2err("===========================================") def translate_key_message(self,event): key = 0 mod = 0 #ioHub.print2err('translate_key_message:' ,event) if len(event) >0 : key = event[0] self.keys.append(pylink.KeyInput(key,mod)) return key def get_input_key(self): #keep the psychopy window happy ;) if currentSecTime()-self._lastMsgPumpTime>self.IOHUB_HEARTBEAT_INTERVAL: # try to keep ioHub, being blocked. ;( if self._iohub_server: for dm in self._iohub_server.deviceMonitors: dm.device._poll() self._iohub_server._processDeviceEventIteration() self._lastMsgPumpTime=currentSecTime() if len(self.keys) > 0: k= self.keys self.keys=[] #ioHub.print2err('KEY get_input_key: ',k) return k else: return None def _createStim(self): class StimSet(object): def __setattr__(self, item, value): if item in self.__dict__: i=self.__dict__['_stimNameList'].find(item) self.__dict__['_stimValueList'][i]=value else: if '_stimNameList' not in self.__dict__: self.__dict__['_stimNameList']=[] self.__dict__['_stimValueList']=[] self.__dict__['_stimNameList'].append(item) self.__dict__['_stimValueList'].append(value) self.__dict__[item]=value def updateStim(self,name,**kwargs): astim=getattr(self,name) if isinstance(astim,OrderedDict): for stimpart in astim.itervalues(): for argName,argValue in kwargs.iteritems(): a=getattr(stimpart,argName) if callable(a): a(argValue) else: setattr(stimpart,argName,argValue) else: for argName,argValue in kwargs.iteritems(): a=getattr(astim,argName) if callable(a): a(argValue) else: setattr(astim,argName,argValue) def draw(self): for s in self._stimValueList: if isinstance(s,OrderedDict): for stimpart in s.itervalues(): stimpart.draw() else: s.draw() self.calStim=StimSet() self.calStim.calibrationPoint=OrderedDict() self.calStim.calibrationPoint['OUTER'] = visual.Circle(self.window,pos=(0,0), lineWidth=1.0, lineColor=self.CALIBRATION_POINT_OUTER_COLOR, lineColorSpace='rgb255', fillColor=self.CALIBRATION_POINT_OUTER_COLOR, fillColorSpace='rgb255', radius=self.CALIBRATION_POINT_OUTER_RADIUS, name='CP_OUTER', units='pix',opacity=1.0, interpolate=False) self.calStim.calibrationPoint['INNER'] = visual.Circle(self.window, pos=(0,0),lineWidth=1.0,lineColor=self.CALIBRATION_POINT_INNER_COLOR, lineColorSpace='rgb255', fillColor=self.CALIBRATION_POINT_INNER_COLOR, fillColorSpace='rgb255', radius=self.CALIBRATION_POINT_INNER_RADIUS, name='CP_INNER',units='pix',opacity=1.0, interpolate=False) self.imageStim=StimSet() self.imageStim.imageTitle = visual.TextStim(self.window, text = "EL CAL", pos=(0,0), units='pix', alignHoriz='center') def setup_cal_display(self): #ioHub.print2err('setup_cal_display entered') self.window.flip(clearBuffer=True) #ioHub.print2err('setup_cal_display exiting') def exit_cal_display(self): #ioHub.print2err('exit_cal_display entered') self.window.flip(clearBuffer=True) #ioHub.print2err('exit_cal_display exiting') def record_abort_hide(self): pass def clear_cal_display(self): self.window.flip(clearBuffer=True) def erase_cal_target(self): self.window.flip(clearBuffer=True) def draw_cal_target(self, x, y): self.calStim.updateStim('calibrationPoint',setPos=(x,y)) self.calStim.draw() self.window.flip(clearBuffer=True) def play_beep(self, beepid): pass #if (Nsound is not None) and (beepid in self.SOUND_MAPPINGS): # sname=self.SOUND_MAPPINGS[beepid] # audiobuffer,audioplayer=self.SOUND_MAPPINGS[sname] # audiobuffer >> audioplayer #elif Nsound is None: # pass #else: # ioHub.print2err('play_beep ERROR: Unsupported beep id: %d.'%(beepid)) def exit_image_display(self): #ioHub.print2err('exit_image_display entered') self.window.flip(clearBuffer=True) #ioHub.print2err('exit_image_display exiting') def alert_printf(self,msg): ioHub.print2err('**************************************************') ioHub.print2err('EYELINK CG ERROR: %s'%(msg)) ioHub.print2err('**************************************************') def image_title(self, text): #ioHub.print2err('image_title entered') self.imageStim.updateStim('imageTitle',setText=text) self.imageStim.draw() self.window.flip(clearBuffer=True) #ioHub.print2err('image_title exiting') ############# From Pyglet Custom Graphics ##################################### # ## NOT YET CONVERTED # # # ############################################################################### # # # pyglet impl. def get_mouse_state(self): ioHub.print2err('get_mouse_state entered') if len(self.pos) > 0 : l = (int)(self.width*0.5-self.width*0.5*0.75) r = (int)(self.width*0.5+self.width*0.5*0.75) b = (int)(self.height*0.5-self.height*0.5*0.75) t = (int)(self.height*0.5+self.height*0.5*0.75) mx, my = 0,0 if self.pos[0]<l: mx = l elif self.pos[0] >r: mx = r else: mx = self.pos[0] if self.pos[1]<b: my = b elif self.pos[1]>t: my = t else: my = self.pos[1] mx = (int)((mx-l)*self.img_size[0]//(r-l)) my = self.img_size[1] - (int)((my-b)*self.img_size[1]//(t-b)) ioHub.print2err('get_mouse_state exiting') return ((mx, my),self.state) else: ioHub.print2err('get_mouse_state exiting') return((0,0), 0) ############################################################################### # # # PYGLET IMP. def setup_image_display(self, width, height): ioHub.print2err('setup_image_display entered') self.img_size = (width,height) self.window.clearBuffer() self.window.flip(clearBuffer=True) ioHub.print2err('setup_image_display exiting') ############################################################################### # # PYGLET Imp. def draw_image_line(self, width, line, totlines,buff): pass # ioHub.print2err('draw_image_line entered') # i =0 # while i <width: # if buff[i]>=len(self.pal): # buff[i]=len(self.pal)-1 # self.imagebuffer.append(self.pal[buff[i]&0x000000FF]) # i = i+1 # if line == totlines: # #asp = ((float)(self.size[1]))/((float)(self.size[0])) # asp = 1 # r = (float)(self.width*0.5-self.width*0.5*0.75) # l = (float)(self.width*0.5+self.width*0.5*0.75) # t = (float)(self.height*0.5+self.height*0.5*asp*0.75) # b = (float)(self.height*0.5-self.height*0.5*asp*0.75) # # self.window.clearBuffer() # # tx = (int)(self.width*0.5) # ty = b - 30 # self.stim.drawStim('imageTitle',{'setPos':(tx,ty)}) # # self.draw_cross_hair() # glEnable(GL_TEXTURE_RECTANGLE_ARB) # glBindTexture(GL_TEXTURE_RECTANGLE_ARB, self.texid.value) # glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_LINEAR) # glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_LINEAR) # glTexEnvi( GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE, GL_REPLACE ) # glTexImage2D( GL_TEXTURE_RECTANGLE_ARB, 0,GL_RGBA8, width, totlines, 0, GL_RGBA, GL_UNSIGNED_BYTE, self.imagebuffer.tostring()) # # glBegin(GL_QUADS) # glTexCoord2i(0, 0) # glVertex2f(r,t) # glTexCoord2i(0, self.img_size[1]) # glVertex2f(r, b) # glTexCoord2i(self.img_size[0],self.img_size[1]) # glVertex2f(l, b) # glTexCoord2i(self.img_size[1],0) # glVertex2f(l, t) # glEnd() # glDisable(GL_TEXTURE_RECTANGLE_ARB) # self.draw_cross_hair() # # self.window.flip(clearBuffer=True) # # self.imagebuffer = array.array('I') # ioHub.print2err('draw_image_line exiting') ############################################################################### # # Pyglet impl. def draw_line(self,x1,y1,x2,y2,colorindex): pass # # ioHub.print2err('draw_line entered') # if colorindex == pylink.CR_HAIR_COLOR: color = (1.0,1.0,1.0,1.0) # elif colorindex == pylink.PUPIL_HAIR_COLOR: color = (1.0,1.0,1.0,1.0) # elif colorindex == pylink.PUPIL_BOX_COLOR: color = (0.0,1.0,0.0,1.0) # elif colorindex == pylink.SEARCH_LIMIT_BOX_COLOR: color = (1.0,0.0,0.0,1.0) # elif colorindex == pylink.MOUSE_CURSOR_COLOR: color = (1.0,0.0,0.0,1.0) # else: color =(0.0,0.0,0.0,0.0) # # #asp = ((float)(self.size[1]))/((float)(self.size[0])) # asp = 1 # r = (float)(self.width*0.5-self.width*0.5*0.75) # l = (float)(self.width*0.5+self.width*0.5*0.75) # t = (float)(self.height*0.5+self.height*0.5*asp*0.75) # b = (float)(self.height*0.5-self.height*0.5*asp*0.75) # # x11= float(float(x1)*(l-r)/float(self.img_size[0]) + r) # x22= float(float(x2)*(l-r)/float(self.img_size[0]) + r) # y11= float(float(y1)*(b-t)/float(self.img_size[1]) + t) # y22= float(float(y2)*(b-t)/float(self.img_size[1]) + t) # ## glBegin(GL_LINES) ## glColor4f(color[0],color[1],color[2],color[3] ) ## glVertex2f(x11,y11) ## glVertex2f(x22,y22) ## glEnd() # ioHub.print2err('draw_line exiting') # ############################################################################### # # Pyglet Implementation def draw_lozenge(self,x,y,width,height,colorindex): pass # ioHub.print2err('draw_lozenge entered') # if colorindex == pylink.CR_HAIR_COLOR: color = (1.0,1.0,1.0,1.0) # elif colorindex == pylink.PUPIL_HAIR_COLOR: color = (1.0,1.0,1.0,1.0) # elif colorindex == pylink.PUPIL_BOX_COLOR: color = (0.0,1.0,0.0,1.0) # elif colorindex == pylink.SEARCH_LIMIT_BOX_COLOR: color = (1.0,0.0,0.0,1.0) # elif colorindex == pylink.MOUSE_CURSOR_COLOR: color = (1.0,0.0,0.0,1.0) # else: color =(0.0,0.0,0.0,0.0) # # width=int((float(width)/float(self.img_size[0]))*self.img_size[0]) # height=int((float(height)/float(self.img_size[1]))*self.img_size[1]) # # #asp = ((float)(self.size[1]))/((float)(self.size[0])) # asp = 1 # r = (float)(self.width*0.5-self.width*0.5*0.75) # l = (float)(self.width*0.5+self.width*0.5*0.75) # t = (float)(self.height*0.5+self.height*0.5*asp*0.75) # b = (float)(self.height*0.5-self.height*0.5*asp*0.75) # # x11= float(float(x)*(l-r)/float(self.img_size[0]) + r) # x22= float(float(x+width)*(l-r)/float(self.img_size[0]) + r) # y11= float(float(y)*(b-t)/float(self.img_size[1]) + t) # y22= float(float(y+height)*(b-t)/float(self.img_size[1]) + t) # # r=x11 # l=x22 # b=y11 # t=y22 # # #glColor4f(color[0],color[1],color[2],color[3]) # # xw = math.fabs(float(l-r)) # yw = math.fabs(float(b-t)) # sh = min(xw,yw) # rad = float(sh*0.5) # # x = float(min(l,r)+rad) # y = float(min(t,b)+rad) # # if xw==sh: # st = 180 # else: # st = 90 # glBegin(GL_LINE_LOOP) # i=st # degInRad = (float)(float(i)*(3.14159/180.0)) # # for i in range (st, st+180): # degInRad = (float)(float(i)*(3.14159/180.0)) # glVertex2f((float)(float(x)+math.cos(degInRad)*rad),float(y)+(float)(math.sin(degInRad)*rad)) # # if xw == sh: #short horizontally # y = (float)(max(t,b)-rad) # else: # short vertically # x = (float)(max(l,r)-rad) # # i = st+180 # for i in range (st+180, st+360): # degInRad = (float)(float(i)*(3.14159/180.0)) # glVertex2f((float)(float(x)+math.cos(degInRad)*rad),float(y)+(float)(math.sin(degInRad)*rad)) # # glEnd() # ioHub.print2err('draw_lozenge exiting') ############################################################################### # # PYGLET Imp. def set_image_palette(self, r,g,b): ioHub.print2err('set_image_palette entered') self.imagebuffer = array.array('I') self.clear_cal_display() sz = len(r) i =0 self.pal = [] while i < sz: rf = int(r[i]) gf = int(g[i]) bf = int(b[i]) if self.byteorder: self.pal.append(0xff<<24|(bf<<16)|(gf<<8)|(rf)) else: self.pal.append((rf<<24)|(gf<<16)|(bf<<8)|0xff) i = i+1 ioHub.print2err('set_image_palette exiting')
class TobiiPsychopyCalibrationGraphics(object): IOHUB_HEARTBEAT_INTERVAL=0.050 # seconds between forced run through of # micro threads, since one is blocking # on camera setup. WINDOW_BACKGROUND_COLOR=(128,128,128) CALIBRATION_POINT_OUTER_RADIUS=15.0,15.0 CALIBRATION_POINT_OUTER_EDGE_COUNT=64 CALIBRATION_POINT_OUTER_COLOR=(255,255,255) CALIBRATION_POINT_INNER_RADIUS=3.0,3.0 CALIBRATION_POINT_INNER_EDGE_COUNT=32 CALIBRATION_POINT_INNER_COLOR=(25,25,25) CALIBRATION_POINT_LIST=[(0.5, 0.5),(0.1, 0.1),(0.9, 0.1),(0.9, 0.9),(0.1, 0.9),(0.5, 0.5)] TEXT_POS=[0,0] TEXT_COLOR=[0,0,0] TEXT_HEIGHT=48 def __init__(self, eyetrackerInterface, targetForegroundColor=None, targetBackgroundColor=None, screenColor=None, targetOuterDiameter=None, targetInnerDiameter=None, calibrationPointList=None): self._eyetrackerinterface=eyetrackerInterface self._tobii = eyetrackerInterface._tobii._eyetracker self.screenSize = eyetrackerInterface._display_device.getPixelResolution() self.width=self.screenSize[0] self.height=self.screenSize[1] self._ioKeyboard=None self._msg_queue=Queue.Queue() self._lastCalibrationOK=False self._lastCalibrationReturnCode=0 self._lastCalibration=None TobiiPsychopyCalibrationGraphics.CALIBRATION_POINT_OUTER_COLOR=targetForegroundColor TobiiPsychopyCalibrationGraphics.CALIBRATION_POINT_INNER_COLOR=targetBackgroundColor TobiiPsychopyCalibrationGraphics.WINDOW_BACKGROUND_COLOR=screenColor TobiiPsychopyCalibrationGraphics.CALIBRATION_POINT_OUTER_RADIUS=targetOuterDiameter/2.0,targetOuterDiameter/2.0 TobiiPsychopyCalibrationGraphics.CALIBRATION_POINT_INNER_RADIUS=targetInnerDiameter/2.0,targetInnerDiameter/2.0 if calibrationPointList is not None: TobiiPsychopyCalibrationGraphics.CALIBRATION_POINT_LIST=calibrationPointList calibration_methods = dict(THREE_POINTS=3, FIVE_POINTS=5, NINE_POINTS=9, THIRTEEN_POINTS=13) cal_type=self._eyetrackerinterface.getConfiguration()['calibration']['type'] if cal_type in calibration_methods: num_points=calibration_methods[cal_type] if num_points == 3: TobiiPsychopyCalibrationGraphics.CALIBRATION_POINT_LIST=[(0.5,0.1), (0.1,0.9), (0.9,0.9), (0.5,0.1)] elif num_points == 9: TobiiPsychopyCalibrationGraphics.CALIBRATION_POINT_LIST=[(0.5, 0.5), (0.1, 0.5), (0.9, 0.5), (0.1, 0.1), (0.5, 0.1), (0.9, 0.1), (0.9, 0.9), (0.5, 0.9), (0.1, 0.9), (0.5, 0.5)] # elif num_points == 13: # TobiiPsychopyCalibrationGraphics.CALIBRATION_POINT_LIST=[(x,y), # (x,y), # (x,y), # (x,y), # (x,y), # (x,y), # (x,y), # (x,y), # (x,y), # (x,y), # (x,y), # (x,y), # (x,y)] self.window = FullScreenWindow(self._eyetrackerinterface._display_device) self.window.setColor(self.WINDOW_BACKGROUND_COLOR,'rgb255') self.window.flip(clearBuffer=True) self._createStim() self._registerEventMonitors() self._lastMsgPumpTime=currentTime() self.clearAllEventBuffers() def clearAllEventBuffers(self): self._eyetrackerinterface._iohub_server.eventBuffer.clear() for d in self._eyetrackerinterface._iohub_server.devices: d.clearEvents() def _registerEventMonitors(self): if self._eyetrackerinterface._iohub_server: for dev in self._eyetrackerinterface._iohub_server.devices: #ioHub.print2err("dev: ",dev.__class__.__name__) if dev.__class__.__name__ == 'Keyboard': kbDevice=dev if kbDevice: eventIDs=[] for event_class_name in kbDevice.__class__.EVENT_CLASS_NAMES: eventIDs.append(getattr(EventConstants,convertCamelToSnake(event_class_name[:-5],False))) self._ioKeyboard=kbDevice self._ioKeyboard._addEventListener(self,eventIDs) else: ioHub.print2err("Warning: Tobii Cal GFX could not connect to Keyboard device for events.") def _unregisterEventMonitors(self): if self._ioKeyboard: self._ioKeyboard._removeEventListener(self) def _handleEvent(self,ioe): event_type_index=ioHub.devices.DeviceEvent.EVENT_TYPE_ID_INDEX if ioe[event_type_index] == EventConstants.KEYBOARD_CHAR: if ioe[-5] == 'SPACE': self._msg_queue.put("SPACE_KEY_ACTION") self.clearAllEventBuffers() if ioe[-5] == 'ESCAPE': self._msg_queue.put("QUIT") self.clearAllEventBuffers() def MsgPump(self): #keep the psychopy window happy ;) if currentTime()-self._lastMsgPumpTime>self.IOHUB_HEARTBEAT_INTERVAL: # try to keep ioHub, being blocked. ;( if self._eyetrackerinterface._iohub_server: for dm in self._eyetrackerinterface._iohub_server.deviceMonitors: dm.device._poll() self._eyetrackerinterface._iohub_server._processDeviceEventIteration() self._lastMsgPumpTime=currentTime() def getNextMsg(self): try: msg=self._msg_queue.get(block=True,timeout=0.02) self._msg_queue.task_done() return msg except Queue.Empty: pass def _createStim(self): self.calibrationPointOUTER = visual.Circle(self.window,pos=(0,0) ,lineWidth=0.0, radius=self.CALIBRATION_POINT_OUTER_RADIUS, name='CP_OUTER', units='pix',opacity=1.0, interpolate=False) self.calibrationPointINNER = visual.Circle(self.window,pos=(0,0), lineWidth=0.0, radius=self.CALIBRATION_POINT_INNER_RADIUS, name='CP_INNER',units='pix', opacity=1.0, interpolate=False) self.calibrationPointOUTER.setFillColor(self.CALIBRATION_POINT_OUTER_COLOR,'rgb255') self.calibrationPointOUTER.setLineColor(None,'rgb255') self.calibrationPointINNER.setFillColor(self.CALIBRATION_POINT_INNER_COLOR,'rgb255') self.calibrationPointINNER.setLineColor(None,'rgb255 ') instuction_text="Press Space Key to Start Eye Tracker Calibration." self.startCalibrationTextScreen=visual.TextStim(self.window, text=instuction_text, pos = self.TEXT_POS, height=self.TEXT_HEIGHT, color=self.TEXT_COLOR, colorSpace='rgb255', alignHoriz='center', alignVert='center', wrapWidth=self.width*0.9) def runCalibration(self): """ Performs a simple calibration routine. Args: None Result: bool: True if calibration was successful. False if not, in which case exit the application. """ import tobii self._lastCalibrationOK=False self._lastCalibrationReturnCode=0 self._lastCalibration=None calibration_sequence_completed=False quit_calibration_notified=False instuction_text="Press Space Key to Start Eye Tracker Calibration." self.startCalibrationTextScreen.setText(instuction_text) self.startCalibrationTextScreen.draw() self.window.flip() self.clearAllEventBuffers() stime=currentTime() while currentTime()-stime<60*5.0: msg=self.getNextMsg() if msg == 'SPACE_KEY_ACTION': break self.MsgPump() self.clearAllEventBuffers() auto_pace=self._eyetrackerinterface.getConfiguration()['calibration']['auto_pace'] pacing_speed=self._eyetrackerinterface.getConfiguration()['calibration']['pacing_speed'] randomize_points=self._eyetrackerinterface.getConfiguration()['calibration']['randomize'] cal_target_list=self.CALIBRATION_POINT_LIST[1:-1] if randomize_points is True: import random random.seed(None) random.shuffle(cal_target_list) cal_target_list.insert(0,self.CALIBRATION_POINT_LIST[0]) cal_target_list.append(self.CALIBRATION_POINT_LIST[-1]) self._tobii.StartCalibration(self.on_start_calibration) i=0 for pt in cal_target_list: w,h=self.screenSize #ioHub.print2err("Screen Size: ",w," ",h) self.clearAllEventBuffers() pix_pt=int(w*pt[0]-w/2),int(h*(1.0-pt[1])-h/2) #ioHub.print2err( "Cal point Mapping: ",pt," == ",pix_pt) self.drawCalibrationTarget(pix_pt) self.clearAllEventBuffers() stime=currentTime() def waitingForNextTargetTime(): return True if auto_pace is True: def waitingForNextTargetTime(): return currentTime()-stime<float(pacing_speed) while waitingForNextTargetTime(): msg=self.getNextMsg() if msg == 'SPACE_KEY_ACTION': break elif msg == 'QUIT': quit_calibration_notified=True self.MsgPump() if quit_calibration_notified: break pt2D=tobii.sdk.types.Point2D(pt[0],pt[1]) self._tobii.AddCalibrationPoint(pt2D,self.on_add_calibration_point) time.sleep(0.5) self.clearCalibrationWindow() self.clearAllEventBuffers() i+=1 if i == len(cal_target_list): calibration_sequence_completed=True if calibration_sequence_completed: self._tobii.ComputeCalibration(self.on_compute_calibration) msg=1 while msg not in ["CALIBRATION_COMPUTATION_COMPLETE","CALIBRATION_COMPUTATION_FAILED"]: msg=self.getNextMsg() self._tobii.StopCalibration(self.on_stop_calibration) msg=1 while msg is not "CALIBRATION_FINISHED": msg=self.getNextMsg() if self._lastCalibrationOK is True: self._tobii.GetCalibration(self.on_calibration_result) msg=1 while msg is not "CALIBRATION_RESULT_RECEIVED": msg=self.getNextMsg() cal_data_dict={} import math for cal_point_result in self._lastCalibration.plot_data: left_eye_data=cal_point_result.left.map_point left_eye_data=(left_eye_data.x*self.width,left_eye_data.y*self.height),cal_point_result.left.validity right_eye_data=cal_point_result.right.map_point right_eye_data=(right_eye_data.x*self.width,right_eye_data.y*self.height),cal_point_result.right.validity target_pos=cal_point_result.true_point.x*self.width,cal_point_result.true_point.y*self.height if target_pos not in cal_data_dict: cal_data_dict[target_pos]=[] cal_data_dict[target_pos].append((left_eye_data,right_eye_data)) cal_stats=dict() for (targ_x,targ_y),eye_cal_result_list in cal_data_dict.iteritems(): left_stats=dict(pos_sample_count=0,invalid_sample_count=0,avg_err=0.0,min_err=100000.0,max_err=0.0) right_stats=dict(pos_sample_count=0,invalid_sample_count=0,avg_err=0.0,min_err=100000.0,max_err=0.0) for ((left_x,left_y),left_validity),((right_x,right_y),right_validity) in eye_cal_result_list: left_stats['pos_sample_count']+=1.0 right_stats['pos_sample_count']+=1.0 if left_validity==1: x_err=targ_x-left_x y_err=targ_y-left_y left_err=math.sqrt(x_err*x_err+y_err*y_err) if left_err<left_stats['min_err']: left_stats['min_err']=left_err elif left_err>left_stats['max_err']: left_stats['max_err']=left_err left_stats['avg_err']+=left_err else: left_stats['invalid_sample_count']+=1.0 if right_validity==1: x_err=targ_x-right_x y_err=targ_y-right_y right_err=math.sqrt(x_err*x_err+y_err*y_err) if right_err<right_stats['min_err']: right_stats['min_err']=right_err elif right_err>right_stats['max_err']: right_stats['max_err']=right_err right_stats['avg_err']+=right_err else: right_stats['invalid_sample_count']+=1.0 if right_stats['invalid_sample_count']==0: right_stats['valid_sample_percentage']=100.0 else: right_stats['valid_sample_percentage']=(1.0-right_stats['invalid_sample_count']/right_stats['pos_sample_count'])*100.0 if left_stats['invalid_sample_count']==0: left_stats['valid_sample_percentage']=100.0 else: left_stats['valid_sample_percentage']=(1.0-left_stats['invalid_sample_count']/left_stats['pos_sample_count'])*100.0 if int(right_stats['pos_sample_count']-right_stats['invalid_sample_count'])>0: right_stats['avg_err']=right_stats['avg_err']/(right_stats['pos_sample_count']-right_stats['invalid_sample_count']) else: right_stats['avg_err']=-1.0 if int(left_stats['pos_sample_count']-left_stats['invalid_sample_count'])>0: left_stats['avg_err']=left_stats['avg_err']/(left_stats['pos_sample_count']-left_stats['invalid_sample_count']) else: left_stats['avg_err']=-1.0 cal_stats[(targ_x,targ_y)]=dict(left=left_stats,right=right_stats) # TODO Use calibration stats to show graphical results of calibration instuction_text="Calibration Passed. PRESS 'SPACE' KEY TO CONTINUE." self.startCalibrationTextScreen.setText(instuction_text) self.startCalibrationTextScreen.draw() self.window.flip() self.clearAllEventBuffers() while 1: msg=self.getNextMsg() if msg == 'SPACE_KEY_ACTION': return True self.MsgPump() if self._lastCalibrationOK is False: instuction_text="Calibration Failed. Options: SPACE: Re-run Calibration; ESCAPE: Exit Program" self.startCalibrationTextScreen.setText(instuction_text) self.startCalibrationTextScreen.draw() self.window.flip() self.clearAllEventBuffers() while 1: msg=self.getNextMsg() if msg == 'SPACE_KEY_ACTION': return self.runCalibration() elif msg == 'QUIT': return False self.MsgPump() return True def clearCalibrationWindow(self): self.window.flip(clearBuffer=True) def drawCalibrationTarget(self,tp): self.calibrationPointOUTER.setPos(tp) self.calibrationPointINNER.setPos(tp) self.calibrationPointOUTER.draw() self.calibrationPointINNER.draw() self.window.flip(clearBuffer=True) def on_start_calibration(self,*args,**kwargs): #ioHub.print2err('on_start_calibration: ',args,kwargs) pass def on_add_calibration_point(self,*args,**kwargs): #ioHub.print2err('on_add_calibration_point: ',args,kwargs) self._msg_queue.put('DRAW_NEXT') def on_stop_calibration(self,*args,**kwargs): #ioHub.print2err('on_stop_calibration: ',args,kwargs) self._msg_queue.put("CALIBRATION_FINISHED") def on_compute_calibration(self,*args,**kwargs): self._lastCalibrationReturnCode=args[0] if self._lastCalibrationReturnCode!=0: ioHub.print2err("ERROR: Tobii Calibration Calculation Failed. Error code: {0}".format(self._lastCalibrationReturnCode)) self._lastCalibrationOK=False self._msg_queue.put("CALIBRATION_COMPUTATION_FAILED") else: self._msg_queue.put("CALIBRATION_COMPUTATION_COMPLETE") self._lastCalibrationOK=True def on_calibration_result(self,*args,**kwargs): self._lastCalibration=args[1] self._msg_queue.put("CALIBRATION_RESULT_RECEIVED")