def __init__(self, settings_file=None, eyetracker_on=False): """ Initializes base Session class. parameters ---------- settings_file : str Path to settings file. If None, default_settings.yml is used eyetracker_on : bool Whether to enable eyetracker """ self.settings_file = settings_file self.eyetracker_on=eyetracker_on self.clock = Clock() self.timer = Clock() self.start_exp = None self.current_trial = None self.log = [] self.logfile = logging.LogFile(f='log.txt', filemode='w', level=logging.EXP) # Initialize self.settings = self._load_settings() self.monitor = self._create_monitor() self.win = self._create_window() self.mouse = Mouse(**self.settings['mouse']) self.default_fix = Circle(self.win, radius=0.3, fillColor='white', edges=1000) self.mri_simulator = self._setup_mri_simulator() if self.settings['mri']['simulate'] else None self.tracker = None
def __init__(self, logfile, imgDir, screenType, trialDuration, ISI, trialsPer, selfPaced, practiceTrials, inputButtons, pauseButton): self.logfile = logfile self.trialDuration = trialDuration self.selfPaced = selfPaced self.ISI = ISI self.trialsPer = trialsPer self.numTrials = (self.trialsPer * 4) #Trials/phase = 4x trials/cond self.imgDir = imgDir self.imgIdx = 0 self.runPracticeTrials = practiceTrials self.leftButton = inputButtons[0] self.rightButton = inputButtons[1] self.pauseButton = pauseButton if (screenType == 'Windowed'): screenSelect = False elif (screenType == 'Fullscreen'): screenSelect = True self.window = Window(fullscr=screenSelect, units='pix', color='White', allowGUI=False) self.imageWidth = self.window.size[1] / 6 #Window must be set up before imgs, as img position based on window size self.imageList = self.SegmentImages() self.clock = Clock() #Initialize scorelist for 4 categories;; [correct,inc,resp] self.scoreList = [] for i in range(0, 4): self.scoreList.append([0, 0, 0])
def present_target(self): if not self.target_is_on and self.onset_countdown.getTime() < 0: # rotate target into view self.display.onset(self.which_target, self.trial_type) self.target_is_on = True self.response_timer = Clock() self.mark_event('target on', channel=2) self.display.draw()
def __init__(self, logfile, imgDir, screenType, expVariant, trialDuration, ISI, trialsPer, selfPaced, practiceTrials, inputButtons, pauseButton): self.logfile = logfile self.expVariant = expVariant self.trialDuration = trialDuration self.selfPaced = selfPaced self.ISI = ISI self.trialsPer = trialsPer self.imgDir = imgDir self.leftOvers = [] self.splitLures = self.SplitLures() self.splitSingles = self.SplitSingles() self.runPracticeTrials = practiceTrials self.leftButton = inputButtons[0] self.rightButton = inputButtons[1] self.pauseButton = pauseButton if (screenType == 'Windowed'): screenSelect = False elif (screenType == 'Fullscreen'): screenSelect = True self.window = Window(fullscr=screenSelect, units='pix', color='White', allowGUI=False) self.imageWidth = self.window.size[1] / 3 #Define the black box that appears in the lower left, to signal EEG rW = 110 #Width rH = 60 #Height rectVertices = [[rW, -rH], [-rW, -rH], [-rW, rH], [rW, rH]] rectCenter = [(-self.window.size[0] / 2 + rW), (-self.window.size[1] / 2) + rH] self.blackBox = ShapeStim(self.window, fillColor='black', units='pix', fillColorSpace='rgb', vertices=rectVertices, closeShape=True, interpolate=True, pos=rectCenter) self.rangeITI = numpy.arange(1, 1.4, .001) self.clock = Clock() #Initialize scorelist for 4 categories|| [correct,incorrect,response] self.scoreList = [] for i in range(0, 4): self.scoreList.append([0, 0, 0])
def __init__(self, logfile, imgDir, subjectNum, screenType, numStim, numBlocks, trialDuration, ISI, selfPaced, runPractice, inputButtons, pauseButton): self.logfile = logfile self.imgDir = imgDir self.subjectNum = subjectNum self.numStim = numStim self.numBlocks = numBlocks self.trialDuration = trialDuration self.selfPaced = selfPaced self.ISI = ISI self.numCats = 4 self.trialsPer = int((self.numStim / self.numCats) / 2) self.runPractice = runPractice self.leftButton = inputButtons[0] self.rightButton = inputButtons[1] self.pauseButton = pauseButton #Set up window, center, left and right image sizes + positions if (screenType == 'Windowed'): screenSelect = False elif (screenType == 'Fullscreen'): screenSelect = True self.window = Window(fullscr=screenSelect, units='pix', color='White', allowGUI=False) self.imageWidth = self.window.size[1] / 5.5 self.centerImage = ImageStim(self.window) self.centerImage.setSize((self.imageWidth, self.imageWidth)) self.leftImage = ImageStim(self.window) self.leftImage.setPos((-1.5 * self.imageWidth, 0)) self.leftImage.setSize((self.imageWidth, self.imageWidth)) self.rightImage = ImageStim(self.window) self.rightImage.setPos((1.5 * self.imageWidth, 0)) self.rightImage.setSize((self.imageWidth, self.imageWidth)) self.clock = Clock() #Init score list for 4 categories: [correct,incorrect,response] self.scoreList = [] for i in range(0, 4): self.scoreList.append([0, 0, 0])
def present_target(self): if not self.target_is_on and self.onset_countdown.getTime() < 0: # rotate target into view self.display.onset(self.which_target, self.trial_type) self.target_is_on = True self.response_timer = Clock() self.mark_event('target on', channel=2) self.display.draw()
def feature_dimension_fit(matrix, k_range, fName = None): Var = [] timer = Clock() for k in k_range: print "Reducing feature-rank to {}".format(k) timer.reset() Rn = TruncatedSVD(n_components = k, random_state = 42).fit(matrix) t = timer.getTime() Var.append([k,sum(Rn.explained_variance_ratio_)*100,t]) print "processing time (s): {}".format(t) Var = np.array(Var) if fName != None: plt.plot(Var.T[0],Var.T[1]) plt.ylabel("%variance") plt.xlabel("dimension in feature space") plt.savefig(fName) plt.show() return Var, Rn
colorSpace='rgb', blendMode='avg', useFBO=True, units='norm') #system settings (session duration, interval length and size of COD) session = 60 #minutes phase_time = session / 6 COD = 5 schedule_list = ['ConcVI15FI15', 'ConcFI15VI15'] #where random schedule is located interval = 15 #initialization #counts time from the start of reinforcement global_time = Clock() #timers for the schedule of reinforcement T1, T2 = CountdownTimer(0), CountdownTimer(0) phase_timer = CountdownTimer(0) #tracks responses lpressed, rpressed = False, False mouse = Mouse() R1, n1 = 0, 0 #tracks responses from left and prevents changeovers R2, n2 = 0, 0 #tracks responses from right #tracks consequences Rf1, Rf2, score = 0, 0, 0 #tracks experiment phase = 0 #how many different conditions introduced data = [] #array for data data.append(['time', 'R1', 'R2', 'Rf1', 'Rf2', 'phase',
class Controller: def __init__(self, pars, display, logger, joystick): self.pars = pars self.display = display self.trialnum = 0 self.score = 0 self.end_task = False self.mark_event = logger self.joystick = joystick def open_trial(self): self.trialnum += 1 self.result = '' self.pts_this_trial = 0 self.trial_over = False self.target_is_on = False self.input_received = False self.no_response = False self.response_timer = None self.rt = float('NaN') self.data = [] numtargs = np.prod(self.pars['grid']) self.which_target = np.random.randint(0, numtargs) self.onset_interval = np.random.uniform(self.pars['min_onset'], self.pars['max_onset']) self.is_nogo = np.random.rand() < self.pars['frac_nogo'] if self.is_nogo: self.trial_type = 'no' else: self.trial_type = 'go' self.onset_countdown = CountdownTimer(self.onset_interval) self.mark_event('trial_start', channel=1) def run_trial(self): self.open_trial() while not self.trial_over: self.wait_for_input() if self.input_received: self.handle_input() self.display_outcome() else: self.handle_no_input() self.refresh() self.close_trial() return self.data def wait_for_input(self): pressed = [] while True: self.present_target() pressed = event.getKeys(keyList=['left', 'right', 'escape']) if 'escape' in pressed: self.end_task = True break elif pressed or True in self.joystick.getAllButtons(): self.input_received = True self.mark_event('responded', channel=3) break elif self.target_is_on and (self.response_timer.getTime() > self.pars['max_rt']): self.no_response = True self.mark_event('no_response', channel=4) break def present_target(self): if not self.target_is_on and self.onset_countdown.getTime() < 0: # rotate target into view self.display.onset(self.which_target, self.trial_type) self.target_is_on = True self.response_timer = Clock() self.mark_event('target on', channel=2) self.display.draw() def handle_input(self): if self.target_is_on: self.result = 'hit' self.trial_over = True self.rt = self.response_timer.getTime() self.correct = not self.is_nogo if self.correct: self.pts_this_trial = self.calculate_points(self.pars, self.rt) self.outcome_sound = self.display.cashsnd else: self.pts_this_trial = -self.pars['pts_per_correct'] self.outcome_sound = self.display.buzzsnd self.outcome_delay = self.pars['disp_resp'] else: self.result = 'premature' self.pts_this_trial = -self.pars['pts_per_correct'] self.outcome_sound = self.display.firesnd self.outcome_delay = 0.3 # remember to reset input self.input_received = False def handle_no_input(self): self.result = 'no response' self.correct = self.is_nogo self.trial_over = True def display_outcome(self): # update text onscreen self.display.set_target_text(self.which_target, str(self.pts_this_trial)) self.score += self.pts_this_trial self.display.set_score(self.score) # refresh screen self.outcome_sound.play() self.mark_event('outcome', channel=5) # during static period, code between start and complete will run iti = StaticPeriod() iti.start(self.outcome_delay) self.display.draw() iti.complete() # remove text overlay on target self.display.set_target_text(self.which_target, '') def refresh(self): if self.target_is_on: self.display.offset(self.which_target) self.display.draw() def close_trial(self): # print to screen self.mark_event('trial_over', channel=8) print 'Trial {0:d}: Type {1} Result: {2} RT: {3:0.3g} Correct: {4:d} Points: {5:d}'.format( self.trialnum, self.trial_type, self.result, self.rt, self.correct, self.pts_this_trial) def calculate_points(self, pars, rt): return int( np.floor(pars['pts_per_correct'] * np.exp(-(rt - pars['pts_offset']) / pars['pts_decay'])))
def callback(self, clock: core.Clock, stimuli: str) -> None: if self.first_time: self.timing = [stimuli, clock.getTime()] self.first_time = False
class MDTT(object): def __init__(self, logfile, imgDir, subjectNum, screenType, numStim, numBlocks, trialDuration, ISI, selfPaced, runPractice, inputButtons, pauseButton): self.logfile = logfile self.imgDir = imgDir self.subjectNum = subjectNum self.numStim = numStim self.numBlocks = numBlocks self.trialDuration = trialDuration self.selfPaced = selfPaced self.ISI = ISI self.numCats = 4 self.trialsPer = int((self.numStim / self.numCats) / 2) self.runPractice = runPractice self.leftButton = inputButtons[0] self.rightButton = inputButtons[1] self.pauseButton = pauseButton #Set up window, center, left and right image sizes + positions if (screenType == 'Windowed'): screenSelect = False elif (screenType == 'Fullscreen'): screenSelect = True self.window = Window(fullscr=screenSelect, units='pix', color='White', allowGUI=False) self.imageWidth = self.window.size[1] / 5.5 self.centerImage = ImageStim(self.window) self.centerImage.setSize((self.imageWidth, self.imageWidth)) self.leftImage = ImageStim(self.window) self.leftImage.setPos((-1.5 * self.imageWidth, 0)) self.leftImage.setSize((self.imageWidth, self.imageWidth)) self.rightImage = ImageStim(self.window) self.rightImage.setPos((1.5 * self.imageWidth, 0)) self.rightImage.setSize((self.imageWidth, self.imageWidth)) self.clock = Clock() #Init score list for 4 categories: [correct,incorrect,response] self.scoreList = [] for i in range(0, 4): self.scoreList.append([0, 0, 0]) def SplitRange(self, rangeMin, rangeMax, start=0): """Creates a pair of indexes separated by a value. The value itself can be between a min-max range. Neither index can be an index that is in a list of already used indexes. rangeMin: minimum amount that indexes can be separated by rangeMax: maximum amount that indexes can be separated by start: start index of used list return: a pair of indexes (index1,index2) (-1,-1) if range split failed """ #Search through list of used indexes to ensure no duplicates #Ignore start/end of list, as it's already used by primacy/recency for i in range(0, self.numStim): if (i in self.usedList): continue added = random.randint(rangeMin, rangeMax) startPt = added searchedRange = False #Loop through the min to max range of added values while not searchedRange: if ((i + added < self.numStim) and (i + added not in self.usedList)): self.usedList.append(i) self.usedList.append(i + added) return (i, i + added) if (added > rangeMin): added -= 1 else: added = rangeMax if (added == startPt): searchedRange = True return (-1, -1) def CreatePairsSpaced(self): """Creates a list, each element containing two indexes as well as a trial type. The trial type is based upon the spacing of the indexes as follows: adjacent (1): numbers next to eachother e.g. (3,4) or (8,9) eightish (2): numbers separated by between 7-9 e.g. (5,12) or (14,23) sixteenish (3): numbers separated by between 15-17 e.g. (3,18) or (8,25) primacy/recency: (4): start and end of list numbers e.g. (1,30) or (0,31) Occassionally, the list will fail to successfully split into index pairs. (SplitRange() returns (-1,-1) when this happens). The function will retry the index splitting until all indexes are used return: list containing elements each with: (index1,index2,trialType) """ startList = range(0, self.trialsPer) endList = range(self.numStim - self.trialsPer, self.numStim) trialOrder = range(0, (self.trialsPer * 3)) #3 categories besides P/R random.shuffle(startList) random.shuffle(endList) #Attempt to split 0-31 range into 4 index categories #Split fails if any one of the index pairs is (-1,-1) at end def AttemptSplit(): # 3 categories besides P/R trialOrder = range(0, (self.trialsPer * 3)) random.shuffle(trialOrder) self.usedList = [] attemptList = [] finalList = [] #Add edge index pairs (primacy/recency) for i in range(0, self.trialsPer): finalList.append((startList[i], endList[i], 4)) self.usedList.append(startList[i]) self.usedList.append(endList[i]) #Add spaced (separated) pairs of indexes to list for trial in trialOrder: if (trial % 3 == 0): #Adjacent (idxOne, idxTwo) = self.SplitRange(1, 1) attemptList.append((idxOne, idxTwo, 1)) elif (trial % 3 == 1): #Eightish (idxOne, idxTwo) = self.SplitRange(7, 9) attemptList.append((idxOne, idxTwo, 2)) elif (trial % 3 == 2): #Sixteenish (idxOne, idxTwo) = self.SplitRange(15, 17) attemptList.append((idxOne, idxTwo, 3)) #Ensures PR trials (type 4) occur first. Randomize successive trials random.shuffle(attemptList) finalList.extend(attemptList) return finalList #Try AttemptSplit() until index split is successful splitSuccess = False while (not splitSuccess): splitList = AttemptSplit() foundError = False for pair in splitList: if ((pair[0] == -1) or (pair[1] == -1)): foundError = True if (foundError == True): continue else: splitSuccess = True return splitList def RunTrialSingle(self, img): """Displays a single image at the center of the screen for a period of time, and captures keypresses and their respective reaction times. img: the image to Displays return: a list of keypresses and respective reaction times """ self.centerImage.setImage(self.imgDir + "/%s" % (img)) self.centerImage.draw(self.window) clearEvents() self.window.flip() self.clock.reset() keyPresses = [] if (self.selfPaced == False): wait(self.trialDuration, self.trialDuration) keyPresses = getKeys(keyList=[ self.leftButton, self.rightButton, self.pauseButton, "escape" ], timeStamped=self.clock) elif (self.selfPaced == True): keyPresses = waitKeys(keyList=[ self.leftButton, self.rightButton, self.pauseButton, "escape" ], timeStamped=self.clock) self.window.flip() wait(self.ISI) return keyPresses def RunTrialDual(self, leftImg, rightImg): """Displays two images on the screen for a period of time, and captures keypresses and their respective reaction times. leftimg: the image to display on the left rightimg: the image to display on the right return: a list of keypresses and respective reaction times """ self.leftImage.setImage(self.imgDir + "/%s" % (leftImg)) self.rightImage.setImage(self.imgDir + "/%s" % (rightImg)) self.leftImage.draw(self.window) self.rightImage.draw(self.window) clearEvents() self.window.flip() self.clock.reset() if (self.selfPaced == False): wait(self.trialDuration, self.trialDuration) keyPresses = getKeys(keyList=[ self.leftButton, self.rightButton, self.pauseButton, "escape" ], timeStamped=self.clock) elif (self.selfPaced == True): keyPresses = waitKeys(keyList=[ self.leftButton, self.rightButton, self.pauseButton, "escape" ], timeStamped=self.clock) self.window.flip() wait(self.ISI) return keyPresses def RunStudy(self, imageBlock, session): """Runs the study, i.e. the first half of each experimental block. Writes all relevant information about the study to a logfile. imageBlock: List of images to display during the study session: the number of the session (block number) that is running """ studyPrompt = ( "Test Session {}/{}: Are the following objects indoor or outdoor?\n\n('{}' to continue)" .format(session, 10, self.pauseButton)) studyText = TextStim(self.window, studyPrompt, color='Black') studyText.draw(self.window) self.window.flip() continueKey = waitKeys(keyList=[self.pauseButton, 'escape']) if (continueKey[0] == 'escape'): self.logfile.write("\n\n\nStudy Not Run Early\n\n\n") return self.logfile.write("\nBegin Study %d\n" % (session)) self.logfile.write("{h1:<6}{h2:<23}{h3:<10}{h4}\n".format( h1="Trial", h2="Image", h3="Response", h4="RT")) #Run trial for each image in the image block for i in range(0, len(imageBlock)): keyPresses = self.RunTrialSingle(imageBlock[i]) if (keyPresses == []): respKey = '' respRT = 0 else: respKey = keyPresses[0][0] respRT = keyPresses[0][1] if (respKey == "escape"): self.logfile.write("\n\n\nStudy block terminated early\n\n\n") break elif (respKey == self.pauseButton): self.Pause() self.logfile.write("{:^5}{:<23}{:^11}{:<1.3f}\n".format( i + 1, imageBlock[i], respKey, respRT)) return def RunTest(self, imageBlock, pairList, session): """Runs the test, i.e. the second half of each experimental block. Wites all relevant information about the test to a logfile imageBlock: List of images to display during the test pairList: List of paired image indexes w/ trial type session: the number of the session (block number) that is running """ testPrompt = ( "In this phase, the same series of objects will be shown\n\nWhich came first: Left or Right?\n\n('{}' to continue)" .format(self.pauseButton)) testText = TextStim(self.window, testPrompt, color='Black') testText.draw(self.window) self.window.flip() continueKey = waitKeys(keyList=[self.pauseButton, 'escape']) if (continueKey[0] == 'escape'): self.logfile.write("\n\n\nTest Not Run\n\n\n") return 0 self.logfile.write("\nBegin Test %d\n" % (session)) lghead = "{a:<7}{b:<7}{c:<23}{d:<23}{e:<7}{f:<7}{g:<10}{h:<7}{i}\n".format( a="Trial", b="TType", c="LeftImage", d="RightImage", e="LNum", f="RNum", g="CorResp", h="Resp", i="RT") self.logfile.write(lghead) #Randomize if pair is shown: (bef > aft) or (aft > bef) order sideOrder = range(0, len(pairList)) random.shuffle(sideOrder) correct = '' keyPresses = [] #Run dual image trial for each pair in the pairlist for i in range(0, len(pairList)): trialNum = i + 1 trialType = pairList[i][2] firstIdx = pairList[i][0] secondIdx = pairList[i][1] firstImg = imageBlock[pairList[i][0]] secondImg = imageBlock[pairList[i][1]] #Preserve the order images were shown in if (sideOrder[i] % 2 == 0): correct = self.leftButton leftIdx = firstIdx rightIdx = secondIdx leftImg = firstImg rightImg = secondImg keyPresses = self.RunTrialDual(leftImg, rightImg) #Reverse order images were shown elif (sideOrder[i] % 2 == 1): correct = self.rightButton leftIdx = secondIdx rightIdx = firstIdx leftImg = secondImg rightImg = firstImg keyPresses = self.RunTrialDual(leftImg, rightImg) #Get first response, or set to none if no response if (keyPresses == []): respKey = '' respRT = 0 else: respKey = keyPresses[0][0] respRT = keyPresses[0][1] #Break out of image block with escape, break out of program with f5 if (respKey == 'escape'): self.logfile.write("\n\nTest block terminated early\n\n") break elif (respKey == self.pauseButton): self.Pause() #Keep track of score if (respKey): self.scoreList[pairList[i][2] - 1][2] += 1 if (respKey == correct): self.scoreList[pairList[i][2] - 1][0] += 1 else: self.scoreList[pairList[i][2] - 1][1] += 1 #Write info to logfile lgspace = "{:^5}{:^9}{:<23}{:<23}{:<7}{:<10}{:<8}{:<6}{:<1.3f}\n" lgform = (lgspace.format(trialNum, trialType, leftImg, rightImg, leftIdx, rightIdx, correct, respKey, respRT)) self.logfile.write(lgform) return 1 def Pause(self): """Pauses the task, and displays a message waiting for a spacebar input from the user before continuing to proceed. """ pauseMsg = "Experiment Paused\n\nPress '{}' to continue".format( self.pauseButton) pauseText = TextStim(self.window, text=pauseMsg, color='Black', height=40) pauseText.draw(self.window) self.window.flip() waitKeys(keyList=[self.pauseButton]) clearEvents() def SegmentPracticeImages(self, images): ''' Return the indexes for the test, it will index the image list from study: [[index_left_image, index_right_image, trialType]] ''' # Since we know that the images are already randomized, we can just iterate over them # In order for the code to work, we want 4 practice images per practice block if len(images) != 4: print "Assertion error: length of practice images is not equal to 4" self.window.close() sys.exit() # Trial type of 4 means long distance large_dist = (0, 3, 4) if random.random() > .5 else (3, 0, 4) mid_dist_1 = (0, 2, 2) if random.random() > .5 else (2, 0, 2) mid_dist_2 = (1, 3, 2) if random.random() > .5 else (3, 1, 2) adjacent = (0, 1, 1) if random.random() > .5 else (1, 0, 1) adjacent_2 = (1, 2, 1) if random.random() > .5 else (2, 1, 1) adjacent = adjacent if random.random() > .5 else adjacent_2 all = [large_dist, mid_dist_1, mid_dist_2, adjacent] random.shuffle(all) return all def ShowPromptAndWaitForSpace(self, prompt, keylist=['space', 'escape']): ''' Show the prompt on the screen and wait for space, or the keylist specified returns the key pressed ''' keylist = [self.pauseButton, 'escape'] text = TextStim(self.window, prompt, color='Black') text.draw(self.window) self.window.flip() continueKey = waitKeys(keyList=keylist) if len(continueKey) != 0 and continueKey[0] == 'escape': self.logfile.write("Terminated early.") self.logfile.close() sys.exit() return continueKey def RunSinglePractice(self, practiceBlock, imgs): ''' Read in the images we want, and run the practice block for this subject Run encoding and test, and write to the logs Return: float: ratio correct ''' random.shuffle(imgs) ### Encoding # imgs = [[img, trialType, Study(x,y), Test(x,y)]] testIdxs = self.SegmentPracticeImages(imgs) self.ShowPromptAndWaitForSpace( " Indoor or Outdoor?\n\n('{}' to continue)".format( self.pauseButton)) self.logfile.write("\nBegin Practice Study {}\n".format(practiceBlock)) self.logfile.write("{h1:<6}{h2:<23}{h3:<10}{h4}\n".format( h1="Trial", h2="Image", h3="Response", h4="RT")) # Run the trial for each encoding trial for i in range(0, len(imgs)): keyPresses = self.RunTrialSingle(imgs[i]) if (keyPresses == []): respKey = '' respRT = 0 else: respKey = keyPresses[0][0] respRT = keyPresses[0][1] if (respKey == "escape"): self.logfile.write("\n\n\nStudy block terminated early\n\n\n") break elif (respKey == self.pauseButton): self.Pause() self.logfile.write("{:^5}{:<23}{:^11}{:<1.3f}\n".format( i + 1, imgs[i], respKey, respRT)) ### Test self.ShowPromptAndWaitForSpace( " Which came first? Left or right? ('{}' to continue)".format( self.pauseButton)) self.logfile.write("\nBegin Practice Test {}\n".format(practiceBlock)) self.logfile.write( "{a:<7}{b:<7}{c:<23}{d:<23}{e:<7}{f:<7}{g:<10}{h:<7}{i}\n".format( a="Trial", b="TType", c="LeftImage", d="RightImage", e="LNum", f="RNum", g="CorResp", h="Resp", i="RT")) # Keep track of the total number they got correct totalCorrect = 0 for trialNum, idxes in enumerate(testIdxs): leftImgIdx, rightImgIdx, trialType = idxes leftImg = imgs[leftImgIdx] rightImg = imgs[rightImgIdx] keyPresses = self.RunTrialDual(leftImg, rightImg) correct = self.leftButton if leftImgIdx < rightImgIdx else self.rightButton #Get first response, or set to none if no response if (keyPresses == []): respKey = '' respRT = 0 else: respKey = keyPresses[0][0] respRT = keyPresses[0][1] #Break out of image block with escape, break out of program with f5 if (respKey == 'escape'): self.logfile.write("\n\nPractice block terminated early\n\n") self.logfile.close() sys.exit() #Write info to logfile lgspace = "{:^5}{:^9}{:<23}{:<23}{:<7}{:<10}{:<8}{:<6}{:<1.3f}\n" lgform = (lgspace.format(trialNum + 1, trialType, leftImg, rightImg, leftImgIdx, rightImgIdx, correct, respKey, respRT)) self.logfile.write(lgform) if respKey == correct: totalCorrect += 1 # Return the percentage correct return totalCorrect / len(imgs) def RunPractice(self): ''' Runs three rounds of practice trials. If the participant gets a certain amount correct, they move on to the real test. ''' dirFiles = os.listdir(self.imgDir) practiceImages = [img for img in dirFiles if "PR_" in img] if len(practiceImages) == 0: print "No practice images found" self.window.close() sys.exit() random.shuffle(practiceImages) # Split the practice images into three sets practiceImages = np.array_split(practiceImages, 3) # Run each practice session for i in range(3): practicePrompt = "Let's practice\n\n('{}' to continue)".format( self.pauseButton) self.ShowPromptAndWaitForSpace(practicePrompt) results = self.RunSinglePractice( i + 1, [img for img in practiceImages[i]]) # If they get a certain percentage correct, then stop the practice self.ShowPromptAndWaitForSpace( "You got {}% correct! ('{}' to continue)".format( int(results * 100), self.pauseButton)) if results > .6: return def RunExp(self): """Runs through an instance of the MDT-T experiment, which includes arranging the images into lists/sublists, running through a given number of study/test blocks, and writing the scores to a logfile. """ #Print task ending message to the screen; wait for user to press escape def EndExp(): exitPrompt = ("This concludes the session. Thank you for " "participating!\n\nPress Escape to quit") exitText = TextStim(self.window, exitPrompt, color='Black') exitText.draw(self.window) self.window.flip() waitKeys(keyList=['escape']) self.window.close() # Run practice if self.runPractice: self.RunPractice() #Put image files from folder into list imageList = [] for img in os.listdir(self.imgDir): if ( img[-4:] == ".jpg" and "PR_" not in img ): # Make sure that PR (practice image) is not included for study/tests imageList.append(img) random.shuffle(imageList) #Divide imagelist into <numBlocks> # of lists, put into one list #Each sublist contains <numStim> # of stimuli imageBlockList = [] for i in range(0, self.numBlocks): block = [] for j in range(i * self.numStim, self.numStim + (i * self.numStim)): block.append(imageList[j]) imageBlockList.append(block) #Run through each study/test block blockOrder = range(0, self.numBlocks) random.shuffle(blockOrder) writeScores = True for i in range(0, len(blockOrder)): pairList = self.CreatePairsSpaced() self.RunStudy(imageBlockList[i], i + 1) testFinished = self.RunTest(imageBlockList[i], pairList, i + 1) if not testFinished: writeScores = False continue EndExp() #Return logfile and scorelist if all study/test blocks gone through if writeScores: return (self.logfile, self.scoreList) else: return (-1, -1)
def offset(self, index): self.rotation_clocks[index] = Clock() self.type[index] = 'default'
increment = 0.1 #text formatting options: fixationHeight = 0.09 units = 'norm' textStimHeight = 0.06 posLeft = [-0.4, 0] posRight = [0.4, 0] #further tweaks for the practice trials have to be set in the corresponding code chunk below (beginning at line 482) #----------------------------------------------------------------------------------------------------------------------- clock = Clock() #genrate random dates in the form of - JAN 15 - MAI 23 - etc. change lang to generate in different languages def randomdate(dateNumber=200, year=2019, language=lang): locale.setlocale(locale.LC_ALL, language) dates = [] monthRange = list(range(1, 13, 1)) for date in range(dateNumber): month = calendar.month_abbr[random.choice(monthRange)] day = list( range(calendar.monthrange(year, random.choice(monthRange))[1]))[1:] day.insert(len(day), len(day) + 1) day = random.choice(day) dates.append(' '.join((month.upper(), str(day)))) return (dates)
def mainLoop(blocks=1, trialNumber=216): #global blockCount blockCount = 0 trialCount = 0 timer = Clock() maskDelay = delayForMask #make finalItems which will be reused for every block generatedItems = finalItems(blocks) itemsForDisplay = generatedItems #main trial loop runns for n-blocks for block in range(blocks): #f' is the python3 equivalent of "something{}something.format()" function for python2 betweenBlocksInfo = ( f'Der {blockCount}. Block ist nun vorbei, wenn Sie möchten können Sie eine kurze Pause machen.\n\ \n\ Fortfahren mit "Leertaste".') if blockCount == blocks - 1 and blockCount > 0: TextStim(win, ( f'Der {blockCount}. Block ist nun vorbei, wenn Sie möchten können Sie eine kurze Pause machen. Nun folgt der letzte Durchgang.\n\ \n\ Fortfahren mit "Leertaste".'), units='norm', pos=(0.0, 0.0)).draw() win.flip() waitKeys(keyList=['space']) elif blockCount > 0: TextStim(win, betweenBlocksInfo, units='norm', pos=(0.0, 0.0)).draw() win.flip() waitKeys(keyList=['space']) fixationCross.draw() win.flip() wait(0.6) win.flip() blockCount = block + 1 #loop run for n-trials for trial in range(trialNumber): if itemsForDisplay[trial]['itemInfo'][ 'side'] == -1 and itemsForDisplay[trial]['itemInfo'][ 'first'] == 1: corr = -1 elif itemsForDisplay[trial]['itemInfo'][ 'side'] == 1 and itemsForDisplay[trial]['itemInfo'][ 'first'] == 1: corr = 1 elif itemsForDisplay[trial]['itemInfo'][ 'side'] == -1 and itemsForDisplay[trial]['itemInfo'][ 'first'] == 0: corr = 1 elif itemsForDisplay[trial]['itemInfo'][ 'side'] == 1 and itemsForDisplay[trial]['itemInfo'][ 'first'] == 0: corr = -1 timeDelayBetweenNames = itemsForDisplay[trial]['itemInfo']['delay'] timer.reset() time1 = timer.getTime() intertDelay = 0 trialCount += 1 waitDuration = StaticPeriod(screenHz=60) d = interTrialDelay() print('interTrialDelay: ', d) t1 = clock.getTime() if timeDelayBetweenNames != 0.0: waitDuration.start(d) nameToDrawFirst = itemsForDisplay[trial]['firstName'] nameToDrawSecond = itemsForDisplay[trial]['secondName'] if expVersion == 1: mask1 = itemsForDisplay[trial]['firstMask'] mask2 = itemsForDisplay[trial]['secondMask'] maskStimuli = [mask1, mask2] stimuli = [nameToDrawFirst, nameToDrawSecond] intertDelay += d print('delay should be: ', timeDelayBetweenNames) nameToDrawFirst.draw() waitDuration.complete() print('1. loop dur: ', clock.getTime() - t1) win.flip() t1 = clock.getTime() w1 = clock.getTime() waitDuration.start(timeDelayBetweenNames) for s in stimuli: s.draw() waitDuration.complete() print('2. loop dur: ', clock.getTime() - w1) win.flip() realTimeDelay = clock.getTime() - t1 print('effective time delay: ', realTimeDelay) if expVersion == 1: waitDuration.start(maskDelay) for m in maskStimuli: m.draw() waitDuration.complete() win.flip() else: waitDuration.start(d) nameToDrawFirst = itemsForDisplay[trial]['firstName'] nameToDrawSecond = itemsForDisplay[trial]['secondName'] if expVersion == 1: mask1 = itemsForDisplay[trial]['firstMask'] mask2 = itemsForDisplay[trial]['secondMask'] maskStimuli = [mask1, mask2] stimuli = [nameToDrawFirst, nameToDrawSecond] print('delay should be: ', timeDelayBetweenNames) for s in stimuli: s.draw() waitDuration.complete() realTimeDelay = 0 win.flip() print('effective time delay: ', realTimeDelay) if expVersion == 1: waitDuration.start(maskDelay) for m in maskStimuli: m.draw() waitDuration.complete() win.flip() #start response record responseStart = clock.getTime() #wait a specific amount of time for the keypress pressedKey = waitKeys( keyList=['f', 'j', 'space', quitkey]) #escape for quiting the experiment #below just for simulating persons # pressedKey = random.choice(['f','j','space']) # wait(0.2) #end response clock responseEnd = clock.getTime() #calculate response time RT = responseEnd - responseStart if pressedKey == None: pass elif quitkey in pressedKey: quit() print('response time: ', RT) correct = 0 #if-else statements for 'possible' feedback and response recording if pressedKey == None: correct = 0 elif itemsForDisplay[trial]['itemInfo']['delay'] == 0.0: correct = -2 #with 0 delay there cannot be a correct answer elif 'f' in pressedKey and corr == 1: print('incorrect') correct = 0 elif 'j' in pressedKey and corr == -1: print('incorrect') correct = 0 elif 'f' in pressedKey and corr == -1: print('correct') correct = 1 elif 'j' in pressedKey and corr == 1: print('correct') correct = 1 elif 'space' in pressedKey: correct = -1 # -1 then means didnt know win.flip() d = interTrialDelay() print('interTrialDelay: ', d) intertDelay += d wait(d) fixationCross.draw() win.flip() #aand again save the current trials while waiting the inter-trial duration instead of doing nothing d = interTrialDelay() print('interTrialDelay: ', d) t1 = clock.getTime() waitDuration.start(d) intertDelay += d interTrialDuration = intertDelay trialDuration = timer.getTime() - time1 + d #what is saved: #name class #if garbled or not #left name #right name #intended time delay #real time delay #which name was displayed first #response time #if the response was correct #person ID, first name, last name, country, adress if itemsForDisplay[trial]['itemInfo']['side'] == -1: leftName = itemsForDisplay[trial]['itemInfo']['unfamWord'] rightName = itemsForDisplay[trial]['itemInfo']['famWord'] leftNameType = 'unfamiliar' rightNameType = 'familiar' else: leftName = itemsForDisplay[trial]['itemInfo']['famWord'] rightName = itemsForDisplay[trial]['itemInfo']['unfamWord'] leftNameType = 'familiar' rightNameType = 'unfamiliar' if itemsForDisplay[trial]['itemInfo']['first'] == 1: firstNameType = 'unfamiliar' else: firstNameType = 'familiar' if os.path.isfile(f'exp_degrade_{person.id}_{startDate}.txt'): with open(f'exp_degrade_{person.id}_{startDate}.txt', 'a') as file: file.write('\t'.join([itemsForDisplay[trial]['itemInfo']['class'],str(itemsForDisplay[trial]['itemInfo']['garbled']),leftName ,\ rightName , leftNameType, rightNameType, str(itemsForDisplay[trial]['itemInfo']['delay']) , str(realTimeDelay), \ nameToDrawFirst.text.replace('%',''), firstNameType , str(RT) , str(pressedKey) ,str(correct) , str(blockCount) , str(trialCount) , person.id ,\ str(trialDuration), str(interTrialDuration)])+ '\n') else: with open(f'exp_degrade_{person.id}_{startDate}.txt', 'w+') as file: file.write( 'class\tgarbled\tleft Name\tright Name\tleftNameType\trightNameType\tintendedTimeDelay\trealTimeDelay\t\ nameShowedFirst\tfirstNameType\tresponseTime\tpressedKey\tcorrectAnswer\tblock\ttrial\tID\ttrialDuration\tinterTrialDuration' + '\n') with open(f'exp_degrade_{person.id}_{startDate}.txt', 'a') as file: file.write('\t'.join([itemsForDisplay[trial]['itemInfo']['class'],str(itemsForDisplay[trial]['itemInfo']['garbled']),leftName ,\ rightName , leftNameType, rightNameType, str(itemsForDisplay[trial]['itemInfo']['delay']) , str(realTimeDelay), \ nameToDrawFirst.text.replace('%',''), firstNameType , str(RT) , str(pressedKey) ,str(correct) , str(blockCount) , str(trialCount) , person.id ,\ str(trialDuration), str(interTrialDuration)])+ '\n') waitDuration.complete() print('3. loop dur: ', clock.getTime() - t1) win.flip() print('trialDuration: ', trialDuration) endTime = expTime.getTime() - startTime #write person demographics with open(f'exp_degrade_{person.id}_{startDate}.txt', 'a') as file: file.write(' '.join(['experiment Duration: ', str(endTime),' ID: ',person.id,' first name: ',person.first,\ ' last name: ',person.last,' country: ',person.country,\ ' adress: ',person.adress,' birhtday: ',person.adress,' animal: ',person.animal])) TextStim(win, AppEndText, font='Calibri', units='norm', pos=(0, 0), height=0.08, wrapWidth=0.9).draw() win.flip() waitKeys(keyList=['escape'])
allowGUI=False, allowStencil=False, monitor='testMonitor', color='black', colorSpace='rgb', blendMode='avg', useFBO=True, units='norm') session, phases = 30, 120 R1, R2, score, phase, zeros, ones = 0, 0, 0, 0, 0, 0 lpressed, rpressed = False, False mouse = Mouse() stimuli_timer = CountdownTimer(0) phase_timer = CountdownTimer(0) global_time = Clock() p_list = [0.25, 0.75] data = [] #array for data data.append(['time', 'R1', 'R2', 'score', 'number', 'zeros', 'ones', 'p']) #column names in csv file #displayed text text = TextStim(win=win, pos=(0, 0), text=' ') #defines click boxes lbox = Rect(win=win, width=0.3, height=0.3, lineColor='red', lineWidth=5,
class Controller: def __init__(self, pars, display, logger, joystick): self.pars = pars self.display = display self.trialnum = 0 self.score = 0 self.end_task = False self.mark_event = logger self.joystick = joystick def open_trial(self): self.trialnum += 1 self.result = '' self.pts_this_trial = 0 self.trial_over = False self.target_is_on = False self.input_received = False self.no_response = False self.response_timer = None self.rt = float('NaN') self.data = [] numtargs = np.prod(self.pars['grid']) self.which_target = np.random.randint(0, numtargs) self.onset_interval = np.random.uniform(self.pars['min_onset'], self.pars['max_onset']) self.is_nogo = np.random.rand() < self.pars['frac_nogo'] if self.is_nogo: self.trial_type = 'no' else: self.trial_type = 'go' self.onset_countdown = CountdownTimer(self.onset_interval) self.mark_event('trial_start', channel=1) def run_trial(self): self.open_trial() while not self.trial_over: self.wait_for_input() if self.input_received: self.handle_input() self.display_outcome() else: self.handle_no_input() self.refresh() self.close_trial() return self.data def wait_for_input(self): pressed = [] while True: self.present_target() pressed = event.getKeys(keyList=['left', 'right', 'escape']) if 'escape' in pressed: self.end_task = True break elif pressed or (self.joystick and True in self.joystick.getAllButtons()): self.input_received = True self.mark_event('responded', channel=3) break elif self.target_is_on and (self.response_timer.getTime() > self.pars['max_rt']): self.no_response = True self.mark_event('no_response', channel=4) break def present_target(self): if not self.target_is_on and self.onset_countdown.getTime() < 0: # rotate target into view self.display.onset(self.which_target, self.trial_type) self.target_is_on = True self.response_timer = Clock() self.mark_event('target on', channel=2) self.display.draw() def handle_input(self): if self.target_is_on: self.result = 'hit' self.trial_over = True self.rt = self.response_timer.getTime() self.correct = not self.is_nogo if self.correct: self.pts_this_trial = self.calculate_points(self.pars, self.rt) self.outcome_sound = self.display.cashsnd else: self.pts_this_trial = -self.pars['pts_per_correct'] self.outcome_sound = self.display.buzzsnd self.outcome_delay = self.pars['disp_resp'] else: self.result = 'premature' self.pts_this_trial = -self.pars['pts_per_correct'] self.outcome_sound = self.display.firesnd self.outcome_delay = 0.3 # remember to reset input self.input_received = False def handle_no_input(self): self.result = 'no response' self.correct = self.is_nogo self.trial_over = True def display_outcome(self): # update text onscreen self.display.set_target_text(self.which_target, str(self.pts_this_trial)) self.score += self.pts_this_trial self.display.set_score(self.score) # refresh screen self.outcome_sound.play() self.mark_event('outcome', channel=5) # during static period, code between start and complete will run iti = StaticPeriod() iti.start(self.outcome_delay) self.display.draw() iti.complete() # remove text overlay on target self.display.set_target_text(self.which_target, '') def refresh(self): if self.target_is_on: self.display.offset(self.which_target) self.display.draw() def close_trial(self): # print to screen self.mark_event('trial_over', channel=8) print 'Trial {0:d}: Type {1} Result: {2} RT: {3:0.3g} Correct: {4:d} Points: {5:d}'.format(self.trialnum, self.trial_type, self.result, self.rt, self.correct, self.pts_this_trial) def calculate_points(self, pars, rt): return int(np.floor(pars['pts_per_correct'] * np.exp( -(rt - pars['pts_offset']) / pars['pts_decay'])))
def run_lextale(): global trial_num, stim_text, stim_type, stim_status, incorrect, response, rt_start print("len(blck_itms):", len(lextale_items)) maus = Mouse() instruction_page.setText(lextale_instructions) instruction_page.draw() ok_button.draw() ok_text.draw() win.flip() while not maus.isPressedIn(ok_button, buttons=[0]): pass stopwatch = Clock() for trial_num in range( len(lextale_items)): # go through all stimuli of current block print("------- Trial number:", trial_num) stim_current = lextale_items[trial_num] stim_type = stim_current["dummy"] stim_text = stim_current["word"] stim_status = stim_current["wstatus"] center_disp.setText(stim_text) draw_labels() center_disp.draw() win.callOnFlip(stopwatch.reset) clearEvents() win.flip() while True: if maus.isPressedIn(right_bg, buttons=[0]): rt_start = stopwatch.getTime() response = 'yes' right_bg.fillColor = "darkgreen" draw_labels() center_disp.draw() win.flip() while maus.isPressedIn(right_bg, buttons=[0]): pass right_bg.fillColor = "green" if stim_status == 1: incorrect = 0 if stim_type == 0: lextale_data[ 'corr_word'] = lextale_data['corr_word'] + 1 else: incorrect = 1 break elif maus.isPressedIn(left_bg, buttons=[0]): rt_start = stopwatch.getTime() response = 'no' left_bg.fillColor = "darkred" draw_labels() center_disp.draw() win.flip() while maus.isPressedIn(left_bg, buttons=[0]): pass left_bg.fillColor = "red" if stim_status == 0: incorrect = 0 if stim_type == 0: lextale_data[ 'corr_nonword'] = lextale_data['corr_nonword'] + 1 else: incorrect = 1 break elif len(getKeys(keyList=[escape_key], timeStamped=stopwatch)) > 0: end_on_esc(escape_key) draw_labels() center_disp.draw() win.flip() add_resp() # store trial data
if exp_info['participant_nr'] > 99 or int(exp_info['age']) < 18: quit() else: # let's star the experiment! print( f"Started experiment for participant {exp_info['participant_nr']} " f"with age {exp_info['age']}.") # Initialize a fullscreen window with my monitor (HD format) size # and my monitor specification called "samsung" from the monitor center win = Window(size=(1920, 1080), fullscr=False, monitor='samsung') # Also initialize a mouse, although we're not going to use it mouse = Mouse(visible=False) # Initialize a (global) clock clock = Clock() # Initialize Keyboard kb = Keyboard() kb.clearEvents() ### START BODY OF EXPERIMENT ### # # This is where we'll add stuff from the second # Coder tutorial. # ### END BODY OF EXPERIMENT ### ### WELCOME ROUTINE ### # Create a welcome screen and show for 2 seconds welcome_txt_stim = TextStim(win,
def test_Clock(): try: c = Clock() t1 = c.getTime() time.sleep(1.0) t2 = c.getTime() startTime = c.getLastResetTime() assert t2 > t1 assert t2 - t1 > 0.95 assert t2 - t1 < 1.05 assert startTime > 0 c.reset() t = c.getTime() assert t < 0.01 c.reset(10) t = c.getTime() assert t > -10.0 assert t < -9.9 t1 = c.getTime() c.add(50) t2 = c.getTime() assert t2 - t1 > -50.0 assert t2 - t1 < -49.9 printf(">> Clock Test: PASSED") except Exception: printf(">> Clock Test: FAILED") printExceptionDetails() printf("-------------------------------------\n")
units='deg', pos=[0, 0], height=1, color='White') else: trialACC = 0 feedback = TextStim(win, text="""x""", units='deg', pos=[0, 0], height=1, color='Red') return (trialACC, feedback) clock = Clock() #Inputs for the participants info = { 'ID': '99', 'Gender': ['Male', 'Female'], 'Age': '', 'Order': ['1', '2', '3', '4'] } infoDlg = gui.DlgFromDict(dictionary=info, order=['ID', 'Gender', 'Age', 'Order']) #Order Conditions: #1 = LE, LD, HE, HD; L-H, E-D #2 = LD, LE, HD, HE; L-H, D-E #3 = HE, HD, LE, LD; H-L, E-D #4 = HD, HE, LD, LE; H-L, D-E
class Session: """ Base Session class """ def __init__(self, settings_file=None, eyetracker_on=False): """ Initializes base Session class. parameters ---------- settings_file : str Path to settings file. If None, default_settings.yml is used eyetracker_on : bool Whether to enable eyetracker """ self.settings_file = settings_file self.eyetracker_on=eyetracker_on self.clock = Clock() self.timer = Clock() self.start_exp = None self.current_trial = None self.log = [] self.logfile = logging.LogFile(f='log.txt', filemode='w', level=logging.EXP) # Initialize self.settings = self._load_settings() self.monitor = self._create_monitor() self.win = self._create_window() self.mouse = Mouse(**self.settings['mouse']) self.default_fix = Circle(self.win, radius=0.3, fillColor='white', edges=1000) self.mri_simulator = self._setup_mri_simulator() if self.settings['mri']['simulate'] else None self.tracker = None def _load_settings(self): """ Loads settings and sets preferences. """ if self.settings_file is None: self.settings_file = op.join(op.dirname(__file__), 'default_settings.yml') logging.warn(f"Using default logfile ({self.settings_file}") with open(self.settings_file, 'r') as f_in: settings = yaml.load(f_in) exp_prefs = settings['preferences'] # set preferences globally for preftype, these_settings in exp_prefs.items(): for key, value in these_settings.items(): pref_subclass = getattr(psychopy_prefs, preftype) pref_subclass[key] = value setattr(psychopy_prefs, preftype, pref_subclass) return settings def _create_monitor(self): monitor = Monitor(**self.settings['monitor']) monitor.save() # needed for iohub eyetracker return monitor def _create_window(self): win = Window(monitor=self.monitor, **self.settings['window']) return win def _setup_mri_simulator(self): args = self.settings['mri'].copy() args.pop('simulate') return SyncGenerator(**args) def start_experiment(self): """ Logs the onset of the start of the experiment """ self.start_exp = getTime() # abs time self.clock.reset() # resets global clock self.timer.reset() # phase-timer if self.mri_simulator is not None: self.mri_simulator.start() def display_text(self, text, keys=['return'], **kwargs): stim = TextStim(self.win, text=text, **kwargs) stim.draw() self.win.flip() waitKeys(keyList=keys) def close(self): self.exp_stop = self.clock.getTime() print(f"Duration experiment: {self.exp_stop:.3f}\n") self.log = pd.concat(self.log) self.log['onset_abs'] = self.log['onset'] + self.start_exp print(self.log) if self.mri_simulator is not None: self.mri_simulator.stop() quit() def init_eyetracker(self): if not self.eyetracker_on: raise ValueError("Cannot initialize eyetracker if eyetracker_on=False!") EYETRACKER_NAME = 'eyetracker.hw.sr_research.eyelink.EyeTracker' # default_native_data_file_name: et_data self.iohub = launchHubServer( psychopy_monitor_name=self.monitor.name, datastore_name='test_et', **{EYETRACKER_NAME: { 'enable_interface_without_connection': True }} ) self.tracker = self.iohub.getDevice('eyetracker.hw.sr_research.eyelink.EyeTracker') def start_recording_eyetracker(self): self.tracker.setRecordingState(True) def stop_recording_eyetracker(self): self.tracker.setRecordingState(False) def calibrate_eyetracker(self): self.tracker.runSetupProcedure() def close_tracker(self): self.stop_recording_eyetracker() self.iohub.quit()
class MDTS(object): def __init__(self, logfile, imgDir, screenType, trialDuration, ISI, trialsPer, selfPaced, practiceTrials, inputButtons, pauseButton): self.logfile = logfile self.trialDuration = trialDuration self.selfPaced = selfPaced self.ISI = ISI self.trialsPer = trialsPer self.numTrials = (self.trialsPer * 4) #Trials/phase = 4x trials/cond self.imgDir = imgDir self.imgIdx = 0 self.runPracticeTrials = practiceTrials self.leftButton = inputButtons[0] self.rightButton = inputButtons[1] self.pauseButton = pauseButton if (screenType == 'Windowed'): screenSelect = False elif (screenType == 'Fullscreen'): screenSelect = True self.window = Window(fullscr=screenSelect, units='pix', color='White', allowGUI=False) self.imageWidth = self.window.size[1] / 6 #Window must be set up before imgs, as img position based on window size self.imageList = self.SegmentImages() self.clock = Clock() #Initialize scorelist for 4 categories;; [correct,inc,resp] self.scoreList = [] for i in range(0, 4): self.scoreList.append([0, 0, 0]) def Pause(self): """Pauses the task, and displays a message waiting for a spacebar input from the user before continuing to proceed. """ pauseMsg = "Experiment Paused\n\nPress '{}' to continue".format( self.pauseButton) pauseText = TextStim(self.window, text=pauseMsg, color='Black', height=40) pauseText.draw(self.window) self.window.flip() waitKeys(keyList=[self.pauseButton]) clearEvents() def CreatePosPair(self, moveType): """Generates two (x,y) coordinates to be associated with a particular image - the first being the study phase position, and second being the test phase position, which is a translation in any direction by 4 degrees of distances - none, small, big, corner. moveType: the amount of relative distance across the screen to move -0: Non move (xA,yA) = (xB,yB) -1: Lure High: Max Distance / 3 -2: Lure Low: (Max Distance*2) / 3 -3: Opposite Corners: Max Distance return: a tuple of two coordinate pairs ((xA,yA),(xB,yB)) (start position of img, end position of img) """ #Map a bottom left oriented coordinate system to a center oriented one def CoordMap(x, y): xM = int(x - winL / 2) yM = int(y - winH / 2) return (xM, yM) #Checks whether (x,y) coordinate is near corner def IsNearCorner(xTest, yTest): if ((xTest <= (x1 + imgDis) and yTest <= (y1 + imgDis)) or (xTest <= (x1 + imgDis) and yTest >= (y2 - imgDis)) or (xTest >= (x2 - imgDis) and yTest <= (y1 + imgDis)) or (xTest >= (x2 - imgDis) and yTest >= (y2 - imgDis))): return True else: return False #Generates a random point on a circle about (xA,yA) with a given radius def GenCoordMove(xA, yA, radius): deg = random.randint(0, 359) rad = math.radians(deg) vecX = math.cos(rad) vecY = math.sin(rad) xR = int(radius * vecX) yR = int(radius * vecY) xB = xA + xR yB = yA + yR return (xB, yB) #Standard distance formula - use for diagnostics def Dist(x1, y1, x2, y2): return (math.sqrt(math.pow(x2 - x1, 2) + math.pow(y2 - y1, 2))) #Calculate the length, in pixels, of the small/large moves winL = self.window.size[0] winH = self.window.size[1] midDis = self.imageWidth / 2 imgDis = self.imageWidth x2 = math.ceil(winL - midDis) x1 = math.floor(0 + midDis) y2 = math.ceil(winH - midDis) y1 = math.floor(0 + midDis) maxDis = math.sqrt(math.pow(x2 - x1, 2) + math.pow(y2 - y1, 2)) distSmall = math.floor(maxDis / 4) distLarge = math.floor((maxDis * 2) / 4) #Opposite corner condition: randomly choose 1 of 4 corner moves if (moveType == 3): corner = random.randint(0, 3) if (corner == 0): return (CoordMap(x2, y2), CoordMap(x1, y1)) elif (corner == 1): return (CoordMap(x1, y2), CoordMap(x2, y1)) elif (corner == 2): return (CoordMap(x1, y1), CoordMap(x2, y2)) elif (corner == 3): return (CoordMap(x2, y1), CoordMap(x1, y2)) #If not corner, generate random starting position and create #ending position based on else: while (1): xT = random.randint(x1, x2) yT = random.randint(y1, y2) #Check that starting coorindates are not near corner if (IsNearCorner(xT, yT)): continue else: (xA, yA) = (xT, yT) if (moveType == 0): #moveType (0): maintain same position (xB, yB) = (xA, yA) elif (moveType == 1): #moveType (1): small move (1/3 max dist) (xB, yB) = GenCoordMove(xA, yA, distSmall) elif (moveType == 2): #moveType (2): large move (2/3 max dist) (xB, yB) = GenCoordMove(xA, yA, distLarge) #Redo random generation if ending coordinates are near corner if (IsNearCorner(xB, yB)): continue #Redo random generation if ending coordinates are out of bounds if ((xB < x1) or (xB > x2) or (yB < y1) or (yB > y2)): continue else: return (CoordMap(xA, yA), CoordMap(xB, yB)) def SegmentImages(self): """Shuffles images in a folder into a list, then successively adds two coordinate pairs to each image. The image list is divided into 4 sections, one for each type of trial (repeat, move small, move big, and opposite corners). For each section of images, the images are given a corresponding type of coordinate pair as governed by createPosPair() return: createdList, each element as: [image,<study(x,y)>,<test(x,y)>] """ #Nested function - populates each division of the image list def SegmentFill(imageList, addingList, moveType): order = range(0, self.trialsPer) random.shuffle(order) for i in order: pospair = self.CreatePosPair(moveType) addingList.append( [imageListSec[self.imgIdx], pospair[0], pospair[1]]) self.imgIdx += 1 return addingList imageListFull = [] imageListSec = [] #First put all available images into list and shuffle for i in os.listdir(self.imgDir): if i.lower().find('.jpg') != -1 and i.lower().find('PR_') == -1: imageListFull.append(i) random.shuffle(imageListFull) #Fill another list with the number of images per phase using prev list for j in range(0, self.numTrials): imageListSec.append(imageListFull[j]) #Successively add each group of trials to madeImgList madeImgList = [] addRepeatList = SegmentFill(imageListSec, madeImgList, 0) addLureSmall = SegmentFill(imageListSec, addRepeatList, 1) addLureLarge = SegmentFill(imageListSec, addLureSmall, 2) addCorners = SegmentFill(imageListSec, addLureLarge, 3) createdList = addCorners return createdList def ImageDiagnostic(self): """Draws colored dots onto the window. The dots' positions represent the respective location of where images will be placed throughout the course of the task. This function is to only be used as a diagnostic tool, so that one can get a general sense of where images might appear, without having to actually run through the task. To use this function properly: taskSpatial = mdts.MDTS(...) taskSpatial.ImageDiagnostic #taskSpatial.RunExp() """ win = self.window cRad = 50 tp = self.trialsPer ls = self.imageList shapes = [] leng = len(ls) for i in range(0, leng): color = "black" #print "{:<24}{:<15}{:<15}".format(img[0],img[1],img[2]) img = ls[i] if i < tp: color = "black" elif (i > tp) and (i < tp * 2): color = "blue" elif (i > tp * 2) and (i < tp * 3): color = "orange" elif i > tp * 3: color = "green" shapes.append(Circle(win, radius=cRad, pos=img[1], fillColor=color)) shapes.append(Circle(win, radius=cRad, pos=img[2], fillColor=color)) shapes.append( ShapeStim(win, units='pix', lineWidth=5, lineColor=color, vertices=(img[1], img[2]))) for shape in shapes: shape.draw(self.window) self.window.flip() waitKeys(keyList=['escape']) self.window.close() def RunTrial(self, image, pos): """Runs a particular trial, which includes displaying the image to the screen, and gathering the keypresses and their respective response times. image: The filename of the image to display pos: Coordinates (on 6x4 grid) where image will be displayed return: tuple of first keypress info: (keyPress, reactionTime) """ ShownImage = ImageStim(self.window) ShownImage.setPos(pos) ShownImage.setSize((self.imageWidth, self.imageWidth)) ShownImage.setImage(self.imgDir + '/%s' % (image)) ShownImage.draw(self.window) self.window.flip() clearEvents() self.clock.reset() keypresses = [] if (self.selfPaced == False): wait(self.trialDuration, self.trialDuration) keypresses = getKeys(keyList=[ self.leftButton, self.rightButton, self.pauseButton, "escape" ], timeStamped=self.clock) elif (self.selfPaced == True): keypresses = waitKeys(keyList=[ self.leftButton, self.rightButton, self.pauseButton, "escape" ], timeStamped=self.clock) self.window.flip() wait(self.ISI) if len(keypresses) < 1: return '', 0 return keypresses[0][0], keypresses[0][1] def ShowPromptAndWaitForSpace(self, prompt, keylist=['p', 'escape']): ''' Show the prompt on the screen and wait for space, or the keylist specified returns the key pressed ''' keylist = [self.pauseButton, 'escape'] text = TextStim(self.window, prompt, color='Black') text.draw(self.window) self.window.flip() continueKey = waitKeys(keyList=keylist) if len(continueKey) != 0 and continueKey[0] == 'escape': self.logfile.write("Terminated early.") self.logfile.close() sys.exit() return continueKey def RunPhase(self, phaseType): """Runs a phase (study or test) of the task, which includes randomizing a list of images, running trials for each of those images, writing trial information to a logfile for each trial ran, and keeping track of a subject's score, based on their response to each trial. phaseType: 0 -> Run Study (use starting position of image) 1 -> Run Test (use ending position of image) return: 0 -> task terminated early 1 -> task ran to completion """ studyPrompt = ( "Let's do the real test. \n\n Are the following objects indoor or outdoor?\n\n('{}' to continue)" .format(self.pauseButton)) testPrompt = ( "In this phase, you will see the same series of objects one at a time.\n\nAre the object locations same or new? \n\n('{}' to continue)" .format(self.pauseButton)) studyText = TextStim(self.window, studyPrompt, color='Black') testText = TextStim(self.window, testPrompt, color='Black') if (phaseType == 0): studyText.draw(self.window) #phaseType = 0 -> Study Phase self.window.flip() self.logfile.write("\nBegin Study\n") elif (phaseType == 1): testText.draw(self.window) #phaseType = 1 -> Test Phase self.window.flip() self.logfile.write("\nBegin Test\n") log = self.logfile log.write("{a:<22}{b:<12}{c:<14}{d:<11}{e:<9}{f:<8}{g}\n".format( a='Image', b='Type', c='Start', d='End', e='Correct', f='Resp', g='RT')) continueKey = waitKeys(keyList=[self.pauseButton, 'escape']) if (continueKey[0] == 'escape'): self.logfile.write("\n\n\nPhase Not Run\n\n\n") return 0 imgs = self.imageList trialOrder = range(0, len(imgs)) random.shuffle(trialOrder) #Run through each trial for i in range(0, len(trialOrder)): imgIdx = trialOrder[i] correct = "" #Divide image index by the trials/cond, take floor for trial type trialFactor = int(math.floor(imgIdx / self.trialsPer)) trialType = "" if (trialFactor == 0): trialType = "Same" if (phaseType == 1): correct = self.leftButton elif (trialFactor == 1): trialType = "Small" if (phaseType == 1): correct = self.rightButton elif (trialFactor == 2): trialType = "Large" if (phaseType == 1): correct = self.rightButton elif (trialFactor == 3): trialType = "Crnr" if (phaseType == 1): correct = self.rightButton #Display image in start position in study, end position in test if (phaseType == 0): (response, RT) = self.RunTrial(imgs[imgIdx][0], imgs[imgIdx][1]) elif (phaseType == 1): (response, RT) = self.RunTrial(imgs[imgIdx][0], imgs[imgIdx][2]) if (response == "escape"): self.logfile.write("\n\nPhase terminated early\n\n") break elif (response == self.pauseButton): self.Pause() #Write formatted info about trial to logfile log.write("{:<22}{:<9}{:<14}{:<17}{:<7}{:<6}{:>0.3f}\n".format( imgs[imgIdx][0], trialType, imgs[imgIdx][1], imgs[imgIdx][2], correct, response, RT)) #If in test phase, tally responses, correct + incorrect answers if (phaseType == 1): if (response): if (trialType == 'Same'): self.scoreList[0][2] += 1 if (response == correct): self.scoreList[0][0] += 1 else: self.scoreList[0][1] += 1 elif (trialType == 'Small'): self.scoreList[1][2] += 1 if (response == correct): self.scoreList[1][0] += 1 else: self.scoreList[1][1] += 1 elif (trialType == 'Large'): self.scoreList[2][2] += 1 if (response == correct): self.scoreList[2][0] += 1 else: self.scoreList[2][1] += 1 elif (trialType == 'Crnr'): self.scoreList[3][2] += 1 if (response == correct): self.scoreList[3][0] += 1 else: self.scoreList[3][1] += 1 #Implies test phase ran through to completion return 1 def SegmentPracticeImages(self, images): ''' Segment practice image list into the 4 conditions and add two coordinate pairs to each image. Add a study coordinate location and test coordinate location Return: List [image, trialType, studyCoord(x,y), testCoord(x,y)] ''' images = np.array_split(images, 4) allImages = [] for idx, imageType in enumerate([0, 1, 2, 3]): for img in images[idx]: xyStudy, xyTest = self.CreatePosPair(imageType) allImages.append([img, imageType, xyStudy, xyTest]) return allImages def RunSinglePractice(self, practiceBlock, images): ''' Read in the images we want, and run the practice block for this subject Run encoding and test, and write to the logs Return: float: ratio correct ''' ### Encoding # imgs = [[img, trialType, Study(x,y), Test(x,y)]] imgs = self.SegmentPracticeImages(images) self.ShowPromptAndWaitForSpace( " Outdoor or Indoor? ('{}' to continue)".format(self.pauseButton)) random.shuffle(imgs) self.logfile.write( "\nBegin Practice Encoding {}\n\n".format(practiceBlock)) self.logfile.write( "{a:<22}{b:<12}{c:<14}{d:<11}{e:<9}{f:<8}{g}\n".format(a='Image', b='Type', c='Start', d='End', e='Correct', f='Resp', g='RT')) # Run the trial for each encoding trial for i, trial in enumerate(imgs): img, trialType, studyCoord, testCoord = trial response, RT = self.RunTrial(img, studyCoord) if (response == "escape"): self.logfile.write("\n\n Practice terminated early\n\n") self.logfile.close() sys.exit() elif (response == self.pauseButton): self.Pause() trialTypeMap = {0: 'Same', 1: 'Small', 2: 'Large', 3: 'Crnr'} trialTypeStr = trialTypeMap[trialType] correct = "" self.logfile.write( "{:<22}{:<9}{:<14}{:<17}{:<7}{:<6}{:>0.3f}\n".format( img, trialTypeStr, studyCoord, testCoord, correct, response, RT)) ### Test self.ShowPromptAndWaitForSpace( "Is the object location same or new? ('{}' to continue)".format( self.pauseButton)) random.shuffle(imgs) self.logfile.write( "\nBegin Practice Test {}\n\n".format(practiceBlock)) self.logfile.write( "{a:<22}{b:<12}{c:<14}{d:<11}{e:<9}{f:<8}{g}\n".format(a='Image', b='Type', c='Start', d='End', e='Correct', f='Resp', g='RT')) # Keep track of the total number they got correct totalCorrect = 0 for i, trial in enumerate(imgs): img, trialType, studyCoord, testCoord = trial response, RT = self.RunTrial(img, testCoord) if (response == "escape"): self.logfile.write("\n\n Practice terminated early\n\n") self.logfile.close() sys.exit() elif (response == self.pauseButton): self.Pause() trialTypeMap = {0: 'Same', 1: 'Small', 2: 'Large', 3: 'Crnr'} trialTypeStr = trialTypeMap[trialType] correct = self.leftButton if trialType == 0 else self.rightButton # It should only be correct if its 'Same' self.logfile.write( "{:<22}{:<9}{:<14}{:<17}{:<7}{:<6}{:>0.3f}\n".format( img, trialTypeStr, studyCoord, testCoord, correct, response, RT)) if correct == response: totalCorrect += 1 # Return the percentage correct return totalCorrect / len(imgs) def RunPractice(self): ''' Runs three rounds of practice trials. If the participant gets a certain amount correct, they move on to the real test. ''' dirFiles = os.listdir(self.imgDir) practiceImages = [img for img in dirFiles if "PR_" in img] random.shuffle(practiceImages) # Split the practice images into three sets practiceImages = np.array_split(practiceImages, 3) # Run each practice session for i in range(3): practicePrompt = "Let's practice.\n\n('{}' to continue)".format( self.pauseButton) self.ShowPromptAndWaitForSpace(practicePrompt) results = self.RunSinglePractice( i + 1, [img for img in practiceImages[i]]) # If they get a certain percentage correct, then stop the practice self.ShowPromptAndWaitForSpace( "You got {}% correct! ('{}' to continue)".format( int(results * 100), self.pauseButton)) if results > .6: return def RunExp(self): """Run through an instance of the task, which includes the study and test phases. Also prints the exit message at the end, and closes the logfile if scores are not being written to it. return: (logfile, scorelist) if the test was run through. Assuming this happens, scores will be written to the logfile return: (-1,-1) if the task was quit prior to the completion of the study phase, meaning scores will not be writtent to the logfile """ def EndExp(): exitPrompt = ("This concludes the session. Thank you for " "participating!\n\nPress Escape to quit") exitText = TextStim(self.window, exitPrompt, color='Black') exitText.draw(self.window) self.window.flip() waitKeys(keyList=['escape']) self.window.close() # Show main welcome window welcomePrompt = "Thank you for participating in our study! Press '{}' to begin".format( self.pauseButton) self.ShowPromptAndWaitForSpace(welcomePrompt) # If run practice trials, then RunPractice if self.runPracticeTrials: self.RunPractice() self.RunPhase(0) testFinished = self.RunPhase(1) if (testFinished): EndExp() return (self.logfile, self.scoreList) else: EndExp() self.logfile.close() return (-1, -1)
def onset(self, index, value): self.rotation_clocks[index] = Clock() self.type[index] = value
units='norm') #system settings (session duration, interval length and size of COD) session = 60 #minutes phase_time = session / 6 COD = 5 schedule_list = ['ConcChFR20VI5/FR20FI5', 'ConcChFR20FI5/FR20VI5'] #where random schedule is located interval = 5 ratio = 20 link_time = 100 initial = False #initialization #counts time from the start of reinforcement global_time = Clock() #timers for the schedule of reinforcement interval_timer = CountdownTimer(0) phase_timer = CountdownTimer(0) link_timer = CountdownTimer(0) #tracks responses lpressed, rpressed, cpressed = False, False, False mouse = Mouse() R1, n1 = 0, 0 #tracks responses from left and prevents changeovers R2, n2 = 0, 0 #tracks responses from right R3 = 0 #tracks responses from center #tracks consequences Cs1, Cs2, Cs3, score = 0, 0, 0, 500 #tracks experiment phase = 0 #how many different conditions introduced
def testClock(): try: c = Clock() t1=c.getTime() time.sleep(1.0) t2=c.getTime() startTime=c.getLastResetTime() assert t2>t1 assert t2-t1 > 0.95 assert t2-t1 < 1.05 assert startTime > 0 c.reset() t=c.getTime() assert t < 0.01 c.reset(10) t=c.getTime() assert t > -10.0 assert t < -9.9 t1=c.getTime() c.add(50) t2=c.getTime() assert t2-t1 > -50.0 assert t2-t1 < -49.9 printf(">> Clock Test: PASSED") except Exception: printf(">> Clock Test: FAILED") printExceptionDetails() printf("-------------------------------------\n")
class MDTO(object): def __init__(self, logfile, imgDir, screenType, expVariant, trialDuration, ISI, trialsPer, selfPaced, practiceTrials, inputButtons, pauseButton): self.logfile = logfile self.expVariant = expVariant self.trialDuration = trialDuration self.selfPaced = selfPaced self.ISI = ISI self.trialsPer = trialsPer self.imgDir = imgDir self.leftOvers = [] self.splitLures = self.SplitLures() self.splitSingles = self.SplitSingles() self.runPracticeTrials = practiceTrials self.leftButton = inputButtons[0] self.rightButton = inputButtons[1] self.pauseButton = pauseButton if (screenType == 'Windowed'): screenSelect = False elif (screenType == 'Fullscreen'): screenSelect = True self.window = Window(fullscr=screenSelect, units='pix', color='White', allowGUI=False) self.imageWidth = self.window.size[1] / 3 #Define the black box that appears in the lower left, to signal EEG rW = 110 #Width rH = 60 #Height rectVertices = [[rW, -rH], [-rW, -rH], [-rW, rH], [rW, rH]] rectCenter = [(-self.window.size[0] / 2 + rW), (-self.window.size[1] / 2) + rH] self.blackBox = ShapeStim(self.window, fillColor='black', units='pix', fillColorSpace='rgb', vertices=rectVertices, closeShape=True, interpolate=True, pos=rectCenter) self.rangeITI = numpy.arange(1, 1.4, .001) self.clock = Clock() #Initialize scorelist for 4 categories|| [correct,incorrect,response] self.scoreList = [] for i in range(0, 4): self.scoreList.append([0, 0, 0]) def GrabFileType(self, fileList, exts): """Takes an inputted list, as well as extension, and returns a list with the elements in the original list that have the desired extension. fileList: The list of files to search through ext: string containing the extension type, e.g. ".jpg", ".png" return: a new list, containing files only of ext type """ fileListofType = [] for aFile in fileList: for ext in exts: ftLen = len(ext) if (aFile[-ftLen:] == ext): fileListofType.append(aFile) break return fileListofType def SplitLures(self): """Creates and returns a list of image lures. Lures are taken from both the lure high and lure low directory, and an equal amount of each are put into the list. Each element in the returned list has 2 elements: [imgA,imgB] and a number, pertaining to the difficulty of the lure, i.e. the degree of apparent difference between the two images. Return: a list, each element being: [[imgA,imgB], lureNum] """ dirFiles = os.listdir(self.imgDir) imgTypes = ['.jpg', '.jpeg', '.JPG'] allImgs = self.GrabFileType(dirFiles, imgTypes) allImgs = [img for img in allImgs if "PR" not in img] lureLowImgs = [] lureHighImgs = [] for img in allImgs: if (img[5] == "1"): lureHighImgs.append(img) elif (img[5] == "2"): lureLowImgs.append(img) #Sort images by name lureHighImgs.sort() lureLowImgs.sort() #Return a list of lures as a list w/: [[imgA,imgB],lureType] def LureListGroup(imgList): lureList = [] for i in range(0, int(len(imgList) / 2)): imgA = imgList[i * 2] imgB = imgList[(i * 2) + 1] highSet = [imgA, imgB] lureList.append(highSet) return lureList #Create list of lures with embedded structure, then shuffle lureHighList = LureListGroup(lureHighImgs) lureLowList = LureListGroup(lureLowImgs) random.shuffle(lureHighList) random.shuffle(lureLowList) #Put number (num of trials) of list items from both lists into list selectedList = [] for i in range(0, self.trialsPer): selectedList.append(lureHighList[i]) selectedList.append(lureLowList[i]) #Unused "leftover" A images will be used as singles #Probably want to use other method while seeded rand unimplemented #OK for now for i in range(self.trialsPer + 1, len(lureHighList)): try: self.leftOvers.append(lureHighList[i][0]) except IndexError: pass try: self.leftOvers.append(lureLowList[i][0]) except IndexError: pass random.shuffle(selectedList) return selectedList def SplitSingles(self): """Creates and returns a list of image "singles". Each item in the list is an image, followed by either "sF" (single Foil) or "sR" (single repeat), indicating if it is to be shown once or twice. return: list composed of [imageFileName, type] type: "sR" or "sF" """ singles = self.leftOvers targetsFoils = [] for i in range(0, self.trialsPer * 2): if (i % 2 == 0): targetsFoils.append([singles[i], "sR"]) else: targetsFoils.append([singles[i], "sF"]) random.shuffle(targetsFoils) return targetsFoils def Pause(self): """Pauses the task, and displays a message waiting for a spacebar input from the user before continuing to proceed. """ pauseMsg = "Experiment Paused\n\nPress '{}' to continue".format( self.pauseButton) pauseText = TextStim(self.window, text=pauseMsg, color='Black', height=40) pauseText.draw(self.window) self.window.flip() waitKeys(keyList=[self.pauseButton]) clearEvents() def ScaleImage(self, image, maxSize=350): """Scales the size of the image to fit as largely as it can within the window of the defined maxSize, while preserving its aspect ratio. image: the filename of the image to be scaled maxSize: maximum size, in pixels of image return: maximum scaling of image """ im = Image.open(image) larger = im.size[0] if (im.size[0] < im.size[1]): larger = im.size[1] scale = larger / maxSize scaledSize = (im.size[0] / scale, im.size[1] / scale) return scaledSize def RunTrialECog(self, image, phase): """Runs a particular trial for an ECog (Electrocorticography) based task. An ECog trial runs as follows: display the image along with the black box for <trial duration> amount of time, clear the screen for <ISI> amount of time, then asking for and getting subject input for <ITI> amount of time. image: the stimuli to display on screen phase: 0 (Study Phase) - prompts user "Indoor / Outdoor" 1 (Test Phase) - prompts user "Old / New" return: [keyPress, reactionTime] """ theImage = ImageStim(self.window) #Set the full path of the image, based on the image's lure type if (image[0][5] == "3"): image = (self.imgSnglDir + '%s' % (image[0])) elif ((image[0][5] == "1") or (image[0][5] == "2")): image = (self.lureHighDir + '%s' % (image[0])) elif ((image[0][5] == "4") or (image[0][5] == "5")): image = (self.lureLowDir + '%s' % (image[0])) theImage.setImage(image) imageSize = self.ScaleImage(image, self.imageWidth) theImage.setSize(imageSize) ecogISI = 0.5 posLeftText = (-(self.window.size[0] / 8), 0) posRightText = ((self.window.size[0] / 8), 0) if (phase == 0): ecogTrialDur = 2.0 leftMsg = "Indoor\n\n 1" rightMsg = "Outdoor\n\n 2" else: ecogTrialDur = 1.0 leftMsg = "Old\n\n 1" rightMsg = "New\n\n 2" theImage.draw(self.window) self.blackBox.draw(self.window) self.window.flip() wait(ecogTrialDur, ecogTrialDur) self.window.flip() wait(ecogISI, ecogISI) textLeft = TextStim(self.window, text=leftMsg, pos=posLeftText, color='Black', height=50) textRight = TextStim(self.window, text=rightMsg, pos=posRightText, color='Black', height=50) textLeft.draw(self.window) textRight.draw(self.window) self.window.flip() clearEvents() self.clock.reset() keyPresses = waitKeys(keyList=['1', '2', 'space', 'escape'], timeStamped=self.clock, maxWait=1.5) self.window.flip() random.shuffle(self.rangeITI) wait(self.rangeITI[0], self.rangeITI[0]) if (not keyPresses): return '', 0 return keyPresses[0][0], keyPresses[0][1] def RunTrial(self, image): """Runs a particular trial, which includes displaying the image to the screen, and gathering the keypresses and their respective response times. image: the image (filename) to display returns: [keyPress, reaction time] """ theImage = ImageStim(self.window) imagePath = os.path.normpath(self.imgDir + "/%s" % (image)) theImage.setImage(imagePath) imageSize = self.ScaleImage(imagePath, self.imageWidth) theImage.setSize(imageSize) theImage.draw(self.window) self.window.flip() clearEvents() self.clock.reset() keyPresses = [] if (self.selfPaced == False): wait(self.trialDuration, self.trialDuration) keyPresses = getKeys(keyList=[ self.leftButton, self.rightButton, self.pauseButton, 'escape' ], timeStamped=self.clock) elif (self.selfPaced == True): keyPresses = waitKeys(keyList=[ self.leftButton, self.rightButton, self.pauseButton, 'escape' ], timeStamped=self.clock) self.window.flip() wait(self.ISI) if (not keyPresses): return '', 0 return keyPresses[0][0], keyPresses[0][1] def RunStudy(self): """Runs the first part of the MDT-O experiment, or the Study phase. In this phase, all target versions of the image pairs are shown, as well as half of the "singles" images. Keypresses and their repsective reaction times are recorded during this period, and no "right or wrong" answers are graded. """ ecog = False if self.expVariant != "ECog" else True studyPromptN = ( "Let's do the real test. \n\n Are the following objects indoor or outdoor? \n\n Press 'p' to continue" ) ''' studyPromptE = ("In the following phase, a sequence of images will be " "shown.\n\n-Press '1' if the image is of an indoor " "object.\n\n-Press '2' if the image is of an outdoor " "object.\n\n\nPress space to begin" ) ''' studyText = TextStim(self.window, studyPromptN, color='Black') if ecog: studyText = TextStim(self.window, studyPromptE, color='Black') studyText.draw(self.window) self.window.flip() continueKey = waitKeys(keyList=[self.pauseButton, 'escape']) if (continueKey[0] == 'escape'): self.logfile.write("\n\n\nStudy Not Run\n\n") return 0 self.logfile.write("\nBegin Study\n\n") logStudyFormat = '{:<7}{:<12s} {:<10s} {:<10s} {:<4s}\n'.format( 'Trial', 'Image', 'ImageType', 'Response', 'RT') self.logfile.write(logStudyFormat) #Create list for study: "A" pairs (targets), and repeat singles studyImgList = [] for pair in self.splitLures: studyImgList.append([pair[0], pair[0][5]]) for img in self.splitSingles: if (img[1] == "sR"): studyImgList.append(img) #Shuffle study list random.shuffle(studyImgList) #Run trial for each study image for i in range(0, len(studyImgList)): if not ecog: (response, RT) = self.RunTrial(studyImgList[i][0]) else: (response, RT) = self.RunTrialECog(studyImgList[i][0], 0) if (response == "escape"): self.logfile.write("\n\nStudy terminated early\n\n") return 0 elif (response == self.pauseButton): self.Pause() trialFormat = '{:<7}{:<17s}{:<10s}{:<6s}{:<4.3f}\n'.format( i + 1, studyImgList[i][0], studyImgList[i][1], response, RT) self.logfile.write(trialFormat) return 1 def RunTest(self): """Runs the second part of the MDT-O experiment, or the Test phase. In this phase, high and low "lures" are shown, as well as all of the "singles" shown in the study phase, as well as entirely new images known as "foils". All keypresses and their respective reaction times are recorded during this period. Additionally, a tally is kept of whether the subjects answer was wrong or right, with a separate score for "pair" answers. """ ecog = False if self.expVariant != "ECog" else True testPromptN = ( "In this phase, another sequence of images will be shown" "\n\nAre the objects old or new?\n\n Press 'p' to continue.") '''testPromptE = ("In this phase, another sequence of images will be shown." "\n\n-Press '1' if the image presented was also shown " "in the previous phase. (Old Image)\n\n-Press '2' if the" "image presented was not shown in the previous phase." " (New Image)\n\n\nPress space to begin" ) ''' testText = TextStim(self.window, text=testPromptN, color='Black') if ecog: testText = TextStim(self.window, text=testPromptE, color='Black') testText.draw(self.window) self.window.flip() continueKey = waitKeys(keyList=[self.pauseButton, 'escape']) if (continueKey[0] == 'escape'): self.logfile.write("\n\n\nTest Not Run\n\n") return 0 self.logfile.write("\nBegin Test\n\n") logTestFormat = '{:<7}{:<12}{:<11}{:<9}{:<10}{:<4}\n'.format( 'Trial', 'Image', 'ImageType', 'CorResp', 'Response', 'RT') self.logfile.write(logTestFormat) #Create trial list for test: B and C lures, and all singles testImgList = [] for pair in self.splitLures: testImgList.append([pair[1], pair[1][5]]) for img in self.splitSingles: testImgList.append(img) #Shuffle trial list random.shuffle(testImgList) #Run trial for each image in list, get responses for i in range(0, len(testImgList)): correct = self.rightButton trialType = testImgList[i][1] if (trialType == "sR"): correct = self.leftButton if not ecog: (response, RT) = self.RunTrial(testImgList[i][0]) else: (response, RT) = self.RunTrialECog(testImgList[i][0], 1) if (response == "escape"): self.logfile.write("\n\nTest terminated early\n\n") break elif (response == self.pauseButton): self.Pause() trialFormat = '{:<7}{:<15}{:<11}{:<9}{:<6}{:<4.3f}\n'.format( i + 1, testImgList[i][0], testImgList[i][1], correct, response, RT) self.logfile.write(trialFormat) #Tally scores of correct/responses if (response): if (trialType == "sR"): self.scoreList[0][2] += 1 if (response == correct): self.scoreList[0][0] += 1 else: self.scoreList[0][1] += 1 elif (trialType == "1"): self.scoreList[1][2] += 1 if (response == correct): self.scoreList[1][0] += 1 else: self.scoreList[1][1] += 1 elif (trialType == "2"): self.scoreList[2][2] += 1 if (response == correct): self.scoreList[2][0] += 1 else: self.scoreList[2][1] += 1 elif (trialType == "sF"): self.scoreList[3][2] += 1 if (response == correct): self.scoreList[3][0] += 1 else: self.scoreList[3][1] += 1 return 1 def ShowPromptAndWaitForSpace(self, prompt, keylist=['p', 'escape']): ''' Show the prompt on the screen and wait for space, or the keylist specified returns the key pressed ''' keylist = [self.pauseButton, 'escape'] text = TextStim(self.window, prompt, color='Black') text.draw(self.window) self.window.flip() continueKey = waitKeys(keyList=keylist) if len(continueKey) != 0 and continueKey[0] == 'escape': self.logfile.write("Terminated early.") self.logfile.close() sys.exit() return continueKey def RunSinglePractice(self, practiceBlock, images): ''' Read in the images we want, and run the practice block for this subject Run encoding and test, and write to the logs Return: float: ratio correct ''' imgPairs = [] for i in range(0, len(images) - 1, 2): if "foil" in images[i]: t = "sF" elif "target" in images[i]: t = "sR" elif "high" in images[i]: t = "2" elif "low" in images[i]: t = "1" imgPairs.append([images[i], images[i + 1], t]) ### Encoding self.ShowPromptAndWaitForSpace( " Outdoor or Indoor? ('{}' to continue)".format(self.pauseButton)) random.shuffle(imgPairs) self.logfile.write( "\nBegin Practice Encoding {}\n\n".format(practiceBlock)) logPracticeFormat = '{:<7}{:<17}{:<11}{:<9}{:<10}{:<4}\n'.format( 'Trial', 'Image', 'ImageType', 'CorResp', 'Response', 'RT') self.logfile.write(logPracticeFormat) # Run the trial for each encoding trial for i, trial in enumerate(imgPairs): imgA, imgB, trialType = trial if trialType != 'sF': response, RT = self.RunTrial(imgA) if (response == 'escape'): self.logfile.write( "\n\nPractice block terminated early\n\n") self.logfile.close() sys.exit() elif (response == self.pauseButton): self.Pause() trialFormat = '{:<7}{:<17}{:<11}{:<9}{:<6}{:<4.3f}\n'.format( i + 1, imgA, trialType, '', response, RT) self.logfile.write(trialFormat) ### Test self.ShowPromptAndWaitForSpace( " Old or new? ('{}' to continue)".format(self.pauseButton)) random.shuffle(imgPairs) self.logfile.write( "\nBegin Practice Test {}\n\n".format(practiceBlock)) self.logfile.write(logPracticeFormat) # Keep track of the total number they got correct totalCorrect = 0 for i, trial in enumerate(imgPairs): imgA, imgB, trialType = trial if trialType == 'sR' or trialType == 'sF': response, RT = self.RunTrial(imgA) else: response, RT = self.RunTrial(imgB) correct = self.leftButton if trialType == 'sR' else self.rightButton if response == correct: totalCorrect += 1 if (response == "escape"): self.logfile.write("\n\nPractice terminated early\n\n") return -1 elif (response == self.pauseButton): self.Pause() trialFormat = '{:<7}{:<17}{:<11}{:<9}{:<6}{:<4.3f}\n'.format( i + 1, imgA, trialType, correct, response, RT) self.logfile.write(trialFormat) # Return the percentage correct return totalCorrect / len(imgPairs) def RunPractice(self): ''' Runs three rounds of practice trials. If the participant gets a certain amount correct, they move on to the real test. ''' dirFiles = os.listdir(self.imgDir) practiceImages = [img for img in dirFiles if "PR" in img] # Run each practice session for i in range(3): practicePrompt = "Let's practice. ('{}' to continue)".format( self.pauseButton) self.ShowPromptAndWaitForSpace(practicePrompt) imagesThisPracticeSession = sorted([ img for img in practiceImages if "Set_{}".format(i + 1) in img ]) results = self.RunSinglePractice(i + 1, imagesThisPracticeSession) # If they get a certain percentage correct, then stop the practice self.ShowPromptAndWaitForSpace( "You got {}% correct! ('{}' to continue)".format( int(results * 100), self.pauseButton)) if results > .6: return def RunExp(self): """Run through an instance of the task, which includes the study and test phases. Also prints the exit message at the end, and closes the logfile if scores are not being written to it. return: (logfile, scorelist) if the test was run through. Assuming this happens, scores will be written to the logfile return: (-1,-1) if the task was quit prior to the completion of the study phase, meaning scores will not be writtent to the logfile """ #Print task ending message to the screen, and wait escape to be prssed def EndExp(): exitPrompt = ("This concludes the session. Thank you for " "participating!\n\nPress Esc to quit") exitText = TextStim(self.window, exitPrompt, color='Black') exitText.draw(self.window) self.window.flip() waitKeys(keyList=['escape']) self.window.close() # Show main welcome window welcomePrompt = "Thank you for participating in our study! Press '{}' to begin".format( self.pauseButton) self.ShowPromptAndWaitForSpace(welcomePrompt) # If run practice trials, then RunPractice if self.runPracticeTrials: self.RunPractice() #Run study, terminate if user exits early studyFinished = self.RunStudy() testFinished = self.RunTest() if (not studyFinished): EndExp() self.logfile.close() return (-1, -1) EndExp() return (self.logfile, self.scoreList)