Randomizing animated/moving stimuli

I’ve looked at this post, which seems to outline how to do this in Builder, but I’m uncertain how to implement it with code (i.e. precisely what to write in my csv file and how to call it properly).

I have a loop which reads in the names of images (stored in a csv previously), and would like the images to randomly animate (i.e move up and down, side to side, rotate, etc). I know how to implement the animations each time, for example with the code below:

imgL.ori +=  1.5
imgR.size += numpy.sin((globalClock.getTime()-onset)/40) 

but am not sure how to have these properties change on each trial. My plan was to have a set of 10 or so ways of moving plugged into a csv and then pull them randomly, but I do not know how to implement this within the loop.

Here is my loop:

tidx = 0

    for tidx, trial in enumerate(trials):
        #do some stuff

        logging.log(level=logging.INFO, msg="Trial %i - Stimuli1 %s - Stimuli2 %s" % (tidx+1, trial['path1'], trial['path2']))

        #Set values for trial
        onset = globalClock.getTime()
        trials.data.add('onset', onset)
        rt=None
        Pressed=False

        #while not mouseclick.getPressed():
        #while globalClock.getTime() < (tidx+1)*trialDur:

        while not Pressed:
            background_image.draw()
            imgL.draw()
            imgR.draw()

            win.flip()

            mouseclick=mymouse.getPressed()
            #key=event.getKeys(keyList=('escape'))
            if sum(mouseclick)>0:
                rt=globalClock.getTime()-onset
                core.wait(.1)
                Pressed= True
                event.clearEvents()
               trials.data.add('rt',rt)

First define what “these properties” are and how you wish them to vary across trials. ie how would you describe this in a methods section of a paper?

Sorry for the lack of clarity! These properties meaning the movements of imgR and imgL, which will be one of a set of 10 random movements (e.g. rotating, bouncing, moving side to side, getting bigger and smaller; see the first code block above for two example movements)

If I were to describe it in a manuscript, I would say something like, on each trial, each character moved randomly in one of ten pre-defined ways (rotating, bouncing…) to maintain participant interest. Does that make more sense?

If you really are happy to have them completely random each trial, then looks like around your

    trials.data.add('onset', onset)

code line you should just use python’s random number functions (after including “import random”) to get random numbers on each trial to set the animation characteristics. Does that make sense?

Yes, completely! But my apologies, I am realizing after working further through my issue that I was very unclear in my original post with what exactly the part of this issue is that I’m stuck on (I’m cool with using random to grab attributes, but that’s not what stymied me). Here is a code snippet that shows what I am trying to accomplish.

Basically, I want to pull 10 stimulus attributes from a csv file, randomly pick ones to assign to my two imageStim objects, and then–this is the part I’m stuck on–actually update these objects with the attributes. Is there a way to variably assign attributes without hardcoding what the attribute (e.g. ori, pos) is in advance?

def set_data(subj_id):
#do some stuff
    # import animation conditions and info and set up list
    animateFile = 'stimuli/animation_conds.csv'
    animate_list = [ item for item in csv.DictReader(open(animateFile,'rU'))]
#do other stuff
    return (animate_list)

def do_runtrials(subj_id,trials,logname,runID,animate_list):
 

    fixation.draw()
    win.flip()
    # set clock
    globalClock = core.Clock()
    logging.setDefaultClock(globalClock)
    tidx = 0

    for tidx, trial in enumerate(trials):
        #Set values for trial
        imgL.setImage(trial['path1'])
        imgR.setImage(trial['path2'])
        #pick at random from animate_list
        animateone=random.choice(animate_list)
        animatetwo=random.choice(animate_list)
        ### ASSIGN THE RANDOM ATTRIBUTE HERE...whether it be ori or pos or size or opacity... ### 

        onset = globalClock.getTime()
        trials.addData('onset', onset)
     
        Pressed=False

        while not Pressed:
            ##or set the moving animation characteristics here instead?##
            
            #show the result of the above
            background_image.draw()
            imgL.draw()
            imgR.draw()
            soundL.play()

            win.flip()

            mouseclick=mymouse.getPressed()
            if sum(mouseclick)>0:
                rt=globalClock.getTime()-onset
                core.wait(.1)
                Pressed= True
                event.clearEvents()
            if len(event.getKeys(['escape'])):
                logging.flush()
                win.close()
                core.quit()
                break

And the example csv file looks like this (in a column):

condition
ori +=  numpy.sin(1.5*(globalClock.getTime()-onset)/40))
pos = [-0.5, (globalClock.getTime()-onset)/150]
size = numpy.abs(numpy.sin((globalClock.getTime()-onset))*20)
ori =+ 1.5
pos = [(globalClock.getTime()-onset)/150, 0.5]
opacity = 1/(globalClock.getTime()-onset)
ori =+ 1.5
ori =+ 1.5
size = numpy.abs(numpy.sin((globalClock.getTime()-onset))*20)
ori =+ 1.5

The varying attributes is what I’m stuck on–I know there must be a way to code this but wasn’t sure how to implement it.

I ended up finding a solution to this, which was to stick exec(animateone) and exec(animatetwo) in my script, and hardcode the randomly varying attributes as “imgL.size=np.sin((globalClock.getTime()-onset))*20)”, etc. in the csv file. I am aware this is less than ideal (to execute a string), but couldn’t think of how else to pull arbitrary criteria. Example code is below–I assume it is very inefficient, but I ended up having to reset the baseline attributes each time to prevent them from carrying over from trial to trial.


    for tidx, trial in enumerate(trials):
        print('In trial {} - node1 = {} node2 = {}'. format(tidx+1, trial['node1'], trial['node2']))
        print(trial['path1'],trial['path2'])

        logging.log(level=logging.DATA, msg="Trial %i - Stimuli1 %s - Stimuli2 %s" % (tidx+1, trial['path1'], trial['path2']))

        #Set values for trial
        imgL.setImage(trial['path1'])
        imgR.setImage(trial['path2'])
        #pick at random from animate_list
        animateone=random.choice(animate_list)['condition1']
        animatetwo=random.choice(animate_list)['condition2']
        #add sounds here
        soundL=sound.Sound(trial['sound1'], secs=0.1)
        #soundR=sound.Sound(trial['sound2'])
        onset = globalClock.getTime()
        trials.addData('onset', onset)
        key=None
        rt=None
        Pressed=False
        while not Pressed:
            #img_rect.draw()
            #set moving animation characteristics here after resetting normal!
            imgL.ori=(0)
            imgR.ori=(0)
            imgL.opacity=(1)
            imgR.opacity=(1)
            imgL.size=(10)
            imgR.size=(10)
            imgL.pos=(-7,-4)
            imgR.pos = (7,-4)
            exec(animateone)
            exec(animatetwo)

            #show the result of the above
            background_image.draw()
            imgL.draw()
            imgR.draw()
            soundL.play()

            win.flip()

            mouseclick=mymouse.getPressed()
            if sum(mouseclick)>0:
                rt=globalClock.getTime()-onset
                core.wait(.1)
                Pressed= True
                event.clearEvents()
                soundL.stop()
            if len(event.getKeys(['escape'])):
                logging.flush()
                win.close()
                core.quit()
                break