Getting out of while drawing loop with mouse press

Hello,

I need a help to finish my psychopy code. The following code opens a psychopy window and waits for the mouse to be clicked. Once it is clicked it draws the path with the mouse cursor. However, I don’t seem to be able to program how to stop the drawing with another mouse click. Does anyone know how mouse can be told to disengage and to break the while statement?
My current solution is commented out at the bottom, but it doesn’t work the way it should. I guess this has something to do with the logical statement regarding the mouse, but at this stage these are frustrating guesses rather than informed ones.

Any help/advice is appreciated.

I’m using: PsychoPy3, version 2023.1.2 on macOS Monterey 12.6.6.

# Import libraries
from psychopy import visual, core, event, gui, data, logging
import os
import random
import math
import matplotlib.pyplot as plt
import numpy as np 
import csv

# Set up window
win = visual.Window(size=(800, 600), fullscr=False)

# Component blob 
fixationClock = core.Clock()
fixation = visual.Circle(win, size=0.1, lineColor='white', fillColor='white', pos=(0, 0))
fixation.draw()
win.flip()
core.wait(1)

# Set up mouse
mouse = event.Mouse(visible=True, win=win)
mouse.setPos(newPos=(0, 0))

# set the circle to the mouse position
circ = visual.Circle(win, size=0.02, lineColor='white', fillColor='white', pos=(0, 0))

# inititate the path variable to store the mouse positions
line_paths = [[0, 0]]
line_path = []
create_line = True

# Working 
# wait for first click and start tracking mouse movements 
while create_line:
        mouse_click = mouse.getPressed()
        if mouse_click[0] or mouse_click[1] or mouse_click[2] == 1:
        # start tracking mouse movements and draw line every frame
                x_start, y_start = mouse.getPos()
                line_path.append(visual.Line(win, start=(x_start, y_start), end=(x_start, y_start), lineColor='white', lineWidth=2))
                mouse.clickReset()               
                while True:
                        mouse_pos = mouse.getPos()
                        line_paths.append(mouse_pos)
                        #append the line path with start at the last mouse position and end at the current mouse position
                        line_path.append(visual.Line(win, start=(line_paths[-2][0], line_paths[-2][1]), end=(mouse_pos[0], mouse_pos[1]), lineColor='white', lineWidth=4))                                      
                        for line in line_path:
                                line.setAutoDraw(True)
                        circ.pos = mouse_pos
                        circ.autoDraw = True
                        win.flip()
                        #if mouse_click[0] or mouse_click[1] or mouse_click[2] == 1:
                        #        create_line = False
                        #        break

The way “mouse” works is it just checks if the button is down when getPressed() is called, i.e., at that very instant. What you want, from the sound of things, is that they press and release once to start drawing, then press and release again to stop drawing. However, during the drawing, the mouse buttons should not be held down.

Assuming that’s accurate:

line_paths = [[0, 0]]
line_path = []
wait_click = True

# wait for first click and start tracking mouse movements 
while wait_click: 
        mouse_click = mouse.getPressed()
        if mouse_click[0] or mouse_click[1] or mouse_click[2] == 1:
               # First we just need to wait for the button to be released
               mouse_down = True
               while mouse_down:
                   mouse_click = mouse.getPressed()
                   if mouse_click[0] or mouse_click[1] or mouse_click[2] == 1:
                       pass
                   else:
                       mouse_down = False
                       wait_click = False

# Now we start a separate while loop for the drawing.
# None of this will execute until the above code waiting for the mouse press and release has finished
x_start, y_start = mouse.getPos()
line_path.append(visual.Line(win, start=(x_start, y_start), end=(x_start, y_start), lineColor='white', lineWidth=2))
create_line = True
mouse_down = False
mouse.clickReset()      
while create_line:
    # start tracking mouse movements and draw line every frame
    mouse_pos = mouse.getPos()
    line_paths.append(mouse_pos)
    #append the line path with start at the last mouse position and end at the current mouse position
    line_path.append(visual.Line(win, start=(line_paths[-2][0], line_paths[-2][1]), end=(mouse_pos[0], mouse_pos[1]), lineColor='white', lineWidth=4))                                      
    for line in line_path:
        line.setAutoDraw(True)
    circ.pos = mouse_pos
    circ.autoDraw = True
    win.flip()
    mouse_click = mouse.getPressed()
    # Setting it up this way so it will continue to draw until they RELEASE the mouse button
    if mouse_click[0] or mouse_click[1] or mouse_click[2] == 1 and not mouse_down:
        mouse_down = True
    elif mouse_down: # i.e.,  the mouse WAS down, but now none of the buttons are pressed, end loop
        create_line = False

That should work as described.

Thanks a lot! That’s amazing response.
We were considering something similar, but were totally oblivious to “button down” at getPressed(). It works now the way it should.

Have a lovely day!

Best,
Izabela