Skip to content

Commit

Permalink
Updating demo files
Browse files Browse the repository at this point in the history
  • Loading branch information
jfkominsky committed Nov 19, 2021
1 parent b211cfb commit 563ca03
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 22 deletions.
41 changes: 32 additions & 9 deletions PyHabDemo/PyHab/PyHabBuilder.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,8 @@ def __init__(self, loadedSaved=False, settingsDict={}):
'prefLook': '0',
'startImage': '',
'endImage': '',
'nextFlash': '0'}
'nextFlash': '0',
'loadSep': '0'}
self.condDict = {}
self.baseCondDict = {}
else:
Expand Down Expand Up @@ -122,12 +123,14 @@ def __init__(self, loadedSaved=False, settingsDict={}):
self.settings['autoRedo'] = '[]'
self.settings['onTimeDeadline'] = '{}'
self.settings['durationInclude'] = '1'
if 'loadSep' not in self.settings:
self.settings['loadSep'] = '0'
# Settings requiring evaluation to get sensible values. Mostly dicts.
evalList = ['dataColumns','blockSum','trialSum','maxDur','condList','baseCondList','movieEnd','playThrough',
'trialOrder','stimNames', 'stimList', 'ISI', 'maxOff','minOn','durationCriterion','autoRedo',
'onTimeDeadline','autoAdvance','playAttnGetter','attnGetterList','trialTypes','habTrialList',
'calcHabOver', 'nextFlash', 'blockList', 'dynamicPause','midAG','screenWidth','screenHeight',
'screenIndex','movieWidth','movieHeight', 'durationInclude'] # in 0.9, this becomes necessary.
'screenIndex','movieWidth','movieHeight', 'durationInclude', 'loadSep'] # in 0.9, this becomes necessary.
for i in evalList:
self.settings[i] = eval(self.settings[i])
if i in ['stimList','attnGetterList']:
Expand Down Expand Up @@ -611,7 +614,7 @@ def trialTypeDlg(self, trialType="TrialTypeNew", makeNew=True, prevInfo=[]):
self.win.flip()
#For when a trial is right-clicked, or a new one created, open a dialog with info about it.
skip = False
if len(self.settings['trialTypes']) == len(self.colorsArray):
if len(self.settings['trialTypes']) == len(self.colorsArray) and makeNew:
errDlg = gui.Dlg(title="Max trial types reached!")
errDlg.addText("PyHab's builder currently supports a maximum of " + str(len(self.colorsArray)) + " trial or block types.")
errDlg.show()
Expand Down Expand Up @@ -1003,7 +1006,7 @@ def advTrialDlg(self, trialType):
self.settings['dynamicPause'].append(trialType)
elif adTypeInfo[len(adTypeInfo)-5] in [False, 0, 'False', '0'] and trialType in self.settings['dynamicPause']:
# Remove if this behavior has been turned off.
self.settings['dynamicPause'].pop(self.setting['dynamicPause'].index(trialType))
self.settings['dynamicPause'].pop(self.settings['dynamicPause'].index(trialType))
# Mid-trial AG
if adTypeInfo[len(adTypeInfo)-4] in self.settings['attnGetterList']:
# We have the luxury of only caring about certain inputs if there is a mid-trial AG
Expand Down Expand Up @@ -1316,7 +1319,7 @@ def makeBlockDlg(self, name='', new=True):
:return:
:rtype:
"""
if len(self.settings['trialTypes']) > 0:
if len(self.settings['trialTypes']) > 0 and not (len(self.settings['trialTypes']) == 20 and new):
self.showMainUI(self.UI, self.studyFlowArray, self.trialTypesArray)
self.workingRect.draw()
self.workingText.draw()
Expand Down Expand Up @@ -1377,10 +1380,14 @@ def makeBlockDlg(self, name='', new=True):
# For Windows, because now we snap back to the regular window.
self.win.winHandle.set_visible(visible=True)
self.blockMaker(newBlock[0], new)
else:
elif len(self.settings['trialTypes']) == 0:
errDlg = gui.Dlg(title="No trials to make blocks with!")
errDlg.addText("Make some trial types before trying to add them to a block.")
irrel = errDlg.show()
else:
errDlg = gui.Dlg(title="Max trial/block types reached!")
errDlg.addText("Maximum number of trial/block types has been reached (20), no more can be made.")
irrel = errDlg.show()


def blockMaker(self, blockName, new=True, hab=False):
Expand Down Expand Up @@ -2005,6 +2012,11 @@ def univSettingsDlg(self): #The universal settings button.
3 = durationInclude: Trial duration calculations include last gaze-off or not
4 = loadSeparate: New setting in 0.9.4, movie playback issues have created a situation where some (but not all)
experiments might benefit from going back to the old ways of loading one movie file for *each* individual
instance of a trial, rather than trying to load one movie file and load it once. This setting controls
whether that happens.
:return:
:rtype:
"""
Expand Down Expand Up @@ -2034,6 +2046,12 @@ def univSettingsDlg(self): #The universal settings button.
ch4 = ["No","Yes"]
uDlg.addField("Trial duration calculations include last gaze-off event?", choices=ch4)

if self.settings['loadSep'] in ['1',1,'True',True]:
ch5 = ["Yes","No"]
else:
ch5 = ["No", "Yes"]
uDlg.addField("Load each stimulus file multiple times to prevent rewind glitches? SEE 'Troubleshooting' IN MANUAL", choices=ch5)

uInfo = uDlg.show()
if uDlg.OK:
tryAgain = False
Expand All @@ -2052,6 +2070,10 @@ def univSettingsDlg(self): #The universal settings button.
self.settings['durationInclude'] = 1
else:
self.settings['durationInclude'] = 0
if uInfo[4] == "Yes":
self.settings['loadSep'] = 1
else:
self.settings['loadSep'] = 0
if tryAgain:
self.univSettingsDlg()

Expand Down Expand Up @@ -2333,7 +2355,7 @@ def stimSettingsDlg(self, lastSet=[], redo=False, screen='all'):
sDlg.addField("Height of movie stimuli in pixels", lastSet[4])
sDlg.addField("Freeze first frame for how many seconds after attention-getter?", lastSet[5])
# Get a list of all screens. Requires us to import pyglet, assuming we are using pyglet displays (until glfw works)
defDisp = pyglet.window.get_platform().get_default_display()
defDisp = pyglet.canvas.Display()
allScrs = defDisp.get_screens()
if len(allScrs) > 1:
screenList = list(range(0, len(allScrs)))
Expand Down Expand Up @@ -2855,7 +2877,7 @@ def condSettingsDlg(self): #Settings relating to conditions and randomization
chkBox = False
if self.settings['randPres'] in [1,'1',True,'True']:
chkBox = True
cDlg.addField("Use random presentation? If yes, a new interface will open",initial=chkBox)
cDlg.addField("Use condition-based presentation? If yes, a new interface will open",initial=chkBox)
cDlg.addField("Pre-existing condition file (optional, leave blank to make new file called conditions.csv)", self.settings['condFile'])
if not chkBox:
cDlg.addText("NOTE: This will overwrite any existing file named conditions.csv! If you have an existing conditions file, rename it first.")
Expand Down Expand Up @@ -3993,7 +4015,8 @@ def saveEverything(self):
tempArray2 = []
for l in range(0, len(self.settings['baseCondList'])):
tempArray2.append([self.settings['baseCondList'][l], self.baseCondDict[self.settings['baseCondList'][l]]])
with open(self.folderPath+'base_'+self.settings['condFile'],'w') as bc:
self.settings['baseCondFile'] = 'base_'+self.settings['condFile']
with open(self.folderPath+self.settings['baseCondFile'],'w') as bc:
baseWriter = csv.writer(bc, lineterminator='\n')
for m in range(0, len(tempArray2)):
baseWriter.writerow(tempArray2[m])
Expand Down
53 changes: 40 additions & 13 deletions PyHabDemo/PyHab/PyHabClass.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,11 @@ def __init__(self, settingsDict):
self.onTimeDeadline = {}
self.durationInclude = 1
self.habByDuration = 0
# new setting for 0.9.4
try:
self.loadSep = eval(settingsDict['loadSep'])
except:
self.loadSep = 0


# ORDER OF PRESENTATION
Expand Down Expand Up @@ -156,7 +161,7 @@ def __init__(self, settingsDict):


# 0.9: Screen-specific settings: ['screenWidth','screenHeight','screenColor','movieWidth','movieHeight','screenIndex']
self.screenWidth = eval(settingsDict['screenWidth']) # Display window width, in pixels
self.screenWidth = eval(settingsDict['screenWidth']) # Display window width, in pixels, a dict for each screen.
self.screenHeight = eval(settingsDict['screenHeight']) # Display window height, in pixels
self.movieWidth = eval(settingsDict['movieWidth']) # movie width
self.movieHeight = eval(settingsDict['movieHeight']) # movie height
Expand Down Expand Up @@ -639,6 +644,9 @@ def attnGetter(self, trialType, cutoff=False, onmin=0.0, midTrial=False):
self.readyText.draw()
self.win2.flip() # If you don't refresh the expeirmenter window it doesn't read the keyboard!
if cutoff and self.lookKeysPressed():
# Update the relevant box so it actually shows the key is down.
self.statusSquareA.fillColor = 'green'
self.statusTextA.text = "ON"
if onCheck == 0 and onmin > 0:
onCheck = core.getTime()
elif core.getTime() - onCheck > onmin:
Expand All @@ -647,6 +655,8 @@ def attnGetter(self, trialType, cutoff=False, onmin=0.0, midTrial=False):
break
elif cutoff and onCheck > 0: # A clever little way to say "if they aren't looking but were earlier"
onCheck = 0
self.statusSquareA.fillColor = 'blue'
self.statusTextA.text = "RDY"
elif i > 30 and self.keyboard[self.key.K]:
# If more than half a second (30 frames) has passed and "S" is pressed.
attnGetter['file'].stop(reset=True)
Expand All @@ -669,6 +679,8 @@ def attnGetter(self, trialType, cutoff=False, onmin=0.0, midTrial=False):
self.readyText.draw()
self.win2.flip() # If you don't refresh the expeirmenter window, it doesn't read the keyboard!
if cutoff and self.lookKeysPressed():
self.statusSquareA.fillColor='green'
self.statusTextA.text='ON'
if onCheck == 0 and onmin > 0:
onCheck = core.getTime()
elif core.getTime() - onCheck > onmin:
Expand All @@ -677,6 +689,8 @@ def attnGetter(self, trialType, cutoff=False, onmin=0.0, midTrial=False):
dMovie.pause()
break
elif cutoff and onCheck > 0: # A clever little way to say "if they aren't looking but were earlier"
self.statusSquareA.fillColor='blue'
self.statusTextA.text='RDY'
onCheck = 0
elif self.frameCount['C'] > 30 and self.keyboard[self.key.K]:
# If more than half a second (30 frames) has passed and "K" is pressed.
Expand Down Expand Up @@ -800,12 +814,11 @@ def dispMovieStim(self, trialType, dispMovie, screen='C'):
if self.frameCount[screen] == 0: # initial setup
self.dummyThing.draw()
self.frameCount[screen] += 1
dispMovie.draw()
dispMovie.seek(0.0) # Moved up here from below so that it CAN loop at all
if trialType == 0:
self.frameCount[screen] = 0 # for post-attn-getter pause
dispMovie.pause()
else:
dispMovie.seek(0.0) # Moved up here from below so that it CAN loop at all
dispMovie.draw()
w.flip()
return 0
elif self.frameCount[screen] == 1:
Expand Down Expand Up @@ -872,6 +885,7 @@ def dispAudioStim(self, trialType, dispAudio):
or the audio is looping (2)
:rtype: int
"""

if self.frameCount['C'] == 0: # We're going to use this as a mask for the status of the audio file
dispAudio.play()
self.frameCount['C'] = 1
Expand Down Expand Up @@ -907,6 +921,7 @@ def dispTrial(self, trialType, dispMovie = False): #If no stim, dispMovie defaul
elif dispMovie['stimType'] == 'Image':
t = self.dispImageStim(dispMovie['stim'])
elif dispMovie['stimType'] == 'Audio' and trialType != 0: # No still-frame equivalent
self.win.flip() # This is because otherwise the last frame of the attn-getter will remain onscreen
t = self.dispAudioStim(trialType, dispMovie['stim'])
elif dispMovie['stimType'] == 'Image with audio': # Audio and image together
if trialType != 0: # No still-frame equivalent
Expand Down Expand Up @@ -1136,9 +1151,12 @@ def doExperiment(self):
trialType = trialType[trialType.index('.')+1:]
# select movie for trial
if self.stimPres:
if self.counters[trialType] >= len(self.stimNames[trialType]): # Comes up with multiple repetitions of few movies
if self.counters[trialType] >= len(self.stimNames[trialType]) and not self.loadSep: # Comes up with multiple repetitions of few movies
self.stimName = self.stimNames[trialType][self.counters[trialType] % len(self.stimNames[trialType])]
disMovie = self.stimDict[trialType][self.counters[trialType] % len(self.stimNames[trialType])]
elif self.loadSep:
self.stimName = self.stimNames[trialType][self.counters[trialType] % len(self.stimNames[trialType])]
disMovie = self.stimDict[trialType][self.counters[trialType]]
else:
self.stimName = self.stimNames[trialType][self.counters[trialType]]
disMovie = self.stimDict[trialType][self.counters[trialType]]
Expand Down Expand Up @@ -1504,12 +1522,15 @@ def onDuration(adds=0, subs=0): # A function for the duration switch, while lea
endCondMet = True
endNow = True

if localType in self.autoRedo and nowOff >= self.onTimeDeadline[localType] and not deadlineChecked:
# NB: nowOff in this context is just duration of the trial, period.
deadlineChecked = True
if sumOn < self.minOn[localType]: # this specifically uses sumOn, always.
endCondMet = True
endNow = True
if localType in self.autoRedo and localType in self.onTimeDeadline.keys() and not deadlineChecked:
# Belt-and-suspenders check that mid-trial auto-redu is enabled, because if auto-redo only checks
# at end of trial, then the type isn't int ontimedeadline and it throws an error.
if nowOff >= self.onTimeDeadline[localType]:
# NB: nowOff in this context is just duration of the trial, period.
deadlineChecked = True
if sumOn < self.minOn[localType]: # this specifically uses sumOn, always.
endCondMet = True
endNow = True

if endCondMet:
# if they have previously looked for at least minOn and now looked away for maxOff continuous sec
Expand Down Expand Up @@ -1692,7 +1713,7 @@ def onDuration(adds=0, subs=0): # A function for the duration switch, while lea
for c in range(0, len(onArray)):
tempSum += onArray[c]['duration']
self.habDataCompiled[self.habCount] += tempSum
if ttype == 4:
if number >= len(self.actualTrialOrder) or ttype == 4:
return 2
elif '^' in ttype:
self.habCount += 1 # Note: Occurs after data recording, making recording hab trial number hard.
Expand Down Expand Up @@ -2522,6 +2543,9 @@ def SetupWindow(self):
tempStimObj = visual.ImageStim(self.win, tempStim['stimLoc'], size=[self.movieWidth['C'], self.movieHeight['C']])
tempStimObj.draw()
self.win.flip() # This should now be on the screen until the first attngetter

# 0.9.4: Because of issues with rewinding and the addition of the 'loadSep' option, this can now either load
# once per trial type, or once per trial.
self.stimDict = {x: [] for x in self.stimNames.keys()} # This holds all the loaded movies.
self.counters = {x: 0 for x in self.stimNames.keys()} # list of counters, one per index of the dict, so it knows which movie to play
tempCtr = {x: 0 for x in self.stimNames.keys()}
Expand All @@ -2535,8 +2559,11 @@ def SetupWindow(self):
x = tempCtr[i] # Changed so hab trials get the same treatment as everything else.
if x < len(self.stimNames[i]):
self.stimDict[i].append(self.loadStim(self.stimNames[i][x]))
elif self.loadSep: # Loads per instance, not per type
self.stimDict[i].append(self.loadStim(self.stimNames[i][x%len(self.stimNames[i])]))
tempCtr[i] += 1


if len(list(self.playAttnGetter.keys())) > 0:
for i in list(self.attnGetterList.keys()):
if self.attnGetterList[i]['stimType'] == 'Audio':
Expand All @@ -2556,7 +2583,7 @@ def SetupWindow(self):
self.win2.winHandle.push_handlers(self.keyboard)
if self.stimPres:
self.win.winHandle.push_handlers(self.keyboard)
self.baseSize = 40 # Base size of all attention-getters, in pixels
self.baseSize = round(40*self.screenWidth['C']/1280) # Base size of all attention-getters, in pixels. Scales w/screen width, assuming base size 1280
self.attnGetterSquare = visual.Rect(self.win, height=self.baseSize, width=self.baseSize, pos=[self.testOffset + 0, 0], fillColor='black')
self.attnGetterCross = visual.ShapeStim(self.win, vertices='cross', size=self.baseSize, pos=[self.testOffset + 0, 0], fillColor='black')

Expand Down

0 comments on commit 563ca03

Please sign in to comment.