def getEnabledExtensions(self): return [ OrderedDict(name=e.Name, extensionId=e.ExtensionId, protocolVersion=e.ProtocolVersion, realm=e.Realm) for e in self._eyetracker.GetEnabledExtensions() ]
def __init__(self, port_num, baud=115200, timeout=0): self._port_num = port_num - 1 self._baud = baud self._timeout = timeout self._active_requests = OrderedDict() self._serial_port = None self._rx_events = [] self._request_replies = [] self.connectSerial()
def getTrackerDetails(self): tobiiInfoProperties=['product_id','given_name','model','generation','firmware_version','status'] tprops=OrderedDict() eyetracker_info=self._eyetracker_info for tp in tobiiInfoProperties: ta=getattr(eyetracker_info,tp) if callable(ta): ta=ta() tprops[tp]=ta tprops['factory_info_string']=str(eyetracker_info.factory_info) return tprops
def __init__(self, port_num, baud=115200, timeout=0): self._port_num = port_num self._baud = baud self._timeout = timeout self._active_requests = OrderedDict() self._serial_port = None self._rx_events = [] self._request_replies = [] self._analog_input_thresholds = [ 0, ] * 8 self.connectSerial() self.resetState()
def showSessionInfoDialog(): ''' Display a dialog to collect session or participant level information at the start of an experiment. If the dialog OK button is pressed, a dictionary with the values entered for each dialog input is returned. If thew dialogs Cancel button is pressed, None is returned. :return: dict of session info, or None if dialog was cancelled ''' info = OrderedDict() info['Session Code'] = DEFAULT_SESSION_CODE info['Conditions File'] = getAvailableConditionsFileNames() # info['ExpName'] =EXP_NAME # info['ExpVersion'] = EXP_VERSION infoDlg = gui.DlgFromDict(dictionary=info, title='{} (v{})'.format(EXP_NAME, EXP_VERSION), order = info.keys(), ) # fixed=['ExpName','ExpVersion']) if infoDlg.OK: return info return None
def stop(cls, empty_queue=True): if cls._active: cls._active = False TobiiTrackerBrowser._event_queue.put(None) TobiiTrackerBrowser._browser.stop() if cls._mainloop_referenced is False: TobiiTrackerBrowser._mainloop.stop() TobiiTrackerBrowser._mainloop = None if empty_queue is True: e = 1 while e != None: try: e = TobiiTrackerBrowser.getNextEvent() except Queue.Empty: pass TobiiTrackerBrowser._browser = None TobiiTrackerBrowser._detectedTrackers = OrderedDict()
def run(self, *args, **kwargs): """ Tests the round trip delay from when the experiment runtime requests new events from the ioHub server to when a response with >=1 new event is received and ready for use within the experiment script. Only getEvent requests that return with atleast one new event are used in the calculated statistics to try and ensure the reported delay is measuring the higher processing load case of new events being returned, vs. the case of no new events being available. At the end of the test, a MatPlotLib figure is displayed showing a histogram of the round trip event request delays as well as two figures representing the retrace onset detection stability of PsychoPy when the ioHub is being used. PsychoPy code is taken from an example psychopy script in the coder documentation. """ self.psychoStim = OrderedDict() self.totalEventRequestsForTest = 1000 self.numEventRequests = 0 self.psychoWindow = None self.lastFlipTime = 0.0 self.events = None # create 'shortcuts' to the devices of interest for this experiment self.mouse = self.hub.devices.mouse self.kb = self.hub.devices.kb self.expRuntime = self.hub.devices.experimentRuntime self.display = self.hub.devices.display # create fullscreen pyglet window at current resolution, as well as required resources / drawings self.createPsychoGraphicsWindow() # create stats numpy arrays, set experiment process to high priority. self.initStats() # enable high priority mode for the experiment process #Computer.enableHighPriority() #draw and flip to the updated graphics state. ifi = self.drawAndFlipPsychoWindow() # START TEST LOOP >>>>>>>>>>>>>>>>>>>>>>>>>> while self.numEventRequests < self.totalEventRequestsForTest: # send an Experiment Event to the ioHub server process self.hub.sendMessageEvent("This is a test message %.3f" % self.flipTime) # check for any new events from any of the devices, and return the events list and the time it took to # request the events and receive the reply self.events, callDuration = self.checkForEvents() if self.events: # events were available self.updateStats(self.events, callDuration, ifi) #draw and flip to the updated graphics state. ifi = self.drawAndFlipPsychoWindow() # END TEST LOOP <<<<<<<<<<<<<<<<<<<<<<<<<< # close necessary files / objects, disable high priority. self.spinDownTest() # plot collected delay and retrace detection results. self.plotResults()
def run(self, *args): """ The run method contains your experiment logic. It is equal to what would be in your main psychopy experiment script .py file in a standard psychopy experiment setup. """ display = self.devices.display kb = self.devices.kb ain = self.devices.ain display_resolution = display.getPixelResolution() psychopy_monitor = display.getPsychopyMonitorName() unit_type = display.getCoordinateType() screen_index = display.getIndex() # Create a psychopy window, full screen resolution, full screen mode. window = visual.Window(display_resolution, monitor=psychopy_monitor, units=unit_type, color=[128, 128, 128], colorSpace='rgb255', fullscr=True, allowGUI=False, screen=screen_index) # Get the number of trials selected in the session dialog. # user_params = self.getUserDefinedParameters() print('user_params: ', user_params) trial_count = int(user_params.get('trial_count', 5)) # Create an ordered dictionary of psychopy stimuli. # An ordered dictionary is one that returns keys in the order # they are added, so you can use it to reference stim by a name # or by 'zorder'. # psychoStim = OrderedDict() psychoStim['grating'] = visual.PatchStim(window, mask="circle", size=150, pos=[0, 0], sf=.075) psychoStim['title'] = visual.TextStim( window, text="Analog Input Test. Trial 1 of %d" % (trial_count), pos=[0, 200], height=36, color=[1, .5, 0], colorSpace='rgb', alignHoriz='center', alignVert='center', wrapWidth=800.0) ai_values_string_proto = "AI_0: %.3f\tAI_1: %.3f\tAI_2: %.3f\tAI_3: %.3f\t\nAI_4: %.3f\tAI_5: %.3f\tAI_6: %.3f\tAI_7: %.3f" ai_values = (0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0) psychoStim['analog_input_values'] = visual.TextStim( window, text=ai_values_string_proto % ai_values, pos=[0, -200], height=24, color=[1, 1, 0], colorSpace='rgb', alignHoriz='center', alignVert='center', wrapWidth=800.0) psychoStim['instruction'] = visual.TextStim( window, text="Press SPACE Key for Next Trial", pos=[0, -300], height=36, color=[1, 1, 0.5], colorSpace='rgb', alignHoriz='center', alignVert='center', wrapWidth=800.0) # Clear all events from the global and device level event buffers. self.hub.clearEvents('all') # Run a number of analog input recording /trials/ # for i in range(trial_count): # Clear all events from the global and device level event buffers. psychoStim['title'].setText("Analog Input Test. Trial %d of %d" % (i + 1, trial_count)) self.hub.clearEvents('all') # Start streaming AnalogInput data. # ain.enableEventReporting(True) # Loop until we get a keyboard event where the # SPACE key was pressed. # while 1: # For each retrace, update the grating phase # psychoStim['grating'].setPhase(0.05, '+') # Update analog input values to display # analog_input_events = ain.getEvents() if analog_input_events: event_count = len(analog_input_events) event = analog_input_events[-1] ai_values = (event.AI_0, event.AI_1, event.AI_2, event.AI_3, event.AI_4, event.AI_5, event.AI_6, event.AI_7) psychoStim['analog_input_values'].setText( ai_values_string_proto % ai_values) # redraw the stim [psychoStim[stimName].draw() for stimName in psychoStim] # Flip, storing the time of the start of display update retrace. # flip_time = window.flip() # Send a message to the ioHub Process with indicating # that a flip occurred and at what time. # self.hub.sendMessageEvent("Flip", sec_time=flip_time) # Get any new key values from keyboard *Char* Events. # key_values = [ k.key for k in kb.getEvents(EventConstants.KEYBOARD_RELEASE) ] if u' ' in key_values: break # Clear the screen # window.flip() # Stop analog input recording # ain.enableEventReporting(False) # Delay 1/4 second before next trial # actualDelay = self.hub.wait(0.250) # All trials have been run. End demo.... # Wait 250 msec before ending the experiment actualDelay = self.hub.wait(0.250) print("Delay requested %.6f, actual delay %.6f, Diff: %.6f" % (0.250, actualDelay, actualDelay - 0.250))
class TobiiTracker(object): LEFT = 0 RIGHT = 1 eye_data = OrderedDict(tracker_time_usec=np.NaN, pupil_diameter_mm=np.NaN, gaze_norm=[np.NaN, np.NaN], gaze_mm=[np.NaN, np.NaN, np.NaN], eye_location_norm=[np.NaN, np.NaN, np.NaN], eye_location_mm=[np.NaN, np.NaN, np.NaN], validity_code=np.NaN, status='UNKNOWN', trigger_signal=None) def __init__(self, eyetracker_info=None, product_id=None, model=None, mainloop=None, create_sync_manager=True): self._eyetracker_info = eyetracker_info self._requested_product_id = product_id self._requested_model = model self._mainloop = mainloop self._eyetracker = None self._queue = None self._tobiiClock = None self._getTobiiClockResolution = None self._getTobiiClockTime = None self._sync_manager = None self._syncTimeEventDeque = None self._isRecording = False if eyetracker_info is None: if not TobiiTrackerBrowser.isActive(): TobiiTrackerBrowser.start() self._eyetracker_info = TobiiTrackerBrowser.findDevice( model, product_id) if self._eyetracker_info: self._mainloop = TobiiTrackerBrowser.getMainLoop() TobiiTrackerBrowser.stop() else: self._eyetracker_info = TobiiTrackerBrowser.findDevice( model, product_id) if self._eyetracker_info is None: raise exceptions.BaseException( "Could not find a Tobii Eye Tracker matching requirements.") if self._mainloop is None: if TobiiTrackerBrowser.isActive(): self._mainloop = TobiiTrackerBrowser.getMainLoop() else: TobiiPy.init() self._mainloop = TobiiPyMainloopThread() self._mainloop.start() self._queue = Queue.Queue() TobiiPyEyeTracker.create_async(self._mainloop, self._eyetracker_info, self.on_eyetracker_created) stime = getTime() while getTime() - stime < 10.0: try: event = self._queue.get(block=True, timeout=.1) if isinstance(event, TobiiTrackerCreatedEvent): self._eyetracker = event.tracker_object self._eyetracker.events.OnFramerateChanged += self.on_external_framerate_change if hasattr(self._eyetracker.events, 'OnHeadMovementBoxChanged'): self._eyetracker.events.OnHeadMovementBoxChanged += self.on_head_box_change elif hasattr(self._eyetracker.events, 'OnTrackBoxChanged'): self._eyetracker.events.OnTrackBoxChanged += self.on_head_box_change else: print 'WARNING: TobiiClasses could not set callback hook for "self.on_head_box_change".' self._eyetracker.events.OnXConfigurationChanged += self.on_x_series_physical_config_change break self._queue.task_done() except Queue.Empty: pass if self._eyetracker is None: raise exceptions.BaseException( "Could not connect to Tobii. Timeout.") if create_sync_manager: self._eyetracker.events.OnError += self.on_eyetracker_error self._tobiiClock = TobiiPyClock() self._getTobiiClockResolution = self._tobiiClock.get_resolution self._getTobiiClockTime = self._tobiiClock.get_time self._syncTimeEventDeque = collections.deque(maxlen=32) self._sync_manager = TobiiPySyncManager(self._tobiiClock, self._eyetracker_info, self._mainloop, self.on_sync_error, self.on_sync_status) def on_eyetracker_created(self, *args, **kwargs): #print 'on_eyetracker_created: entered' et = None if len(args) >= 2: et = args[1] else: raise exceptions.BaseException( "WARNING: on_eyetracker_created: Unhandled args count", len(args), args) error = kwargs.get('error', None) if error: raise exceptions.BaseException( "Connection to Tobii failed because of an exception: %s" % (str(error), )) self._queue.put(TobiiTrackerCreatedEvent(et)) return False def on_eyetracker_error(self, *args, **kwargs): print2err("TobiiTracker.on_eyetracker_error: ", args, kwargs) return False def on_sync_error(self, *args, **kwargs): print2err("TobiiTracker.on_sync_error: ", args, kwargs) return False def on_sync_status(self, *args, **kwargs): sync_state = args[0] self._syncTimeEventDeque.append(sync_state) return False def on_eyetracker_data(self, *args, **kwargs): eye_data_event = args[1] LEFT = self.LEFT RIGHT = self.RIGHT eyes = (copy.deepcopy(self.eye_data), copy.deepcopy(self.eye_data)) eyes[LEFT]['validity_code'] = eye_data_event.LeftValidity eyes[RIGHT]['validity_code'] = eye_data_event.RightValidity eyes[LEFT]['tracker_time_usec'] = eye_data_event.Timestamp eyes[RIGHT]['tracker_time_usec'] = eye_data_event.Timestamp if hasattr(eye_data_event, 'TrigSignal'): eyes[LEFT]['trigger_signal'] = eye_data_event.TrigSignal eyes[RIGHT]['trigger_signal'] = eye_data_event.TrigSignal #print "*** lastEyeData.RightGazePoint2D: ",lastEyeData.RightGazePoint2D.__dict__ if eye_data_event.LeftValidity >= 2 and eye_data_event.RightValidity >= 2: # no eye signal eyes[LEFT]['status'] = "Missing" eyes[RIGHT]['status'] = "Missing" elif eye_data_event.LeftValidity < 2 and eye_data_event.RightValidity >= 2: # left eye only available eyes[LEFT]['status'] = "Available" eyes[RIGHT]['status'] = "Missing" eyes[LEFT]['pupil_diameter_mm'] = eye_data_event.LeftPupil eyes[LEFT]['gaze_norm'][0] = eye_data_event.LeftGazePoint2D.x eyes[LEFT]['gaze_norm'][1] = eye_data_event.LeftGazePoint2D.y eyes[LEFT]['gaze_mm'][0] = eye_data_event.LeftGazePoint3D.x eyes[LEFT]['gaze_mm'][1] = eye_data_event.LeftGazePoint3D.y eyes[LEFT]['gaze_mm'][2] = eye_data_event.LeftGazePoint3D.z eyes[LEFT]['eye_location_norm'][ 0] = eye_data_event.LeftEyePosition3DRelative.x eyes[LEFT]['eye_location_norm'][ 1] = eye_data_event.LeftEyePosition3DRelative.y eyes[LEFT]['eye_location_norm'][ 2] = eye_data_event.LeftEyePosition3DRelative.z eyes[LEFT]['eye_location_mm'][ 0] = eye_data_event.LeftEyePosition3D.x eyes[LEFT]['eye_location_mm'][ 1] = eye_data_event.LeftEyePosition3D.y eyes[LEFT]['eye_location_mm'][ 2] = eye_data_event.LeftEyePosition3D.z elif eye_data_event.LeftValidity >= 2 and eye_data_event.RightValidity < 2: # right eye only available eyes[RIGHT]['status'] = "Available" eyes[LEFT]['status'] = "Missing" eyes[RIGHT]['pupil_diameter_mm'] = eye_data_event.RightPupil eyes[RIGHT]['gaze_norm'][0] = eye_data_event.RightGazePoint2D.x eyes[RIGHT]['gaze_norm'][1] = eye_data_event.RightGazePoint2D.y eyes[RIGHT]['gaze_mm'][0] = eye_data_event.RightGazePoint3D.x eyes[RIGHT]['gaze_mm'][1] = eye_data_event.RightGazePoint3D.y eyes[RIGHT]['gaze_mm'][2] = eye_data_event.RightGazePoint3D.z eyes[RIGHT]['eye_location_norm'][ 0] = eye_data_event.RightEyePosition3DRelative.x eyes[RIGHT]['eye_location_norm'][ 1] = eye_data_event.RightEyePosition3DRelative.y eyes[RIGHT]['eye_location_norm'][ 2] = eye_data_event.RightEyePosition3DRelative.z eyes[RIGHT]['eye_location_mm'][ 0] = eye_data_event.RightEyePosition3D.x eyes[RIGHT]['eye_location_mm'][ 1] = eye_data_event.RightEyePosition3D.y eyes[RIGHT]['eye_location_mm'][ 2] = eye_data_event.RightEyePosition3D.z else: # binocular available eyes[RIGHT]['status'] = "Available" eyes[LEFT]['status'] = "Available" eyes[LEFT]['pupil_diameter_mm'] = eye_data_event.LeftPupil eyes[LEFT]['gaze_norm'][0] = eye_data_event.LeftGazePoint2D.x eyes[LEFT]['gaze_norm'][1] = eye_data_event.LeftGazePoint2D.y eyes[LEFT]['gaze_mm'][0] = eye_data_event.LeftGazePoint3D.x eyes[LEFT]['gaze_mm'][1] = eye_data_event.LeftGazePoint3D.y eyes[LEFT]['gaze_mm'][2] = eye_data_event.LeftGazePoint3D.z eyes[LEFT]['eye_location_norm'][ 0] = eye_data_event.LeftEyePosition3DRelative.x eyes[LEFT]['eye_location_norm'][ 1] = eye_data_event.LeftEyePosition3DRelative.y eyes[LEFT]['eye_location_norm'][ 2] = eye_data_event.LeftEyePosition3DRelative.z eyes[LEFT]['eye_location_mm'][ 0] = eye_data_event.LeftEyePosition3D.x eyes[LEFT]['eye_location_mm'][ 1] = eye_data_event.LeftEyePosition3D.y eyes[LEFT]['eye_location_mm'][ 2] = eye_data_event.LeftEyePosition3D.z eyes[RIGHT]['pupil_diameter_mm'] = eye_data_event.RightPupil eyes[RIGHT]['gaze_norm'][0] = eye_data_event.RightGazePoint2D.x eyes[RIGHT]['gaze_norm'][1] = eye_data_event.RightGazePoint2D.y eyes[RIGHT]['gaze_mm'][0] = eye_data_event.RightGazePoint3D.x eyes[RIGHT]['gaze_mm'][1] = eye_data_event.RightGazePoint3D.y eyes[RIGHT]['gaze_mm'][2] = eye_data_event.RightGazePoint3D.z eyes[RIGHT]['eye_location_norm'][ 0] = eye_data_event.RightEyePosition3DRelative.x eyes[RIGHT]['eye_location_norm'][ 1] = eye_data_event.RightEyePosition3DRelative.y eyes[RIGHT]['eye_location_norm'][ 2] = eye_data_event.RightEyePosition3DRelative.z eyes[RIGHT]['eye_location_mm'][ 0] = eye_data_event.RightEyePosition3D.x eyes[RIGHT]['eye_location_mm'][ 1] = eye_data_event.RightEyePosition3D.y eyes[RIGHT]['eye_location_mm'][ 2] = eye_data_event.RightEyePosition3D.z return False def on_start_tracking(self, *args, **kwargs): return False def on_stop_tracking(self, *args, **kwargs): return False def on_external_framerate_change(self, *args, **kwargs): print2err("NOTE: Tobii System Sampling Rate Changed.") return False def on_head_box_change(self, *args, **kwargs): print2err("NOTE: Tobii Head Movement Box Changed.") return False def on_x_series_physical_config_change(self, *args, **kwargs): print2err("NOTE: Tobii X Series Physical Settings Changed.") return False def getTimeSyncManager(self): return self._sync_manager def getTimeSyncState(self): return self._sync_manager.sync_state() def getCurrentEyeTrackerTime(self): return self._sync_manager.convert_from_local_to_remote( self._getTobiiClockTime()) def getCurrentLocalTobiiTime(self): return self._getTobiiClockTime() def getTobiiTimeResolution(self): return self._getTobiiClockResolution() def getMainLoop(self): return self._mainloop def getTrackerDetails(self): tobiiInfoProperties = [ 'product_id', 'given_name', 'model', 'generation', 'firmware_version', 'status' ] tprops = OrderedDict() eyetracker_info = self._eyetracker_info for tp in tobiiInfoProperties: ta = getattr(eyetracker_info, tp) if callable(ta): ta = ta() tprops[tp] = ta tprops['factory_info_string'] = str(eyetracker_info.factory_info) return tprops def getTrackerInfo(self): if hasattr(self._eyetracker, 'GetUnitInfo'): return self._eyetracker.GetUnitInfo() return None def startTracking(self, et_data_rx_callback=None): if et_data_rx_callback: self.on_eyetracker_data = et_data_rx_callback self._eyetracker.events.OnGazeDataReceived += self.on_eyetracker_data self._eyetracker.StartTracking(self.on_start_tracking) self._isRecording = True return True def stopTracking(self): self._eyetracker.events.OnGazeDataReceived -= self.on_eyetracker_data self._eyetracker.StopTracking(self.on_stop_tracking) self._isRecording = False def getName(self): return self._eyetracker.GetUnitName() def setName(self, name): self._eyetracker.SetUnitName(name) def getLowBlinkMode(self): try: return self._eyetracker.GetLowblinkMode() except: pass return None def setLowBlinkMode(self, enable): if hasattr(self._eyetracker, 'SetLowblinkMode'): try: if isinstance(enable, bool) or enable == 0 or enable == 1: self._eyetracker.SetLowblinkMode(enable) return self._eyetracker.GetLowblinkMode() except: pass return None def setSamplingRate(self, rate): if rate in self._eyetracker.EnumerateFramerates(): self._eyetracker.SetFramerate(rate) return rate return self._eyetracker.GetFramerate() def getAvailableSamplingRates(self): return self._eyetracker.EnumerateFramerates() def getSamplingRate(self): return self._eyetracker.GetFramerate() def getIlluminationMode(self): try: return self._eyetracker.getIlluminationMode() except: pass return None def getAvailableIlluminationModes(self): try: return self._eyetracker.EnumerateIlluminationModes() except: return [] def setIlluminationMode(self, imode): imodes = self.getAvailableIlluminationModes() if len(imodes) > 0: if imode in imodes: self._eyetracker.SetIlluminationMode(imode) return imode return self._eyetracker.getIlluminationMode() else: print 'WARNING: setIlluminationMode is not supported by either your Tobii model, or the version of the Tobii SDK being used.' def getHeadBox(self): hb = None if hasattr(self._eyetracker, 'GetTrackBox'): hb = self._eyetracker.GetTrackBox() elif hasattr(self._eyetracker, 'GetHeadMovementBox'): hb = self._eyetracker.GetHeadMovementBox() if hb: return np.asarray([(hb.Point1.x, hb.Point1.y, hb.Point1.z), (hb.Point2.x, hb.Point2.y, hb.Point2.z), (hb.Point3.x, hb.Point3.y, hb.Point3.z), (hb.Point4.x, hb.Point4.y, hb.Point4.z), (hb.Point5.x, hb.Point5.y, hb.Point5.z), (hb.Point6.x, hb.Point6.y, hb.Point6.z), (hb.Point7.x, hb.Point7.y, hb.Point7.z), (hb.Point8.x, hb.Point8.y, hb.Point8.z)]) return None def setXSeriesPhysicalPlacement(self, upperLeft, upperRight, lowerLeft): if self.getTrackerDetails()['generation'] == 'X': self._eyetracker.SetXConfiguration(upperLeft, upperRight, lowerLeft) return True return False def getEyeTrackerPhysicalPlacement(self): etpc = self._eyetracker.GetXConfiguration() ll = etpc.LowerLeft ul = etpc.UpperLeft ur = etpc.UpperRight return dict(lowerLeft=(ll.x, ll.y, ll.z), upperLeft=(ul.x, ul.y, ul.z), upperRight=(ur.x, ur.y, ur.z)) def getAvailableExtensions(self): return [ OrderedDict(name=e.Name, extensionId=e.ExtensionId, protocolVersion=e.ProtocolVersion, realm=e.Realm) for e in self._eyetracker.GetAvailableExtensions() ] def getEnabledExtensions(self): return [ OrderedDict(name=e.Name, extensionId=e.ExtensionId, protocolVersion=e.ProtocolVersion, realm=e.Realm) for e in self._eyetracker.GetEnabledExtensions() ] def enableExtension(self, extension): extension_id = extension if isinstance(extension, OrderedDict): extension_id = extension['extensionId'] self._eyetracker.EnableExtension(extension_id) def disconnect(self): if self._isRecording: self.stopTracking() self._mainloop.stop() self._mainloop = None self._eyetracker_info = None self._requested_product_id = None self._requested_model = None self._eyetracker = None def __del__(self): if self._mainloop: self.disconnect()
class TobiiTrackerBrowser(object): _mainloop = None _detectedTrackers = OrderedDict() _browser = None _event_queue = None _active = False _mainloop_referenced = False TRACKER_FOUND = 1 TRACKER_UPDATED = 2 TRACKER_DROPPED = 3 @classmethod def start(cls): if TobiiTrackerBrowser._mainloop is None: TobiiPy.init() TobiiTrackerBrowser._mainloop = TobiiPyMainloopThread() TobiiTrackerBrowser._mainloop.start() TobiiTrackerBrowser._event_queue = Queue.Queue() TobiiTrackerBrowser._browser = TobiiPyEyetrackerBrowser( TobiiTrackerBrowser._mainloop, TobiiTrackerBrowser.on_eyetracker_browser_event) cls._active = True @classmethod def stop(cls, empty_queue=True): if cls._active: cls._active = False TobiiTrackerBrowser._event_queue.put(None) TobiiTrackerBrowser._browser.stop() if cls._mainloop_referenced is False: TobiiTrackerBrowser._mainloop.stop() TobiiTrackerBrowser._mainloop = None if empty_queue is True: e = 1 while e != None: try: e = TobiiTrackerBrowser.getNextEvent() except Queue.Empty: pass TobiiTrackerBrowser._browser = None TobiiTrackerBrowser._detectedTrackers = OrderedDict() @classmethod def isActive(cls): return cls._active @classmethod def getMainLoop(cls): cls._mainloop_referenced = True return cls._mainloop @classmethod def getNextEvent(cls, timeout=None): if timeout is None: e = TobiiTrackerBrowser._event_queue.get_nowait() TobiiTrackerBrowser._event_queue.task_done() return e e = TobiiTrackerBrowser._event_queue.get(block=True, timeout=timeout) TobiiTrackerBrowser._event_queue.task_done() return e @classmethod def on_eyetracker_browser_event(cls, event_type, event_name, eyetracker_info): if event_type == TobiiPyEyetrackerBrowser.FOUND: TobiiTrackerBrowser._detectedTrackers[ eyetracker_info.product_id] = eyetracker_info TobiiTrackerBrowser._event_queue.put( TrackerFoundEvent(eyetracker_info)) return False if event_type == TobiiPyEyetrackerBrowser.UPDATED: TobiiTrackerBrowser._detectedTrackers[ eyetracker_info.product_id] = eyetracker_info TobiiTrackerBrowser._event_queue.put( TrackerUpdatedEvent(eyetracker_info)) return False if TobiiPyEyetrackerBrowser.REMOVED: del TobiiTrackerBrowser._detectedTrackers[ eyetracker_info.product_id] TobiiTrackerBrowser._event_queue.put( TrackerRemovedEvent(eyetracker_info)) return False raise ioDeviceError( device=cls, msg= "TobiiTrackerBrowser.on_eyetracker_browser_event received unhandled event type {0} for Tobii device with info: {1}" .format(event_type, eyetracker_info)) @classmethod def getTrackerDetails(cls, tracker_product_id): tobiiInfoProperties = [ 'product_id', 'given_name', 'model', 'generation', 'firmware_version', 'status' ] tprops = OrderedDict() eyetracker_info = TobiiTrackerBrowser._detectedTrackers[ tracker_product_id] for tp in tobiiInfoProperties: ta = getattr(eyetracker_info, tp) if callable(ta): ta = ta() tprops[tp] = ta tprops['factory_info_string'] = str(eyetracker_info.factory_info) return tprops @classmethod def getDetectedTrackerList(cls): return [t for t in TobiiTrackerBrowser._detectedTrackers.values()] @classmethod def _checkForMatch(cls, tracker_info, model, product_id): if product_id: if tracker_info.product_id != product_id: return False if model: if model != tracker_info.model: return False return True @classmethod def findDevice(cls, model=None, product_id=None, timeout=10.0): tracker_info = None # check existing detected devices matching_tracker_infos = [ tracker_info for tracker_info in cls.getDetectedTrackerList() if cls._checkForMatch(tracker_info, model, product_id) is True ] if matching_tracker_infos: return matching_tracker_infos[0] started_browsing_time = getTime() while (getTime() - started_browsing_time < timeout): try: tb_event = TobiiTrackerBrowser.getNextEvent(timeout=0.05) if tb_event is None: break if isinstance(tb_event, TrackerFoundEvent): tracker_info = tb_event.tracker_info if TobiiTrackerBrowser._checkForMatch( tracker_info, model, product_id) is True: return tracker_info except Queue.Empty: pass