Module ExperimentObjects |
[hide private]
[frames] | no frames]

# Source Code for Module ExperimentObjects

      1  import StringIO
      2
      3 -class LoopHandler(list):
      4      """A looping experimental control object
      5              (e.g. generating a psychopy TrialHandler or StairHandler).
      6              """
      7 -    def __init__(self, name, loopType, nReps, trialList):
      8          """
      9          @param name: name of the loop e.g. trials
     10          @type name: string
     11          @param loopType:
     12          @type loopType: string
     13          @param nReps: number of reps (for all trial types)
     14          @type nReps:int
     15          @param trialList: list of different trial conditions to be used
     16          @type trialList: list (of dicts?)
     17          """
     18          list.__init__(self)
     19          self.loopType=loopType
     20          self.name = name
     21          self.nReps=nReps
     22          self.trialList=trialList
     23 -    def generateInitCode(self,buff):
     24          buff.write("init loop '%s' (%s)\n" %(self.loop.name, self.loop.loopType))
     25          buff.write("%s=data.TrialHandler(trialList=%s,nReps=%i,\n)" \
     26              %(self.loop.name, self.loop.trialList))
     27 -    def generateRunCode(self,buff, indent):
     28          #work out a name for e.g. thisTrial in trials:
     29          name=self.loop.name
     30          thisName = ("this"+name.capitalize()[:-1])
     31          buff.write("for %s in %s:\n" %(thisName, name))
     32
     33 -class LoopInitiator:
     34      """A simple class for inserting into the flow.
     35      This is created automatically when the loop is created"""
     36 -    def __init__(self, loop):
     37          self.loop=loop
     38 -    def generateInitCode(self,buff):
     39          self.loop.generateInitCode(buff)
     40 -    def generateRunCode(self,buff, indent):
     41          self.loop.generateRunCode(buff, indent)
     42
     43 -class LoopTerminator:
     44      """A simple class for inserting into the flow.
     45      This is created automatically when the loop is created"""
     46 -    def __init__(self, loop):
     47          self.loop=loop
     48 -    def generateInitCode(self,buff):
     49          pass
     50 -    def generateRunCode(self,buff, indent):
     51          #todo: dedent
     52          buff.write("# end of '%s' after %i repeats (of each entry in trialList)\n" %(self.loop.name, self.loop.nReps))
     53
     54 -class Flow(list):
     55      """The flow of the experiment is a list of L{Procedure}s, L{LoopInitiator}s and
     56      L{LoopTerminator}s, that will define the order in which events occur
     57      """
     58 -    def addLoop(self, loop, startPos, endPos):
     59          """Adds initiator and terminator objects for the loop
     60          into the Flow list"""
     61          self.insert(int(endPos), LoopTerminator(loop))
     62          self.insert(int(startPos), LoopInitiator(loop))
     63 -    def addProcedure(self, newProc, pos):
     64          """Adds the object to the Flow list"""
     65          self.insert(int(pos), newProc)
     66
     67 -    def generateCode(self, s):
     68          s.write("from PsychoPy import visual, core, event, sound\n")
     69          s.write("win = visual.Window([400,400])\n")
     70
     71          #initialise objects
     72          for entry in self:
     73              entry.generateInitCode(s)
     74
     75          #run-time code
     76          indentLevel = 0
     77          for entry in self:
     78              #tell the object to write its code at given level
     79              if indentLevel==0:indent=""
     80              else: indent="    "*indentLevel#insert 4 spaces for each level
     81              print entry
     82              entry.generateRunCode(s, indent)
     83              #if object was part of a loop then update level
     84              if isinstance(entry, LoopInitiator):
     85                  indentLevel+=1
     86              if isinstance(entry, LoopTerminator):
     87                  indentLevel-=1
     88
     89 -class Procedure(list):
     90      """
     91      A Procedure determines a single sequence of events, such
     92      as the presentation of trial. Multiple Procedures might be
     93      used to comprise an Experiment (e.g. one for presenting
     94      instructions, one for trials, one for debriefing subjects).
     95
     96      In practice a Procedure is simply a python list of Event objects,
     97      each of which knows when it starts and stops.
     98      """
     99 -    def __init__(self, name):
    100          self.name=name
    101          list.__init__(self)
    102 -    def generateInitCode(self,buff):
    103          buff.write('\n#Initialise objects for %s procedure\n' %self.name)
    104          for thisEvt in self:
    105              thisEvt.generateInitCode(buff)
    106 -    def generateRunCode(self,buff,indent):
    107          for event in self:
    108              event.generateRunCode(buff, indent)
    109
    110 -class EventBase(dict):
    111      """A general template for event objects"""
    112 -    def generateInitCode(self,buff):
    113          pass
    114 -    def generateRunCode(self,buff, indent):
    115          pass
    116
    117 -class EventPatch(EventBase):
    118      """An event class for presenting image-based stimuli"""
    119 -    def __init__(self, name, image, pos, size, ori,onTimes):
    120          dict.__init__(self)
    121          self['name']=name
    122          self['image']= image
    123          self['pos']=pos
    124          self['size']=size
    125          self['ori']=ori
    126          self['onTimes']=onTimes
    127
    128 -    def generateInitCode(self,buff):
    129          s = "%s=PatchStim(win=win, pos=%s, size=%s" %(self['name'], self['pos'],self['size'])
    130          buff.write(s)
    131
    132          buff.write(")\n")
    133 -    def generateRunCode(self,buff, indent):
    134          buff.write("%sdrawing PatchStim '%s'\n" %(indent, self['name']))
    135 -class EventKeyboard(EventBase):
    136      """An event class for checking the keyboard at given times"""
    137 -    def __init__(self, name, allowedKeys,onTimes):
    138          self['allowedKeys']=allowedKeys
    139          self['onTimes']=onTimes
    140 -    def generateInitCode(self,buff):
    141          pass#no need to initialise keyboards?
    142 -    def generateRunCode(self,buff,indent):
    143          buff.write("%sChecking keys" %indent)
    144
    145
    146 -class Experiment:
    147      """
    148      An experiment contains a single Flow and at least one
    149      Procedure. The Flow controls how Procedures are organised
    150      e.g. the nature of repeats and branching of an experiment.
    151      """
    152 -    def __init__(self):
    153          self.flow = Flow()
    154          self.procs={}
    155
    156 -    def addProcedure(self,procName, proc=None):
    157          """Add a Procedure to the current list of them.
    158
    159          Can take a Procedure object directly or will create
    160          an empty one if none is given.
    161          """
    162          if proc==None:
    163              self.procs[procName]=Procedure(procName)
    164          else:
    165              self.procs=proc
    166
    167 -    def generateScript(self):
    168          """Generate a PsychoPy script for the experiment
    169          """
    170          s=StringIO.StringIO(u'') #a string buffer object
    171          s.write("""#This experiment was created using PsychoPyGEN and will
    172          run on any platform on which PsychoPy (www.psychopy.org) can be installed\n\n""")
    173
    174          #delegate most of the code-writing to Flow
    175          self.flow.generateCode(s)
    176
    177          return s
    178