""" Jankovic et al Experiment 2a: TO RUN, REMOVE COMMENT SYMBOLS "#" FROM LINES 687-689 Experiment 1 found no alerting when Flash occurred in Maljkovic & Nakayama (1994) compound task Experiment 2a: to examine effect of alerting Flash in first component task (oddball selection/detection) Conditions: ColourRepeat x Flash x OddballPresence x PrevTrialOddball (2x2x2x2) Each trial coded as a member of a pair - second trials in pair for analysis only 10% catch trials included Alerting stimulus: brightening of the screen background for 48 ms; 100 ms flash --> display SOA @author: nadja """ from psychopy import visual, event, core, misc, gui import random from datetime import datetime from win32api import GetSystemMetrics ##define an error that we can raise (from Bert) class ExpError(Exception): def __init__(self, value): self.value=value def __str__(self): return self.value # Class to define the Maljkovic & Nakayama diamond shaped items with either L or R corner cut off class DiamondItem(object): def __init__(self, Display, Trial, CutPos, Colour, ItemCenter = [0,0], isTarget = False): super(DiamondItem,self).__init__() self.Trial = Trial self.Display = Display self.isTarget = isTarget self.CutPos = CutPos self.ItemCenter = ItemCenter self.Colour = Colour self.Shape = visual.ShapeStim( win = self.Display.Trial.Block.Experiment.win, lineColor = [0,0,0], fillColor = [0,0,0], fillColorSpace = 'rgb', lineColorSpace = 'rgb', ##N&M: 1.0 x 1.0 deg of vis angle, with .14 deg cutoff (at 57 cm away, will be 1 cm by 1 cm, with .14 cm cutoff) ## On Lab screens: 1.0 cm x 1.0 cm, with .15 cut off (at 57 cm away) vertices = [(20,0),(0,20),(-10,10),(-10,-10),(0,-20)], pos = self.ItemCenter, closeShape = True ) ## Change shape colour based on Colour property. Only 2 options: Red or Green try: if self.Colour == "Red": self.Shape.lineColor = [1,-1,-1] self.Shape.fillColor = [1,-1,-1] elif self.Colour == "Green": self.Shape.fillColor = [-1,-.09,-1] self.Shape.lineColor = [-1,-.09,-1] elif self.Colour == None: pass except: raise ExpError("Error in setting DiamondItem's self.Colour. Acceptable arguments: 'Red', 'Green', None.") ## Change orientation based on Left or Right cut position. In degrees. try: if self.CutPos == "Left": self.Shape.ori = 0 elif self.CutPos == "Right": self.Shape.ori = 180 except: raise ExpError("Error in setting DiamondItem's self.CutPos. Acceptable Arguments: 'Left' or 'Right'") def drawItem(self): self.Shape.draw() def targetPosition(self): if self.isTarget: return self.ItemCenter else: return None # defining the Display or the Stimulus Array class: 12 locations around an imaginary annulus. The target and two distractors populate 3 of those locations, and those 3 locations are always equidistant from each other (looks like 3 vertices of an equilateral triangle) class Display(object): def __init__(self, Trial, TargetColour, SetSize, TargetShapeID, isOddballPresent): super(Display,self).__init__() self.isOddballPresent = isOddballPresent self.Trial = Trial self.TargetColour = TargetColour self.SetSize = SetSize self.TargetShapeID = TargetShapeID ## Set target and distractor item colours based on whether it's an oddball present/absent trial if self.TargetColour == "Red": if self.isOddballPresent == True: self.DistractorColour = "Green" elif self.isOddballPresent == False: self.DistractorColour = "Red" elif self.TargetColour == "Green": if self.isOddballPresent == True: self.DistractorColour = "Red" elif self.isOddballPresent == False: self.DistractorColour = "Green" else: self.DistractorColour = "None" def createDisplay(self): Num_Positions = 12 ##hardcoded, but what M&N used - 3 items, 12 possible positions around the array, always equidistant. Coded to draw on every 4th position. Position_List = range(Num_Positions) ##generates a list from which we will pick a multiplier for our angles Position_Multiplier = random.choice(Position_List) ##randomly chooses from list of [0,1,2,3,4,5,6,7,8,9,10,11]. We will multiply this by 360/Num_Positions, which is 30. Angles_List = [] ## N&M: ellipse: major axis (10.0), minor axis (8.1) - so, 5 cm radius chosen ## circular array: Radius = 5 deg of visual angle (diameter = 10 deg) Array_Radius = 184 ## 184 pixels on Main Lab computers = 5 cm = 5 deg vis angle at 57 cm away for i in range(self.SetSize): angle = (i*360/self.SetSize) + (Position_Multiplier * (360/Num_Positions)) ## n * 30 deg added to all calculated angles. In effect, this changes the orientation of the display while keeping items equidistant Angles_List.append(angle) random.shuffle(Angles_List) self.Display_Items_List = [] for i in range(len(Angles_List)): ## choose first iteration to be the Target (even if an Oddball absent trial, technically no "target" per se but for coding purposes, it will just be the first distractor drawn) if i == 0: [surr_x, surr_y] = misc.pol2cart( Angles_List[i], Array_Radius ) target = DiamondItem(Display = self, Trial = self.Trial, CutPos = self.Trial.TargetShapeID, Colour = self.Trial.TargetColour, ItemCenter = [surr_x, surr_y], isTarget = True) self.Display_Items_List.append(target) ## choose following iterations to be the distractors: else: [surr_x, surr_y] = misc.pol2cart( Angles_List[i], Array_Radius ) ## randomly choose the cut position for the distractor: Distractor_ShapeID = random.choice(["Right","Left"]) distractor = DiamondItem(Display = self, Trial = self.Trial, CutPos = Distractor_ShapeID, Colour = self.DistractorColour, ItemCenter = [surr_x, surr_y]) self.Display_Items_List.append(distractor) def draw(self): for i in range(len(self.Display_Items_List)): self.Display_Items_List[i].drawItem() class Trial(object): TrialNo = 0 def __init__(self, Experiment, DoubletOrder, isOddballPresent, CounterBalanceOpt, Block, Repeat, TargetColour, TargetShapeID, Flash, SetSize, isCatchTrial= False): super(Trial, self).__init__() self.Experiment = Experiment self.DoubletOrder = DoubletOrder self.isOddballPresent = isOddballPresent self.CounterBalanceOpt = CounterBalanceOpt self.Block = Block self.Repeat = Repeat self.TargetColour = TargetColour self.TargetShapeID = TargetShapeID self.Flash = Flash self.SetSize = SetSize self.isCatchTrial = isCatchTrial if not self.isCatchTrial: self.TargetShapeID = random.choice(["Left","Right"]) if not self.isOddballPresent: self.DistractorColour = self.TargetColour elif self.isOddballPresent: if self.TargetColour == "Red": self.DistractorColour = "Green" elif self.TargetColour == "Green": self.DistractorColour = "Red" else: self.TargetShapeID = "None" self.DistractorColour = "None" self.FixCross = visual.ShapeStim( win = self.Block.Experiment.win, vertices=([0,-5], [0,5], [0,0], [-5,0], [5,0]), lineWidth=2, units="pix", closeShape=False, lineColor=[-1,-1,-1], ## black: N&M had white fixcross, but I chose black because our screen is flashing white autoDraw= True ) def createTrialDisplay(self): if not self.isCatchTrial: self.TrialDisplay = Display(Trial = self, isOddballPresent = self.isOddballPresent, TargetColour = self.TargetColour, SetSize = self.SetSize, TargetShapeID = self.TargetShapeID) self.TrialDisplay.createDisplay() # If it is a catch trial, then just draw a grey circle (same as background colour) instead of the display elif self.isCatchTrial: self.TrialDisplay = visual.Circle( win = self.Block.Experiment.win, radius = 45, ## 45 = 1 inch on display; 2.54 deg vis angle edges = 255, lineColor = [0,0,0], ##same as background colour fillColor = [0,0,0], pos = (0,0) ) def runTrial(self): Flash_Duration = .044 ## want to approximate flash lasting 50 msec. Best we can do is 48 msec. To be safe from adding an extra frame, subtract from 48 half of the refresh rate Flash_Jitter = random.randint(8,12)/10.0 ## delivers a random floating point number between .8 and 1.2 (to jitter the period before the start of the trial and the Flash. ISI_Duration = .044 ## to achieve total ISI duration of "50" msec - see Experiment 1 comments self.FixCross.autoDraw = True self.FixCross.draw() timer = core.Clock() self.Block.Experiment.win.flip() timer.reset() core.wait(Flash_Jitter) self.Block.Experiment.win.flip() ## screen background changes to white for stim duration if self.Flash == "Flash": timer.reset() while timer.getTime() < Flash_Duration: self.Block.Experiment.win.color = [1,1,1] self.Block.Experiment.win.flip() ##Set screen colour back to grey: self.Block.Experiment.win.color = [0,0,0] self.Block.Experiment.win.flip() ## screen remains gray (with fixcross) for same duration else: timer.reset() while timer.getTime() < Flash_Duration: pass self.Block.Experiment.win.color = [0,0,0] self.Block.Experiment.win.flip() ## ISI between the Flash (or absence of the flash) and the display onset timer.reset() while timer.getTime() < ISI_Duration: pass ## Present the stimulus display self.TrialDisplay.draw() self.Block.Experiment.win.flip() timer.reset() # Collect response to display and record time self.keys = event.waitKeys( maxWait = 2, keyList = ['m','z'], timeStamped = timer ) self.FixCross.autoDraw = False def Response(self): if self.keys: KeyPress = self.keys[0][0] ## to get the actual key pressed: index 0 of self.keys tuple else: KeyPress = "None" return KeyPress ## If CounterBalanceOpt is 1, then the correct response to Oddball present is 'm', and correct response to oddball absent is 'z' (opposite for CBO 2) def Acc(self): if self.isOddballPresent == True: if self.CounterBalanceOpt == 1: if self.keys and self.keys[0][0] == 'm': return 1 else: return 0 elif self.CounterBalanceOpt == 2: if self.keys and self.keys[0][0] == 'z': return 1 else: return 0 elif self.isOddballPresent == False: if self.CounterBalanceOpt == 1: if self.keys and self.keys[0][0] == 'z': return 1 else: return 0 elif self.CounterBalanceOpt == 2: if self.keys and self.keys[0][0] == 'm': return 1 else: return 0 elif self.isOddballPresent == "None" or self.isCatchTrial == True: if self.keys: return 0 else: return 1 def RT(self): if self.keys: ReacTime = self.keys[0][1] else: ReacTime = "None" return ReacTime def CatchResponse(self): if self.isCatchTrial == True and self.keys: CatchKeyPress = 1 else: CatchKeyPress = 0 return CatchKeyPress ## str function to write to the datafile def __str__(self): ## Add 1 to both blockNumber and TrialNo, because these start at 0 return str(self.Block.Experiment.Age) +"," + str(self.Block.Experiment.Gender)+ ","+ str(self.Block.Experiment.Hand)+","+str(self.Block.Experiment.ColourBlindness)+"," +str(self.CounterBalanceOpt)+","+ \ str(self.Block.blockNumber()+1)+ ","+ str(self.TrialNo+1)+","+ str(self.DoubletOrder) + ","+str(self.isCatchTrial)+","+str(self.isOddballPresent)+","+ str(self.Repeat)+","+ \ str(self.Flash)+","+str(self.TargetColour)+","+str(self.TargetShapeID)+","+str(self.CatchResponse())+","+str(self.Response()) + ","+ \ str(self.Acc())+"," +str(self.RT()) ## for the first line of the datafile. def DataHeader(self): return "Age,Gender,Hand,R/G_Colourblindness,CounterBalanceOpt,BlockNo,TrialNo,DoubletOrder,isCatchTrial,isOddballPresent,Repeat,Flash,TargetColour,TargetShapeID,CatchResponse,Response,Acc,RT" def saveTrial(self): ## saves the trial's DataHeader() and __str__() if self.Block.blockNumber() == 0 and self.TrialNo == 0: ## this is the first trial of the first block: write the dataheader to the outputfile self.Block.Experiment.DataFile.write(self.DataHeader()+'\n') ## write the trial's data to the file: self.Block.Experiment.DataFile.write(self.__str__()+'\n') class Block(object): def __init__(self, Experiment, CounterBalanceOpt, TrialsPerBlock, TrialsPerCondition, isPracBlock = False): super(Block, self).__init__() self.Experiment = Experiment self.CounterBalanceOpt = CounterBalanceOpt self.TrialsPerCondition = TrialsPerCondition self.TrialsPerBlock = TrialsPerBlock self.isPracBlock = isPracBlock self.Trials = [] self.Trial_Types = [] ##Trial Type Codes: 2x2x2x2 = 16 trial types ## 1) NoRepeat/Repeat = self.Trial_Types[0] ## 2) NoFlash/Flash = self.Trial_Types[1] ## 3) isOddballPresent =self.Trial_Types[2] ## 4) CutLeft/CutRight = self.Trial_Types[3] def buildDoubletTypes(self): ## Type Two Trials: isOddballPresent x Repeat x Flash x Colour self.TypeTwos= [[True,"Repeat","Flash","Red"],[True,"Repeat","Flash","Green"],[True,"Repeat","NoFlash","Red"],[True,"Repeat","NoFlash","Green"],\ [True,"NoRepeat","Flash","Red"],[True,"NoRepeat","Flash","Green"],[True,"NoRepeat","NoFlash","Red"],[True,"NoRepeat","NoFlash","Green"],\ [False,"Repeat","Flash","Red"],[False,"Repeat","Flash","Green"],[False,"Repeat","NoFlash","Red"],[False,"Repeat","NoFlash","Green"],\ [False,"NoRepeat","Flash","Red"],[False,"NoRepeat","Flash","Green"],[False,"NoRepeat","NoFlash","Red"],[False,"NoRepeat","NoFlash","Green"]] ## Type One Trials: isOddballPresent x Flash self.TypeOnes= [[True, "Flash",],[True,"NoFlash"],[False, "Flash"],[False,"NoFlash"]] NumProgramConditions = len(self.TypeOnes) * len(self.TypeTwos) DoubletsPerBlock = self.TrialsPerBlock / 2 NumDoubletTypesPerBlock = int(DoubletsPerBlock/ NumProgramConditions) ##Populate self.DoubletTypes with all possible combinations from for i in range(NumDoubletTypesPerBlock): for two in range(len(self.TypeTwos)): for one in range(len(self.TypeOnes)): self.Trial_Types.append([self.TypeOnes[one], self.TypeTwos[two]]) def buildCatchTypes(self): if self.isPracBlock == False: NumCatchTrials = int(self.TrialsPerBlock/10) for i in range(NumCatchTrials): self.Trial_Types.append(["None","None","Flash","None"]) # For practice block only. Will have 16 regular trials and 2 catch trials. Responses will not be recorded. def buildPracTrials(self): NumPracCatch = 2 self.Prac_Types= [[True,"Repeat","Flash","Red"],[True,"Repeat","Flash","Green"],[True,"Repeat","NoFlash","Red"],[True,"Repeat","NoFlash","Green"],\ [True,"NoRepeat","Flash","Red"],[True,"NoRepeat","Flash","Green"],[True,"NoRepeat","NoFlash","Red"],[True,"NoRepeat","NoFlash","Green"],\ [False,"Repeat","Flash","Red"],[False,"Repeat","Flash","Green"],[False,"Repeat","NoFlash","Red"],[False,"Repeat","NoFlash","Green"],\ [False,"NoRepeat","Flash","Red"],[False,"NoRepeat","Flash","Green"],[False,"NoRepeat","NoFlash","Red"],[False,"NoRepeat","NoFlash","Green"]] for pt in range(len(self.Prac_Types)): self.Trials.append(Trial(Experiment = self.Experiment, Block = self, CounterBalanceOpt = self.CounterBalanceOpt, DoubletOrder = 1, isOddballPresent = self.Prac_Types[pt][0], Repeat = "None", Flash = self.Prac_Types[pt][2], TargetColour = self.Prac_Types[pt][3], TargetShapeID = "None", SetSize = 3)) for c in range(NumPracCatch): self.Trials.append(Trial( Experiment = self.Experiment, Block = self, CounterBalanceOpt = self.CounterBalanceOpt, DoubletOrder = 0, isOddballPresent = "None", Repeat = "None", TargetColour = "None", TargetShapeID = "None", Flash = "Flash", SetSize = 3, isCatchTrial = True)) random.shuffle(self.Trials) for t in self.Trials: t.createTrialDisplay() def buildTrials(self): self.buildDoubletTypes() self.buildCatchTypes() random.shuffle(self.Trial_Types) CatchTrial = 4 DoubletTrial = 2 for trial in range(len(self.Trial_Types)): if len(self.Trial_Types[trial]) == CatchTrial: #if the indexed item contains a single list of 4 items, it's a catch trial self.Trials.append(Trial( Experiment = self.Experiment, Block = self, CounterBalanceOpt = self.CounterBalanceOpt, DoubletOrder = 0, isOddballPresent = "None", Repeat = "None", TargetColour = "None", TargetShapeID = "None", Flash = "Flash", SetSize = 3, isCatchTrial = True )) elif len(self.Trial_Types[trial]) == DoubletTrial: #if the index item contains two lists, its a doublet trialcounter TrialOne = Trial( Experiment = self.Experiment, Block = self, CounterBalanceOpt = self.CounterBalanceOpt, DoubletOrder = 1, isOddballPresent = self.Trial_Types[trial][0][0], Repeat = "None", Flash = self.Trial_Types[trial][0][1], TargetColour = None, TargetShapeID = "None", SetSize = 3) if self.Trial_Types[trial][1][1] == "Repeat": TrialOne.TargetColour = self.Trial_Types[trial][1][3] elif self.Trial_Types[trial][1][1] =="NoRepeat": if self.Trial_Types[trial][1][3] == "Red": TrialOne.TargetColour = "Green" elif self.Trial_Types[trial][1][3] == "Green": TrialOne.TargetColour = "Red" TrialTwo = Trial( Experiment = self.Experiment, Block = self, CounterBalanceOpt = self.CounterBalanceOpt, DoubletOrder = 2, isOddballPresent = self.Trial_Types[trial][1][0], Repeat = self.Trial_Types[trial][1][1], Flash = self.Trial_Types[trial][1][2], TargetColour = self.Trial_Types[trial][1][3], TargetShapeID = "None", SetSize = 3) self.Trials.append(TrialOne) self.Trials.append(TrialTwo) for t in self.Trials: t.createTrialDisplay() def runTrials(self): trialcounter = 0 for t in self.Trials: t.TrialNo = trialcounter t.runTrial() trialcounter+=1 if self.isPracBlock == False: self.saveTrials() def saveTrials(self): for t in self.Trials: t.saveTrial() def blockNumber(self): ## find this block in parent experiment's collection: try: idx=self.Experiment.Blocks.index(self) ## built in pythod method: list.index() return idx except: raise ExpError('Block blockNumber() method: unable to index block object') # to initialize the block - will be called during the inter-block screens def initBlock(self): if self.isPracBlock == False: self.buildTrials() elif self.isPracBlock == True: self.buildPracTrials() class Experiment(object): def __init__(self, NumBlocks, TrialsPerBlock, TrialsPerCondition, screen = 0, datafile = ""): super(Experiment, self).__init__() self.NumBlocks = NumBlocks self.TrialsPerBlock = TrialsPerBlock self.TrialsPerCondition = TrialsPerCondition self.Blocks = [] expGUI = gui.Dlg(title="Experiment", screen = 0) expGUI.addField("ParticipantID:" , "0") expGUI.addField("Session:", "1") configinfo = expGUI.show() if expGUI.OK: self.ParticipantID = configinfo[0] self.SessionNo = configinfo[1] else: print ("Experiment.init(): Cancelled by user") core.quit() demoGUI = gui.Dlg(title = "Demographic Information", screen = 0) demoGUI.addField("Age:", "0") demoGUI.addField("Gender:", choices = ["Female", "Male", "Other"]) demoGUI.addField("Dominant Hand:", choices = ["Right", "Left"]) demoGUI.addField("Red/Green Colourblindness?:", choices = ["No", "Yes","Not sure"]) demoinfo = demoGUI.show() if demoGUI.OK: self.Age = demoinfo[0] self.Gender = demoinfo[1] self.Hand = demoinfo[2] self.ColourBlindness = demoinfo[3] ## If nothing passed as datafile argument, then a new .csv file will be opened to write to if datafile == "": d = datetime.now() ## fn = 20181112188_s999_1.csv filename = str(d.year)+str(d.month)+str(d.day)+str(d.hour)+str(d.minute)+"_"+"s"+str(self.ParticipantID)+"_"+str(self.SessionNo)+".csv" self.DataFile = open(filename, 'w') ## If filename is passed to datafile argument, then the file will be opened to append to else: self.DataFile = open(datafile, 'a') self.win = visual.Window( size = [GetSystemMetrics(0), GetSystemMetrics(1)], ## How to fit to all screens. from win32api import GetSystemMetrics ; width = GetSystemMetics(0), height = GetSystemMetrics(1) units = 'pix', fullscr = True, color = [0,0,0], ## grey allowGUI = False, ## hide mouse waitBlanking = True ) # Counterbalancing: if even number, counterbalanceopt = 2; if odd number, counterbalanceopt = 1 if int(self.ParticipantID) % 2 == 0: self.CounterBalanceOpt = 2 else: self.CounterBalanceOpt = 1 def drawInstructions(self): ## Create and draw Instructions screen from file. FILE MUST BE IN SAME FOLDER AS THE PROGRAM! try: self.InstructionsSlide = visual.ImageStim( win = self.win, image = "Instructions_"+str(self.CounterBalanceOpt)+".bmp" ) ## Display error text instead of 'Instructions.bmp' if instructions file not found except IOError: self.InstructionsSlide = visual.TextStim( win = self.win, color = (1,1,1), text = "Oops! Unable to display Instructions. Press space to continue", font = "Calibri", height = 40) self.InstructionsSlide.draw() ## Create BlockScreen ("Block # / press space to continue") in between each block. def drawBlockScreen(self, BlockNo): self.BlockScreen = visual.TextStim( win = self.win, color = (1,1,1), pos = [0, 30], text = "Block " + str(BlockNo + 1), font = "Calibri", height = 40) self.pressSpace = visual.TextStim( win = self.win, color = (1,1,1), pos = [0, -30], text = "(press the spacebar to continue)", font = "Calibri", height = 30) self.BlockScreen.draw() self.pressSpace.draw() def drawPracStartScreen(self): self.PracStartScreen = visual.TextStim( win = self.win, color = (1,1,1), pos = [0, 30], text = "About to start the practice trials.", font = "Calibri", height = 30) self.pressSpaceBegin = visual.TextStim( win = self.win, color = (1,1,1), pos = [0, -30], text = "(press the spacebar to begin)", font = "Calibri", height = 25) self.PracStartScreen.draw() self.pressSpaceBegin.draw() def drawPracEndScreen(self): self.PracEndScreen = visual.TextStim( win = self.win, color = (1,1,1), pos = [0, 30], text = "That's the end of the practice trials. Ready to begin the experiment?", font = "Calibri", height = 30) self.pressSpaceExp = visual.TextStim( win = self.win, color = (1,1,1), pos = [0, -40], text = "(press the spacebar to start the experiment)", font = "Calibri", height = 25) self.PracEndScreen.draw() self.pressSpaceExp.draw() def drawDebrief(self): ## Create debrief slide imagestim object try: self.DebriefSlide = visual.ImageStim( win = self.win, image = "Debrief.bmp" # FILE MUST BE IN SAME FOLDER AS PROGRAM ) ## Display textstim error message instead of 'Debrief.bmp' if file not found except IOError: self.DebriefSlide = visual.TextStim( win = self.win, color = (1,1,1), text = "Oops! Unable to display Debrief. Press spacebar to exit", font = "Calibri", height = 40) self.DebriefSlide.draw() def createPracBlock(self): self.PracBlock = Block(Experiment = self, CounterBalanceOpt = self.CounterBalanceOpt, TrialsPerBlock = self.TrialsPerBlock, TrialsPerCondition = self.TrialsPerCondition, isPracBlock = True) self.PracBlock.initBlock() def createBlocks(self): for i in range(self.NumBlocks): self.Blocks.append(Block(Experiment = self, CounterBalanceOpt = self.CounterBalanceOpt, TrialsPerBlock = self.TrialsPerBlock, TrialsPerCondition = self.TrialsPerCondition)) random.shuffle(self.Blocks) def run(self): self.drawInstructions() self.win.flip() self.createPracBlock() self.createBlocks() event.waitKeys(keyList =['space']) self.win.flip() self.drawPracStartScreen() self.win.flip() event.waitKeys(keyList =['space']) self.PracBlock.runTrials() self.win.flip() self.drawPracEndScreen() self.win.flip() event.waitKeys(keyList=['space']) self.win.flip() self.runAllBlocks() self.drawDebrief() self.win.flip() event.waitKeys() def runAllBlocks(self): for b in range(len(self.Blocks)): self.drawBlockScreen(BlockNo = b) ## draws screen that displays block number & asks for keypress (spacebar) to continue self.win.flip() self.Blocks[b].initBlock() ## initialize the block while BlockScreen is being displayed keypress = event.waitKeys(keyList = ['space','escape']) ## If need to exit out of experiment, press esc key during block break. Will take you to debrief screen. if keypress == ["escape"]: self.drawDebrief() self.win.flip() event.waitKeys() self.cleanup() else: self.win.flip() self.runBlock(BlockNo=b) ##run a single block of trials ## Run one block of trials def runBlock(self, BlockNo=0): self.Blocks[BlockNo].runTrials() ## closes the datafile and the window; DO NOT FORGET TO CALL AT END OF EXPERIMENT def cleanup(self): self.DataFile.close() self.win.close() ## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ RUN EXPERIMENT ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ## ## ------------- test -------------## #NOTE: TRIALS PER BLOCK ARE BEFORE ADDITION OF CATCH TRIALS. #Total trials per block = 128+12 = 140 #test = Experiment(NumBlocks = 1, TrialsPerBlock = 128, TrialsPerCondition = 28) #test.run() #test.cleanup() ## ------------ EXP --------------## #Exp = Experiment(NumBlocks = 7, TrialsPerBlock = 128, TrialsPerCondition = 28) #Exp.run() #Exp.cleanup()