Exemplo n.º 1
0
    def drawIA(self, x, y, size, index, color, name):
        '''Draws square interest area in EDF and a corresponding filled box on
           eye-tracker display.
        
        Parameters
            x, y  -- x and y coordinates in degrees visual angle
            size  -- length of one edge of square in degrees visual angle
            index -- number to assign interest area in EDF
            color -- color of box drawn on eye-tracker display (0 - 15)
            name  -- string to name interest area in EDF
        '''

        # Convert units to eyelink space
        elx = deg2pix(x, self.win.monitor) + (self.sres[0] / 2.0)
        ely = -(deg2pix(y, self.win.monitor) - (self.sres[1] / 2.0))
        elsz = deg2pix(size, self.win.monitor) / 2.0

        # Make top left / bottom right coordinates for square
        tplf = map(round, [elx - elsz, ely - elsz])
        btrh = map(round, [elx + elsz, ely + elsz])

        # Construct command strings
        flist = [index, name, color] + tplf + btrh
        iamsg = '!V IAREA RECTANGLE {0} {3} {4} {5} {6} {1}'.format(*flist)
        bxmsg = 'draw_filled_box {3} {4} {5} {6} {2}'.format(*flist)

        # Send commands
        self.tracker.sendMessage(iamsg)
        self.tracker.sendCommand(bxmsg)
Exemplo n.º 2
0
    def _get_tobii_pos(self, p):
        """Convert PsychoPy coordinates to Tobii ADCS coordinates.

        Args:
            p: Gaze position (x, y) in PsychoPy coordinate systems.

        Returns:
            Gaze position in Tobii ADCS.
        """

        if self.win.units == 'norm':
            return ((p[0] + 1) / 2, (p[1] - 1) / -2)
        elif self.win.units == 'height':
            return (p[0] * (self.win.size[1] / self.win.size[0]) + 0.5,
                    -p[1] + 0.5)
        elif self.win.units == 'pix':
            return self._pix2tobii(p)
        elif self.win.units in ['cm', 'deg', 'degFlat', 'degFlatPos']:
            if self.win.units == 'cm':
                p_pix = (cm2pix(p[0], self.win.monitor),
                         cm2pix(p[1], self.win.monitor))
            elif self.win.units == 'deg':
                p_pix = (deg2pix(p[0], self.win.monitor),
                         deg2pix(p[1], self.win.monitor))
            elif self.win.units in ['degFlat', 'degFlatPos']:
                p_pix = (deg2pix(
                    np.array(p), self.win.monitor, correctFlat=True))

            return self._pix2tobii(p_pix)
        else:
            raise ValueError('unit ({}) is not supported'.format(
                self.win.units))
Exemplo n.º 3
0
    def get_tobii_pos(self, p):
        """
        Convert Tobii position to PsychoPy coordinate system.
        
        :param p: Position (x, y)
        """

        if self.win.units == 'norm':
            gp = ((p[0] + 1) / 2, (p[1] + 1) / 2)
        elif self.win.units == 'height':
            gp = (p[0] * self.win.size[1] / self.win.size[0] + 0.5, p[1] + 0.5)
        elif self.win.units == 'pix':
            gp = (p[0] / self.win.size[0] + 0.5, p[1] / self.win.size[1] + 0.5)
        elif self.win.units == 'cm':
            p_pix = (cm2pix(p[0],
                            self.win.monitor), cm2pix(p[1], self.win.monitor))
            gp = (p_pix[0] / self.win.size[0] + 0.5,
                  p_pix[1] / self.win.size[1] + 0.5)
        elif self.win.units == 'deg':
            p_pix = (deg2pix(p[0], self.win.monitor),
                     deg2pix(p[1], self.win.monitor))
            gp = (p_pix[0] / self.win.size[0] + 0.5,
                  p_pix[1] / self.win.size[1] + 0.5)
        elif self.win.units in ['degFlat', 'degFlatPos']:
            p_pix = (deg2pix(np.array(p), self.win.monitor, correctFlat=True))
            gp = (p_pix[0] / self.win.size[0] + 0.5,
                  p_pix[1] / self.win.size[1] + 0.5)
        else:
            raise ValueError('unit ({}) is not supported'.format(
                self.win.units))

        return (gp[0], 1 - gp[1])  # flip vert
Exemplo n.º 4
0
 def _calcFieldCoordsRendered(self):
     if self.units in ['norm', 'pix','height']:
         self._fieldSizeRendered=self.fieldSize
         self._fieldPosRendered=self.fieldPos
     elif self.units in ['deg', 'degs']:
         self._fieldSizeRendered=deg2pix(self.fieldSize, self.win.monitor)
         self._fieldPosRendered=deg2pix(self.fieldPos, self.win.monitor)
     elif self.units=='cm':
         self._fieldSizeRendered=cm2pix(self.fieldSize, self.win.monitor)
         self._fieldPosRendered=cm2pix(self.fieldPos, self.win.monitor)
Exemplo n.º 5
0
 def _calcVerticesRendered(self):
     self.needVertexUpdate=False
     if self.units in ['norm', 'pix', 'height']:
         self._verticesRendered=self.vertices
         self._posRendered=self.pos
     elif self.units in ['deg', 'degs']:
         self._verticesRendered=deg2pix(self.vertices, self.win.monitor)
         self._posRendered=deg2pix(self.pos, self.win.monitor)
     elif self.units=='cm':
         self._verticesRendered=cm2pix(self.vertices, self.win.monitor)
         self._posRendered=cm2pix(self.pos, self.win.monitor)
     self._verticesRendered = self._verticesRendered * self.size
Exemplo n.º 6
0
 def setHeight(self,height, log=True):
     """Set the height of the letters (including the entire box that surrounds the letters
     in the font). The width of the letters is then defined by the font.
     """
     #height in pix (needs to be done after units)
     if self.units=='cm':
         if height==None: self.height = 1.0#default text height
         else: self.height = height
         self.heightPix = cm2pix(self.height, self.win.monitor)
     elif self.units in ['deg', 'degs']:
         if height==None: self.height = 1.0
         else: self.height = height
         self.heightPix = deg2pix(self.height, self.win.monitor)
     elif self.units=='norm':
         if height==None: self.height = 0.1
         else: self.height = height
         self.heightPix = self.height*self.win.size[1]/2
     elif self.units=='height':
         if height==None: self.height = 0.2
         else: self.height = height
         self.heightPix = self.height*self.win.size[1]
     else: #treat units as pix
         if height==None: self.height = 20
         else: self.height = height
         self.heightPix = self.height
     #need to update the font to reflect the change
     self.setFont(self.fontname, log=False)
     if log and self.autoLog:
         self.win.logOnFlip("Set %s height=%.2f" %(self.name, height),
             level=logging.EXP,obj=self)
Exemplo n.º 7
0
    def sac_detect(self, x, y, radius):
        """
        Checks if current gaze position is outside a circular interest area.

        :param x: X coordinate in degrees visual angle for center of circle IA.
        :type x: float or int
        :param y: Y coordinate in degrees visual angle for center of circle IA.
        :type y: float or int
        :param radius: Radius of detection circle in degrees visual angle.
        :type radius: float or int
        :return: List of whether saccade was detected and the gaze coordinates
        at time of detection. In the form of [bool, [x, y]].
        :rtype: bool, list
        """

        # Convert coordinates to Eyelink space
        elx, ely = self.convert_coords(x, y)
        elsr = deg2pix(radius, self.win.monitor)

        if self.realconnect:
            # Get current gaze position
            gaze = self.get_gaze()

            # Calculate distance of gaze from circle center
            gdist = ((gaze[0] - elx)**2) + ((gaze[1] - ely)**2)

            # Compare to radius
            outcirc = gdist > (elsr**2)

            # Convert gaze to psychopy units
            gaze = self.convert_coords(gaze[0], gaze[1], to='psychopy')

            return outcirc, gaze
Exemplo n.º 8
0
    def _windowUnitsToPix(self, pos):
        """Convert user specified window units to 'pix'. This method is the
        inverse of `_pixToWindowUnits`.

        Parameters
        ----------
        pos : ArrayLike
            Position `(x, y)` in 'pix' coordinates to convert.

        Returns
        -------
        ndarray
            Position `(x, y)` in window units.

        """
        pos = np.asarray(pos, dtype=np.float32)

        if self.win is None:
            return pos

        if self.win.units == 'pix':
            return pos
        elif self.win.units == 'norm':
            return pos * self.win.size / 2.0
        elif self.win.units == 'cm':
            return cm2pix(pos, self.win.monitor)
        elif self.win.units == 'deg':
            return deg2pix(pos, self.win.monitor)
        elif self.win.units == 'height':
            return pos * float(self.win.size[1])
Exemplo n.º 9
0
    def contains(self, x, y=None):
        """Determines if a point x,y is inside the extent of the stimulus.

        Can accept: a) two args, x and y; b) one arg, as a point (x,y) that is
        list-like; or c) an object with a getPos() method that returns x,y, such
        as a mouse. Returns True if the point is within the area defined by `vertices`.
        This handles complex shapes, including concavities and self-crossings.

        Note that, if your stimulus uses a mask (such as a Gaussian blob) then
        this is not accounted for by the `contains` method; the extent of the
        stmulus is determined purely by the size, pos and orientation settings
        (and by the vertices for shape stimuli).

        See coder demo, shapeContains.py
        """
        if self.needVertexUpdate:
            self._calcVerticesRendered()
        if hasattr(x, 'getPos'):
            x, y = x.getPos()
        elif type(x) in [list, tuple, numpy.ndarray]:
            x, y = x[0], x[1]
        if self.units in ['deg','degs']:
            x, y = deg2pix(numpy.array((x, y)), self.win.monitor)
        elif self.units == 'cm':
            x, y = cm2pix(numpy.array((x, y)), self.win.monitor)
        if self.ori:
            oriRadians = numpy.radians(self.ori)
            sinOri = numpy.sin(oriRadians)
            cosOri = numpy.cos(oriRadians)
            x0, y0 = x-self._posRendered[0], y-self._posRendered[1]
            x = x0 * cosOri - y0 * sinOri + self._posRendered[0]
            y = x0 * sinOri + y0 * cosOri + self._posRendered[1]

        return pointInPolygon(x, y, self)
Exemplo n.º 10
0
 def setHeight(self, height, log=True):
     """Set the height of the letters (including the entire box that surrounds the letters
     in the font). The width of the letters is then defined by the font.
     """
     #height in pix (needs to be done after units)
     if self.units == 'cm':
         if height == None: self.height = 1.0  #default text height
         else: self.height = height
         self.heightPix = cm2pix(self.height, self.win.monitor)
     elif self.units in ['deg', 'degs']:
         if height == None: self.height = 1.0
         else: self.height = height
         self.heightPix = deg2pix(self.height, self.win.monitor)
     elif self.units == 'norm':
         if height == None: self.height = 0.1
         else: self.height = height
         self.heightPix = self.height * self.win.size[1] / 2
     elif self.units == 'height':
         if height == None: self.height = 0.2
         else: self.height = height
         self.heightPix = self.height * self.win.size[1]
     else:  #treat units as pix
         if height == None: self.height = 20
         else: self.height = height
         self.heightPix = self.height
     #need to update the font to reflect the change
     self.setFont(self.fontname, log=False)
     if log and self.autoLog:
         self.win.logOnFlip("Set %s height=%.2f" % (self.name, height),
                            level=logging.EXP,
                            obj=self)
Exemplo n.º 11
0
    def _windowUnits2pix(self, win_handle, pos):
        win = self._iohub_server._psychopy_windows.get(win_handle)
        win_units = win['units']
        monitor = win['monitor']
        pos = np.asarray(pos)
        if win_units == 'pix':
            return pos
        elif win_units == 'norm':
            return pos * win['size'] / 2.0
        elif win_units == 'cm':
            if monitor:
                return cm2pix(pos, monitor['monitor'])
            else:
                # should raise exception?
                print2err(
                    "iohub Mouse error: Window is using units %s but has no Monitor definition."
                    % win_units)

        elif win_units == 'deg':
            if monitor:
                return deg2pix(pos, monitor['monitor'])
            else:
                # should raise exception
                print2err(
                    "iohub Mouse error: Window is using units %s but has no Monitor definition."
                    % win_units)
        elif win_units == 'height':
            return pos * float(win['size'][1])
Exemplo n.º 12
0
 def _calcSizeRendered(self):
     """Calculate the size of the stimulus in coords of the :class:`~psychopy.visual.Window` (normalised or pixels)"""
     if self.units in ['norm','pix', 'height']: self._sizeRendered=self.size
     elif self.units in ['deg', 'degs']: self._sizeRendered=deg2pix(self.size, self.win.monitor)
     elif self.units=='cm': self._sizeRendered=cm2pix(self.size, self.win.monitor)
     else:
         logging.ERROR("Stimulus units should be 'height', 'norm', 'deg', 'cm' or 'pix', not '%s'" %self.units)
def deg2tobii(pos, mon):
    ''' Converts from degrees to Tobiis coordinate system [0, 1].
    Note that the Tobii coordinate system start in the upper left corner
    and the PsychoPy coordinate system in the center
    '''

    # First convert from deg to pixels
    pos[:, 0] = deg2pix(pos[:, 0], mon, correctFlat=False)
    pos[:, 1] = deg2pix(pos[:, 1], mon, correctFlat=False)

    # Then normalize data -1,1
    pos[:, 0] = pos[:, 0] / float(mon.getSizePix()[0])/2
    pos[:, 1] = pos[:, 1] / float(mon.getSizePix()[1])/2

    #.. finally shift to tobii coordinate system
    return norm2tobii(pos)
Exemplo n.º 14
0
 def _calcSizeRendered(self):
     """DEPRECATED in 1.80.00. This funtionality is now handled by _updateVertices() and verticesPix"""
     #raise DeprecationWarning, "_calcSizeRendered() was deprecated in 1.80.00. This funtionality is nowhanded by _updateVertices() and verticesPix"
     if self.units in ['norm','pix', 'height']: self._sizeRendered=copy.copy(self.size)
     elif self.units in ['deg', 'degs']: self._sizeRendered=deg2pix(self.size, self.win.monitor)
     elif self.units=='cm': self._sizeRendered=cm2pix(self.size, self.win.monitor)
     else:
         logging.ERROR("Stimulus units should be 'height', 'norm', 'deg', 'cm' or 'pix', not '%s'" %self.units)
Exemplo n.º 15
0
 def _calcPosRendered(self):
     """DEPRECATED in 1.80.00. This funtionality is now handled by _updateVertices() and verticesPix"""
     #raise DeprecationWarning, "_calcSizeRendered() was deprecated in 1.80.00. This funtionality is now handled by _updateVertices() and verticesPix"
     if self.units in ['norm', 'pix', 'height']:
         self._posRendered = copy.copy(self.pos)
     elif self.units in ['deg', 'degs']:
         self._posRendered = deg2pix(self.pos, self.win.monitor)
     elif self.units == 'cm':
         self._posRendered = cm2pix(self.pos, self.win.monitor)
Exemplo n.º 16
0
 def __init__(self, win, actors, duration_frames, attn_video_size):
     """
     Initialize class
     :param win: window to use
     :param actors: actors to show
     :param duration_frames: how long to show (in frames)
     """
     self.win = win
     self.actors = actors
     self.duration_frames = duration_frames
     self.left_roi=visual.Rect(self.win, width=550, height=750, units='pix')
     self.left_roi.pos=[deg2pix(-12,win.monitor),deg2pix(0,win.monitor)]
     self.left_roi.lineColor = [1, -1, -1]
     self.left_roi.lineWidth = 10
     self.right_roi=visual.Rect(self.win, width=550, height=750, units='pix')
     self.right_roi.pos=[deg2pix(12,win.monitor),deg2pix(0,win.monitor)]
     self.right_roi.lineColor = [1, -1, -1]
     self.right_roi.lineWidth = 10
     self.attn_video=MovieStimulus(self.win, '', '', 'attn.mpg', attn_video_size)
Exemplo n.º 17
0
 def _windowUnits2pix(self, pos):
     if self.win.units == "pix":
         return pos
     elif self.win.units == "norm":
         return pos * self.win.size / 2.0
     elif self.win.units == "cm":
         return cm2pix(pos, self.win.monitor)
     elif self.win.units == "deg":
         return deg2pix(pos, self.win.monitor)
     elif self.win.units == "height":
         return pos * float(self.win.size[1])
Exemplo n.º 18
0
 def _windowUnits2pix(self, pos):
     if self.win.units == 'pix':
         return pos
     elif self.win.units == 'norm':
         return pos * self.win.size / 2.0
     elif self.win.units == 'cm':
         return cm2pix(pos, self.win.monitor)
     elif self.win.units == 'deg':
         return deg2pix(pos, self.win.monitor)
     elif self.win.units == 'height':
         return pos * float(self.win.size[1])
Exemplo n.º 19
0
 def _windowUnits2pix(self, pos):
     if self.win.units == 'pix':
         return pos
     elif self.win.units == 'norm':
         return pos * self.win.size / 2.0
     elif self.win.units == 'cm':
         return cm2pix(pos, self.win.monitor)
     elif self.win.units == 'deg':
         return deg2pix(pos, self.win.monitor)
     elif self.win.units == 'height':
         return pos * float(self.win.size[1])
Exemplo n.º 20
0
 def _calcPosRendered(self):
     """Calculate the pos of the stimulus in pixels"""
     if self.units == 'pix':
         self._posRendered = self.pos
     elif self.units == 'cm':
         self._posRendered = cm2pix(self.pos, self.win.monitor)
     elif self.units =='deg':
         self._posRendered = deg2pix(self.pos, self.win.monitor)
     elif self.units == 'norm':
         self._posRendered = self.pos * self.win.size/2.0
     elif self.units == 'height':
         self._posRendered = self.pos * self.win.size[1]
Exemplo n.º 21
0
 def _calcPosRendered(self):
     """Calculate the pos of the stimulus in pixels"""
     if self.units == 'pix':
         self._posRendered = self.pos
     elif self.units == 'cm':
         self._posRendered = cm2pix(self.pos, self.win.monitor)
     elif self.units == 'deg':
         self._posRendered = deg2pix(self.pos, self.win.monitor)
     elif self.units == 'norm':
         self._posRendered = self.pos * self.win.size / 2.0
     elif self.units == 'height':
         self._posRendered = self.pos * self.win.size[1]
Exemplo n.º 22
0
    def convert_coords(self, x, y, to='eyelink'):
        """
        Converts from degrees visual angle units to EyeLink Pixel units.

        :param x: X coordinate in visual angle.
        :type x: float or int
        :param y: Y coordinate in viusal angle.
        :type y: float or int
        :param to: Direction of conversion. Options: 'eyelink' or 'psychopy'.
        :return: Two values in order x, y
        """
        if to == 'eyelink':
            # Convert coordinates to Eyelink space
            elx = deg2pix(x, self.win.monitor) + self.scenter[0]
            ely = -(deg2pix(y, self.win.monitor) - self.scenter[1])

        elif to == 'psychopy':
            elx = pix2deg(x - self.scenter[0], self.win.monitor)
            ely = pix2deg(-(y - self.scenter[1]), self.win.monitor)

        return [elx, ely]
Exemplo n.º 23
0
 def _calcSizeRendered(self):
     """DEPRECATED in 1.80.00. This funtionality is now handled by _updateVertices() and verticesPix"""
     #raise DeprecationWarning, "_calcSizeRendered() was deprecated in 1.80.00. This funtionality is nowhanded by _updateVertices() and verticesPix"
     if self.units in ['norm', 'pix', 'height']:
         self._sizeRendered = copy.copy(self.size)
     elif self.units in ['deg', 'degs']:
         self._sizeRendered = deg2pix(self.size, self.win.monitor)
     elif self.units == 'cm':
         self._sizeRendered = cm2pix(self.size, self.win.monitor)
     else:
         logging.ERROR(
             "Stimulus units should be 'height', 'norm', 'deg', 'cm' or 'pix', not '%s'"
             % self.units)
Exemplo n.º 24
0
    def _get_tobii_pos(self, p, units=None):
        """Convert PsychoPy coordinates to Tobii ADCS coordinates.

        Args:
            p: Gaze position (x, y) in PsychoPy coordinate systems.
            units: The PsychoPy coordinate system of p.

        Returns:
            Gaze position in Tobii ADCS. For example: (0,0).
        """
        if units is None:
            units = self.win.units

        if units == "norm":
            return (p[0] / 2 + 0.5, p[1] / -2 + 0.5)
        elif units == "height":
            return (p[0] * (self.win.size[1] / self.win.size[0]) + 0.5,
                    -p[1] + 0.5)
        elif units == "pix":
            return self._pix2tobii(p)
        elif units in ["cm", "deg", "degFlat", "degFlatPos"]:
            if units == "cm":
                p_pix = (cm2pix(p[0], self.win.monitor),
                         cm2pix(p[1], self.win.monitor))
            elif units == "deg":
                p_pix = (
                    deg2pix(p[0], self.win.monitor),
                    deg2pix(p[1], self.win.monitor),
                )
            elif units in ["degFlat", "degFlatPos"]:
                p_pix = deg2pix(np.array(p),
                                self.win.monitor,
                                correctFlat=True)
            p_pix = tuple(round(pos) for pos in p_pix)
            return self._pix2tobii(p_pix)
        else:
            raise ValueError("unit ({}) is not supported".format(units))
    def fix_check(self, size, ftime, button):
        """
        Checks that fixation is maintained for certain time.

        :param size: Length of one side of box in degrees visual angle.
        :type size: float or int
        :param ftime: Length of time to check for fixation in seconds.
        :type ftime: float
        :param button: Key to press to recalibrate eye-tracker.
        :type button: char
        """

        # Calculate Fix check borders
        size = deg2pix(size, self.win.monitor) / 2.0

        xbdr = [self.scenter[0] - size, self.scenter[0] + size]
        ybdr = [self.scenter[1] - size, self.scenter[1] + size]

        # Set status message & Draw box
        self.set_status('Fixation Check')
        bxmsg = 'draw_box {} {} {} {} 1'.format(xbdr[0], ybdr[0], xbdr[1],
                                                ybdr[1])
        self.tracker.sendCommand(bxmsg)

        # Begin recording
        self.tracker.startRecording(0, 0, 1, 1)

        # Begin polling
        fixtime = time.clock()
        while self.realconnect:  # only start check loop if real connection

            # Check for recalibration button
            keys = event.getKeys(button)
            if keys:
                self.tracker.stopRecording()
                self.calibrate()
                break

            gaze = self.get_gaze()

            # Are we in the box?
            if xbdr[0] < gaze[0] < xbdr[1] and ybdr[0] < gaze[1] < ybdr[1]:
                # Have we been in the box long enough?
                if (time.clock() - fixtime) > ftime:
                    self.tracker.stopRecording()
                    break
            else:
                # Reset clock if not in box
                fixtime = time.clock()
Exemplo n.º 26
0
    def _addRuntimeInfoToDisplayConfig(self):

        if self not in Display._enabled_display_instances:
            Display._enabled_display_instances.append(self)

        display_config = self.getConfiguration()

        runtime_info = display_config.get("runtime_info", None)
        if runtime_info is None:
            runtime_info = self._getRuntimeInfoByIndex(self.device_number)
            display_config["runtime_info"] = runtime_info

            self._createPsychopyCalibrationFile()

            pixel_width = runtime_info["pixel_width"]
            pixel_height = runtime_info["pixel_height"]

            phys_width = display_config["physical_dimensions"]["width"]
            phys_height = display_config["physical_dimensions"]["height"]
            phys_unit_type = display_config["physical_dimensions"]["unit_type"]

            # add pixels_per_degree to runtime info
            ppd_x = deg2pix(
                1.0, self._psychopy_monitor
            )  # math.tan(math.radians(0.5))*2.0*viewing_distance*pixel_width/phys_width
            ppd_y = deg2pix(
                1.0, self._psychopy_monitor
            )  # math.tan(math.radians(0.5))*2.0*viewing_distance*pixel_height/phys_height
            runtime_info["pixels_per_degree"] = ppd_x, ppd_y

            self._calculateCoordMappingFunctions(pixel_width, pixel_height, phys_unit_type, phys_width, phys_height)

            left, top, right, bottom = runtime_info["bounds"]
            coord_left, coord_top = self._pixel2DisplayCoord(left, top, self.device_number)
            coord_right, coord_bottom = self._pixel2DisplayCoord(right, bottom, self.device_number)
            runtime_info["coordinate_bounds"] = coord_left, coord_top, coord_right, coord_bottom
Exemplo n.º 27
0
    def drawIA(self, x, y, size, index, color, name):
        """
        Draws square interest area in EDF and a corresponding filled box on
        eye-tracker display.

        :param x: X coordinate in degrees visual angle for center of check area.
        :type x: float or int
        :param y: Y coordinate in degrees visual angle for center of check area.
        :type y: float or int
        :param size: length of one edge of square in degrees visual angle.
        :type size: float or int
        :param index: number to assign interest area in EDF
        :type index: int
        :param color: color of box drawn on eye-tracker display (0 - 15)
        :type color: int
        :param name: Name interest area in EDF
        :type name: str
        """

        # Convert units to eyelink space
        elx = deg2pix(x, self.win.monitor) + (self.sres[0] / 2.0)
        ely = -(deg2pix(y, self.win.monitor) - (self.sres[1] / 2.0))
        elsz = deg2pix(size, self.win.monitor) / 2.0
    
        # Make top left / bottom right coordinates for square
        tplf = map(round, [elx - elsz, ely - elsz])
        btrh = map(round, [elx + elsz, ely + elsz])
    
        # Construct command strings
        flist = [index, name, color] + tplf + btrh
        iamsg = '!V IAREA RECTANGLE {0} {3} {4} {5} {6} {1}'.format(*flist)
        bxmsg = 'draw_filled_box {3} {4} {5} {6} {2}'.format(*flist)

        # Send commands
        self.tracker.sendMessage(iamsg)
        self.tracker.sendCommand(bxmsg)
Exemplo n.º 28
0
    def __init__(self, win, balloonID, color, maxPumps, gainpoints, initsize, gainsize, mon):
        self.win = win
        self.balloonID = balloonID
        self.color = color
        self.maxPumps = maxPumps
        self.points = gainsize
        self.initSize = initsize  # units 'deg'
        self.initSizeX = 3.5
        self.initSizeY = 5
        self.gainSize = gainsize  # units 'deg'
        self.mon = mon
        self.pump = visual.ImageStim(win, image="assets/pump.png", pos=[0, 0], size=[10, 10])
        self.explosionSound = sound.Sound(value='assets\explode.wav')
        self.explosionSound.setVolume(1.0)
        self.pumpSound = sound.Sound(value='assets\pump4.wav')
        self.pumpSound.setVolume(1.0)

        self.pump.pos = [0, pix2deg(-690 + (deg2pix(self.pump.size[1], mon) / 2), mon)]
        self.stimuli = visual.ImageStim(win, image="assets/bal_blue.png", pos=[0,pix2deg(-400+(deg2pix(self.initSizeY,mon)/2),mon)],size=[self.initSizeX,self.initSizeY])
Exemplo n.º 29
0
    def draw_ia(self, x, y, size, index, color, name):
        """
        Draws square interest area in EDF and a corresponding filled box on
        eye-tracker display. Must be called after :py:func:`set_trialid` for
        interest areas to appear in the EDF.

        :param x: X coordinate in degrees visual angle for center of check area.
        :type x: float or int
        :param y: Y coordinate in degrees visual angle for center of check area.
        :type y: float or int
        :param size: length of one edge of square in degrees visual angle.
        :type size: float or int
        :param index: number to assign interest area in EDF
        :type index: int
        :param color: color of box drawn on eye-tracker display (0 - 15)
        :type color: int
        :param name: Name of interest area in EDF
        :type name: str
        """

        # Convert units to eyelink space
        elx, ely = self.convert_coords(x, y)
        elsz = deg2pix(size, self.win.monitor) / 2.0

        # Make top left / bottom right coordinates for square
        tplf = map(round, [elx - elsz, ely - elsz])
        btrh = map(round, [elx + elsz, ely + elsz])

        # Construct command strings
        flist = [index, name, color] + tplf + btrh
        iamsg = '!V IAREA RECTANGLE {0} {3} {4} {5} {6} {1}'.format(*flist)
        bxmsg = 'draw_filled_box {3} {4} {5} {6} {2}'.format(*flist)

        # Send commands
        self.tracker.sendMessage(iamsg)
        self.tracker.sendCommand(bxmsg)
Exemplo n.º 30
0
 def _calcXYsRendered(self):
     if self.units in ['norm','pix','height']: self._XYsRendered=self.xys
     elif self.units in ['deg', 'degs']: self._XYsRendered=deg2pix(self.xys, self.win.monitor)
     elif self.units=='cm': self._XYsRendered=cm2pix(self.xys, self.win.monitor)
Exemplo n.º 31
0
 def degcoord2pix(self, degx, degy, display_index=None):
     if display_index == self.getIndex():
         return psychopy2displayPix(
             deg2pix(degx, self._psychopy_monitor), cm2pix(degy, self._psychopy_monitor)
         )
     return degx, degy
Exemplo n.º 32
0
    def fix_check_locs(self, xcenters, ycenters, size, ftime, button):
        """
        Like fix_check, check but checks multiple boxes, returns the fixated box 
        number.         
        
        Checks that fixation is maintained for 'ftime'.

        :param size: Length of one side of box in degrees visual angle.
        :type size: float or int
        :param ftime: Length of time to check for fixation in seconds.
        :type ftime: float
        :param button: Key to press to recalibrate eye-tracker.
        :type button: char
        """

        # Calculate Fix check borders
        size = deg2pix(size, self.win.monitor) / 2.0

        mBoundaries = _np.zeros((len(xcenters), 4))
        for i, (x, y) in enumerate(zip(xcenters, ycenters)):
            mBoundaries[i, :] = [x - size, x + size] + [y - size, y + size]

        # Set status message & Draw box
        self.set_status('Fixation Check')
        bxmsg = 'draw_box {} {} {} {} 1'.format(xbdr[0], ybdr[0], xbdr[1],
                                                ybdr[1])
        self.tracker.sendCommand(bxmsg)

        # Begin recording
        self.tracker.startRecording(0, 0, 1, 1)

        # Begin polling

        fixtime = time.clock()
        while self.realconnect:  # only start check loop if real connection

            # Check for recalibration button
            keys = event.getKeys(button)
            if keys:
                self.tracker.stopRecording()
                self.calibrate()
                break

            gaze = self.get_gaze()

            # what a box?
            box, inBox = 0, False
            for x0, x1, y0, y1 in mBoundaries:
                if x0 < gaze[0] < x1 and y0 < gaze[1] < y1:
                    inBox = True
                    box += 1
                    break

            # long enough?
            if inBox:
                if (time.clock() - fixtime) > ftime:
                    self.tracker.stopRecording()
                    break
            else:
                # Reset clock if not in box
                fixtime = time.clock()

        return box
Exemplo n.º 33
0
    def doCalibration(self, calibrationPoints=[(0.5, 0.5), (0.1, 0.9),
                                               (0.1, 0.1), (0.9, 0.9),
                                               (0.9, 0.1)],
                      calinRadius=2.0, caloutRadius=None, moveFrames=60):
        if self.eyetracker is None:
            return

        # set default
        if caloutRadius is None:
            caloutRadius = calinRadius * 20.0
        if calibrationPoints is None:
            calibrationPoints = [(0.5, 0.5), (0.1, 0.9),
                                 (0.1, 0.1), (0.9, 0.9), (0.9, 0.1)]

        self.points = np.random.permutation(calibrationPoints)

        # Make the "outer" circle
        self.calout = psychopy.visual.Circle(self.win, radius=caloutRadius,
                                             lineColor=(0, 1.0, 0),
                                             fillColor=(0.5, 1.0, 0.5),
                                             units='pix', autoDraw=True,
                                             pos=self.acsd2pix(self.points[-1]))
        # Make a dummy message
        self.calmsg = psychopy.visual.TextStim(self.win, color=0.0,
                                               units='norm', height=0.07,
                                               pos=(0.0, -0.5))

        # Put the eye tracker into the calibration state
        self.initcalibration_completed = False
        print "Start new calibration..."
        self.eyetracker.StartCalibration(callback=self.on_calib_start)
        while not self.initcalibration_completed:
            psychopy.core.wait(0.1)
            if psychopy.event.getKeys(keyList=['escape']):
                raise KeyboardInterrupt("You interrupted the script.")
        self.deletecalibration_completed = False
        # Clear out previous calibrations (tobii scanners
        # sometimes store these across many sessions)
        print "Delete old calibration..."
        self.eyetracker.ClearCalibration(callback=self.on_calib_deleted)
        while not self.deletecalibration_completed:
            psychopy.core.wait(0.1)
            if psychopy.event.getKeys(keyList=['escape']):
                raise KeyboardInterrupt("You interrupted the script.")

        # Draw instructions and wait for space key
        self.calmsg.text = ("Please focus your eyes on the green dot, and "
                            "follow it with your eyes when it moves. This "
                            "will help us calibrate the eye tracker.\n"
                            "Press space when you're ready.")
        self.calmsg.draw()
        self.win.flip()
        psychopy.event.waitKeys(keyList=['space'])

        # Go through the calibration points
        for self.point_index in range(len(self.points)):
            # The dot starts at the previous point
            self.calout.pos = \
                self.acsd2pix((self.points[self.point_index - 1][0],
                               self.points[self.point_index - 1][1]))
            # The steps for the movement is new - old divided by frames
            self.step = (self.acsd2pix((self.points[self.point_index][0],
                                        self.points[self.point_index][1])) -
                         self.calout.pos) / moveFrames

            # Create a tobii 2D class
            p = Point2D()
            # Add the X and Y coordinates to the tobii point
            p.x, p.y = self.points[self.point_index]

            # Move the point in position (smooth pursuit)
            for frame in range(moveFrames):
                self.calout.pos += self.step
                # draw & flip
                self.win.flip()

            # Shrink the outer point (gaze fixation)
            for frame in range(moveFrames / 2):
                self.calout.radius -= (caloutRadius -
                                       calinRadius) / (moveFrames / 2)
                self.win.flip()

            # Add this point to the tobii
            psychopy.core.wait(1)  # first wait to let the eyes settle (MIN 0.5)
            self.add_point_completed = False  # this gets updated by callback
            self.eyetracker.AddCalibrationPoint(p,
                                                callback=self.on_add_completed)
            # While this point is being added, do nothing:
            while not self.add_point_completed:
                psychopy.core.wait(0.1)
                if psychopy.event.getKeys(keyList=['escape']):
                    raise KeyboardInterrupt("You interrupted the script.")
            psychopy.core.wait(0.5)  # wait before continuing

            # Reset the radius of the large circle
            self.calout.radius = caloutRadius

        # After calibration, make sure the stimuli aren't drawn
        self.calout.autoDraw = False
        self.calout = None

        # The following two will be set by the tobii SDK
        self.computeCalibration_completed = False
        self.computeCalibration_succeeded = False
        # Do the computation
        self.eyetracker.ComputeCalibration(self.on_calib_compute)
        while not self.computeCalibration_completed:
            psychopy.core.wait(0.1)
            if psychopy.event.getKeys(keyList=['escape']):
                raise KeyboardInterrupt("You interrupted the script.")
        self.eyetracker.StopCalibration(None)

        self.win.flip()

        # Now we retrieve the calibration data
        self.getcalibration_completed = False
        self.calib = self.eyetracker.GetCalibration(self.on_calib_response)
        while not self.getcalibration_completed:
            psychopy.core.wait(0.1)
            if psychopy.event.getKeys(keyList=['escape']):
                raise KeyboardInterrupt("You interrupted the script.")

        if not self.computeCalibration_succeeded:
            # computeCalibration failed.
            self.calmsg.text = ("Not enough data was collected "
                                "(Retry:[r] Abort:[ESC])")
        elif self.calib is None:
            # no calibration data
            self.calmsg.text = ("No calibration data "
                                "(Retry:[r] Abort:[ESC])")
        else:
            # calibration seems to have worked out
            points = {}
            for data in self.calib.plot_data:
                points[data.true_point] = {'left': data.left,
                                           'right': data.right}

            if len(points) == 0:
                # no points in the calibration results
                self.calmsg.text = ("No calibration data "
                                    "(Retry:[r] Abort:[ESC])")
            else:
                # draw the calibration result
                for p, d in points.iteritems():
                    psychopy.visual.Circle(self.win, radius=calinRadius,
                                           fillColor=(1, 1, 1),
                                           units='pix',
                                           pos=(p.x - 0.5, 0.5 - p.y)).draw()
                    if d['left'].status == 1:
                        psychopy.visual.Line(self.win, units='pix',
                                             lineColor='yellow',
                                             start=(self.acsd2pix((p.x, p.y))),
                                             end=(self.acsd2pix((d['left'].
                                                                 map_point.x,
                                                                 d['left'].
                                                                 map_point.y)))
                                             ).draw()
                    if d['right'].status == 1:
                        psychopy.visual.Line(self.win, units='pix',
                                             lineColor='blue',
                                             start=(self.acsd2pix((p.x, p.y))),
                                             end=(self.acsd2pix((d['right'].
                                                                 map_point.x,
                                                                 d['right'].
                                                                 map_point.y)))
                                             ).draw()
                for p in self.points:
                    psychopy.visual.Circle(self.win, radius=calinRadius,
                                           fillColor=1,
                                           units='pix',
                                           pos=self.acsd2pix(p),
                                           ).draw()
                    psychopy.visual.Circle(self.win, units='pix',
                                           lineColor=-0.5,
                                           radius=deg2pix(0.9,
                                                          self.win.monitor),
                                           pos=self.acsd2pix(p),
                                           ).draw()
                self.calmsg.text = ("Accept calibration results\n"
                                    "(Accept:[a] Retry:[r] Abort:[ESC])")

        # Update the screen, then wait for response
        self.calmsg.draw()
        self.win.flip()
        self.response = psychopy.event.waitKeys(keyList=['a', 'r', 'escape'])
        if 'a' in self.response:
            retval = 'accept'
        elif 'r' in self.response:
            retval = 'retry'
        elif 'escape' in self.response:
            retval = 'abort'

        return retval
Exemplo n.º 34
0
 def _calcPosRendered(self):
     """DEPRECATED in 1.80.00. This funtionality is now handled by _updateVertices() and verticesPix"""
     #raise DeprecationWarning, "_calcSizeRendered() was deprecated in 1.80.00. This funtionality is now handled by _updateVertices() and verticesPix"
     if self.units in ['norm','pix', 'height']: self._posRendered= copy.copy(self.pos)
     elif self.units in ['deg', 'degs']: self._posRendered=deg2pix(self.pos, self.win.monitor)
     elif self.units=='cm': self._posRendered=cm2pix(self.pos, self.win.monitor)
Exemplo n.º 35
0
mon.setWidth(52.71)  # cm
myWin = visual.Window([1000, 1000],
                      units='deg',
                      monitor=mon,
                      color=(-1, -1, -1),
                      checkTiming=True)
fps = myWin.getActualFrameRate()  # sometimes this call fails...

event.globalKeys.clear()
event.globalKeys.add(key='q', func=core.quit)  # global quit
globalClock = clock.Clock()
kb = keyboard.Keyboard()  # create kb object

# let's do some calculation before going further
speedFrame = speedDeg / fps  # how many deg/frame
speedPixFrame = monitorunittools.deg2pix(speedFrame, mon)
dotSizePix = monitorunittools.deg2pix(
    dotSize, mon)  # calculate dotSizePix for DotStim object
nDots = round(np.pi * fieldRadius**2 * dotDensity)  # calcuate nDots
maxFrames = int(round(maxDur / myWin.monitorFramePeriod))

# define trial handler
stimList = []
for t in dirRange:
    for d in stimDir:
        stimList.append({'dirRange': t, 'direction': d})
trials = data.TrialHandler(trialList=stimList, nReps=nTrialsPerCond)

#  ====== define stimulus components =======
# define fixation
fixation = circle.Circle(win=myWin,
Exemplo n.º 36
0
 def _calcDotsXYRendered(self):
     if self.units in ['norm','pix', 'height']: self._dotsXYRendered=self._dotsXY
     elif self.units in ['deg','degs']: self._dotsXYRendered=deg2pix(self._dotsXY, self.win.monitor)
     elif self.units=='cm': self._dotsXYRendered=cm2pix(self._dotsXY, self.win.monitor)
Exemplo n.º 37
0
    def fixCheck(self, size, ftime, button):
        '''Checks that fixation is maintained for certain time.
        
        Parameters
            size   -- length of one side of box in degrees visual angle
            ftime  -- length of time to check for fixation in seconds
            button -- key to press to recalibrate eye-tracker
        '''

        # Calculate Fix check borders
        cenX = self.sres[0] / 2.0
        cenY = self.sres[1] / 2.0
        size = deg2pix(size, self.win.monitor) / 2.0

        xbdr = [cenX - size, cenX + size]
        ybdr = [cenY - size, cenY + size]

        # Set status message & Draw box
        self.setStatus('Fixation Check')
        bxmsg = 'draw_box {} {} {} {} 1'.format(xbdr[0], ybdr[0], xbdr[1],
                                                ybdr[1])
        self.tracker.sendCommand(bxmsg)

        # Begin recording
        self.tracker.startRecording(0, 0, 1, 1)

        # Check which eye is being recorded
        eye_used = self.tracker.eyeAvailable()
        RIGHT_EYE = 1
        LEFT_EYE = 0

        # Begin polling
        keys = []
        fixtime = time.clock()
        while self.realconnect:  # only start check loop if real connection

            # Check for recalibration button
            keys = event.getKeys(button)
            if keys:
                self.tracker.stopRecording()
                self.calibrate()
                break

            # Grab latest sample
            sample = self.tracker.getNewestSample()

            # Extract gaze coordinates
            if eye_used == RIGHT_EYE:
                gaze = sample.getRightEye().getGaze()
            else:
                gaze = sample.getLeftEye().getGaze()

            # Are we in the box?
            if xbdr[0] < gaze[0] < xbdr[1] and ybdr[0] < gaze[1] < ybdr[1]:
                # Have we been in the box long enough?
                if (time.clock() - fixtime) > ftime:
                    self.tracker.stopRecording()
                    break
            else:
                # Reset clock if not in box
                fixtime = time.clock()
Exemplo n.º 38
0
 def updateBalloonPos(self):
     return [0, pix2deg(-400 + (deg2pix(self.stimuli.size[1], self.mon) / 2), self.mon)]
Exemplo n.º 39
0
    def fixCheck(self, size, ftime, button):
        """
        Checks that fixation is maintained for certain time.

        :param size: Length of one side of box in degrees visual angle.
        :type size: float or int
        :param ftime: Length of time to check for fixation in seconds.
        :type ftime: int
        :param button: Key to press to recalibrate eye-tracker.
        :type button: char
        """

        # Calculate Fix check borders
        cenX = self.sres[0] / 2.0
        cenY = self.sres[1] / 2.0
        size = deg2pix(size, self.win.monitor) / 2.0

        xbdr = [cenX - size, cenX + size]
        ybdr = [cenY - size, cenY + size]

        # Set status message & Draw box
        self.setStatus('Fixation Check')
        bxmsg = 'draw_box {} {} {} {} 1'.format(xbdr[0], ybdr[0], xbdr[1],
                                                ybdr[1])
        self.tracker.sendCommand(bxmsg)
        
        # Begin recording
        self.tracker.startRecording(0, 0, 1, 1)
        
        # Check which eye is being recorded
        eye_used = self.tracker.eyeAvailable()
        RIGHT_EYE = 1
        LEFT_EYE = 0
        
        # Begin polling
        keys = []
        fixtime = time.clock()
        while self.realconnect:  # only start check loop if real connection

            # Check for recalibration button
            keys = event.getKeys(button)
            if keys:
                self.tracker.stopRecording()
                self.calibrate()
                break 
            
            # Grab latest sample
            sample = self.tracker.getNewestSample()
            
            # Extract gaze coordinates
            if eye_used == RIGHT_EYE:
                gaze = sample.getRightEye().getGaze()
            else:
                gaze = sample.getLeftEye().getGaze()
                
            # Are we in the box?
            if xbdr[0] < gaze[0] < xbdr[1] and ybdr[0] < gaze[1] < ybdr[1]:
                # Have we been in the box long enough?
                if (time.clock() - fixtime) > ftime:
                    self.tracker.stopRecording()
                    break
            else:
                # Reset clock if not in box
                fixtime = time.clock()
Exemplo n.º 40
0
    def __init__(self,
                 win,
                 text="Hello World",
                 font="",
                 pos=(0.0, 0.0),
                 depth=0,
                 rgb=None,
                 color=(1.0, 1.0, 1.0),
                 colorSpace='rgb',
                 opacity=1.0,
                 contrast=1.0,
                 units="",
                 ori=0.0,
                 height=None,
                 antialias=True,
                 bold=False,
                 italic=False,
                 alignHoriz='center',
                 alignVert='center',
                 fontFiles=[],
                 wrapWidth=None,
                 flipHoriz=False,
                 flipVert=False,
                 name='',
                 autoLog=True):
        """
        :Parameters:
            win: A :class:`Window` object.
                Required - the stimulus must know where to draw itself
            text:
                The text to be rendered
            height:
                Height of the characters (including the ascent of the letter and the descent)
            antialias:
                boolean to allow (or not) antialiasing the text
            bold:
                Make the text bold (better to use a bold font name)
            italic:
                Make the text italic (better to use an actual italic font)
            alignHoriz:
                The horizontal alignment ('left', 'right' or 'center')
            alignVert:
                The vertical alignment ('top', 'bottom' or 'center')
            fontFiles:
                A list of additional files if the font is not in the standard system location (include the full path)
            wrapWidth:
                The width the text should run before wrapping
            flipHoriz : boolean
                Mirror-reverse the text in the left-right direction
            flipVert : boolean
                Mirror-reverse the text in the up-down direction
        """

        #what local vars are defined (these are the init params) for use by __repr__
        self._initParams = dir()
        self._initParams.remove('self')

        BaseVisualStim.__init__(self,
                                win,
                                units=units,
                                name=name,
                                autoLog=False)

        self.useShaders = win._haveShaders  #use shaders if available by default, this is a good thing
        self._needUpdate = True
        self.alignHoriz = alignHoriz
        self.alignVert = alignVert
        self.antialias = antialias
        self.bold = bold
        self.italic = italic
        self.text = ''  #NB just a placeholder - real value set below
        self.depth = depth
        self.ori = ori
        self.wrapWidth = wrapWidth
        self.flipHoriz = flipHoriz
        self.flipVert = flipVert
        self._pygletTextObj = None

        self.pos = numpy.array(pos, float)

        #height in pix (needs to be done after units which is done during _Base.__init__)
        if self.units == 'cm':
            if height == None: self.height = 1.0  #default text height
            else: self.height = height
            self.heightPix = cm2pix(self.height, win.monitor)
        elif self.units in ['deg', 'degs']:
            if height == None: self.height = 1.0
            else: self.height = height
            self.heightPix = deg2pix(self.height, win.monitor)
        elif self.units == 'norm':
            if height == None: self.height = 0.1
            else: self.height = height
            self.heightPix = self.height * win.size[1] / 2
        elif self.units == 'height':
            if height == None: self.height = 0.2
            else: self.height = height
            self.heightPix = self.height * win.size[1]
        else:  #treat units as pix
            if height == None: self.height = 20
            else: self.height = height
            self.heightPix = self.height

        if self.wrapWidth == None:
            if self.units in ['height', 'norm']: self.wrapWidth = 1
            elif self.units in ['deg', 'degs']: self.wrapWidth = 15
            elif self.units == 'cm': self.wrapWidth = 15
            elif self.units in ['pix', 'pixels']: self.wrapWidth = 500
        if self.units == 'norm':
            self._wrapWidthPix = self.wrapWidth * win.size[0] / 2
        elif self.units == 'height':
            self._wrapWidthPix = self.wrapWidth * win.size[0]
        elif self.units in ['deg', 'degs']:
            self._wrapWidthPix = deg2pix(self.wrapWidth, win.monitor)
        elif self.units == 'cm':
            self._wrapWidthPix = cm2pix(self.wrapWidth, win.monitor)
        elif self.units in ['pix', 'pixels']:
            self._wrapWidthPix = self.wrapWidth

        #generate the texture and list holders
        self._listID = GL.glGenLists(1)
        if not self.win.winType == "pyglet":  #pygame text needs a surface to render to
            self._texID = GL.GLuint()
            GL.glGenTextures(1, ctypes.byref(self._texID))

        self.colorSpace = colorSpace
        if rgb != None:
            logging.warning(
                "Use of rgb arguments to stimuli are deprecated. Please use color and colorSpace args instead"
            )
            self.setColor(rgb, colorSpace='rgb', log=False)
        else:
            self.setColor(color, log=False)

        self._calcPosRendered()
        for thisFont in fontFiles:
            pyglet.font.add_file(thisFont)
        self.setFont(font, log=False)
        self.opacity = float(opacity)
        self.contrast = float(contrast)
        self.setText(
            text, log=False
        )  #self.width and self.height get set with text and calcSizeRednered is called
        self._needUpdate = True

        #set autoLog (now that params have been initialised)
        self.autoLog = autoLog
        if autoLog:
            logging.exp("Created %s = %s" % (self.name, str(self)))
Exemplo n.º 41
0
    def __init__(
        self, 
        no_demographics = False, 
        task = 'test'):        
        
        if task=='main':
            self.sub = self.solicit_subid()
            demographics = self.solicit_demographics(no_demographics)            
            iohub_config = {
                'experiment_code': 'color',
                'datastore_name': node(),
                'session_code': task,
                'experiment_info': {
                    'version': str(git.repo.fun.rev_parse(git.Repo(), 'HEAD'))[0:6]},
                'session_info': {
                'user_variables': {
                    'date': datetime.now().strftime("%d-%m-%Y_%H-%M-%S"),
                    'sub': self.sub,
                    'sex': demographics[0],
                    'ethnicity': demographics[1],
                    'race': demographics[2],
                    'age': demographics[3]}}}
        else:
            iohub_config = {}
            self.sub = 0

        self.trialdf = self.__prep_df(os.path.join('stimuli','design.csv'))

        self.io = iohub_config

        # Inform the ioHub server about the TrialHandler
        # randomization already done (hence 'sequential')
        self.io.createTrialHandlerRecordTable(data.TrialHandler(
            [x for x in (self.trialdf.T.to_dict()).values()], 
            nReps=1,
            method='sequential')) 

        self.mon = monitors.Monitor("default", distance=60.96)

        # create a window to draw in
        self.win = visual.Window(
            size=(1920, 1080),
            fullscr=True,
            allowGUI=False,
            winType='pyglet',
            blendMode='avg', 
            useFBO=True,
            units="deg",
            monitor=self.mon,
            # gamma = [r.gamma, g.gamma, b.gamma],
            color='black')

        if task == 'main':
            runinfo = RunTimeInfo(verbose=True, userProcsDetailed=True, win=self.win, refreshTest=True)
            with open(os.path.join('data-raw', f'sub-{self.sub}_task-{task}_runinfo.pkl'), 'xb') as f:pickle.dump(runinfo, f)

        self.fix = visual.Circle(win=self.win, radius=self.fix_radius, size=1, fillColor="white")

        # cues for color
        self.triangle = visual.Polygon(win=self.win, radius=self.radius, lineColor="gray", lineWidth=self.linewidth)
        self.circle = visual.Circle(win=self.win, radius=self.radius, lineColor="gray", lineWidth=self.linewidth)

        # cues for direction
        self.line1 = visual.Line(win=self.win, lineWidth=self.linewidth, start=(-math.sqrt(3)/2, math.sqrt(3)/2), 
            end=(math.sqrt(3)/2, -math.sqrt(3)/2), lineColor="gray", size=self.radius)
        self.line2 = visual.Line(win=self.win, lineWidth=self.linewidth, start=(-math.sqrt(3)/2, -math.sqrt(3)/2), 
            end=(math.sqrt(3)/2, math.sqrt(3)/2), lineColor="gray", size=self.radius)
        self.fleur = visual.ShapeStim(win=self.win, size=self.radius, lineColor="gray", lineWidth=self.linewidth, vertices=self.__make_vertices())

        self.dots = WrappedDot(
            win=self.win,
            units='deg',
            fieldShape="circle",
            dotSize=deg2pix(self.dotsize_deg, self.mon),
            dotLife=-1,
            coherence=self.dot_coherence,
            nDots=self.ndots,
            fieldSize=self.dotfield_diameter_deg,
            speed=self.dot_speed_deg_per_sec / self.refresh_rate)

        self.correct = TextStim(self.win, text = "CORRECT", color="green")
        self.incorrect = TextStim(self.win, text = "INCORRECT", color="red")
        self.welcome = TextStim(self.win, text=
        '''
        welcome to the experiment

        Press "c" to continue
        ''',
        wrapWidth=50,
        alignText="left")
        self.rest = TextStim(
            self.win,
            pos=(0, 6),
            alignText="left",
            wrapWidth=50)

        self.waiter = clock.StaticPeriod(screenHz=self.refresh_rate)
## create files
filename = data_dir + os.sep + '%s_%s' % (expInfo['participant'],
                                          expInfo['date'])
filename_short = data_dir + os.sep + expInfo['participant']
output = "subject_id,trial,coherence_trial,pressed_rdk,expected_rdk,rt_rdk, accuracy_rdk, dots_coord\n"
running_filename = filename_short + '_TEMP.txt'

## * Hardware
##def monitor
mon1 = monitors.Monitor('testMonitor')
mon1.setDistance(50)  #cm
mon1.setWidth(30)  #cm
mon1.setSizePix([800, 600])
mon1.saveMon()
ppd = int(deg2pix(1, mon1, correctFlat=False))  ## ppd for monitor= mon1

## keyboard
kb = keyboard.Keyboard()  # initialize keyboards

## * Stimuli
#create a window to draw in
win = visual.Window(fullscr=False,
                    size=(800, 600),
                    monitor=mon1,
                    color=[0, 0, 0],
                    colorSpace='rgb255',
                    units='deg')
win.setMouseVisible(False)  #hide the mouse cursor!!!

## Instructions
Exemplo n.º 43
0
    def __init__(self, params=None, reportobj=None, subroutines=[]):
        logging.LogFile(params['cwd'] + os.sep + params['logfile'],
                        level=logging.INFO,
                        filemode='w')
        logging.LogFile(level=logging.ERROR)

        logging.info('Using Python ' + sys.version)

        self.args = sys.argv
        self.params = self.update_params(self.args, params)
        self.win = None
        self.model = None
        self.routines = subroutines
        self.thisExp = None
        self.name = params['name']
        self.cwd = params['cwd']
        self.reportobj = reportobj

        if params['monitor'] not in monitors.getAllMonitors():
            logging.error('monitor not found: ' + params.monitor)
            logging.info('available monitors: ' +
                         ' '.join(monitors.getAllMonitors()))
            logging.info('Define monitor in monitor center.')
            logging.info('To launch monitor center, type:\n'
                         'python -m psychopy.monitors.MonitorCenter')
            core.quit()
        else:
            self.monitor = monitors.Monitor(
                '',
                width=None,
                distance=self.params['viewing_distance'],
                gamma=None,
                notes=None,
                useBits=None,
                verbose=True,
                currentCalib=None,
                autoLog=True)
            self.monitor.setSizePix((1920, 1280))
            self.monitor.setWidth(54.15)

            logging.exp('using monitor: ' + self.params['monitor'] +
                        ' with viewing_distance=' +
                        str(self.params['viewing_distance']) + ' cm\n' +
                        ' resolution (pixel/deg): ' +
                        str(deg2pix(1, self.monitor)))
            # TODO: change screen_pixel_size back to calculation from monitor
            # TODO: change the monitor definition width so that screen_pixel_size=0.282
            if 'screen_pixel_size' not in self.params['model']:
                self.params['model']['screen_pixel_size'] = pix2cm(
                    1, self.monitor) * 10.0

            #self.params['model']['screen_pixel_size'] = 0.282
            self.params['model'][
                'viewing_distance'] = self.params['viewing_distance'] / 2.54

        self.expInfo = params['exp_info']
        self.expInfo['date'] = data.getDateStr()

        # Ensure that relative paths start from the same directory as this script
        _thisDir = os.path.dirname(os.path.abspath(__file__))
        os.chdir(_thisDir)
        # Data file name stem = absolute path + name; later add .psyexp, .csv, .log, etc
        runsubject = self.params['runsubject']
        if runsubject:
            self.filename = self.cwd + os.sep + u'data' + os.sep + '%s_%s_%s' % (
                self.expInfo['participant'], params['name'],
                self.expInfo['date'])
        else:
            self.filename = self.cwd + os.sep + u'data' + os.sep + 'model-' + params[
                'expName']
Exemplo n.º 44
0
 def _calcPosRendered(self):
     """Calculate the pos of the stimulus in coords of the :class:`~psychopy.visual.Window` (normalised or pixels)"""
     if self.units in ['pix', 'pixels', 'height', 'norm']: self._posRendered=self.pos
     elif self.units in ['deg', 'degs']: self._posRendered=deg2pix(self.pos, self.win.monitor)
     elif self.units=='cm': self._posRendered=cm2pix(self.pos, self.win.monitor)
Exemplo n.º 45
0
    def doCalibration(self,
                      calibrationPoints=[(0.5, 0.5), (0.1, 0.9), (0.1, 0.1),
                                         (0.9, 0.9), (0.9, 0.1)],
                      calinRadius=2.0,
                      caloutRadius=None,
                      moveFrames=60):
        if self.eyetracker is None:
            return

        # set default
        if caloutRadius is None:
            caloutRadius = calinRadius * 20.0
        if calibrationPoints is None:
            calibrationPoints = [(0.5, 0.5), (0.1, 0.9), (0.1, 0.1),
                                 (0.9, 0.9), (0.9, 0.1)]

        self.points = np.random.permutation(calibrationPoints)

        # Make the "outer" circle
        self.calout = psychopy.visual.Circle(self.win,
                                             radius=caloutRadius,
                                             lineColor=(0, 1.0, 0),
                                             fillColor=(0.5, 1.0, 0.5),
                                             units='pix',
                                             autoDraw=True,
                                             pos=self.acsd2pix(
                                                 self.points[-1]))
        # Make a dummy message
        self.calmsg = psychopy.visual.TextStim(self.win,
                                               color=0.0,
                                               units='norm',
                                               height=0.07,
                                               pos=(0.0, -0.5))

        # Put the eye tracker into the calibration state
        self.initcalibration_completed = False
        print "Start new calibration..."
        self.eyetracker.StartCalibration(callback=self.on_calib_start)
        while not self.initcalibration_completed:
            psychopy.core.wait(0.1)
            if psychopy.event.getKeys(keyList=['escape']):
                raise KeyboardInterrupt("You interrupted the script.")
        self.deletecalibration_completed = False
        # Clear out previous calibrations (tobii scanners
        # sometimes store these across many sessions)
        print "Delete old calibration..."
        self.eyetracker.ClearCalibration(callback=self.on_calib_deleted)
        while not self.deletecalibration_completed:
            psychopy.core.wait(0.1)
            if psychopy.event.getKeys(keyList=['escape']):
                raise KeyboardInterrupt("You interrupted the script.")

        # Draw instructions and wait for space key
        self.calmsg.text = ("Please focus your eyes on the green dot, and "
                            "follow it with your eyes when it moves. This "
                            "will help us calibrate the eye tracker.\n"
                            "Press space when you're ready.")
        self.calmsg.draw()
        self.win.flip()
        psychopy.event.waitKeys(keyList=['space'])

        # Go through the calibration points
        for self.point_index in range(len(self.points)):
            # The dot starts at the previous point
            self.calout.pos = \
                self.acsd2pix((self.points[self.point_index - 1][0],
                               self.points[self.point_index - 1][1]))
            # The steps for the movement is new - old divided by frames
            self.step = (self.acsd2pix((self.points[self.point_index][0],
                                        self.points[self.point_index][1])) -
                         self.calout.pos) / moveFrames

            # Create a tobii 2D class
            p = Point2D()
            # Add the X and Y coordinates to the tobii point
            p.x, p.y = self.points[self.point_index]

            # Move the point in position (smooth pursuit)
            for frame in range(moveFrames):
                self.calout.pos += self.step
                # draw & flip
                self.win.flip()

            # Shrink the outer point (gaze fixation)
            for frame in range(moveFrames / 2):
                self.calout.radius -= (caloutRadius -
                                       calinRadius) / (moveFrames / 2)
                self.win.flip()

            # Add this point to the tobii
            psychopy.core.wait(
                1)  # first wait to let the eyes settle (MIN 0.5)
            self.add_point_completed = False  # this gets updated by callback
            self.eyetracker.AddCalibrationPoint(p,
                                                callback=self.on_add_completed)
            # While this point is being added, do nothing:
            while not self.add_point_completed:
                psychopy.core.wait(0.1)
                if psychopy.event.getKeys(keyList=['escape']):
                    raise KeyboardInterrupt("You interrupted the script.")
            psychopy.core.wait(0.5)  # wait before continuing

            # Reset the radius of the large circle
            self.calout.radius = caloutRadius

        # After calibration, make sure the stimuli aren't drawn
        self.calout.autoDraw = False
        self.calout = None

        # The following two will be set by the tobii SDK
        self.computeCalibration_completed = False
        self.computeCalibration_succeeded = False
        # Do the computation
        self.eyetracker.ComputeCalibration(self.on_calib_compute)
        while not self.computeCalibration_completed:
            psychopy.core.wait(0.1)
            if psychopy.event.getKeys(keyList=['escape']):
                raise KeyboardInterrupt("You interrupted the script.")
        self.eyetracker.StopCalibration(None)

        self.win.flip()

        # Now we retrieve the calibration data
        self.getcalibration_completed = False
        self.calib = self.eyetracker.GetCalibration(self.on_calib_response)
        while not self.getcalibration_completed:
            psychopy.core.wait(0.1)
            if psychopy.event.getKeys(keyList=['escape']):
                raise KeyboardInterrupt("You interrupted the script.")

        if not self.computeCalibration_succeeded:
            # computeCalibration failed.
            self.calmsg.text = ("Not enough data was collected "
                                "(Retry:[r] Abort:[ESC])")
        elif self.calib is None:
            # no calibration data
            self.calmsg.text = ("No calibration data "
                                "(Retry:[r] Abort:[ESC])")
        else:
            # calibration seems to have worked out
            points = {}
            for data in self.calib.plot_data:
                points[data.true_point] = {
                    'left': data.left,
                    'right': data.right
                }

            if len(points) == 0:
                # no points in the calibration results
                self.calmsg.text = ("No calibration data "
                                    "(Retry:[r] Abort:[ESC])")
            else:
                # draw the calibration result
                for p, d in points.iteritems():
                    psychopy.visual.Circle(self.win,
                                           radius=calinRadius,
                                           fillColor=(1, 1, 1),
                                           units='pix',
                                           pos=(p.x - 0.5, 0.5 - p.y)).draw()
                    if d['left'].status == 1:
                        psychopy.visual.Line(
                            self.win,
                            units='pix',
                            lineColor='yellow',
                            start=(self.acsd2pix((p.x, p.y))),
                            end=(self.acsd2pix(
                                (d['left'].map_point.x,
                                 d['left'].map_point.y)))).draw()
                    if d['right'].status == 1:
                        psychopy.visual.Line(
                            self.win,
                            units='pix',
                            lineColor='blue',
                            start=(self.acsd2pix((p.x, p.y))),
                            end=(self.acsd2pix(
                                (d['right'].map_point.x,
                                 d['right'].map_point.y)))).draw()
                for p in self.points:
                    psychopy.visual.Circle(
                        self.win,
                        radius=calinRadius,
                        fillColor=1,
                        units='pix',
                        pos=self.acsd2pix(p),
                    ).draw()
                    psychopy.visual.Circle(
                        self.win,
                        units='pix',
                        lineColor=-0.5,
                        radius=deg2pix(0.9, self.win.monitor),
                        pos=self.acsd2pix(p),
                    ).draw()
                self.calmsg.text = ("Accept calibration results\n"
                                    "(Accept:[a] Retry:[r] Abort:[ESC])")

        # Update the screen, then wait for response
        self.calmsg.draw()
        self.win.flip()
        self.response = psychopy.event.waitKeys(keyList=['a', 'r', 'escape'])
        if 'a' in self.response:
            retval = 'accept'
        elif 'r' in self.response:
            retval = 'retry'
        elif 'escape' in self.response:
            retval = 'abort'

        return retval
Exemplo n.º 46
0
    def run(self,
            runmodel=None,
            genstim=False,
            trialparams={},
            params={},
            loopstate={}):
        self.update_params(trialparams, loopstate)
        resp = Response()
        filename = ''
        # If we have a window then either we are generating stimuli or
        #   running a human subject experiment
        if self.win is not None:
            for component in self.components:
                if genstim:
                    filename = self.get_filename(trialparams, loopstate)
                    component.start(trialparams, loopstate)
                else:
                    component.run(trialparams, loopstate)

            left, top = params['model']['view_pos']
            width, height = params['model']['view_size']

            ###################
            #diff_x,diff_y = params['diff_size']
            diff_x, diff_y = 1.0, 1.0  # TODO: remove all this diff_size code
            width_window, height_window = self.win.size
            center_left = int(deg2pix(left, self.win.monitor))
            center_top = int(deg2pix(top, self.win.monitor))
            upper_left = center_left - int(0.5 * width)
            upper_top = center_top + int(0.5 * height)
            if width % 2 == 0:  # width is even number of pixels
                lower_right = upper_left + width
                lower_bottom = upper_top - height
            else:
                lower_right = upper_left + (width + 1)
                lower_bottom = upper_top - (height + 1)
            # convert to normalized coords
            upper_left_norm = upper_left / (0.5 * width_window)
            upper_top_norm = upper_top / (0.5 * height_window)
            lower_right_norm = lower_right / (0.5 * width_window)
            lower_bottom_norm = lower_bottom / (0.5 * height_window)
            rect = [
                upper_left_norm, upper_top_norm, lower_right_norm * diff_x,
                lower_bottom_norm * diff_y
            ]
            extra = 2  # extra space to draw lines around rect
            if not self.viewport:
                self.viewport = visual.Rect(win=self.win,
                                            width=width + extra,
                                            height=height + extra,
                                            autoLog=None,
                                            units='pixels',
                                            lineWidth=1,
                                            lineColor='yellow',
                                            lineColorSpace='rgb',
                                            fillColor=None,
                                            fillColorSpace='rgb',
                                            pos=(center_left, center_top),
                                            ori=0.0,
                                            opacity=0.5,
                                            contrast=0.8,
                                            depth=1000,
                                            interpolate=False,
                                            name=None,
                                            autoDraw=True)
            else:
                self.viewport.setPos((center_left, center_top))

            self.win.flip()

            if genstim:
                model_stimulus = self.win._getFrame(rect=None, buffer='front')
                model_stimulus = np.array(model_stimulus)
                center_height = int(height_window / 2) + center_top
                center_width = int(width_window / 2) + center_left
                top_height = int(center_height - height / 2)
                bottom_height = int(center_height + height / 2)
                top_width = int(center_width - width / 2)
                bottom_width = int(center_width + width / 2)
                model_stimulus = model_stimulus[top_height:bottom_height,
                                                top_width:bottom_width, :]
                model_stimulus = Image.fromarray(model_stimulus)
                if np.shape(model_stimulus)[1] != width or np.shape(
                        model_stimulus)[0] != height:
                    print(np.shape(model_stimulus))
                    import pdb
                    pdb.set_trace()
                    # TODO: Make it so this exception gets caught
                    logging.error('problem with model window')
                    raise Exception(
                        'Error in calculating shape of model window.')

                if 'model' in os.path.join(params['cwd'],
                                           'stimuli' + os.sep + filename):
                    import pdb
                    pdb.set_trace()

                logging.info('saving: ' + filename)
                model_stimulus.save(
                    os.path.join(params['cwd'], 'stimuli' + os.sep + filename),
                    "PNG")
                keys = []
            else:
                keys = event.waitKeys(maxWait=self.timeout, keyList=None)
                resp = Response(key=keys[0])
                for component in self.components:
                    component.end()
        else:
            filename = self.get_filename(trialparams, loopstate)
            # if we don't have a target_contrast in the parameters
            #  then we need to load an image and get its target_contrast
            if 'target_contrast' not in params['model']:
                target_key, target_val = params['target_identifier']
                target_condition = Params(trialparams)
                target_condition.update({target_key: target_val})
                target_filename = self.get_filename(target_condition,
                                                    loopstate)

                # load stim from filename
                im = Image.open(
                    os.path.join(params['cwd'], 'stimuli' + os.sep +
                                 target_filename)).convert('L')
                im_array = np.frombuffer(im.tobytes(), dtype=np.uint8)
                width, height = im.size
                im_array = im_array.reshape((height, width))
                contrast = runmodel.process(data=im_array)
                runmodel.target_contrast = contrast
                logging.exp('Generating target_contrast from ' +
                            target_filename)
                logging.exp('Using target_contrast = ' + str(contrast))
                params['model']['target_contrast'] = contrast
                runmodel.update_decision_params()
                params['model']['decision_K'] = runmodel.decision_K
                params['model']['decision_sigma'] = runmodel.decision_sigma
                logging.exp('***Using decision_K = ' +
                            str(runmodel.decision_K))
                logging.exp('***Using decision_sigma = ' +
                            str(runmodel.decision_sigma))

            # load stim from filename define StimulusNotFound and raise it
            im = Image.open(
                os.path.join(params['cwd'],
                             'stimuli' + os.sep + filename)).convert('L')
            im_array = np.frombuffer(im.tobytes(), dtype=np.uint8)
            width, height = im.size
            im_array = im_array.reshape((height, width))

            print('processing:', filename)
            save_conv_filename = None
            if params['saveimages']:
                #save_conv_filename = 'images/'+filename[:-4]+'-convolved.png'
                save_conv_filename = filename[:-4]
            correct_answer, incorrect_answer = self.get_answer(
                trialparams, loopstate)

            # if we are saving additional plots we also want to pass the
            #  corresponding target data to the runmodel.runsponse
            im_array_target = None
            if save_conv_filename:
                target_key, target_val = params['target_identifier']
                target_condition = Params(trialparams)
                target_condition.update({target_key: target_val})
                target_filename = self.get_filename(target_condition,
                                                    loopstate)

                # load stim from filename
                im_target = Image.open(
                    os.path.join(params['cwd'], 'stimuli' + os.sep +
                                 target_filename)).convert('L')
                im_array_target = np.frombuffer(im_target.tobytes(),
                                                dtype=np.uint8)
                width_target, height_target = im_target.size
                im_array_target = im_array_target.reshape(
                    (height_target, width_target))

            # TODO: if we are generating all the plots then read the target data
            #       from the file using the 'target_identifier' from params
            #       then pass into runmodel.response
            key, prob, contrast = runmodel.response(
                data=im_array,
                correct_answer=correct_answer,
                incorrect_answer=incorrect_answer,
                save_conv_filename=save_conv_filename,
                target_data=im_array_target)

            correct = False
            if key == correct_answer:
                correct = True
            resp = Response(key=key,
                            rt=0,
                            correct=correct,
                            prob=prob,
                            contrast=contrast)
            #keys = ['left']

        if resp.key == 'escape':
            core.quit()

        return resp
Exemplo n.º 47
0
    def __init__(self, win,
                 text="Hello World",
                 font="",
                 pos=(0.0,0.0),
                 depth=0,
                 rgb=None,
                 color=(1.0,1.0,1.0),
                 colorSpace='rgb',
                 opacity=1.0,
                 contrast=1.0,
                 units="",
                 ori=0.0,
                 height=None,
                 antialias=True,
                 bold=False,
                 italic=False,
                 alignHoriz='center',
                 alignVert='center',
                 fontFiles=[],
                 wrapWidth=None,
                 flipHoriz=False, flipVert=False,
                 name='', autoLog=True):
        """
        :Parameters:
            win: A :class:`Window` object.
                Required - the stimulus must know where to draw itself
            text:
                The text to be rendered
            height:
                Height of the characters (including the ascent of the letter and the descent)
            antialias:
                boolean to allow (or not) antialiasing the text
            bold:
                Make the text bold (better to use a bold font name)
            italic:
                Make the text italic (better to use an actual italic font)
            alignHoriz:
                The horizontal alignment ('left', 'right' or 'center')
            alignVert:
                The vertical alignment ('top', 'bottom' or 'center')
            fontFiles:
                A list of additional files if the font is not in the standard system location (include the full path)
            wrapWidth:
                The width the text should run before wrapping
            flipHoriz : boolean
                Mirror-reverse the text in the left-right direction
            flipVert : boolean
                Mirror-reverse the text in the up-down direction
        """
        BaseVisualStim.__init__(self, win, units=units, name=name, autoLog=autoLog)

        self.useShaders = win._haveShaders  #use shaders if available by default, this is a good thing
        self._needUpdate = True
        self.alignHoriz = alignHoriz
        self.alignVert = alignVert
        self.antialias = antialias
        self.bold=bold
        self.italic=italic
        self.text='' #NB just a placeholder - real value set below
        self.depth=depth
        self.ori=ori
        self.wrapWidth=wrapWidth
        self.flipHoriz = flipHoriz
        self.flipVert = flipVert
        self._pygletTextObj=None

        self.pos= numpy.array(pos, float)

        #height in pix (needs to be done after units which is done during _Base.__init__)
        if self.units=='cm':
            if height==None: self.height = 1.0#default text height
            else: self.height = height
            self.heightPix = cm2pix(self.height, win.monitor)
        elif self.units in ['deg', 'degs']:
            if height==None: self.height = 1.0
            else: self.height = height
            self.heightPix = deg2pix(self.height, win.monitor)
        elif self.units=='norm':
            if height==None: self.height = 0.1
            else: self.height = height
            self.heightPix = self.height*win.size[1]/2
        elif self.units=='height':
            if height==None: self.height = 0.2
            else: self.height = height
            self.heightPix = self.height*win.size[1]
        else: #treat units as pix
            if height==None: self.height = 20
            else: self.height = height
            self.heightPix = self.height

        if self.wrapWidth ==None:
            if self.units in ['height','norm']: self.wrapWidth=1
            elif self.units in ['deg', 'degs']: self.wrapWidth=15
            elif self.units=='cm': self.wrapWidth=15
            elif self.units in ['pix', 'pixels']: self.wrapWidth=500
        if self.units=='norm': self._wrapWidthPix= self.wrapWidth*win.size[0]/2
        elif self.units=='height': self._wrapWidthPix= self.wrapWidth*win.size[0]
        elif self.units in ['deg', 'degs']: self._wrapWidthPix= deg2pix(self.wrapWidth, win.monitor)
        elif self.units=='cm': self._wrapWidthPix= cm2pix(self.wrapWidth, win.monitor)
        elif self.units in ['pix', 'pixels']: self._wrapWidthPix=self.wrapWidth

        #generate the texture and list holders
        self._listID = GL.glGenLists(1)
        if not self.win.winType=="pyglet":#pygame text needs a surface to render to
            self._texID = GL.GLuint()
            GL.glGenTextures(1, ctypes.byref(self._texID))

        self.colorSpace=colorSpace
        if rgb!=None:
            logging.warning("Use of rgb arguments to stimuli are deprecated. Please use color and colorSpace args instead")
            self.setColor(rgb, colorSpace='rgb', log=False)
        else:
            self.setColor(color, log=False)

        self._calcPosRendered()
        for thisFont in fontFiles:
            pyglet.font.add_file(thisFont)
        self.setFont(font, log=False)
        self.opacity = float(opacity)
        self.contrast = float(contrast)
        self.setText(text, log=False) #self.width and self.height get set with text and calcSizeRednered is called
        self._needUpdate = True