To measure the hold length on the stimuli without showing the feedback

If this template helps then use it. If not then just delete and start from scratch.

OS (e.g. Win10): MacOS Sequoia 15.1
PsychoPy version (e.g. 1.84.x): v2024.1.5
Standard Standalone? (y/n) If not then what?: y
What are you trying to achieve?: I am trying to measure the length of the mouse click (click and hold or just click), I would like to make the experiment go to the feedback routine only after the release of the mouse click, not at the beginning of the click, the code works fine and measures the click but if the click is not on a stimuli, because if it is on a stim, then it automatically shows the feedback message and image.

What did you try to make it work?: I wrote Python code as shown below

What specifically went wrong when you tried that?: the feedback still appears and I cannot get to measure the click length
Include pasted full error message if possible. “That didn’t work” is not enough information.


My code in the check_corr_export_csv component under the main_routine:
begin routine:
import time
import pandas as pd

Initialize an empty list to collect all the data

all_data =

Retrieve the path to the images from the CSV

question_img_path = stims_fb_loop.thisTrial[‘question’]
ans_1_path = stims_fb_loop.thisTrial[‘ans_1’]
ans_2_path = stims_fb_loop.thisTrial[‘ans_2’]
ans_3_path = stims_fb_loop.thisTrial[‘ans_3’]
ans_4_path = stims_fb_loop.thisTrial[‘ans_4’]
ans_5_path = stims_fb_loop.thisTrial[‘ans_5’]
ans_6_path = stims_fb_loop.thisTrial[‘ans_6’]
ans_7_path = stims_fb_loop.thisTrial[‘ans_7’]
ans_8_path = stims_fb_loop.thisTrial[‘ans_8’]

Set the image for the question and answer image components

question_img.setImage(question_img_path)
img_1.setImage(ans_1_path)
img_2.setImage(ans_2_path)
img_3.setImage(ans_3_path)
img_4.setImage(ans_4_path)
img_5.setImage(ans_5_path)
img_6.setImage(ans_6_path)
img_7.setImage(ans_7_path)
img_8.setImage(ans_8_path)

Initialize mouse tracking variables

click_start_time = None
stimuli_list = [img_1, img_2, img_3, img_4, img_5, img_6, img_7, img_8]
exp_start_time = time.time()
last_recorded_time = exp_start_time
data_interval = 0.1 # 100 milliseconds

Record experiment start time for reference

expInfo[‘expStart’] = data.getDateStr() # Get current date and time

each frame:
mouse_released = False # Initialize as False at the start of the routine

Get current mouse position, click state, and time

x, y = mouse.getPos() # Current mouse position (x, y)
click_info = mouse.getPressed() # Click info (left, middle, right)
current_time = time.time() # Current time in seconds since experiment start

Initialize row data for this frame

row = {
‘time’: current_time,
‘x’: x,
‘y’: y,
‘is_clicking’: any(click_info),
‘click_button’: None,
‘on_stimulus’: False,
‘stimulus_name’: None,
‘is_correct’: None,
‘hovered_stimulus’: None,
‘click_duration’: None # To store the duration if a click was held
}

Check for mouse button press and release

if click_info[0]: # Left button clicked
row[‘click_button’] = ‘left’
elif click_info[1]: # Middle button clicked
row[‘click_button’] = ‘middle’
elif click_info[2]: # Right button clicked
row[‘click_button’] = ‘right’

If a click begins (button pressed down)

if any(click_info) and click_start_time is None:
click_start_time = current_time # Record the start time of the click
feedback_shown = False # Reset feedback shown status
print(“Mouse button pressed down”)

If a click ends (button released)

elif not any(click_info) and click_start_time is not None:
click_duration = current_time - click_start_time # Calculate click duration
row[‘click_duration’] = click_duration
mouse_released = True # Mark that the mouse has been released
click_start_time = None # Reset the click start time
print(“Mouse button released”)

Prevent advancing until the mouse is released

if not mouse_released:
continueRoutine = True
print(“Mouse not released yet”)
else:
continueRoutine = False # End the routine after release
print(“Mouse released, ending routine”)

Check if the mouse click was on a stimulus

if row[‘is_clicking’]:
for stim in stimuli_list:
if mouse.isPressedIn(stim):
row[‘on_stimulus’] = True
row[‘stimulus_name’] = stim.name
row[‘is_correct’] = (stim.name == corr_ans) # Check correctness

if not row[‘is_clicking’]:
for stim in stimuli_list:
if stim.contains(mouse):
row[‘hovered_stimulus’] = stim.name
break

all_data.append(row)

end routine:

Determine if the last click was correct based on corr_ans

if mouse.clicked_name and mouse.clicked_name[0] == corr_ans:
correct = 1
else:
correct = 0

thisExp.addData(‘correct answer’, correct)
print(mouse.clicked_name, corr_ans) # Debugging print to check the clicked answer

Clear the clicked name for the next routine

mouse.clicked_name =

At the end of the experiment (after all routines):

if not ‘all_data_saved’ in globals():
all_data_saved = False

if not all_data_saved:
participant_number = expInfo[‘participant’] # Assuming you have a participant variable in your expInfo
submit_time = time.strftime(‘%Y-%m-%d_%H-%M-%S’) # Get the current date and time

# Specify the filename with the "data/" folder
filename = f"data/{participant_number}_{submit_time}.csv"

# Convert the mouse data to a DataFrame and save it to the CSV file
df = pd.DataFrame(all_data)  # Convert list to DataFrame
df.to_csv(filename, mode='w', header=True, index=False)  # Save to CSV only once

# Mark that data has been saved
all_data_saved = True

and for the fb_code component under feedback_routine:
begin experiment:
fb_msg = “”

begin routine:
if correct == 1:
fb_msg = ‘Good boy!’
feedback_img = ‘img/kiss.jpeg’

else:
fb_msg = ‘STUPID!’
feedback_img = ‘img/stupid.jpg’

print(“Setting feedback image to:”, feedback_img)

fb_img.setImage(feedback_img)

everything else works fine but the hold measurement, thanks in advance

Is your mouse component set to end the routine on a valid click?

1 Like

that is correct

i sat it to never now, and it ends the trial after the release of the mouse but even if the click wasn’t on a stim

That’s what you want, isn’t it?

1 Like

exactly, the release that was on a stim only

The easiest way to do this would be to use a second routine. End the first one on a valid click and the second one using your code for any release.

1 Like

First of all thanks a lot for your help. After changing the mouse component end on a valid click to never as you mentioned above, I was able to solve the second issue differently, and I will share what I did:

in each frame, under check_corr_export_csv:

Initialize mouse_released variable before use

mouse_released = False

Get current mouse position, click state, and time

x, y = mouse.getPos() # Current mouse position (x, y)
click_info = mouse.getPressed() # Click info (left, middle, right)
current_time = time.time() # Current time in seconds since experiment start

Initialize row data for this frame

row = {
‘time’: current_time,
‘x’: x,
‘y’: y,
‘is_clicking’: False, # Start as False, will be updated later if a click occurs on a stimulus
‘click_button’: None,
‘on_stimulus’: False,
‘stimulus_name’: None,
‘is_correct’: None,
‘hovered_stimulus’: None,
‘click_duration’: None # To store the duration if a click was held
}

Check for mouse button press

if any(click_info): # Check if any mouse button is pressed
# Check if the click is on a stimulus
for stim in stimuli_list:
if mouse.isPressedIn(stim): # If the mouse click is within the bounds of the stimulus
row[‘is_clicking’] = True # Set is_clicking to True only if the click is on a stimulus
row[‘click_button’] = ‘left’ if click_info[0] else (‘middle’ if click_info[1] else ‘right’)
row[‘on_stimulus’] = True
row[‘stimulus_name’] = stim.name
row[‘is_correct’] = (stim.name == corr_ans) # Check correctness based on stimulus name
break # Once we find a click on a stimulus, no need to check others

If a click begins (button pressed down)

if row[‘is_clicking’] and click_start_time is None:
click_start_time = current_time # Record the start time of the click
feedback_shown = False # Reset feedback shown status
print(“Mouse button pressed down”)

If a click ends (button released)

elif not any(click_info) and click_start_time is not None:
click_duration = current_time - click_start_time # Calculate click duration
row[‘click_duration’] = click_duration
mouse_released = True # Mark that the mouse has been released
click_start_time = None # Reset the click start time
print(“Mouse button released”)

Prevent advancing until the mouse is released

if not mouse_released:
continueRoutine = True
print(“Mouse not released yet”)
else:
continueRoutine = False # End the routine after release
print(“Mouse released, ending routine”)

Check if hovering over a stimulus without clicking

if not row[‘is_clicking’]:
for stim in stimuli_list:
if stim.contains(mouse):
row[‘hovered_stimulus’] = stim.name
break

Append the row data to all_data

all_data.append(row)