Skip to content

Commit

Permalink
ENH: save without loop for all response components
Browse files Browse the repository at this point in the history
- create an _implicitLoop as part of an Experiment
- enable its use in keyboard, mouse, ratingscale, microphone, cedrus, iolabs
  • Loading branch information
jeremygray committed Mar 3, 2014
1 parent 2fa7e01 commit 932f092
Show file tree
Hide file tree
Showing 6 changed files with 69 additions and 62 deletions.
8 changes: 5 additions & 3 deletions psychopy/app/builder/components/ioLabs.py
Original file line number Diff line number Diff line change
Expand Up @@ -199,12 +199,12 @@ def writeRoutineEndCode(self, buff):
# some shortcuts
name = self.params['name']
store = self.params['store'].val
if store == 'nothing':
return
if len(self.exp.flow._loopList):
currLoop = self.exp.flow._loopList[-1] # last (outer-most) loop
else:
currLoop = None
if store == 'nothing' or not currLoop: # need a loop to store any data!
return
currLoop = self.exp._implicitLoop

# write the actual code
lines = ''
Expand All @@ -231,6 +231,8 @@ def writeRoutineEndCode(self, buff):
buff.writeIndented("%s.addData('%s.corr', %s.corr)\n" % loopnamename)
buff.writeIndented("if %(name)s.btns != None: # add RTs if there are responses\n" % self.params)
buff.writeIndented(" %s.addData('%s.rt', %s.rt)\n" % loopnamename)
if currLoop.params['name'].val == self.exp._implicitLoop.name:
buff.writeIndented("%s.nextEntry()\n" % self.exp._implicitLoop.name)

def writeExperimentEndCode(self, buff):
buff.writeIndented('%(name)s.standby() # lights out etc\n' % self.params)
59 changes: 29 additions & 30 deletions psychopy/app/builder/components/keyboard.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,38 +176,37 @@ def writeRoutineEndCode(self,buff):
#some shortcuts
name = self.params['name']
store=self.params['store'].val
if store == 'nothing':
return
if len(self.exp.flow._loopList):
currLoop=self.exp.flow._loopList[-1]#last (outer-most) loop
currLoop=self.exp.flow._loopList[-1] # last (outer-most) loop
else:
# dummy TrialHandler, same name as the ExperimentHandler that will be created in the exp script:
currLoop = TrialHandler(self.exp, name='thisExp')
currLoop.type = 'ExperimentHandler' # displayed in a code comment
currLoop = self.exp._implicitLoop

#write the actual code
if store != 'nothing': # there's always a loop: user defined or thisExp (implicitly)
buff.writeIndented("# check responses\n" %self.params)
buff.writeIndented("if %(name)s.keys in ['', [], None]: # No response was made\n"%self.params)
buff.writeIndented(" %(name)s.keys=None\n" %(self.params))
if self.params['storeCorrect'].val:#check for correct NON-repsonse
buff.writeIndented(" # was no response the correct answer?!\n" %(self.params))
buff.writeIndented(" if str(%(correctAns)s).lower() == 'none': %(name)s.corr = 1 # correct non-response\n" %(self.params))
buff.writeIndented(" else: %(name)s.corr = 0 # failed to respond (incorrectly)\n" %(self.params))
buff.writeIndented("# store data for %s (%s)\n" %(currLoop.params['name'], currLoop.type))
if currLoop.type in ['StairHandler', 'MultiStairHandler']:
#data belongs to a Staircase-type of object
if self.params['storeCorrect'].val==True:
buff.writeIndented("%s.addResponse(%s.corr)\n" %(currLoop.params['name'], name))
buff.writeIndented("%s.addOtherData('%s.rt', %s.rt)\n" %(currLoop.params['name'], name, name))
else:
#always add keys
buff.writeIndented("%s.addData('%s.keys',%s.keys)\n" \
%(currLoop.params['name'],name,name))
if self.params['storeCorrect'].val==True:
buff.writeIndented("%s.addData('%s.corr', %s.corr)\n" \
%(currLoop.params['name'], name, name))
#only add an RT if we had a response
buff.writeIndented("if %(name)s.keys != None: # we had a response\n" %(self.params))
buff.writeIndented(" %s.addData('%s.rt', %s.rt)\n" \
buff.writeIndented("# check responses\n" %self.params)
buff.writeIndented("if %(name)s.keys in ['', [], None]: # No response was made\n"%self.params)
buff.writeIndented(" %(name)s.keys=None\n" %(self.params))
if self.params['storeCorrect'].val:#check for correct NON-repsonse
buff.writeIndented(" # was no response the correct answer?!\n" %(self.params))
buff.writeIndented(" if str(%(correctAns)s).lower() == 'none': %(name)s.corr = 1 # correct non-response\n" %(self.params))
buff.writeIndented(" else: %(name)s.corr = 0 # failed to respond (incorrectly)\n" %(self.params))
buff.writeIndented("# store data for %s (%s)\n" %(currLoop.params['name'], currLoop.type))
if currLoop.type in ['StairHandler', 'MultiStairHandler']:
#data belongs to a Staircase-type of object
if self.params['storeCorrect'].val==True:
buff.writeIndented("%s.addResponse(%s.corr)\n" %(currLoop.params['name'], name))
buff.writeIndented("%s.addOtherData('%s.rt', %s.rt)\n" %(currLoop.params['name'], name, name))
else:
#always add keys
buff.writeIndented("%s.addData('%s.keys',%s.keys)\n" \
%(currLoop.params['name'],name,name))
if self.params['storeCorrect'].val==True:
buff.writeIndented("%s.addData('%s.corr', %s.corr)\n" \
%(currLoop.params['name'], name, name))
if currLoop.params['name'].val == 'thisExp':
buff.writeIndented("thisExp.nextEntry()\n")
#only add an RT if we had a response
buff.writeIndented("if %(name)s.keys != None: # we had a response\n" %(self.params))
buff.writeIndented(" %s.addData('%s.rt', %s.rt)\n" \
%(currLoop.params['name'], name, name))
if currLoop.params['name'].val == self.exp._implicitLoop.name:
buff.writeIndented("%s.nextEntry()\n" % self.exp._implicitLoop.name)
21 changes: 9 additions & 12 deletions psychopy/app/builder/components/microphone.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,18 +79,15 @@ def writeRoutineEndCode(self,buff):
name = self.params['name']
if len(self.exp.flow._loopList):
currLoop = self.exp.flow._loopList[-1] #last (outer-most) loop
else: currLoop=None
else:
currLoop = self.exp._implicitLoop

#write the actual code
if currLoop: #need a loop to do the storing of data!
buff.writeIndented("# check responses\n" %self.params)
buff.writeIndented("if not %(name)s.savedFile:\n"%self.params)
buff.writeIndented(" %(name)s.savedFile = None\n" %(self.params))
buff.writeIndented("# store data for %s (%s)\n" %(currLoop.params['name'], currLoop.type))
buff.writeIndented("# check responses\n" %self.params)
buff.writeIndented("if not %(name)s.savedFile:\n"%self.params)
buff.writeIndented(" %(name)s.savedFile = None\n" %(self.params))
buff.writeIndented("# store data for %s (%s)\n" %(currLoop.params['name'], currLoop.type))

#always add saved file name
buff.writeIndented("%s.addData('%s.filename', %s.savedFile)\n" % (currLoop.params['name'],name,name))
#only add loudness / rms if we have a file
#buff.writeIndented("if %(name)s.savedFile != None:\n" %(self.params))
#buff.writeIndented(" %s.addData('%s.rms', %s.rms)\n" \
# %(currLoop.params['name'], name, name))
#always add saved file name
buff.writeIndented("%s.addData('%s.filename', %s.savedFile)\n" % (currLoop.params['name'],name,name))
# best not to do loudness / rms or other processing here
24 changes: 12 additions & 12 deletions psychopy/app/builder/components/mouse.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

from _base import *
from os import path
from psychopy.app.builder.experiment import Param, TrialHandler
from psychopy.app.builder.experiment import Param

thisFolder = path.abspath(path.dirname(__file__))#the absolute path to the folder containing this path
iconFile = path.join(thisFolder,'mouse.png')
Expand Down Expand Up @@ -135,19 +135,19 @@ def writeRoutineEndCode(self,buff):
#some shortcuts
name = self.params['name']
store = self.params['saveMouseState'].val#do this because the param itself is not a string!
if store == 'nothing':
return

forceEnd = self.params['forceEndRoutineOnPress'].val
if len(self.exp.flow._loopList):
currLoop=self.exp.flow._loopList[-1]#last (outer-most) loop
currLoop=self.exp.flow._loopList[-1] # last (outer-most) loop
else:
# dummy TrialHandler, same name as the ExperimentHandler that will be created in the exp script:
currLoop = TrialHandler(self.exp, name='thisExp')
currLoop.type = 'ExperimentHandler' # displayed in a code comment
currLoop = self.exp._implicitLoop

if store != 'nothing':
if currLoop.type=='StairHandler':
buff.writeIndented("# NB PsychoPy doesn't handle a 'correct answer' for mouse events so doesn't know how to handle mouse with StairHandler\n")
else:
buff.writeIndented("# store data for %s (%s)\n" %(currLoop.params['name'], currLoop.type))
if currLoop.type=='StairHandler':
buff.writeIndented("# NB PsychoPy doesn't handle a 'correct answer' for mouse events so doesn't know how to handle mouse with StairHandler\n")
else:
buff.writeIndented("# store data for %s (%s)\n" %(currLoop.params['name'], currLoop.type))
if store == 'final':
#buff.writeIndented("# get info about the %(name)s\n" %(self.params))
buff.writeIndented("x, y = %(name)s.getPos()\n" %(self.params))
Expand All @@ -166,5 +166,5 @@ def writeRoutineEndCode(self,buff):
else:
#we only had one click so don't return a list
buff.writeIndented("%s.addData('%s.%s', %s.%s[0])\n" %(currLoop.params['name'], name,property,name,property))
if store != 'nothing' and currLoop.params['name'].val == 'thisExp':
buff.writeIndented("thisExp.nextEntry()\n")
if currLoop.params['name'].val == self.exp._implicitLoop.name:
buff.writeIndented("%s.nextEntry()\n" % self.exp._implicitLoop.name)
11 changes: 6 additions & 5 deletions psychopy/app/builder/components/ratingscale.py
Original file line number Diff line number Diff line change
Expand Up @@ -235,14 +235,15 @@ def writeRoutineEndCode(self, buff):
if len(self.exp.flow._loopList):
currLoop = self.exp.flow._loopList[-1] # last (outer-most) loop
else:
currLoop = None
currLoop = self.exp._implicitLoop

#write the actual code
if currLoop and (self.params['storeRating'].val or self.params['storeRatingTime'].val):
if self.params['storeRating'].val or self.params['storeRatingTime'].val:
if currLoop.type in ['StairHandler', 'QuestHandler']:
buff.writeIndented("# NB PsychoPy doesn't handle a 'correct answer' for ratingscale " +
"events so doesn't know what to tell a StairHandler (or QuestHandler)")
elif currLoop.type == 'TrialHandler':
"events so doesn't know what to tell a StairHandler (or QuestHandler)\n")
elif currLoop.type in ['TrialHandler', 'ExperimentHandler']:
buff.writeIndented("# store data for %s (%s)\n" %(currLoop.params['name'], currLoop.type))
if self.params['storeRating'].val == True:
buff.writeIndented("%s.addData('%s.response', %s.getRating())\n" \
% (currLoop.params['name'], name, name))
Expand All @@ -253,4 +254,4 @@ def writeRoutineEndCode(self, buff):
buff.writeIndented("%s.addData('%s.history', %s.getHistory())\n" \
% (currLoop.params['name'], name, name))
else:
buff.writeIndented("# RatingScaleComponent: unknown loop type, not saving any data.")
buff.writeIndented("# RatingScaleComponent: unknown loop type, not saving any data.\n")
8 changes: 8 additions & 0 deletions psychopy/app/builder/experiment.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,14 @@ def __init__(self, prefs=None):
self.settings=getComponents(fetchIcons=False)['SettingsComponent'](parentName='', exp=self)
self._doc=None#this will be the xml.dom.minidom.doc object for saving
self.namespace = NameSpace(self) # manage variable names

# _implicitLoop is a hack to allow saving data from components not inside a loop.
# data-saving machinery is tied to loops, not worth rewriting, hence a hack:
self._implicitLoop = TrialHandler(exp=self, name='thisExp')
# it will be an ExperimentHandler when used in the generated script,
# but its easier to use treat it as a TrialHandler during script generation:
self._implicitLoop.type = 'ExperimentHandler'
self._implicitLoop.name = self._implicitLoop.params['name'].val
def requirePsychopyLibs(self, libs=[]):
"""Add a list of top-level psychopy libs that the experiment will need.
e.g. [visual, event]
Expand Down

0 comments on commit 932f092

Please sign in to comment.