예제 #1
0
 def labelHeight(self, value):
     if isinstance(value, layout.Vector):
         # If given a Size, use it
         self._labelHeight = value
     else:
         # Otherwise, convert to a Size object
         self._labelHeight = layout.Size([None, value],
                                         units=self.units,
                                         win=self.win)
예제 #2
0
 def _getMarkerParams(self):
     """
     Calculates location and size of marker based on own location and size
     """
     # Calculate pos
     pos = self._ratingToPos(self.rating or 0)
     # Get size (and correct for norm)
     size = layout.Size([min(self._size.pix)] * 2, 'pix', self.win)
     # Store marker details
     self.markerParams = {
         'units':
         self.units,
         'pos':
         pos,
         'size':
         size * np.array(self._markerSizeMultiplier) +
         layout.Size(self._markerSizeAddition, self.units, self.win),
     }
예제 #3
0
 def test_unit_mismatch(self):
     """
     Test that a given stimulus can be drawn without error in all combinations of stimulus units x window units and
     checking that it looks the same as when both units are pix.
     """
     # Test all unit types apart from None and ""
     unitTypes = layout.unitTypes[2:]
     # Create window (same size as was used for other tests)
     win = visual.Window(self.obj.win.size, pos=self.obj.win.pos, monitor="testMonitor")
     # Create object
     obj = copy(self.obj)
     obj.win = win
     # Create model image (pix units for both)
     win.units = 'pix'
     obj.units = 'pix'
     obj.draw()
     filename = Path(utils.TESTS_DATA_PATH) / "test_unit_mismatch.png"
     win.getMovieFrame(buffer='back').save(filename)
     if hasattr(obj, "_size"):
         # Get model sizes
         targetSizes = {units: getattr(obj._size, units) for units in unitTypes}
     # Flip screen
     win.flip()
     # Iterate through window and object units
     for winunits in unitTypes:
         for objunits in unitTypes:
             # Create a window and object
             win.units = winunits
             obj.units = objunits
             # Draw object
             obj.draw()
             # Compare appearance
             utils.compareScreenshot(filename, win, tag=f"{winunits}X{objunits}")
             if hasattr(obj, "_size"):
                 # Compare reported size
                 assert layout.Size(obj.size, obj.units, obj.win) == layout.Size(targetSizes[objunits], objunits, obj.win)
             # Flip screen
             win.flip()
     # Close window
     win.close()
     # Delete model image
     filename.unlink()
예제 #4
0
 def labelWrapWidth(self, value):
     if value is None:
         pass
     elif isinstance(value, layout.Vector):
         # If given a Size, use it
         self._labelWrapWidth = value
     else:
         # Otherwise, convert to a Size object
         self._labelWrapWidth = layout.Size([value, None],
                                            units=self.units,
                                            win=self.win)
예제 #5
0
 def test_alignment(self):
     # Define classic use cases
     exemplars = [
         "top left",
         "top center",
         "top right",
         "center left",
         "center center",
         "center right",
         "bottom left",
         "bottom center",
         "bottom right",
     ]
     # Some potentially problematic use cases which should be handled
     tykes = [
         "center",
         "centre",
         "centre centre",
         "someword",
         "more than two words",
     ]
     # Get intial textbox params
     initParams = {}
     for param in [
             "units", "fillColor", "color", "padding", "letterHeight",
             "alignment", "text"
     ]:
         initParams[param] = getattr(self.textbox, param)
     # Test
     for case in exemplars + tykes:
         for units in layout.unitTypes:
             # Setup textbox
             self.textbox.text = "A PsychoPy zealot knows a smidge of wx but JavaScript is the question."
             self.textbox.fillColor = "white"
             self.textbox.color = "black"
             self.textbox.padding = 0
             self.textbox.letterHeight = layout.Size((0, 10),
                                                     units="pix",
                                                     win=self.win)
             # Set test attributes
             self.textbox.units = units
             self.textbox.alignment = case
             # Draw and compare
             self.textbox.draw()
             #self.win.getMovieFrame(buffer='back').save(Path(utils.TESTS_DATA_PATH) / f"textbox_{self.textbox._lineBreaking}_align_{case.replace(' ', '_')}.png")
             utils.compareScreenshot(
                 Path(utils.TESTS_DATA_PATH) /
                 f"textbox_{self.textbox._lineBreaking}_align_{case.replace(' ', '_')}.png",
                 self.win,
                 crit=20)
             self.win.flip()
     # Reset initial params
     for param, value in initParams.items():
         setattr(self.textbox, param, value)
예제 #6
0
 def _getLineParams(self):
     """
     Calculates location and size of the line based on own location and size
     """
     # Store line details
     self.lineParams = {
         'units':
         self.units,
         'pos':
         self.pos,
         'size':
         self._extent * np.array(self._lineSizeMultiplier) +
         layout.Size(self._lineSizeAddition, self.units, self.win)
     }
예제 #7
0
 def _getTickParams(self):
     """ Calculates the locations of the line, tickLines and labels from
     the rating info
     """
     # If categorical, create tick values from labels
     if self.categorical:
         self.ticks = np.arange(len(self.labels))
         self.granularity = 1.0
     # Calculate positions
     xys = self._ratingToPos(self.ticks)
     # Get size (and correct for norm)
     size = layout.Size([min(self._extent.pix)] * 2, 'pix', self.win)
     # Store tick details
     self.tickParams = {
         'units':
         self.units,
         'xys':
         xys,
         'sizes':
         np.tile(
             getattr(size, self.units) * np.array(self._tickSizeMultiplier)
             + np.array(self._tickSizeAddition), (len(self.ticks), 1)),
     }
예제 #8
0
    def __init__(
            self,
            win,
            ticks=(1, 2, 3, 4, 5),
            labels=None,
            startValue=None,
            pos=(0, 0),
            size=None,
            units=None,
            flip=False,
            ori=0,
            style='rating',
            styleTweaks=[],
            granularity=0,
            readOnly=False,
            labelColor='White',
            markerColor='Red',
            lineColor='White',
            colorSpace='rgb',
            opacity=None,
            font='Helvetica Bold',
            depth=0,
            name=None,
            labelHeight=None,
            labelWrapWidth=None,
            autoDraw=False,
            autoLog=True,
            # Synonyms
            color=False,
            fillColor=False,
            borderColor=False):
        """

        Parameters
        ----------
        win : psychopy.visual.Window
            Into which the scale will be rendered

        ticks : list or tuple
            A set of values for tick locations. If given a list of numbers then
            these determine the locations of the ticks (the first and last
            determine the endpoints and the rest are spaced according to
            their values between these endpoints.

        labels : a list or tuple
            The text to go with each tick (or spaced evenly across the ticks).
            If you give 3 labels but 5 tick locations then the end and middle
            ticks will be given labels. If the labels can't be distributed
            across the ticks then an error will be raised. If you want an
            uneven distribution you should include a list matching the length
            of ticks but with some values set to None

        pos : XY pair (tuple, array or list)

        size : w,h pair (tuple, array or list)
            The size for the scale defines the area taken up by the line and
            the ticks.
            This also controls whether the scale is horizontal or vertical.

        units : the units to interpret the pos and size

        flip : bool
            By default the labels will be below or left of the line. This
            puts them above (or right)

        granularity : int or float
            The smallest valid increments for the scale. 0 gives a continuous
            (e.g. "VAS") scale. 1 gives a traditional likert scale. Something
            like 0.1 gives a limited fine-grained scale.

        labelColor / color :
            Color of the labels according to the color space

        markerColor / fillColor :
            Color of the marker according to the color space

        lineColor / borderColor :
            Color of the line and ticks according to the color space

        font : font name

        autodraw :

        depth :

        name :

        autoLog :
        """
        # what local vars are defined (these are the init params) for use by
        # __repr__
        self._initParams = dir()
        super(Slider, self).__init__(name=name, autoLog=False)

        self.win = win
        if ticks is None:
            self.ticks = None
        else:
            self.ticks = np.array(ticks)
        self.labels = labels
        # Set pos and size via base method as objects don't yet exist to layout
        self.units = units
        WindowMixin.pos.fset(self, pos)
        if size is None:
            size = layout.Size((1, 0.1), 'height', self.win)
        WindowMixin.size.fset(self, size)
        # Set multiplier and additions to each component's size
        self._markerSizeMultiplier = (1, 1)
        self._markerSizeAddition = (0, 0)
        self._lineSizeMultiplier = (1, 1)
        self._lineSizeAddition = (0, 0)
        self._tickSizeMultiplier = (1, 1)
        self._tickSizeAddition = (0, 0)

        self.granularity = granularity
        self.colorSpace = colorSpace
        self.color = color if color is not False else labelColor
        self.fillColor = fillColor if fillColor is not False else markerColor
        self.borderColor = borderColor if borderColor is not False else lineColor
        self.opacity = opacity
        self.font = font
        self.autoDraw = autoDraw
        self.depth = depth
        self.name = name
        self.autoLog = autoLog
        self.readOnly = readOnly
        self.ori = ori
        self.flip = flip

        self.startValue = self.markerPos = startValue
        self.rt = None
        self.history = []
        self.marker = None
        self.line = None
        self.tickLines = None
        self.labelWrapWidth = labelWrapWidth
        self.labelHeight = labelHeight or min(self.size) / 2
        self._updateMarkerPos = True
        self._dragging = False
        self.mouse = event.Mouse(win=win)
        self._mouseStateClick = None  # so we can rule out long click probs
        self._mouseStateXY = None  # so we can rule out long click probs

        self.validArea = None
        # Create elements
        self._createElements()
        self.styleTweaks = []
        self.style = style
        self.styleTweaks += styleTweaks
        self._layout()
        # some things must wait until elements created
        self.contrast = 1.0

        # set autoLog (now that params have been initialised)
        self.autoLog = autoLog
        if autoLog:
            logging.exp("Created %s = %s" % (self.name, repr(self)))
        self.status = NOT_STARTED
        self.responseClock = core.Clock()
예제 #9
0
 def extent(self, value):
     self._extent = layout.Size(self.extent, self.units, self.win)
예제 #10
0
def contains_overlaps(testType):
    for units in ['pix', 'height', 'norm', 'cm', 'deg']:
        win.units = units
        # Create shape to test with
        vertices = [(0.05, 0.24), (0.05, -0.24), (-0.05, -0.24), (-0.05, 0.24)]
        shape = visual.ShapeStim(win, vertices=vertices, autoLog=False)
        # Create circles to show where each point is on screen
        testPoints = []
        for p in points:
            testPoints.append(
                visual.Circle(win,
                              size=layout.Size((10, 10), 'pix', win),
                              pos=p,
                              units=units,
                              autoLog=False))
        # Try each point / posture combo
        for i in range(len(postures)):
            # Set shape attrs
            shape.setOri(postures[i]['ori'], log=False)
            shape.setSize(getattr(postures[i]['size'], units), log=False)
            shape.setPos(getattr(postures[i]['pos'], units), log=False)
            shape.draw()
            # Test each point
            for j in range(len(testPoints)):
                pointMarker = testPoints[j]
                p = points[j]
                # Check contains / overlap
                if testType == 'contains':
                    res = shape.contains(getattr(p, units))
                    # test for two parameters
                    x = getattr(p, units)[0]
                    y = getattr(p, units)[1]
                    assert (shape.contains(x, y) == res)
                elif testType == 'overlaps':
                    res = shape.overlaps(testPoints[j])
                else:
                    raise ValueError('Invalid value for parameter `testType`.')

                # Is the point within the shape? Green == yes, red == no.
                pointMarker.setFillColor('green' if res else 'red', log=False)
                pointMarker.draw()

                # Assert
                if res != correctResults[i][j]:
                    # Output debug image
                    pointMarker.setBorderColor(
                        'green' if correctResults[i][j] else 'red', log=False)
                    for marker in testPoints:
                        marker.draw()
                    shape.draw()
                    win.flip()
                    win.screenshot.save(
                        Path(utils.TESTS_DATA_PATH) /
                        f"{testType}_error_local_{i}_{j}.png")
                    # Raise error
                    print(res, points[j], i, j)
                    raise AssertionError(
                        dbgStr %
                        (testType, units, postures[i]['ori'], shape._size,
                         shape._pos, points[j], correctResults[i][j]))
                # Un-highlight marker
                pointMarker.draw()
            win.flip()
예제 #11
0
unitDist = 0.2
sqrt2 = sqrt(2)

points = [
    layout.Position((0, 0), 'height', win),
    layout.Position((0, unitDist), 'height', win),
    layout.Position((0, unitDist * 2), 'height', win),
    layout.Position(((unitDist / sqrt2), (unitDist / sqrt2)), 'height', win),
    layout.Position((unitDist * sqrt2, 0), 'height', win),
    layout.Position((unitDist * sqrt2, unitDist * sqrt2), 'height', win)
]

postures = [{
    'ori': 0,
    'size': layout.Size((1.0, 1.0), 'height', win),
    'pos': layout.Position((0, 0), 'height', win)
}, {
    'ori': 0,
    'size': layout.Size((1.0, 2.0), 'height', win),
    'pos': layout.Position((0, 0), 'height', win)
}, {
    'ori': 45,
    'size': layout.Size((1.0, 1.0), 'height', win),
    'pos': layout.Position((0, 0), 'height', win)
}, {
    'ori': 45,
    'size': layout.Size((2.0, 2.0), 'height', win),
    'pos': layout.Position((0, 0), 'height', win)
}, {
    'ori': 0,
예제 #12
0
    def __init__(self,
                 win,
                 newPos=None,
                 visible=True,
                 leftLimit=None,
                 topLimit=None,
                 rightLimit=None,
                 bottomLimit=None,
                 showLimitBox=False,
                 clickOnUp=False,
                 pointer=None,
                 name=None,
                 autoLog=None):
        """Class for customizing the appearance and behavior of the mouse.

        Use a custom mouse for extra control over the pointer appearance
        and function. It's probably slower to render than the regular
        system mouse. Create your `visual.Window` before creating a
        CustomMouse.

        Parameters
        ----------

        win : required, `visual.Window`
            the window to which this mouse is attached
        visible : **True** or False
            makes the mouse invisible if necessary
        newPos : **None** or [x,y]
            gives the mouse a particular starting position
        leftLimit :
            left edge of a virtual box within which the mouse can move
        topLimit :
            top edge of virtual box
        rightLimit :
            right edge of virtual box
        bottomLimit :
            lower edge of virtual box
        showLimitBox : default is False
            display the boundary within which the mouse can move.
        pointer :
            The visual display item to use as the pointer;
            must have .draw() and setPos() methods. If your item has
            .setOpacity(), you can alter the mouse's opacity.
        clickOnUp : when to count a mouse click as having occurred
            default is False, record a click when the mouse is first
            pressed down. True means record a click when the mouse
            button is released.

        """
        # what local vars are defined (these are the init params) for use by
        # __repr__
        self._initParams = dir()
        self._initParams.remove('self')
        super(CustomMouse, self).__init__(name=name, autoLog=False)
        self.autoLog = False  # set properly at end of init

        self.win = win
        self.mouse = event.Mouse(win=self.win)

        # maybe inheriting from Mouse would be easier? its not that simple
        self.getRel = self.mouse.getRel
        self.getWheelRel = self.mouse.getWheelRel
        self.mouseMoved = self.mouse.mouseMoved  # FAILS
        self.mouseMoveTime = self.mouse.mouseMoveTime
        self.getPressed = self.mouse.getPressed
        self.clickReset = self.mouse.clickReset  # ???
        self._pix2windowUnits = self.mouse._pix2windowUnits
        self._windowUnits2pix = self.mouse._windowUnits2pix

        # the graphic to use as the 'mouse' icon (pointer)
        if pointer:
            self.pointer = pointer
        else:
            self.pointer = vm = visual.ShapeStim(
                win,
                vertices=[
                    [-0.5, 0.5],
                    [-0.5, -0.35],
                    [-0.3, -0.15],
                    [-0.1, -0.5],
                    [0.025, -0.425],
                    [-0.175, -0.1],
                    [0.1, -0.1],
                    [-0.5, 0.5],
                ],
                fillColor="white",
                lineColor="black",
                lineWidth=1,
                anchor="top left",
                size=layout.Size((20, 20), 'pix', win),
            )
        self.mouse.setVisible(False)  # hide the actual (system) mouse
        self.visible = visible  # the custom (virtual) mouse

        self.leftLimit = self.rightLimit = None
        self.topLimit = self.bottomLimit = None
        self.setLimit(leftLimit=leftLimit,
                      topLimit=topLimit,
                      rightLimit=rightLimit,
                      bottomLimit=bottomLimit)
        self.showLimitBox = showLimitBox

        self.lastPos = None
        self.prevPos = None
        if newPos is not None:
            self.lastPos = newPos
        else:
            self.lastPos = self.mouse.getPos()

        # for counting clicks:
        self.clickOnUp = clickOnUp
        self.wasDown = False  # state of mouse 1 frame prior to current frame
        self.clicks = 0  # how many mouse clicks since last reset
        self.clickButton = 0  # which button to count clicks for; 0 = left

        # set autoLog now that params have been initialised
        wantLog = autoLog is None and self.win.autoLog
        self.__dict__['autoLog'] = autoLog or wantLog
        if self.autoLog:
            logging.exp("Created %s = %s" % (self.name, str(self)))