psychopy.org | Reference | Downloads | Github

Want to timestamp ratings during movie using current frame

I have written code which presents movies using movieStim3 and a slider scale using Slider simultaneously. The code currently stores and writes out to a file the position of the marker on the scale every second as well as the timestamp. To do this, I start a clock at the start of the video and store a timestamp every time a rating is stored. However, I think it would be better to timestamp each rating with the current frame. Does anyone know of a way to do this?

Here is my code currently:

from psychopy import visual 
from psychopy import gui, data, core,event
import csv
import time
from time import localtime, strftime, gmtime
from datetime import datetime
import os.path

# Kill switch for Psychopy3
#event.globalKeys.clear() # clear global keys 
esc_key= 'escape' # create global key for escape
# define function to quit programme
def quit():
    print ('User exited')
    win.close()
    core.quit()
# call globalKeys so that whenever user presses escape, quit function called
event.globalKeys.add(key=esc_key, func=quit)

# User should set cwd to the experiment directory 
os.chdir('\\\\storage.its.york.ac.uk\\pshome\\blam500\\Downloads\\movie_study_0.5')
# User should set directory for output files to be stored 
save_path= '\\\\storage.its.york.ac.uk\\pshome\\blam500\\Downloads\\movie_study_0.5\\output'

# instructions for the task can be updated here 
instructions = """You will be presented with several videos.
\nThese videos will contain dangerous behaviour, strong language, disturbing images,threat and violence.If you find these types of videos distressing ,please do not participate and let the experimenter know.
\nIf at any point, you become distressed and would like to stop the task, please inform the experimenter.
You will not be penalised for withdrawing from the study. 
\nPlease continuously rate how frightening you feel the video is moment-to-moment using the wheel of the mouse. 
\nPress Enter to begin."""

# Create a dictionary to store information from the dialogue box
inputbox = {'expdate': datetime.now().strftime('%Y%m%d_%H%M'),'part_number':'','videoCondition':''}
# Create dialogue box
# user enters participant number + video condition (i.e. the Header of the column of video lists in the film csvFile)
dlg=gui.DlgFromDict(inputbox, title = 'Input participation info',
                  	fixed='expdate',
                  	order=['expdate', 'part_number','videoCondition'])

# If the user doesn't press ok, the programme will quit and inform user.
if not dlg.OK:
	print ("User exited")
	core.quit()

# Store participant number, video condition and experiment date provided by user as variables for later use 
part_number = inputbox['part_number']
videoCondition = inputbox['videoCondition']
expdate = inputbox['expdate']

# create filename based on user input 
filename = '{}_{}_{}_{}.csv'.format(part_number, expdate, videoCondition,'continuous')
# update filename to include absolute path so that it is stored in output directory 
completeName = os.path.join(save_path, filename)
# open file for writing 
outputfile = open(completeName, "w")

# Create list of headers for output csv file 
fieldnames = ['videoName','Timestamp', 'Rating']

# create variable which calls DictWriter to write to outputfile and specifies fieldnames 
writer = csv.DictWriter(outputfile, fieldnames)
# writes headers using fieldnames as specified above when creating writer variable
writer.writeheader()

#function to present break and end screen
def break_end(video_name, participant_number, last=0):
    # Present break screen at the end of each set of questions
    if last==0:
        stim.setText("""You are welcome to take a break if you need to.
        \nPress enter when you are ready to continue.""")
        stim.draw()
        win.flip()
        # Wait for user to press Return to continue 
        key = event.waitKeys(keyList=(['return']), timeStamped = True)


    else:
        # Presents end screen at the end of task 
        stim.setText("""You have reached the end of the experiment. 
        \nPlease let the experimenter know you have finished. 
        \nThank you for your participation.""")
        stim.draw()
        win.flip()
        # Waits for user to press escape to exit experiment
        key = event.waitKeys(keyList=(['return']), timeStamped = True)
        
# Create white window for stimuli to be presented on throughout task 
win = visual.Window(size=[1024, 768], color=[1,1,1,], monitor="testMonitor", fullscr= True, allowGUI = False)

# Create text stimuli to be updated for start screen, break and end 
stim = visual.TextStim(win, "", color = [-1,-1,-1], wrapWidth = 1300, units = "pix", height=40)

# Use trialhandler to sequentially present films listed in filmlist csv file 
filmDict = data.TrialHandler(nReps = 1, method = 'sequential', trialList = data.importConditions('references\\filmList.csv'), name = 'filmList') 

# Create sliding scale to take continuous rating of uncertainty while movie plays
slidingscale = visual.Slider(win, ticks = [0,100], labels = ['certain', 'uncertain'], pos = (0.0, -0.7), granularity = 0)

# Name mouse to be able to use for continuous rating below
mouse = event.Mouse()

# Update text to include instructions for task 
stim.setText(instructions) 
# draw text stimuli 
stim.draw()
# flip window to show text 
win.flip()
# Wait for user to press Return to continue 
key = event.waitKeys(keyList=(['return']), timeStamped = True)

# Loop through each film stored in filmDict created above using trialhandler 
for film in filmDict:
    
    # Store video name in thisRunDict to write to outputfile  
    thisRunDict= {'videoName': film[videoCondition] }
    
    # present movie using moviestim3
    mov = visual.MovieStim3 (win, film[videoCondition], size=(640, 500), flipVert=False, flipHoriz=False, loop=False) #TO DO: fix size of video 

    # create a clock at the start of movie presentation 
    clock = core.Clock()
    clock.reset()  

    # set position of marker to middle of slider at the start of each video 
    slidingscale.markerPos = 50
    
    
    # create variable to use in loop to record rating every n seconds 
    rtscaleframe = 0

    # While moving is playing, present slidingscale and record marker position and timestamp in outputfile 
    while mov.status != visual.FINISHED:
        
        slidingscale.markerPos += mouse.getWheelRel()[1]
        slidingscale.draw()
        mov.draw()
        win.flip()
        # Run every 1 sec 
        if rtscaleframe % 60
        == 0:
            rating = slidingscale.markerPos # store marker position 
            thisRunDict['Rating'] = str(rating) # store rating in thisRunDict to write to outputfile
            thisRunDict['Timestamp'] = str(clock.getTime()) # store Timestamp of video to write to outputfile  
            # write responses and timings stored in thisRunDict to outputfile 
            writer.writerows([thisRunDict])
        rtscaleframe += 1 # needed to collect rating every 1 sec 

    # If statement to either present break screen or end screen
    nextTrial = filmDict.getFutureTrial(n=1) #fixes error for end screen  
    if nextTrial is None or nextTrial[videoCondition] != None:
        break_end(film[videoCondition], part_number) 
    else:
        break_end(film[videoCondition], part_number,1)

    outputfile.flush()

Hi @BronteMckeown, you can use the time returned by the flip command which returns the wall-clock time in seconds the flip completed (time since the Python process started). See docs. You need to get the flip time before your movie starts, and subtract it from the current flip time each time you save it:

timeZero = win.flip()  # Get frame time before your movie starts
# ...
# Loop through each film stored in filmDict created above using trialhandler 
for film in filmDict:
# ...etc
     while mov.status != visual.FINISHED:
     # ...etc
         currentTime = win.flip()  # Get frame time on flip
         if rtscaleframe % 60
         == 0:
            rating = slidingscale.markerPos # store marker position 
            thisRunDict['flipTime'] = str((currentTime - timeZero)) # store last flip time 

Thank you for that @dvbridges!