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

Hello,
I am having a follow up question to this discussion. I am using this code in PsychoPy builder under the code component in Begin Routine. It works perfectly and is just what I need for my experiment. It’s an offline experiment where one of the tasks that participants are prompted to do is to draw the line. However, the problem that seems to be occurring is that the entire experiment ends after I finish the drawing routine even though I have more routines after. I am guessing that if I add something to the End Routine tab of the code component, the drawing routine can progress into the next routine, however, I am having difficulty coming up with a solution. I am attaching my experiment, you can find the code component under the Routine titled draw_small_square.
exp.imagery.psyexp (101.9 KB)