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')
Пример #2
0
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")