Skip to content

Commit

Permalink
Merge pull request #47 from jfkominsky/0.10.5
Browse files Browse the repository at this point in the history
0.10.5
  • Loading branch information
jfkominsky committed Aug 2, 2023
2 parents a7ba1bc + d106e4d commit 2c085ab
Show file tree
Hide file tree
Showing 20 changed files with 2,301 additions and 176 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/pytest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,11 @@ jobs:
sudo apt-get install -y -qq libgstreamer1.0-0 gstreamer1.0-plugins-base
sudo apt-get install -y -qq libwebkit2gtk-4.0-dev
sudo apt-get install -y -qq libpng-dev libjpeg-dev libtiff-dev libnotify-dev libsm-dev
sudo apt-get install -y -qq libsdl-dev libsdl2-mixer-2.0-0 libsdl2-image-2.0-0 libsdl2-2.0-0
sudo apt-get install -y -qq libsdl2-dev libsdl2-mixer-2.0-0 libsdl2-image-2.0-0 libsdl2-2.0-0
sudo apt-get install -y -qq libportaudio2
# virtual frame buffer
sudo apt install llvm-6.0-dev # for xvfb
sudo apt install xvfb xauth libgl1-mesa-dri
#sudo apt install llvm-6.0-dev # for xvfb
#sudo apt install xvfb xauth libgl1-mesa-dri
# set up fake sound device?
sudo apt-get install -y -qq libasound2-dev alsa-utils alsa-oss
sudo apt-get install -y linux-modules-extra-$(uname -r) # needed for modprobe snd-dummy
Expand Down
Binary file modified PyHab User Manual.pdf
Binary file not shown.
99 changes: 60 additions & 39 deletions PyHab/PyHabBuilder.py

Large diffs are not rendered by default.

99 changes: 85 additions & 14 deletions PyHab/PyHabClass.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import os, sys
from psychopy import gui, visual, event, core, data, monitors, tools, prefs, logging
from psychopy import gui, visual, event, core, data, monitors, tools, prefs, logging, __version__
from psychopy.constants import (STARTED, PLAYING) # Added for new stimulus types
prefs.hardware['audioLib'] = ['PTB']
if eval(__version__[0:4]) < 2023: # PTB has proven itself unreliable on too many systems, but sounddevice isn't included in 2023.
prefs.hardware['audioLib'] = ['sounddevice'] # change to 'PTB' if you get audio errors
else:
prefs.hardware['audioLib'] = ['PTB']
if os.name == 'posix':
prefs.general['audioDevice'] = ['Built-in Output']
from psychopy import sound
Expand Down Expand Up @@ -955,6 +958,56 @@ def dispAudioStim(self, trialType, dispAudio):
else:
return 0

def dispAnimationStim(self, trialType, dispAnim, screen='C'):
"""
Placeholder function for displaying an animation. Requires some code customization to use. Create an experiment
then go into its copy of PyHabClass and modify it to have different animation routines based on dispAnim. Note
that the objects for the animations need to be created in SetupWindow.
You can feel free to use self.frameCount[screen] to regulate your animations and reset is as needed.
A demo animation is written below but never referenced.
:param trialType: the current trial type
:type trialType: str
:param dispAnim: Name of the animation to display
:type dispAnim: str
:param screen: Which screen to display on (matters for HPP)
:type screen: str
:return: int specifying animation in progress (0), paused on last frame (1), or ending and looping (2)
:rtype: int
"""

if dispAnim == 'demoAnim':
if self.frameCount[screen] == 0:
self.demoAnimationObject.draw()
self.frameCount[screen] += 1
self.win.flip()
return 0
elif self.frameCount[screen] < 60:
self.frameCount[screen] += 1
self.demoAnimationObject.pos[0] += 1
self.demoAnimationObject.draw()
self.win.flip()
return 0
elif self.frameCount[screen] < 120:
self.frameCount[screen] += 1
self.demoAnimationObject.pos[0] -= 1
self.demoAnimationObject.draw()
self.win.flip()
return 0
elif self.frameCount[screen] < 120 + self.pauseCount[screen]:
self.frameCount[screen] += 1
self.demoAnimationObject.draw()
self.win.flip()
return 1
else:
self.frameCount[screen] = 0
self.win.flip()
return 0


return 1


def dispTrial(self, trialType, dispMovie = False): #If no stim, dispMovie defaults to false.
"""
Expand All @@ -973,6 +1026,8 @@ def dispTrial(self, trialType, dispMovie = False): #If no stim, dispMovie defaul
if self.stimPres:
if dispMovie['stimType'] == 'Movie':
t = self.dispMovieStim(trialType, dispMovie['stim'])
elif dispMovie['stimType'] == 'Animation':
t = self.dispAnimationStim(trialType, dispMovie['stim'])
elif dispMovie['stimType'] == 'Image':
t = self.dispImageStim(dispMovie['stim'])
elif dispMovie['stimType'] == 'Audio' and trialType != 0: # No still-frame equivalent
Expand Down Expand Up @@ -1220,26 +1275,23 @@ def insertHab(self, tn, block, hn=-1):
trialNum = tn
if hn == -1:
hn = self.habCount[block]
habNum = hn
if len(self.blockList[block]['trialList']) > 0:
self.blockExpander(self.blockList[block], block, hab=True, habNum=habNum+1, insert=trialNum-1)
self.blockExpander(self.blockList[block], block, hab=True, habNum=hn+1, insert=trialNum-1) # insert being one index before was a problem
trialType = self.actualTrialOrder[trialNum - 1]
while '.' in trialType:
trialType = trialType[trialType.index('.') + 1:]
if self.stimPres and habNum == self.habCount[block]: # If we're inserting something way down the line, don't mess with it yet.
if self.stimPres and hn == self.habCount[block]: # If we're inserting something way down the line, don't mess with it yet.
if self.counters[trialType] >= len(self.stimNames[trialType]): # Comes up with multiple repetitions of few movies
self.stimName = self.stimNames[trialType][self.counters[trialType] % len(self.stimNames[trialType])]
else:
self.stimName = self.stimNames[trialType][self.counters[trialType]]

if self.counters[trialType] >= len(self.stimNames[trialType]): # 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])]
disMovie = self.stimDict[trialType][self.counters[trialType] % len(self.stimNames[trialType])]
else:
self.stimName = self.stimNames[trialType][self.counters[trialType]]
disMovie = self.stimDict[trialType][
self.counters[trialType]]
disMovie = self.stimDict[trialType][self.counters[trialType]]
self.counters[trialType] += 1
if self.counters[trialType] < 0:
self.counters[trialType] = 0
Expand Down Expand Up @@ -1331,6 +1383,7 @@ def doExperiment(self):
self.rdyTextAppend = " NEXT: " + self.actualTrialOrder[trialNum - 1] + " TRIAL"
end = False
skip = False
habInsert = False # Adding this to track whether a hab trial has been inserted already
if trialType not in AA and self.nextFlash in [1,'1',True,'True']: # The 'flasher' to alert the experimenter they need to start the next trial
self.flashCoderWindow()
while not self.keyboard[self.key.A] and trialType not in AA and not end: # wait for 'ready' key, check at frame intervals
Expand Down Expand Up @@ -1364,12 +1417,13 @@ def doExperiment(self):
if self.habMetWhen[topBlockName] == -1: # jump to test in a hab design
[disMovie, trialType] = self.jumpToTest(trialNum, topBlockName)
# Insert add'l hab trial. Problem: this can only be invoked outside of a hab block, but needs to refer back to the hab block!
elif self.keyboard[self.key.I] and len(self.habMetWhen.keys()) > 0: # insert additional hab trial
elif self.keyboard[self.key.I] and len(self.habMetWhen.keys()) > 0 and not habInsert: # insert ONE additional hab trial
# Find the most recent hab block. It should be the most recent trial, in fact.
if '*' in self.actualTrialOrder[trialNum-2]:
for k, t in self.habMetWhen.items():
if self.actualTrialOrder[trialNum-2][0:len(k)] == k and t > 0:
[disMovie, trialType] = self.insertHab(trialNum, k)
habInsert = True
while '.' in trialType:
trialType = trialType[trialType.index('.') + 1:]
# Eye-tracker only: Redo calibration. Can't be done before first trial because recording not started yet.
Expand Down Expand Up @@ -1424,7 +1478,8 @@ def doExperiment(self):
startAG = core.getTime()
onCheck = 0
while simAG:
if core.getTime() - startAG >= self.attnGetterList[self.playAttnGetter[trialType]['attnGetter']]['stimDur']:
# 150ms shortening to account for RT delay. This is a conservative number.
if core.getTime() - startAG + .5 >= self.attnGetterList[self.playAttnGetter[trialType]['attnGetter']]['stimDur']:
simAG = False
elif self.playAttnGetter[trialType]['cutoff'] and self.lookKeysPressed():
if onCheck == 0 and self.playAttnGetter[trialType]['onmin'] > 0:
Expand All @@ -1433,6 +1488,7 @@ def doExperiment(self):
simAG = False
elif onCheck > 0: # A clever little way to say "if they aren't looking but were earlier"
onCheck = 0
self.dispCoderWindow(0)
core.wait(self.freezeFrame) # an attempt to match the delay caused by the attention-getter playing.
waitStart = True
else:
Expand Down Expand Up @@ -1467,7 +1523,8 @@ def doExperiment(self):
startAG = core.getTime()
onCheck = 0
while simAG:
if core.getTime() - startAG >= self.attnGetterList[self.playAttnGetter[trialType]['attnGetter']]['stimDur']:
# 150ms shortening to account for RT delay. This is a conservative number.
if core.getTime() - startAG + .5 >= self.attnGetterList[self.playAttnGetter[trialType]['attnGetter']]['stimDur']:
simAG = False
elif self.playAttnGetter[trialType]['cutoff'] and self.lookKeysPressed():
if onCheck == 0 and self.playAttnGetter[trialType]['onmin'] > 0:
Expand All @@ -1476,6 +1533,7 @@ def doExperiment(self):
simAG = False
elif onCheck > 0: # A clever little way to say "if they aren't looking but were earlier"
onCheck = 0
self.dispCoderWindow(0)
core.wait(self.freezeFrame)
elif self.lookKeysPressed():
waitStart = False
Expand Down Expand Up @@ -1506,12 +1564,13 @@ def doExperiment(self):
elif self.keyboard[self.key.J] and topBlockName in self.habMetWhen.keys():
if self.habMetWhen[topBlockName] == -1: # jump to test in a hab design
[disMovie, trialType] = self.jumpToTest(trialNum)
elif self.keyboard[self.key.I] and len(self.habMetWhen.keys()) > 0: # insert additional hab trial
elif self.keyboard[self.key.I] and len(self.habMetWhen.keys()) > 0 and not habInsert: # insert additional hab trial
# Only works if trial before this one was a hab block trial.
if '*' in self.actualTrialOrder[trialNum - 2]:
for k, t in self.habMetWhen.items():
if self.actualTrialOrder[trialNum - 2][0:len(k)] == k and t > 0:
[disMovie, trialType] = self.insertHab(trialNum, k)
habInsert = True
while '.' in trialType:
trialType = trialType[trialType.index('.') + 1:]
elif self.keyboard[self.key.S] and '*' not in trialType: # Skip trial. Doesn't work on things required for habituation.
Expand Down Expand Up @@ -2014,7 +2073,7 @@ def onDuration(adds=0, subs=0): # A function for the duration switch, while lea
self.dataRec(onArray, offArray, number, dataType, onArray2, offArray2, self.stimName, habDataRec, habCrit)
# If this is a habituation block
if habTrial:
if self.habMetWhen[habBlock] == -1 and not abort: # if still during habituation
if self.habMetWhen[habBlock] == -1: # if still during habituation
if localType in self.blockList[habBlock]['calcHabOver']:
tempSum = 0
# Check if computing habituation by duration or on-time
Expand Down Expand Up @@ -2049,6 +2108,8 @@ def onDuration(adds=0, subs=0): # A function for the duration switch, while lea
return 0
else:
return 0
else:
return 0 # edge case of inserted hab trial.
elif number >= len(self.actualTrialOrder) or ttype == 4:
# End experiment
return 2
Expand Down Expand Up @@ -2876,6 +2937,8 @@ def loadStim(self, stim, screen='C'):
tempStimObj = visual.MovieStim3(w, tempStim['stimLoc'],
size=[self.movieWidth[screen], self.movieHeight[screen]], flipHoriz=False,
flipVert=False, loop=False)
elif tempStim['stimType'] == 'Animation':
tempStimObj = tempStim['stimLoc'] # in this case it's just a string referencing a custom function
elif tempStim['stimType'] == 'Image':
tempStimObj = visual.ImageStim(w, tempStim['stimLoc'],
size=[self.movieWidth[screen], self.movieHeight[screen]])
Expand Down Expand Up @@ -3026,6 +3089,13 @@ def SetupWindow(self):
self.endImageObject = visual.ImageStim(self.win, tempStim['stimLoc'], size=[self.movieWidth['C'], self.movieHeight['C']])
else:
self.endImageObject = None

"""
Add any custom objects for animation stimuli here.
"""
self.demoAnimationObject = visual.Rect(self.win, height=100, width=100, fillColor='red')

self.win.flip() # Clears "checking framerate" text from display window.
self.keyboard = self.key.KeyStateHandler()
self.win2.winHandle.push_handlers(self.keyboard)
if self.stimPres:
Expand Down Expand Up @@ -3058,4 +3128,5 @@ def SetupWindow(self):
color='white', bold=True, height=30)
self.trialText = visual.TextStim(self.win2, text="Trial no: ", pos=[-100, 150], color='white')
self.readyText = visual.TextStim(self.win2, text="Trial not active", pos=[-25, 100], color='white')

self.doExperiment() # Get this show on the road!
18 changes: 16 additions & 2 deletions PyHab/PyHabClassHPP.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import os, sys
from psychopy import visual, event, core, data, gui, monitors, tools, prefs, logging
from psychopy import visual, event, core, data, gui, monitors, tools, prefs, logging, __version__
from psychopy.constants import (STARTED, PLAYING) # Added for new stimulus types
prefs.hardware['audioLib'] = ['PTB']
if eval(__version__[0:4]) < 2023: # PTB has proven itself unreliable on too many systems, but sounddevice isn't included in 2023.
prefs.hardware['audioLib'] = ['sounddevice'] # change to 'PTB' if you get audio errors
else:
prefs.hardware['audioLib'] = ['PTB']
if os.name == 'posix':
prefs.general['audioDevice'] = ['Built-in Output']
from psychopy import sound
Expand Down Expand Up @@ -1414,6 +1417,17 @@ def SetupWindow(self):
self.winR = visual.Window((self.screenWidth['R'], self.screenHeight['R']), fullscr=False, screen=self.screenIndex['R'], allowGUI=False,
units='pix', color=self.screenColor['R'])
self.dummyThing = visual.Circle(self.win, size=1, color=self.win.color) # This is for fixing a display glitch in PsychoPy3 involving multiple windows of different sizes.

"""
Put any objects for animation stimuli here
"""


# Clearing "checking framerate"
self.win.flip()
self.winL.flip()
self.winR.flip()

# Coder window
self.win2 = visual.Window((400, 400), fullscr=False, screen=self.expScreenIndex, allowGUI=True, units='pix', waitBlanking=False,
rgb=[-1, -1, -1])
Expand Down
7 changes: 5 additions & 2 deletions PyHab/PyHabClassPL.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import os, sys
from psychopy import visual, event, core, data, gui, monitors, tools, prefs, logging
from psychopy import visual, event, core, data, gui, monitors, tools, prefs, logging, __version__
from psychopy.constants import (STARTED, PLAYING) # Added for new stimulus types
prefs.hardware['audioLib'] = ['PTB']
if eval(__version__[0:4]) < 2023: # PTB has proven itself unreliable on too many systems, but sounddevice isn't included in 2023.
prefs.hardware['audioLib'] = ['sounddevice'] # change to 'PTB' if you get audio errors
else:
prefs.hardware['audioLib'] = ['PTB']
if os.name == 'posix':
prefs.general['audioDevice'] = ['Built-in Output']
from psychopy import sound
Expand Down

0 comments on commit 2c085ab

Please sign in to comment.