Wrong mouse location with Psychopy 3.06 with MacOS Retina display

I’m running the following program on macOS 10.14.3 Mojave

from psychopy import core,visual,event

def checkKeyboard():
    for key in event.getKeys():
        if key in ['q']:
            sys.exit()
        else:
            event.clearEvents()

win = visual.Window([1024,768],monitor="monitor",rgb = [0,0,0],winType='pyglet',waitBlanking=True,units='pix', allowGUI=False,fullscr=False)
myMouse = event.Mouse(visible=True,newPos=[0,0],win=win)

while True:
    print(myMouse.getPos())
    win.flip()
    checkKeyboard()

When I use Psychopy2 1.85.4, the program works as intended with the mouse appearing in the middle of the screen though myMouse.getPos() does not return (0,0) but (-171,364).

When I run the same program with Psychopy3 1.06, the mouse appears at (1024,768), with at least, myMouse.getPos() returning the correct (x,y) coordinates).

This seems to be the cause of some major problems I’m encountering with a more complicated program that used to run smoothly in Psychopy2 1.85.4 but now behave strangely under Psychopy3 1.06.

Does anybody know the source of the problem and if it will be fixed in future version of psychopy?

Thanks for the information. I edited my post following your guidelines.

My guess is that this is an issue with a retina display (is your mac a retina display?)

In essence, we couldn’t decide the best way to handle reporting of resolution given that Apple took the policy of having these double-resolution monitors but “pretending” that their screen was regular resolution.

When I run the following, adapted version of your script I get sensible values reported for my retina display and the message appears on the mouse icon as intended. Does this explain the issue you’ve got?

cheers

Thanks for the answer! My Mac has indeed a Retina display so the problem has to come from there. I could not find the code you mentioned in your answer to see if it solves the problem: Did I miss it? Thanks again!

Apologies:

from psychopy import core,visual,event

win = visual.Window([1024,768],monitor="monitor",rgb = [0,0,0],winType='pyglet',waitBlanking=True,units='pix', allowGUI=False,fullscr=False)
myMouse = event.Mouse(visible=True,newPos=[0,0],win=win)

msg =  visual.TextStim(win, text='')
msg.setAutoDraw(True)

if win.useRetina:
    multiplier = 0.5

while True:
    pos = myMouse.getPos()
    msg.text = str(pos)
    msg.pos = pos * multiplier
    win.flip()
    if 'escape' in event.getKeys():
        core.quit()
    
    

Strangely, the code still does not work on my computer. The mouse and the message still appear in the upper left corner of the window with the message saying [1024, 768].

The coordinates which appear in the message seem correct. For instance, when I move the mouse manually to the center of the screen, it correctly indicates that the mouse is at (0,0) but the program ignores any instruction I give it regarding the location of the mouse, either when creating the mouse or by invoking the setPos() method of the mouse object.

Actually, I noticed that, no matter the instruction I give to the program regarding the location of the mouse, the mouse always appears in the upper right corner of the window. For instance, with the following program

from psychopy import core,visual,event

FULLSCREEN = False
posMouseX = 800
poMouseY = 600

win = visual.Window([1024,768],monitor="monitor",rgb = [0,0,0],winType='pyglet',waitBlanking=True,units='pix', allowGUI=False,fullscr=FULLSCREEN)
myMouse = event.Mouse(visible=True,newPos=[posMouseX,posMouseY],win=win)

msg =  visual.TextStim(win, text='')
msg.setAutoDraw(True)

while True:
    pos = myMouse.getPos()
    msg.text = str(pos)
    msg.pos = pos * multiplier
    win.flip()
    if 'escape' in event.getKeys():
        core.quit()
    ```
The mouse will appear at [1024,768] (adding a line myMouse.setPos([800,600]) does not change anything).

If FULLSCREEN is set to True, the mouse will appear at [1440,900], which is half of what is supposed to be my actual screen resolution (2880x1800) but I guess this is related to the issue you mentioned with the Retina display. 

In a nutshell, psychopy seems to ignore any instruction I give it regarding the location of the mouse.

So, one thing that’s an FYI but doesn’t fix your problem: The way Python coordinates work, [0,0] is in the dead center of the window. So if you have a window that’s 1024x768, then [800,600] is actually way out of bounds to the top and right. The top-right corner of the window would have the coordinates [512,384].

However, even if I set the coordinates to something in-bounds, it’s still putting the mouse in the top-right corner of the window no matter what, which is very weird. Even if I try to set it to [0,0] there, which should not be affected by retina wonkiness, it ends up in the top right. However, if I set it to [-512, -384], it puts the mouse at 0,0! Similarly, if I fullscreen it and set the coordinates to [-720, -450], it ends up at 0,0.

Some kind of very wonky math is happening with retina displays and window location/dimensions in relation to mouse coordinates. This is not news, I’ve been trying to wrestle a related set of issues to the ground for months (it gets very complicated if you have two screens and one of them is not a retina display). The mouse coordinates that are being reported by getPos are correct to the grid centered on 0,0. Those used by newPos seem instead to set 0,0 as the top-right corner of the window. That at least gives you reliable behavior so you can do some kind of workaround, but it’s definitely weird.

EDIT: @jon Found the offending line of code in event.py (610). I’ll have a go at a bug-fix shortly.

from psychopy import core,visual,event

FULLSCREEN = False
posMouseX = -512
posMouseY = -384

win = visual.Window([1024,768],monitor="monitor",rgb = [0,0,0],winType='pyglet',waitBlanking=True,units='pix', allowGUI=False,fullscr=FULLSCREEN)
myMouse = event.Mouse(visible=True,newPos=[posMouseX,posMouseY],win=win)

msg =  visual.TextStim(win, text='')
msg.setAutoDraw(True)

if win.useRetina:
    multiplier = 0.5
else:
    multiplier=1

while True:
    pos = myMouse.getPos()
    msg.text = str(pos)
    msg.pos = pos * multiplier
    win.flip()
    if 'escape' in event.getKeys():
        core.quit()
1 Like

I still wonder if it’s worth going a different route with retina displays and providing everything in native pixel coords instead of this confusion with a (sometimes failed) attempt to use the silly virtual coords

I’m not opposed, but I’m not sure it’s entirely on PsychoPy’s end. This seems to be something about how Pyglet reports window size and the locations of certain things. Like, in this case, it’s not that PsychoPy is trying to work in one set of units and Pyglet in another, it seems to be that Pyglet is reporting size in raw pixels but expecting mouse coordinates in points. If we could find an easy solution to make it just commit one way or the other, I’d be all for it.

Experimenting a bit after reading your comments, I found out that the following program puts the mouse correctly at the center of the screen

from psychopy import core,visual,event

FULLSCREEN = True
win = visual.Window([800,600],monitor="monitor",rgb = [0,0,0],winType='pyglet',waitBlanking=True,units='pix', allowGUI=False,fullscr=FULLSCREEN)
myMouse = event.Mouse(visible=True,newPos=[0,0],win=win)
print(win.size)
if win.useRetina:
    #myMouse.setPos([0-800*0.5,0-600*0.5])
    myMouse.setPos([0-win.size[0]*0.25,0-win.size[1]*0.25])

msg =  visual.TextStim(win, text='')
msg.setAutoDraw(True)

while True:
    pos = myMouse.getPos()
    msg.text = str(pos)
    win.flip()
    if 'escape' in event.getKeys():
        core.quit()

This solves this mouse location problem but I’m encountering other problems which seems to be linked to the mouse location but that cannot be fixed as easily. Notably, the program below is based on a program for which I started noticing this problem. It used to work well on Psychopy2 1.85.4.

from psychopy import visual,event,core

cl = core.Clock()

#RESPONSE PANEL PARAMETERS
serif = ['Arial']
scaling = 0.95 #To increase/decrease all the elements in the response panel
responsePanelTextSizeButton = (40/1.3)*scaling #Control the size of the text inside the button of the response panel

#Control the line in the lickert scale
lineScale = 1.2                                                      #Control the size of the line. Allows the button to scale immediately with it
posLineX = 0*scaling                                            #X position of the center of the line
posLineY = -0*scaling                                           #Y position of the center of the line
lineWidth = 590*scaling*lineScale                      #Width of the line
lineHeight = 2.5*scaling                                        #Heigth of the line 

#General appearance for the appearance of the buttons and the accompanying lables
buttonTextSize = 17.0*scaling                                                           #13 Determines the size of the font
borderColor = "Black"                                                                    #Determines the color of the border of the buttons
inactiveColor = "White"                                                                   #Determines the color of the buttons when inactive
activeColor = "Black"                                                                      #Determines the color of the buttons when active
betweenButtons = 56*scaling*lineScale                                  #Distance between buttons
buttonRadius = 22*scaling*lineScale                                          #radius of the buttons
buttonOutWidth = 2                                                                           #Thickness of the border line of the buttons
buttonX = -280*scaling*lineScale                                                #X position of the left most button
textY = 1.2*(-60/1.3)*scaling                                                                                        #Y position of the first level of text

#Control the location of the numbers inside the buttons. Watch out! Number 10 requires special adjustment
buttonNumberPosAdjX = 2*(-12/1.3)*scaling
buttonNumberPosAdjY = (-25/1.3)*scaling

#MOUSE PARAMETERS
xMouse = 0                                   #xlocation of the mouse when the response panel is shown. 
yMouse = -200                                   #ylocation of the mouse when the response panel is shown. 
clickDuration = 0.15                      #Control the duration of the feedback given when participants click on the response panel (sec)

def waitForMouseClick(myMouse):
    waiting = True
    #Waiting for mouse to be pressed
    while (waiting):
        buffer = checkKeyboard()
        if buffer == 'escape':
            sys.exit()
        buttons = myMouse.getPressed()
        if (buttons[0] == 1):
            waiting = False
    waiting = True
    #Waiting for mouse to be released
    while (waiting):
        buttons = myMouse.getPressed()
        if sum(buttons) == 0:
            waiting = False
    return
    
def checkKeyboard():
    for key in event.getKeys():
        if key in ['q']:
            sys.exit()
        else:
            event.clearEvents()
            
def wait(duration):
    t = cl.getTime()
    while(cl.getTime()-t)<duration:
        pass
    return

class responsePanel:
    def __init__ (self):
        self.respLine = visual.GratingStim(win,
                                                units="pix",
                                                tex="None",
                                                mask="None",
                                                texRes=256, 
                                                pos=(posLineX,posLineY), 
                                                size=[lineWidth,lineHeight], 
                                                sf=[0,0], 
                                                ori = 0, 
                                                name='picFrame', 
                                                rgb=(-1,-1,-1))
        self.b1 = visual.Circle(win,
                        units='pix',
                        radius = buttonRadius,
                        edges=32,
                        lineWidth = buttonOutWidth,
                        fillColor = inactiveColor,
                        lineColor = borderColor,
                        pos=(buttonX,posLineY))
        self.b2 = visual.Circle(win,
                        units='pix',
                        radius = buttonRadius,
                        edges=32,
                        lineWidth = buttonOutWidth,
                        fillColor = inactiveColor,
                        lineColor = borderColor,
                        pos=((buttonX+betweenButtons*1),posLineY))
        self.b3 = visual.Circle(win,
                        units='pix',
                        radius = buttonRadius,
                        edges=32,
                        lineWidth = buttonOutWidth,
                        fillColor = inactiveColor,
                        lineColor = borderColor,
                        pos=((buttonX+betweenButtons*2),posLineY))
        self.b4 = visual.Circle(win,
                        units='pix',
                        radius = buttonRadius,
                        edges=32,
                        lineWidth = buttonOutWidth,
                        fillColor = inactiveColor,
                        lineColor = borderColor,
                        pos=((buttonX+betweenButtons*3),posLineY))
        self.b5 = visual.Circle(win,
                        units='pix',
                        radius = buttonRadius,
                        edges=32,
                        lineWidth = buttonOutWidth,
                        fillColor = inactiveColor,
                        lineColor = borderColor,
                        pos=((buttonX+betweenButtons*4),posLineY))
        self.b6 = visual.Circle(win,
                        units='pix',
                        radius = buttonRadius,
                        edges=32,
                        lineWidth = buttonOutWidth,
                        fillColor = inactiveColor,
                        lineColor = borderColor,
                        pos=((buttonX+betweenButtons*5),posLineY))
        self.b7 = visual.Circle(win,
                        units='pix',
                        radius = buttonRadius,
                        edges=32,
                        lineWidth = buttonOutWidth,
                        fillColor = inactiveColor,
                        lineColor = borderColor,
                        pos=((buttonX+betweenButtons*6),posLineY))
        self.b8 = visual.Circle(win,
                        units='pix',
                        radius = buttonRadius,
                        edges=32,
                        lineWidth = buttonOutWidth,
                        fillColor = inactiveColor,
                        lineColor = borderColor,
                        pos=((buttonX+betweenButtons*7),posLineY))
        self.b9 = visual.Circle(win,
                        units='pix',
                        radius = buttonRadius,
                        edges=32,
                        lineWidth = buttonOutWidth,
                        fillColor = inactiveColor,
                        lineColor = borderColor,
                        pos=((buttonX+betweenButtons*8),posLineY))
        self.b10 = visual.Circle(win,
                        units='pix',
                        radius = buttonRadius,
                        edges=32,
                        lineWidth = buttonOutWidth,
                        fillColor = inactiveColor,
                        lineColor = borderColor,
                        pos=((buttonX+betweenButtons*9),posLineY))
        self.b11 = visual.Circle(win,
                        units='pix',
                        radius = buttonRadius,
                        edges=32,
                        lineWidth = buttonOutWidth,
                        fillColor = inactiveColor,
                        lineColor = borderColor,
                        pos=((buttonX+betweenButtons*10),posLineY))
        self.b1Number = visual.TextStim(win, 
                            units='pix',height = responsePanelTextSizeButton,
                            pos=((buttonX+buttonNumberPosAdjX),(posLineY+buttonNumberPosAdjY)), text=" 0",
                            font=serif, 
                            wrapWidth=700,
                            alignHoriz = 'left',alignVert='bottom',
                            color='Black')
        self.b2Number = visual.TextStim(win, 
                            units='pix',height = responsePanelTextSizeButton,
                            pos=(((buttonX+betweenButtons*1)+buttonNumberPosAdjX),(posLineY+buttonNumberPosAdjY)), text="10",
                            font=serif, 
                            wrapWidth=700,
                            alignHoriz = 'left',alignVert='bottom',
                            color='Black')
        self.b3Number = visual.TextStim(win, 
                            units='pix',height = responsePanelTextSizeButton,
                            pos=(((buttonX+betweenButtons*2)+buttonNumberPosAdjX),(posLineY+buttonNumberPosAdjY)), text="20",
                            font=serif, 
                            wrapWidth=700,
                            alignHoriz = 'left',alignVert='bottom',
                            color='Black')
        self.b4Number = visual.TextStim(win, 
                            units='pix',height = responsePanelTextSizeButton,
                            pos=(((buttonX+betweenButtons*3)+buttonNumberPosAdjX),(posLineY+buttonNumberPosAdjY)), text="30",
                            font=serif, 
                            wrapWidth=700,
                            alignHoriz = 'left',alignVert='bottom',
                            color='Black')
        self.b5Number = visual.TextStim(win, 
                            units='pix',height = responsePanelTextSizeButton,
                            pos=(((buttonX+betweenButtons*4)+buttonNumberPosAdjX),(posLineY+buttonNumberPosAdjY)), text="40",
                            font=serif, 
                            wrapWidth=700,
                            alignHoriz = 'left',alignVert='bottom',
                            color='Black')
        self.b6Number = visual.TextStim(win, 
                            units='pix',height = responsePanelTextSizeButton,
                            pos=(((buttonX+betweenButtons*5)+buttonNumberPosAdjX),(posLineY+buttonNumberPosAdjY)), text="50",
                            font=serif, 
                            wrapWidth=700,
                            alignHoriz = 'left',alignVert='bottom',
                            color='Black')
        self.b7Number = visual.TextStim(win, 
                            units='pix',height = responsePanelTextSizeButton,
                            pos=(((buttonX+betweenButtons*6)+buttonNumberPosAdjX),(posLineY+buttonNumberPosAdjY)), text="60",
                            font=serif, 
                            wrapWidth=700,
                            alignHoriz = 'left',alignVert='bottom',
                            color='Black')
        self.b8Number = visual.TextStim(win, 
                            units='pix',height = responsePanelTextSizeButton,
                            pos=(((buttonX+betweenButtons*7)+buttonNumberPosAdjX),(posLineY+buttonNumberPosAdjY)), text="70",
                            font=serif, 
                            wrapWidth=700,
                            alignHoriz = 'left',alignVert='bottom',
                            color='Black')
        self.b9Number = visual.TextStim(win, 
                            units='pix',height = responsePanelTextSizeButton,
                            pos=(((buttonX+betweenButtons*8)+buttonNumberPosAdjX),(posLineY+buttonNumberPosAdjY)), text="80",
                            font=serif, 
                            wrapWidth=700,
                            alignHoriz = 'left',alignVert='bottom',
                            color='Black')
        self.b10Number = visual.TextStim(win, 
                            units='pix',height = responsePanelTextSizeButton,
                            pos=(((buttonX+betweenButtons*9)+buttonNumberPosAdjX),(posLineY+buttonNumberPosAdjY)), text="90",
                            font=serif, 
                            wrapWidth=700,
                            alignHoriz = 'left',alignVert='bottom',
                            color='Black')
        self.b11Number = visual.TextStim(win, 
                            units='pix',height = responsePanelTextSizeButton,
                            pos=(((buttonX+betweenButtons*10)+buttonNumberPosAdjX-((12/1.3)*scaling)),(posLineY+buttonNumberPosAdjY)), text="100",
                            font=serif, 
                            wrapWidth=700,
                            alignHoriz = 'left',alignVert='bottom',
                            color='Black')
                            
    def draw(self):
        self.respLine.draw()
        self.b1.draw()
        self.b2.draw()
        self.b3.draw()
        self.b4.draw()
        self.b5.draw()
        self.b6.draw()
        self.b7.draw()
        self.b8.draw()
        self.b9.draw()
        self.b10.draw()
        self.b11.draw()
        self.b1Number.draw()
        self.b2Number.draw()
        self.b3Number.draw()
        self.b4Number.draw()
        self.b5Number.draw()
        self.b6Number.draw()
        self.b7Number.draw()
        self.b8Number.draw()
        self.b9Number.draw()
        self.b10Number.draw()
        self.b11Number.draw()
        
    def showPanel(self,mouse): #[retentionInterval,firstRecovery,[inst1a,inst1b,inst2]]
        self.draw()
        mouse.setPos(newPos=[0,0])
        mouse.setVisible(1)
        win.flip()
        resp = self.recordResponse(mouse)
        mouse.setVisible(0)
        return resp
            
    def recordResponse(self,mouse):
        event.clearEvents()
        while True:
            buffer = waitForMouseClick(mouse)
            if self.b1.contains(mouse):
                self.activateButton(self.b1)
                return 0
            elif self.b2.contains(mouse):
                self.activateButton(self.b2)
                return 10
            elif self.b3.contains(mouse):
                self.activateButton(self.b3)
                return 20
            elif self.b4.contains(mouse):
                self.activateButton(self.b4)
                return 30
            elif self.b5.contains(mouse):
                self.activateButton(self.b5)
                return 40
            elif self.b6.contains(mouse):
                self.activateButton(self.b6)
                return 50
            elif self.b7.contains(mouse):
                self.activateButton(self.b7)
                return 60
            elif self.b8.contains(mouse):
                self.activateButton(self.b8)
                return 70
            elif self.b9.contains(mouse):
                self.activateButton(self.b9)
                return 80
            elif self.b10.contains(mouse):
                self.activateButton(self.b10)
                return 90
            elif self.b11.contains(mouse):
                self.activateButton(self.b11)
                return 100
              
    def activateButton(self,button):
        button.setFillColor(activeColor)
        self.draw()
        win.flip()
        wait(clickDuration)
        button.setFillColor(inactiveColor)
        win.flip()
        
FULLSCREEN = False
win = visual.Window([1024,768],monitor="monitor",rgb=[0,0,0],winType='pyglet',waitBlanking=True,units='pix', allowGUI=False,fullscr=False)
myMouse = event.Mouse(visible=True,newPos=[0,0],win=win)

testPanel = responsePanel()
while True:
    print (testPanel.showPanel(myMouse))

I apologise as the code is a bit long but basically, it creates a a 11-point Lickert scale going from 0 to 10: the participant click on the number to provide his answer.

In Psychopy3, and I think in versions of Psychopy2 released after 1.85.4, the program works strangely: (a) If you click on button 50 (whose location must be 0,0 at the center of the screen), button 50 is activated but (b) if you click on button 40, button 30 is activated; if you click on button 30, button 10 is activated; nothing happens if you click on buttons 20, 10 and 0. Likewise, © if you click on button 60, button 70 is activated; if you click on button 70, button 90 is activated; nothing happens if you click on buttons 80, 90 and 100.

So there seems some kind of non-linear distortion between the actual position of the mouse on the x-axis and the position recorded by psychopy.

The part of the program dealing with the location of the mouse is the following method

def recordResponse(self,mouse):
        event.clearEvents()
        while True:
            buffer = waitForMouseClick(mouse)
            if self.b1.contains(mouse):
                self.activateButton(self.b1)
                return 0
            elif self.b2.contains(mouse):
                self.activateButton(self.b2)
                return 10
            elif self.b3.contains(mouse):
                self.activateButton(self.b3)
                return 20
            elif self.b4.contains(mouse):
                self.activateButton(self.b4)
                return 30
            elif self.b5.contains(mouse):
                self.activateButton(self.b5)
                return 40
            elif self.b6.contains(mouse):
                self.activateButton(self.b6)
                return 50
            elif self.b7.contains(mouse):
                self.activateButton(self.b7)
                return 60
            elif self.b8.contains(mouse):
                self.activateButton(self.b8)
                return 70
            elif self.b9.contains(mouse):
                self.activateButton(self.b9)
                return 80
            elif self.b10.contains(mouse):
                self.activateButton(self.b10)
                return 90
            elif self.b11.contains(mouse):
                self.activateButton(self.b11)
                return 100

It waits for a mouse click and check if that click has taken place in one of the b1 to b11 visual.Circle objects, using the contains(mouse) method build in in the visual.Circle class. I believe the problem is in that contains() method and the way it handles the mouse position with a retina display.

You are probably correct. I’m not sure why, but contains is doing something different with the mouse position.

I have had success using mouse.isPressedIn for this. You can read more about that here: https://www.psychopy.org/api/event.html#psychopy.event.Mouse.isPressedIn

I think it would work for your setup as well. It might run into the same issue, but I’m using a retina display and it’s worked fine for me.

Thanks a lot for your answer. I tried your solution using the mouse method isPressedIn instead of contains(). I put the code below. Unfortunately, this did not work: nothing happens at all when I click with the mouse on any of the buttons. Did I implement the method correctly?

def recordResponse(self,mouse):
    event.clearEvents()
    while True:
        buffer = waitForMouseClick(mouse)
        if mouse.isPressedIn(self.b1):
            self.activateButton(self.b1)
            return 0
        elif mouse.isPressedIn(self.b2):
            self.activateButton(self.b2)
            return 10
        elif mouse.isPressedIn(self.b3):
            self.activateButton(self.b3)
            return 20
        elif mouse.isPressedIn(self.b4):
            self.activateButton(self.b4)
            return 30
        elif mouse.isPressedIn(self.b5):
            self.activateButton(self.b5)
            return 40
        elif mouse.isPressedIn(self.b6):
            self.activateButton(self.b6)
            return 50
        elif mouse.isPressedIn(self.b7):
            self.activateButton(self.b7)
            return 60
        elif mouse.isPressedIn(self.b8):
            self.activateButton(self.b8)
            return 70
        elif mouse.isPressedIn(self.b9):
            self.activateButton(self.b9)
            return 80
        elif mouse.isPressedIn(self.b10):
            self.activateButton(self.b10)
            return 90
        elif mouse.isPressedIn(self.b11):
            self.activateButton(self.b11)
            return 100

Hmm. It looks like it should work to me. The only difference from my code is that I use the ‘buttons’ argument and I define mouse as a class-level object (i.e., ‘self.mouse’). So, you could try this:

if mouse.isPressedIn(object,buttons=[0]):

The one other thing, and this is based on pure intuition so it might be entirely wrong, is that you might want to try flipping the window on every loop here. The mouse object is tied to the pyglet window, and when I’m doing this loop it’s drawing every object and calling win.flip() on every loop. It’s possible that things aren’t updating correctly if you aren’t drawing the objects and flipping on every loop, but that’s a complete guess.

Otherwise I have no idea why it works on my computer but not on yours. If you have a full functional piece of your code I could try running it on my computer to isolate that factor at least.

I tried both solutions but unfortunately to no avail. The mouse click are still not detected.

OK, next thought: In coder view, go to demos -> stimuli -> shapeContains. It’s a demo of the isPressedIn method, but it has some live feedback about when it thinks something is inside a shape or not. Try messing around with that and see if it gives you a hint as to what’s going on. It definitely has some retina scaling issues on my computer with regard to the position of the buffer zone, but somehow still registers when the mouse is clicked inside the shape. See how it behaves for you.

the shapeContains.py demo works perfectly: not only does it correctly detect the mouse click when I click on the shape to terminate the program but it also correctly record the location of the mouse. I’ll study the code closely to see what is the difference between it and mine. I’ll let you now if I find something. Thanks for your help anyway!

Let me amend my previous comment. I misunderstood what the shapeContains demo was trying to do: I thought the circle around the mouse cursor was supposed to lag behind it but after reading the code and testing it on Psychopy 2 1.85.4, I noticed that the circle is supposed to be centred on the mouse. That means that this line of code in the demo is not working properly

 bufzone.pos = mouse.getPos() * win.size / 2  # follow the mouse

It does not draw the circle (bufzon) around the mouse cursor but with the same kind of distortion that cause my program to crash. The culprit seems to be the mouse.getPos() method. Though it is very likely linked to the retina display of the mac, the same demo is working perfectly in Psychopy2 1.85.4 so a change to the getPos() method of the mouse made between Psychopy2 1.85.4 and Psychopy3 3.07 is responsible for the bug.

This is the behavior I get as well, but does it recognize correctly when you click inside the shape?

Yes, it does. I need to take a close look at the code to see why my own program is not working correctly.