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