class Test_textbox(object): def setup_class(self): self.win = Window([128, 128], pos=[50, 50], allowGUI=False, autoLog=False) def teardown_class(self): self.win.close() def test_basic(self): for units in ['norm', 'pix']: self.win.units = units tb = TextBox(self.win, size=(1, .5), pos=(0, -.25)) tb.setBackgroundColor('white') text = 'abc DEF' self.win.flip() tb.setText(text) tb.draw() self.win.flip() assert tb.getText() == tb.getDisplayedText() == text def test_something(self): # to-do: test visual display, char position, etc pass
def testStaticPeriod(): static = StaticPeriod() static.start(0.1) wait(0.05) assert static.complete() == 1 static.start(0.1) wait(0.11) assert static.complete() == 0 win = Window(autoLog=False) static = StaticPeriod(screenHz=60, win=win) static.start(.002) assert win.recordFrameIntervals == False static.complete() assert static._winWasRecordingIntervals == win.recordFrameIntervals win.close() # Test if screenHz parameter is respected, i.e., if after completion of the # StaticPeriod, 1/screenHz seconds are still remaining, so the period will # complete after the next flip. refresh_rate = 100.0 period_duration = 0.1 timer = CountdownTimer() win = Window(autoLog=False) static = StaticPeriod(screenHz=refresh_rate, win=win) static.start(period_duration) timer.reset(period_duration) static.complete() assert np.allclose(timer.getTime(), 1.0 / refresh_rate, atol=0.001) win.close()
def testStaticPeriod(): static = StaticPeriod() static.start(0.1) wait(0.05) assert static.complete()==1 static.start(0.1) wait(0.11) assert static.complete()==0 win = Window(autoLog=False) static = StaticPeriod(screenHz=60, win=win) static.start(.002) assert win.recordFrameIntervals == False static.complete() assert static._winWasRecordingIntervals == win.recordFrameIntervals win.close() # Test if screenHz parameter is respected, i.e., if after completion of the # StaticPeriod, 1/screenHz seconds are still remaining, so the period will # complete after the next flip. refresh_rate = 100.0 period_duration = 0.1 timer = CountdownTimer() win = Window(autoLog=False) static = StaticPeriod(screenHz=refresh_rate, win=win) static.start(period_duration) timer.reset(period_duration ) static.complete() assert np.allclose(timer.getTime(), 1.0/refresh_rate, atol=0.001) win.close()
def test_StaticPeriod_recordFrameIntervals(): win = Window(autoLog=False) static = StaticPeriod(screenHz=60, win=win) static.start(.002) assert win.recordFrameIntervals is False static.complete() assert static._winWasRecordingIntervals == win.recordFrameIntervals win.close()
def main(): groups = create_stim_sequence(BLOCK1, BLOCK2, BLOCK3, TRIALREPEATS) print groups disp = Window(size=SIZE, monitor=MON, units='deg', color=BACKCOL, screen=1, fullscr=True) mouse = Mouse() fixmark = Circle(disp, radius=0.05, edges=32, pos=CENTER, lineColor=FIXCOL) images = [] for item in CIRCLES.keys(): image = ImageStim(disp, image=CIRCLES[item][1], pos=CIRCLES[item][0], size=CIRCLES[item][3]) images.append(image) fixmark.draw() draw_group(images) disp.flip() while True: button = mouse.getPressed() if button[0]: break for item in groups: flashes = [] for i in item: flash = ImageStim(disp, image=CIRCLES[i][2], pos=CIRCLES[i][0], size=CIRCLES[i][3]) flashes.append(flash) fixmark.draw() draw_group(images) draw_group(flashes) disp.flip() wait(FLASH) fixmark.draw() draw_group(images) wait(PAUSE) disp.flip() disp.close()
class Test_class_ProjectorFramePacker(object): """ """ def setup_class(self): self.win = Window(monitor='LightCrafter4500', screen=2, fullscr=True, color='gray', useFBO = True) self.win.setRecordFrameIntervals() self.packer = ProjectorFramePacker (self.win) def teardown_class(self): self.win.close() def flip (self, frames=120): for i in range(frames): self.win.flip()
class Test_class_WindowWarp: def setup_class(self): self.win = Window(monitor='testMonitor', screen=1, fullscr=True, color='gray', useFBO = True) self.warper = Warper (self.win, warp='spherical', warpfile = "", warpGridsize = 128, eyepoint = [0.5, 0.5], flipHorizontal = False, flipVertical = False) self.warper.dist_cm=15 self.g = ProjectionsLinesAndCircles(self.win, self.warper) def teardown_class(self): self.win.close() def draw_projection (self, frames=120): self.g.updateInfo() for i in range(frames): self.g.update_sweep() def test_spherical(self): self.warper.changeProjection('spherical') self.draw_projection() def test_cylindrical(self): self.warper.changeProjection('cylindrical') self.draw_projection() def test_warpfile(self): self.warper.changeProjection('warpfile', warpfile="") #jayb todo self.draw_projection() def test_distance(self): self.test_spherical() for i in range (1, 50, 2): self.warper.dist_cm = i self.warper.changeProjection(self.warper.warp) self.g.updateInfo() self.g.update_sweep() self.test_cylindrical() for i in range (1, 50, 2): self.warper.dist_cm = i self.warper.changeProjection(self.warper.warp) self.g.updateInfo() self.g.update_sweep() def test_flipHorizontal(self): self.warper.changeProjection(self.warper.warp, self.warper.warpfile, flipHorizontal = not self.warper.flipHorizontal) self.draw_projection() def test_flipVertical(self): self.warper.changeProjection(self.warper.warp, self.warper.warpfile, flipVertical = not self.warper.flipVertical) self.draw_projection()
class Test_class_WindowWarp: def setup_class(self): self.win = Window(monitor='testMonitor', screen=1, fullscr=True, color='gray', useFBO = True, autoLog=False) self.warper = Warper (self.win, warp='spherical', warpfile = "", warpGridsize = 128, eyepoint = [0.5, 0.5], flipHorizontal = False, flipVertical = False) self.warper.dist_cm=15 self.g = ProjectionsLinesAndCircles(self.win, self.warper) def teardown_class(self): self.win.close() def draw_projection (self, frames=120): self.g.updateInfo() for i in range(frames): self.g.update_sweep() def test_spherical(self): self.warper.changeProjection('spherical') self.draw_projection() def test_cylindrical(self): self.warper.changeProjection('cylindrical') self.draw_projection() def test_warpfile(self): self.warper.changeProjection('warpfile', warpfile="") #jayb todo self.draw_projection() def test_distance(self): self.test_spherical() for i in range (1, 50, 2): self.warper.dist_cm = i self.warper.changeProjection(self.warper.warp) self.g.updateInfo() self.g.update_sweep() self.test_cylindrical() for i in range (1, 50, 2): self.warper.dist_cm = i self.warper.changeProjection(self.warper.warp) self.g.updateInfo() self.g.update_sweep() def test_flipHorizontal(self): self.warper.changeProjection(self.warper.warp, self.warper.warpfile, flipHorizontal = not self.warper.flipHorizontal) self.draw_projection() def test_flipVertical(self): self.warper.changeProjection(self.warper.warp, self.warper.warpfile, flipVertical = not self.warper.flipVertical) self.draw_projection()
class Test_textbox(object): def setup_class(self): self.win = Window([128, 128], pos=[50, 50], allowGUI=False, autoLog=False) def teardown_class(self): self.win.close() def test_basic(self): pass def test_something(self): # to-do: test visual display, char position, etc pass
def test_StaticPeriod_screenHz(): """Test if screenHz parameter is respected, i.e., if after completion of the StaticPeriod, 1/screenHz seconds are still remaining, so the period will complete after the next flip. """ refresh_rate = 100.0 period_duration = 0.1 timer = CountdownTimer() win = Window(autoLog=False) static = StaticPeriod(screenHz=refresh_rate, win=win) static.start(period_duration) timer.reset(period_duration) static.complete() assert np.allclose(timer.getTime(), 1.0 / refresh_rate, atol=0.001) win.close()
class Test_textbox(object): def setup_class(self): self.win = Window([128,128], pos=[50,50], allowGUI=False, autoLog=False) def teardown_class(self): self.win.close() def test_basic(self): for units in ['norm', 'pix']: self.win.units = units tb = TextBox(self.win, size=(1,.5), pos=(0,-.25)) tb.setBackgroundColor('white') text = 'abc DEF' self.win.flip() tb.setText(text) tb.draw() self.win.flip() assert tb.getText() == tb.getDisplayedText() == text def test_something(self): # to-do: test visual display, char position, etc pass
message = incorrect_message if bl == 0: # only give a message in the learning block trial_routine.wait_for_time_limit( components=[message], time_seconds=messages_duration, label='choice_feedback') data = data.append( {'trial':int(t+1), 'rt':rt, 'choice':key, 'accuracy':accuracy_trial, 'image':image_trial, 'block':['learning', 'transfer'][bl], 'difficulty':block['difficulty'][t], 'correct_response':correct_resp_trial, 'category':block['category'][t]}, ignore_index=True) # record the responses #save data to file for label in expInfo.keys(): data[label] = expInfo[label] data.to_csv(fileName + '.csv') # final message with accuracy feedback accuracy_learning = int(data.loc[data.block == 'learning', 'accuracy'].mean()*100) accuracy_transfer = int(data.loc[data.block == 'transfer', 'accuracy'].mean()*100) end_transfer_message.text = "Congratulations, you finished the experiment. You accuracy was {}% in the learning part and {}% in the test.".format(accuracy_learning, accuracy_transfer) trial_routine.wait_for_time_limit( components=[end_transfer_message], time_seconds=message_beginning_duration, label='message_end') #cleanup mywin.close() core.quit()
userResponse( '', False, qpos, image ) # call the userResponse -> double while loop to save the user taped response core.wait(loadTime) # let psychopy breath... # ----------------------------------------- # # ENDING : DISPLAY A THANK YOU MESSAGE # # SAVE THE NUMBER OF TURNS FOR THE RESULTS # # THEN CLOSE THE DISPLAY # # ----------------------------------------- # qstim = ImageStim(disp, image='images/Merci.gif') # thank you image qstim.draw() # afficher le message image disp.flip() # passer au screen au suivant -> on met la question par-dessus core.wait( learnTime) # delay of 10 seconds before passing to the learning phase disp.close() # close the display """ ############################################################################################################################## ############################################################################################################################## ############################################################################################################################## ############################################################################################################################## """ ################# ################# # SAVE RESULTS # ################# ################# with open( 'retrieval_practice_results_final_test' + ID + '_' + AGE + '_' + SEXE +
class TestGLobalEventKeys(object): @classmethod def setup_class(self): self.win = Window([128, 128], winType='pyglet', pos=[50, 50], autoLog=False) @classmethod def teardown_class(self): self.win.close() def setup_method(self, test_method): # Disable auto-creation of shutdown key. prefs.general['shutdownKey'] = '' def _func(self, *args, **kwargs): return [args, kwargs] def test_shutdownKey_prefs(self): key = 'escape' modifiers = ('ctrl', 'alt') prefs.general['shutdownKey'] = key prefs.general['shutdownKeyModifiers'] = modifiers global_keys = event._GlobalEventKeys() e = list(global_keys)[0] assert key, modifiers == e assert global_keys[e].func == core.quit def test_add(self): key = 'a' func = self._func global_keys = event._GlobalEventKeys() global_keys.add(key=key, func=func) assert global_keys[key, ()].func == func assert global_keys[key, ()].name == func.__name__ def test_add_key_twice(self): key = 'a' func = self._func global_keys = event._GlobalEventKeys() global_keys.add(key=key, func=func) with pytest.raises(ValueError): global_keys.add(key=key, func=func) def test_add_name(self): key = 'a' name = 'foo' func = self._func global_keys = event._GlobalEventKeys() global_keys.add(key=key, func=func, name=name) assert global_keys[key, ()].name == name def test_add_args(self): key = 'a' func = self._func args = (1, 2, 3) global_keys = event._GlobalEventKeys() global_keys.add(key=key, func=func, func_args=args) assert global_keys[key, ()].func_args == args def test_add_kwargs(self): key = 'a' func = self._func kwargs = dict(foo=1, bar=2) global_keys = event._GlobalEventKeys() global_keys.add(key=key, func=func, func_kwargs=kwargs) assert global_keys[key, ()].func_kwargs == kwargs def test_add_args_and_kwargs(self): key = 'a' func = self._func args = (1, 2, 3) kwargs = dict(foo=1, bar=2) global_keys = event._GlobalEventKeys() global_keys.add(key=key, func=func, func_args=args, func_kwargs=kwargs) assert global_keys[key, ()].func_args == args assert global_keys[key, ()].func_kwargs == kwargs def test_add_invalid_key(self): key = 'foo' func = self._func global_keys = event._GlobalEventKeys() with pytest.raises(ValueError): global_keys.add(key=key, func=func) def test_add_invalid_modifiers(self): key = 'a' modifiers = ('foo', 'bar') func = self._func global_keys = event._GlobalEventKeys() with pytest.raises(ValueError): global_keys.add(key=key, modifiers=modifiers, func=func) def test_remove(self): keys = ['a', 'b', 'c'] modifiers = ('ctrl', ) func = self._func global_keys = event._GlobalEventKeys() [ global_keys.add(key=key, modifiers=modifiers, func=func) for key in keys ] global_keys.remove(keys[0], modifiers) with pytest.raises(KeyError): _ = global_keys[keys[0], modifiers] def test_remove_modifiers_list(self): key = 'a' modifiers = ['ctrl', 'alt'] func = self._func global_keys = event._GlobalEventKeys() global_keys.add(key=key, modifiers=modifiers, func=func) global_keys.remove(key, modifiers) with pytest.raises(KeyError): _ = global_keys[key, modifiers] def test_remove_invalid_key(self): key = 'a' global_keys = event._GlobalEventKeys() with pytest.raises(KeyError): global_keys.remove(key) def test_remove_all(self): keys = ['a', 'b', 'c'] func = self._func global_keys = event._GlobalEventKeys() [global_keys.add(key=key, func=func) for key in keys] global_keys.remove('all') assert len(global_keys) == 0 def test_getitem(self): key = 'escape' modifiers = ('ctrl', 'alt') func = self._func global_keys = event._GlobalEventKeys() global_keys.add(key=key, modifiers=modifiers, func=func) assert global_keys[key, modifiers] == global_keys._events[key, modifiers] def test_getitem_string(self): key = 'escape' func = self._func global_keys = event._GlobalEventKeys() global_keys.add(key=key, func=func) assert global_keys[key] == global_keys._events[key, ()] def test_getitem_modifiers_list(self): key = 'escape' modifiers = ['ctrl', 'alt'] func = self._func global_keys = event._GlobalEventKeys() global_keys.add(key=key, modifiers=modifiers, func=func) assert ( global_keys[key, modifiers] == global_keys._events[key, tuple(modifiers)]) def test_setitem(self): keys = 'a' modifiers = () global_keys = event._GlobalEventKeys() with pytest.raises(NotImplementedError): global_keys[keys, modifiers] = None def test_delitem(self): key = 'escape' modifiers = ('ctrl', 'alt') func = self._func global_keys = event._GlobalEventKeys() global_keys.add(key=key, modifiers=modifiers, func=func) del global_keys[key, modifiers] with pytest.raises(KeyError): _ = global_keys[key, modifiers] def test_delitem_string(self): key = 'escape' func = self._func global_keys = event._GlobalEventKeys() global_keys.add(key=key, func=func) del global_keys[key] with pytest.raises(KeyError): _ = global_keys[key] def test_len(self): prefs.general['shutdownKey'] = '' key = 'escape' func = self._func global_keys = event._GlobalEventKeys() assert len(global_keys) == 0 global_keys.add(key=key, func=func) assert len(global_keys) == 1 del global_keys[key, ()] assert len(global_keys) == 0 def test_event_processing(self): key = 'a' modifiers = 0 func = self._func args = (1, 2, 3) kwargs = dict(foo=1, bar=2) event.globalKeys.add(key=key, func=func, func_args=args, func_kwargs=kwargs) r = event._process_global_event_key(key, modifiers) assert r[0] == args assert r[1] == kwargs def test_index_keys(self): key = 'escape' modifiers = ('ctrl', 'alt') func = self._func global_keys = event._GlobalEventKeys() global_keys.add(key=key, modifiers=modifiers, func=func) index_key = global_keys.keys()[-1] assert index_key.key == key assert index_key.modifiers == modifiers
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 main(): # I set up my siplay size and background colour DISPSIZE = (1400, 800) BGC = (-1, -1, -1) # for my game I need to create some variables: score = 0 lives = 3 level = 1 mouse_x = 0 mouse_y = 0 # I create some objects: win = Window(size=DISPSIZE, units='pix', fullscr=False, color=BGC) mouse = Mouse(win) target = ImageStim(win, 'target.png', size=(420, 420)) # I will display three text stimuli to the player while playing the game: lives_count = TextStim( win, text=f'Lives = {lives}', height=35, color=(1, 0.2, 0.6), pos=(100, 330), ) score_count = TextStim(win, text=f'Score = {score}', height=35, color=(0.2, 0.2, 0.8), pos=(450, 330)) level_count = TextStim(win, text=f'Level = {level}', height=35, color=(1, -0.5, 1), pos=(850, 330)) # I define the messages to show the player the outcome of the game: you_have_lost = TextStim( win, text='Boo! Not a great game, pal... Get it together!', height=35, color=(0.2, 0.2, 0.8), pos=(250, 230)) you_have_won = TextStim(win, text='Yey! Well done, champ! Time to celebrate!', height=35, color=(0.2, 0.2, 0.8), pos=(250, 230)) # These are the images I use for the winning and loosing scenarios: looser = ImageStim(win, 'failed.jpg', pos=(0, -100), size=(420, 420)) winner = ImageStim(win, 'tiny_trash.jpg', pos=(0, -100), size=(420, 420)) # I introduce this dialog to save the user's ID: user_id_dialog = gui.Dlg(title="Target Game") user_id_dialog.addText('Please write your subject ID: a 4-digit code') user_id_dialog.addField('Subject ID:') ok_data = user_id_dialog.show() # show dialog and wait for OK or Cancel if not user_id_dialog.OK: print('user cancelled') # NOW THE GAME WILL START: # If enabled, intro will play: enable_intro = True if enable_intro: show_intro(win) # We create this list to save our results into target_hits_per_level = [ [], [], [], [], ] move_target_at_random_pos( target) # first the target is shown on the screen lives_timer = CountdownTimer( 5) # Level 1 starts with 5 sec to hit the target mouse_click_clock = Clock() reaction_time_clock = Clock() change_target = False while level < 4 and lives > 0: target.draw() target_x, target_y = target.pos lives_count.draw() score_count.draw() level_count.draw() win.flip() keys_pressed = getKeys() if 'q' in keys_pressed: break mouse_is_pressed = mouse.getPressed()[0] == True mouse_x, mouse_y = mouse.getPos() level_count.setText(f'Level = {level}') #if the player does not click, the target moves and the player looses a life if lives_timer.getTime() <= 0: lives -= 1 lives_count.setText(f'Lives = {lives}') mouse_in_target = None mouse_in_target_x = None mouse_in_target_y = None change_target = True # Check for a mouse click every 0.2s, so that we don't accept more than 1 # press on mouse hold if mouse_is_pressed and mouse_click_clock.getTime() > 0.2: mouse_click_clock.reset() change_target = True if mouse_clicked_in_target(mouse, target): mouse_in_target = True mouse_in_target_x = mouse_x - target_x mouse_in_target_y = mouse_y - target_y score += 1 score_count.setText(f'Score = {score}') else: lives -= 1 lives_count.setText(f'Lives = {lives}') mouse_in_target = False mouse_in_target_x = None mouse_in_target_y = None if change_target: mouse_click = { 'mouse_x': mouse_in_target_x, 'mouse_y': mouse_in_target_y, 'reaction_time': reaction_time_clock.getTime(), 'mouse_in_target': mouse_in_target, } target_hits_per_level[level - 1].append( mouse_click) # inddexes start from 0 --> level - 1 if score == 5: lives_timer.reset(3) level = 2 elif score == 10: lives_timer.reset(1) level = 3 elif score == 15: level = 4 move_target_at_random_pos(target) lives_timer.reset() reaction_time_clock.reset() change_target = False # Here we display the outcome of the game: if level == 4: you_have_won.draw() winner.draw() else: you_have_lost.draw() looser.draw() win.flip() wait(3) # Finally, we draw the overwivew for thr player draw_overview_target( win=win, level=1, target_pos=(-450, 0), text_pos=(50, 300), mouse_clicks_all_levels=target_hits_per_level, ) draw_overview_target( win=win, level=2, target_pos=(0, 0), text_pos=(450, 300), mouse_clicks_all_levels=target_hits_per_level, ) draw_overview_target( win=win, level=3, target_pos=(450, 0), text_pos=(850, 300), mouse_clicks_all_levels=target_hits_per_level, ) win.flip() wait(4) # The user has not clicked Cancel on the subject ID window if ok_data is not None: write_results(target_hits_per_level, 'results-' + ok_data[0] + '.csv') win.close()
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)
class Experiment: """ Base experiment class """ def __init__(self, exp_info, file_name): """ Initialize experiment - read XML file, setup window, connect to netstation and tobii exp_info - experiment information file_name - name of XML file containing experiment definition """ self.exp_info = exp_info self.name = None self.type = None self.num_blocks = 0 self.blocks = {} self.block_order = [] # Window to use wintype = 'pyglet' # use pyglet if possible, it's faster at event handling # Add 14cm to distance - this is distance from eyetracker to monitor mon = monitors.Monitor(exp_info['monitor'], distance=float(exp_info['monitor distance'])) self.win = Window( [1280, 1024], monitor=mon, screen=SCREEN, units="deg", fullscr=True, #fullscr=False, color=[-1, -1, -1], winType=wintype) self.win.setMouseVisible(False) event.clearEvents() # Measure frame rate self.mean_ms_per_frame, std_ms_per_frame, median_ms_per_frame = visual.getMsPerFrame(self.win, nFrames=60, showVisual=True) self.debug_sq=None if exp_info['monitor']=='tobii': self.debug_sq=psychopy.visual.Rect(self.win, width=30, height=30, units='pix') self.debug_sq.setFillColor((1,1,1)) self.debug_sq.setPos((630,-500)) # Compute distractor duration in frames based on frame rate distractor_duration_frames = int(2000.0/self.mean_ms_per_frame) # Initialize set of distractors self.distractor_set = DistractorSet(os.path.join(DATA_DIR, 'images', 'distractors', 'space'), os.path.join(DATA_DIR, 'sounds', 'distractors'), os.path.join(DATA_DIR, 'movies', 'distractors'), os.path.join(DATA_DIR, 'images', 'distractors', 'star-cartoon.jpg'), distractor_duration_frames, self.win) # Connect to nestation self.ns = None if exp_info['eeg']: # connect to netstation self.ns = egi.Netstation() ms_localtime = egi.ms_localtime self.eye_tracker = None mouse_visible = False if exp_info['eyetracking source'] == 'tobii': # Initialize eyetracker self.eye_tracker = TobiiController(self.win) self.eye_tracker.waitForFindEyeTracker() self.eye_tracker.activate(EYETRACKER_NAME) elif exp_info['eyetracking source'] == 'mouse': mouse_visible = True # Initialize mouse self.mouse = event.Mouse(visible=mouse_visible, win=self.win) self.gaze_debug=None if self.exp_info['debug mode']: self.gaze_debug=psychopy.visual.Circle(self.win, radius=1, fillColor=(1.0,-1.0,-1.0)) self.read_xml(file_name) # Initialize netstation and eyetracker self.initialize() def calibrate_eyetracker(self): """ Run eyetracker calibration routine """ retval = 'retry' while retval == 'retry': waitkey = True retval = None can_accept = self.eye_tracker.doCalibration(EYETRACKER_CALIBRATION_POINTS) while waitkey: for key in psychopy.event.getKeys(): if can_accept: num_entered=True try: calib_idx=int(key) can_accept = self.eye_tracker.doCalibration([EYETRACKER_CALIBRATION_POINTS[calib_idx-1]], calib=self.eye_tracker.calib) except: num_entered=False if not num_entered and key == 'a': retval = 'accept' waitkey = False elif key == 'r': retval = 'retry' waitkey = False elif key == 'escape': retval = 'abort' waitkey = False self.eye_tracker.calresult.draw() self.eye_tracker.calresultmsg.draw() for point_label in self.eye_tracker.point_labels: point_label.draw() self.win.flip() if retval == 'abort': self.eye_tracker.closeDataFile() self.eye_tracker.destroy() self.win.close() core.quit() def initialize(self): """ Start netstation recording, calibrate eyetracker """ if self.ns is not None: try: self.ns.initialize(NETSTATION_IP, 55513) self.ns.BeginSession() self.ns.StartRecording() except: print('Could not connect with NetStation!') # Initialize logging logfile = os.path.join(DATA_DIR, 'logs', self.exp_info['experiment'], '%s_%s_%s.log' % (self.exp_info['child_id'], self.exp_info['date'], self.exp_info['session'])) if self.eye_tracker is not None: self.eye_tracker.setDataFile(logfile, self.exp_info) else: datafile = open(logfile, 'w') datafile.write('Recording date:\t' + datetime.datetime.now().strftime('%Y/%m/%d') + '\n') datafile.write('Recording time:\t' + datetime.datetime.now().strftime('%H:%M:%S') + '\n') datafile.write('Recording resolution\t%d x %d\n' % tuple(self.win.size)) for key, data in self.exp_info.iteritems(): datafile.write('%s:\t%s\n' % (key, data)) datafile.close() # Create random block order n_repeats = int(self.num_blocks/len(self.blocks.keys())) for i in range(n_repeats): subblock_order = copy.copy(self.blocks.keys()) np.random.shuffle(subblock_order) self.block_order.extend(subblock_order) # Synch with netstation in between trials if self.ns is not None: self.ns.sync() if self.eye_tracker is not None: self.eye_tracker.startTracking() def close(self): """ Disconnect from eyetracker and netstation """ if self.eye_tracker is not None: self.eye_tracker.stopTracking() self.eye_tracker.closeDataFile() # close netstation connection if self.ns: self.ns.StopRecording() self.ns.EndSession() self.ns.finalize() self.win.close() core.quit() if self.eye_tracker is not None: self.eye_tracker.destroy() def run(self): """ Run task ns - netstation connection """ pass def read_xml(self, file_name): """ Read experiment definition file :param file_name: file to read definition from """ pass
class Test_textbox(object): def setup_class(self): self.win = Window([128, 128], pos=[50, 50], allowGUI=False, autoLog=False) def teardown_class(self): self.win.close() def test_glyph_rendering(self): textbox = TextBox2(self.win, "", "Arial", pos=(0, 0), size=(1, 1), letterHeight=0.1, units='height') # Add all Noto Sans fonts to cover widest possible base of handles characters textbox.fontMGR.addGoogleFonts([ "Noto Sans", "Noto Sans HK", "Noto Sans JP", "Noto Sans KR", "Noto Sans SC", "Noto Sans TC", "Niramit", "Indie Flower" ]) # Some exemplar text to test basic TextBox rendering exemplars = [ # An English pangram { "text": "A PsychoPy zealot knows a smidge of wx, but JavaScript is the question.", "font": "Noto Sans", "screenshot": "textbox_exemplar_1.png" }, # The same pangram in IPA { "text": "ə saɪkəʊpaɪ zɛlət nəʊz ə smidge ɒv wx, bʌt ˈʤɑːvəskrɪpt ɪz ðə ˈkwɛsʧən", "font": "Noto Sans", "screenshot": "textbox_exemplar_2.png" }, # The same pangram in Hangul { "text": "아 프시초피 제알롣 크노W스 아 s믿게 오f wx, 붇 자v앗c립t 잇 테 q왯디온", "font": "Noto Sans KR", "screenshot": "textbox_exemplar_3.png" }, # A noticeably non-standard font { "text": "A PsychoPy zealot knows a smidge of wx, but JavaScript is the question.", "font": "Indie Flower", "screenshot": "textbox_exemplar_4.png", } ] # Some text which is likely to cause problems if something isn't working tykes = [ # Text which doesn't render properly on Mac (Issue #3203) { "text": "कोशिकायें", "font": "Noto Sans", "screenshot": "textbox_tyke_1.png" }, # Thai text which old Text component couldn't handle due to Pyglet { "text": "ขาว แดง เขียว เหลือง ชมพู ม่วง เทา", "font": "Niramit", "screenshot": "textbox_tyke_2.png" } ] # Test each case and compare against screenshot for case in exemplars + tykes: textbox.reset() textbox.fontMGR.addGoogleFont(case['font']) textbox.font = case['font'] textbox.text = case['text'] self.win.flip() textbox.draw() if case['screenshot']: # Uncomment to save current configuration as desired #self.win.getMovieFrame(buffer='back').save(Path(utils.TESTS_DATA_PATH) / case['screenshot']) utils.compareScreenshot(Path(utils.TESTS_DATA_PATH) / case['screenshot'], self.win, crit=20) def test_basic(self): pass def test_something(self): # to-do: test visual display, char position, etc pass
def run(): from psychopy.visual import Window, TextStim, ImageStim from psychopy.core import wait from psychopy.event import getKeys, waitKeys, clearEvents from numpy.random import shuffle from psychopy import gui import numpy as np import random ''' Next, we want to create the window in which the experiment takes place and open up a log file. ''' #create window DISPSIZE = (1000, 700) #in case that doesn't work, use DISPSIZE = (1000,700) #for windows only DISPSIZE = (GetSystemMetrics(0),GetSystemMetrics(1)) BGC = 'white' #log file log = open('logfileprisonersdilemma.txt', 'w') log.write('trial round\topponent strategy\participant strategy\n' ) #insert headers of what should be logged ''' Now we create the objects we will use. The code here is in the same order as it will be used once we start the game. We'll start with the objects for introduction and instruction. ''' win = Window(size=DISPSIZE, units='pix', color=BGC, fullscr=False) backgroundimage = ImageStim(win, 'prisonwall.png', size=(1000, 1000)) #objects for introduction and instruction #introduction text subject_name = 'Kathi' introstr = f''' Welcome to the prisoner's dilemma game, {subject_name}! This game will take about 10 minutes. Please wait for further instructions after you've finished. Press 'space' to continue. ''' introtxt = TextStim(win, text=introstr, font='Arial', height=30, color='white', wrapWidth=400) #instruction text inststr = ''' Imagine you are one of two people being questioned about the same crime. They are each talking to the interrogator separately. The interrogator gives each person the same deal: they can choose to vouch for the other person’s innocence (COOPERATING) or rat them out (DEFECTING). And of course, there’s a twist. If both people cooperate with each other, they’ll each get 3 months off their sentence, but if the first person vouches for the second person, and the second person rats them out, the first person will get no time off their sentence and the second person will get 5 months off their time. Lastly, if they both rat each other out, they each get 1 month off their time. Press 'space' to start the game. ''' insttxt = TextStim(win, text=inststr, font='Arial', height=25, color='white', wrapWidth=900, pos=(000, -70)) ''' Participants as well as their opponent are represented in the game by avatars. These objects are used for explaining this and defining the image objects. These are avatars you can use created with www.hexatar.com, however you are free to use your own. This is redunant since I coulnd't figure it out, but I wanted to keep it for the future. ''' #objects for representing participants, images #telling people to choose avatar avatstr = ''' Please choose one of these avatars to represent you in the game. Just press the key that responds to the letter next to the image. ''' avattxt = TextStim(win, text=avatstr, font='Arial', height=20, color='black', wrapWidth=900, pos=(000, -300)) #avatars: created with http://www.hexatar.com partoverview = ImageStim( win, image='partchoice.png', size=(500, 500) ) #start screen from which participant chooses an avatar to represent them in the game ''' Here we define objects during the actual game: instructions about controls, a connecting screen, text to be displayed after every round and a goodbye screen. ''' #objects for during the game, in the loop sentcountp = 35 sentcounto = 35 #info about controls/gameplay, should be displayed on the bottom left during the game controlinfo = TextStim( win, text= '''Press the left arrow to defect and the right arrow to cooperate''', font='Arial', height=20, color='white', wrapWidth=900, pos=(000, -300)) # txt within game connectstr = f'Your prison sentence is {sentcountp} months.\n' 'Your partner`s sentence is {sentcounto} months.\n' 'The authorities are still not sure what to do with the two of you. Therefore, you and your partner\n' 'will be interrogated again by a different police officer. Again you have the choice to cooperate or defect.' connecttxt = TextStim(win, text=connectstr, font='Arial', height=20, color='black', wrapWidth=400) cooptxt = TextStim(win, text='Your opponent chose to cooperate.', font='Arial', height=20, color='black', wrapWidth=400) deftxt = TextStim(win, text='Your opponent chose to defect.', font='Arial', height=20, color='black', wrapWidth=400) #goodbye screen goodbyestr = ('The game is now over.\n' + 'Thank you for playing!\n') goodbye = TextStim(win, text=goodbyestr, font='Arial', height=40, color='black', wrapWidth=500) strategy = [1, 2, 3, 4, 5] #random assignment of opponents strategy shuffle(strategy) randostrategy = [1, 2] #for strategy 3 condlist = [1, 2, 3] #1 is in-group, 2 is outgroup, 3 is anon ntrials = 7 oppstrategy = '...' #for updating the sentence counter #function that determines participants sentence and adds it to the sentence counter #add unitest here ''' During the experiment, we need to update how many months the participant has to spend in prison now, depending on the choice they made and the choice their opponent made. For doing this during the trial loop, we write the function sentencetracker, that checks the participants choice (either cooperating or defecting) as well as the opponents strategy and updates the sentence based on this. As a reminder: left arrow to defect and the right arrow to cooperate. ''' def sentencetracker(sentcounto, sentcountp, response, oppstrategy): if response == 'right' and oppstrategy == 'cooperation': #both cooperate sentcounto -= 3 sentcountp -= 3 elif response == 'right' and oppstrategy == 'defect': #participant wants to cooperate, but opponent rats them out sentcountp -= 5 elif response == 'left' and oppstrategy == 'cooperation': #participant rats opponent out, but opponent cooperates sentcounto -= 5 else: #response == 'left' & oppstrategy == 'defect': #both defect sentcountp -= 1 sentcounto -= 1 return sentcounto, sentcountp ''' Time to start the experiment! Let's start with the introduction. After that, we explain how it works. We also define the image stimuli for displaying the avatars. ''' backgroundimage.draw() #background prisonwall introtxt.draw() win.flip() waitKeys(keyList=['space' ]) #wait until participant pressed space to continue clearEvents() ''' that part worked but couldn't figure out avatar assignment :() #choosing avatar that represents participant avattxt.draw() partoverview.draw() win.flip() aresponse = waitKeys(keyList = ['a','b','c','d']) shuffle(condlist) #randomly assigning condition (in group, out group, anonymous opponent) #condition = condlist[1] ''' opponent = ImageStim(win, image="opponent1.png", pos=(300, 000)) participant = ImageStim(win, image='partA.png', pos=(-300, 000)) ''' was supposed to assign condition and set the right avatars, but doesn't work condition = np.random.randint(1,3) if condition == 1: #playing with an in-group member print('in-group condition') if aresponse == 'a': #participant is female caucasian opponent = ImageStim(win, image="opponent2.png", pos=(300,000)) participant = ImageStim(win, image='partA.png', pos=(-300, 000)) elif aresponse == 'c': #participant is male caucasian opponent = ImageStim(win, image="opponent2.png", pos=(300,000)) participant = ImageStim(win, image='partC.png', pos=(-300, 000)) elif aresponse == 'b': #participant is female non-caucasian opponent = ImageStim(win, image="opponent1.png", pos=(300,000)) participant = ImageStim(win, image='partB.png', pos=(-300, 000)) elif aresponse == 'd': #option d, participant is male non-caucasian opponent = ImageStim(win, image="opponent1.png", pos=(300,000)) participant = ImageStim(win, image='partD.png', pos=(-300, 000)) elif condition == 2: #playing with an out-group member print('out-group condition') if aresponse == 'a': #participant is female caucasian opponent = ImageStim(win, image="opponent1.png", pos=(300,000)) participant = ImageStim(win, image='partA.png', pos=(-300, 000)) elif aresponse == 'c': #participant is male caucasian opponent = ImageStim(win, image="opponent1.png", pos=(300,000)) participant = ImageStim(win, image='partC.png', pos=(-300, 000)) elif aresponse == 'b': #participant is female non-caucasian opponent = ImageStim(win, image="opponent2.png", pos=(300,000)) participant = ImageStim(win, image='partB.png', pos=(-300, 000)) elif aresponse == 'd': #option d, participant is male non-caucasian opponent = ImageStim(win, image="opponent2.png", pos=(300,000)) participant = ImageStim(win, image='partD.png', pos=(-300, 000)) elif condition == 3: #playing with no information about opponent print('anonymous condition') opponent = ImageStim(win, image="anonymous.jpg", pos=(300,000)) if aresponse == 'a': #participant is female caucasian participant = ImageStim(win, image='partA.png', pos=(-300, 000)) elif aresponse == 'c': #participant is male caucasian participant = ImageStim(win, image='partC.png', pos=(-300, 000)) elif aresponse == 'b': #participant is female non-caucasian participant = ImageStim(win, image='partB.png', pos=(-300, 000)) elif aresponse == 'd': #option d, participant is male non-caucasian participant = ImageStim(win, image='partD.png', pos=(-300, 000)) clearEvents() ''' #explaining the game backgroundimage.draw() insttxt.draw() win.flip() waitKeys( keyList=['space'] ) #wait until participant read instructions and pressed space to start the trial loop strategy = np.random.randint(1, 5) ''' Sentence count for the participant and the opponent. You can change this according to your needs, however please note that it's 35 because we defined 7 trial rounds and the maximum the sentence can change during each round is 5. 7 x 5 is 35, so this is set to prevent the sentence count from going below 0. ''' sentcounto = 35 sentcountp = 35 ''' This is the trial loop, it's now set to 7 rounds (see object ntrials = 7 above to change it). Opponents stick to the same strategy through all 7 rounds. Sentence count as well as strategies are printed in the console as well as logged in the log file. ''' i = 0 for i in range(ntrials): backgroundimage.draw() i += 1 log.write(str(i)) log.write('\t') #opponent always cooperates if strategy == 1: opponent.draw() participant.draw() controlinfo.draw() win.flip() log.write('strategy cooperation\t') response = waitKeys(keyList=['left', 'right']) if response == 'left': log.write('defect\n') elif response == 'right': log.write('cooperate\n') cooptxt.draw() win.flip() wait(4) #seconds oppstrategy = 'cooperation' print('strategy cooperation') sentcounto, sentcountp = sentencetracker(sentcounto, sentcountp, response, oppstrategy) print(sentcounto, sentcountp) connectstr = f'''Your prison sentence is {sentcountp} months. Your partner`s sentence is {sentcounto} months. The authorities are still not sure what to do with the two of you. Therefore, you and your partner will be interrogated again by a different police officer. Again you have the choice to cooperate or defect.''' connecttxt = TextStim(win, text=connectstr, font='Arial', height=20, color='black', wrapWidth=400) connecttxt.draw() win.flip() wait(7) #opponent always defects elif strategy == 2: opponent.draw() participant.draw() controlinfo.draw() win.flip() log.write('strategy defect\t') response = waitKeys(keyList=['left', 'right']) if response == 'left': log.write('defect\n') elif response == 'right': log.write('cooperate\n') deftxt.draw() win.flip() wait(4) #seconds oppstrategy = 'defect' print('strategy defect') sentcounto, sentcountp = sentencetracker(sentcounto, sentcountp, response, oppstrategy) print(sentcounto, sentcountp) connectstr = f'''Your prison sentence is {sentcountp} months. Your partner`s sentence is {sentcounto} months. The authorities are still not sure what to do with the two of you. Therefore, you and your partner will be interrogated again by a different police officer. Again you have the choice to cooperate or defect.''' connecttxt = TextStim(win, text=connectstr, font='Arial', height=20, color='black', wrapWidth=400) connecttxt.draw() win.flip() wait(7) #opponent cooperates or defects on a random basis elif strategy == 3: rando = np.random.randint(1, 3) print('rando') if rando == 1: opponent.draw() participant.draw() controlinfo.draw() win.flip() log.write('strategy random cooperation\t') response = waitKeys(keyList=['left', 'right']) if response == 'left': log.write('defect\n') elif response == 'right': log.write('cooperate\n') cooptxt.draw() win.flip() wait(4) oppstrategy = 'cooperation' print('rando coop') sentcounto, sentcountp = sentencetracker( sentcounto, sentcountp, response, oppstrategy) print(sentcounto, sentcountp) connectstr = f'''Your prison sentence is {sentcountp} months. Your partner`s sentence is {sentcounto} months. The authorities are still not sure what to do with the two of you. Therefore, you and your partner will be interrogated again by a different police officer. Again you have the choice to cooperate or defect.''' connecttxt = TextStim(win, text=connectstr, font='Arial', height=20, color='black', wrapWidth=400) connecttxt.draw() win.flip() wait(7) else: opponent.draw() participant.draw() controlinfo.draw() win.flip() log.write('strategy random defect\t') wait(4) response = waitKeys(keyList=['left', 'right']) if response == 'left': log.write('defect\n') elif response == 'right': log.write('cooperate\n') deftxt.draw() win.flip() wait(4) #seconds oppstrategy = 'defect' print('rando defect') sentcounto, sentcountp = sentencetracker( sentcounto, sentcountp, response, oppstrategy) print(sentcounto, sentcountp) connectstr = f'''Your prison sentence is {sentcountp} months. Your partner`s sentence is {sentcounto} months. The authorities are still not sure what to do with the two of you. Therefore, you and your partner will be interrogated again by a different police officer. Again you have the choice to cooperate or defect.''' connecttxt = TextStim(win, text=connectstr, font='Arial', height=20, color='black', wrapWidth=400) connecttxt.draw() win.flip() wait(7) elif strategy == 4: print('nice tit for tat') log.write('strategy nice tit for tat\t') if i == 1: opponent.draw() participant.draw() controlinfo.draw() win.flip() response = waitKeys(keyList=['left', 'right']) if response == 'left': log.write('defect\n') elif response == 'right': log.write('cooperate\n') cooptxt.draw() win.flip() wait(4) oppstrategy = 'cooperation' print('cooperation') sentcounto, sentcountp = sentencetracker( sentcounto, sentcountp, response, oppstrategy) print(sentcounto, sentcountp) connectstr = f'''Your prison sentence is {sentcountp} months. Your partner`s sentence is {sentcounto} months. The authorities are still not sure what to do with the two of you. Therefore, you and your partner will be interrogated again by a different police officer. Again you have the choice to cooperate or defect.''' connecttxt = TextStim(win, text=connectstr, font='Arial', height=20, color='black', wrapWidth=400) connecttxt.draw() win.flip() wait(7) #seconds elif i > 1: if response == 'right': opponent.draw() participant.draw() controlinfo.draw() win.flip() response = waitKeys(keyList=['left', 'right']) if response == 'left': log.write('defect\n') elif response == 'right': log.write('cooperate\n') cooptxt.draw() win.flip() wait(4) oppstrategy = 'cooperation' print('cooperation') sentcounto, sentcountp = sentencetracker( sentcounto, sentcountp, response, oppstrategy) print(sentcounto, sentcountp) connectstr = f'''Your prison sentence is {sentcountp} months. Your partner`s sentence is {sentcounto} months. The authorities are still not sure what to do with the two of you. Therefore, you and your partner will be interrogated again by a different police officer. Again you have the choice to cooperate or defect.''' connecttxt = TextStim(win, text=connectstr, font='Arial', height=20, color='black', wrapWidth=400) connecttxt.draw() win.flip() wait(7) #seconds elif response == 'left': opponent.draw() participant.draw() controlinfo.draw() win.flip() response = waitKeys(keyList=['left', 'right']) if response == 'left': log.write('defect\n') elif response == 'right': log.write('cooperate\n') deftxt.draw() win.flip() wait(4) oppstrategy = 'defect' print('defect') sentcounto, sentcountp = sentencetracker( sentcounto, sentcountp, response, oppstrategy) print(sentcounto, sentcountp) connectstr = f'''Your prison sentence is {sentcountp} months. Your partner`s sentence is {sentcounto} months. The authorities are still not sure what to do with the two of you. Therefore, you and your partner will be interrogated again by a different police officer. Again you have the choice to cooperate or defect.''' connecttxt = TextStim(win, text=connectstr, font='Arial', height=20, color='black', wrapWidth=400) connecttxt.draw() win.flip() wait(7) #seconds elif strategy == 5: print('suspicious tit for tat') log.write('strategy suspicious tit for tat\t') if i == 1: opponent.draw() participant.draw() controlinfo.draw() win.flip() response = waitKeys(keyList=['left', 'right']) if response == 'left': log.write('defect\n') elif response == 'right': log.write('cooperate\n') deftxt.draw() win.flip() wait(4) oppstrategy = 'defect' print('defect') sentcounto, sentcountp = sentencetracker( sentcounto, sentcountp, response, oppstrategy) print(sentcounto, sentcountp) connectstr = f'''Your prison sentence is {sentcountp} months. Your partner`s sentence is {sentcounto} months. The authorities are still not sure what to do with the two of you. Therefore, you and your partner will be interrogated again by a different police officer. Again you have the choice to cooperate or defect.''' connecttxt = TextStim(win, text=connectstr, font='Arial', height=20, color='black', wrapWidth=400) connecttxt.draw() win.flip() wait(7) #seconds elif i > 1: if response == 'right': opponent.draw() participant.draw() controlinfo.draw() win.flip() response = waitKeys(keyList=['left', 'right']) if response == 'left': log.write('defect\n') elif response == 'right': log.write('cooperate\n') cooptxt.draw() win.flip() wait(4) oppstrategy = 'cooperation' print('cooperation') sentcounto, sentcountp = sentencetracker( sentcounto, sentcountp, response, oppstrategy) print(sentcounto, sentcountp) connectstr = f'''Your prison sentence is {sentcountp} months. Your partner`s sentence is {sentcounto} months. The authorities are still not sure what to do with the two of you. Therefore, you and your partner will be interrogated again by a different police officer. Again you have the choice to cooperate or defect.''' connecttxt = TextStim(win, text=connectstr, font='Arial', height=20, color='black', wrapWidth=400) connecttxt.draw() win.flip() wait(7) #seconds elif response == 'left': opponent.draw() participant.draw() controlinfo.draw() win.flip() response = waitKeys(keyList=['left', 'right']) if response == 'left': log.write('defect\n') elif response == 'right': log.write('cooperate\n') deftxt.draw() win.flip() wait(4) oppstrategy = 'defect' print('defect') sentcounto, sentcountp = sentencetracker( sentcounto, sentcountp, response, oppstrategy) print(sentcounto, sentcountp) connectstr = f'''Your prison sentence is {sentcountp} months. Your partner`s sentence is {sentcounto} months. The authorities are still not sure what to do with the two of you. Therefore, you and your partner will be interrogated again by a different police officer. Again you have the choice to cooperate or defect.''' connecttxt = TextStim(win, text=connectstr, font='Arial', height=20, color='black', wrapWidth=400) connecttxt.draw() win.flip() wait(7) #seconds #exit early: quitt = getKeys(keyList='q') if len(quitt) > 0: break goodbye.draw() win.flip() wait(3) win.close() log.close()
def close(self): self.server.stop() Window.close(self)
def RunButtonDiagnostic(self): ''' Run a test to make sure that the buttons are being recorded correctly Creates a temporary window and accepts keypresses Shows the buttonpresses with a highlighting circle ''' if (self.screenType == 'Windowed'): screenSelect = False elif (self.screenType == 'Fullscreen'): screenSelect = True window = Window(fullscr=screenSelect, units='pix', color='White', allowGUI=False) indRadius = 100 tHeight = 2 * indRadius / 5 posC1 = (-window.size[0] / 4, 0) posC2 = (window.size[0] / 4, 0) #creating circle opjects circ1 = Circle(window, indRadius, lineColor='White', lineWidth=6, pos=posC1) #training and p1 circles circ2 = Circle(window, indRadius, lineColor='White', lineWidth=6, pos=posC2) #creating text objects for cicles trtext1 = TextStim(window, " Button 1", color='White', height=tHeight, pos=posC1) #training text trtext2 = TextStim(window, " Button 2", color='White', height=tHeight, pos=posC2) #List of final circle and text objects trCircs = [circ1, circ2] trTexts = [trtext1, trtext2] trTxt = TextStim( window, "This is a test to ensure that the buttons are being recorded correctly.\n Press each button to make sure it is being recorded correctly.\n Press escape to move on", pos=(0, window.size[1] / 4), color="Black", height=40, wrapWidth=0.8 * window.size[0]) for circ in trCircs: circ.fillColor = 'Gray' trTxt.draw(window) for circ in trCircs: circ.draw(window) for text in trTexts: text.draw(window) window.flip() while 1: key = waitKeys(keyList=self.inputButtons + [self.pauseButton, 'escape'])[0] for circ in trCircs: circ.fillColor = 'Gray' if key == 'escape' or key == self.pauseButton: #print "Press ecape one more time\n" break elif key == self.inputButtons[0]: trCircs[0].fillColor = 'Green' elif key == self.inputButtons[1]: trCircs[1].fillColor = 'Green' trTxt.draw(window) for circ in trCircs: circ.draw(window) for text in trTexts: text.draw(window) window.flip() window.flip() window.close()
wait((mask_backw_durat-0.008)) #display the mask for the requested duration (minus half a frame) win.flip() #clear screen (either after backward mask or after target) log.write(f'{trial_number}\t') #log trial number, before we get to task-specific logging #Implement the tasks (insofar as requested) #Detection task if task_detec == True: detec_instructions.draw() key_detec = waitKeys(keyList=[button_detec_pres, button_detec_abs]) #get the relevant pressed key log.write('{key_detec}\t') #log the key press #Localization task if task_local == True: local_instructions.draw() key_local = waitKeys(keyList=[button_local_left, button_local_right]) log.write('{key_local}\t') #Discrimination task if task_disc == True: disc_instructions.draw() key_disc = waitKeys(keyList=[button_disc_A, button_disc_B]) log.write('{key_disc}\t') log.write('\n') #after the tasks, go to the next row in the log (for the next trial) win.close() #after the participant made it through all trials, shut down the screen log.close() #and close the log
resplist = waitKeys(keyList=['left', 'right'], timeStamped=True) presstime = stopwatch.getTime() response, presstime = resplist[0] RT = presstime - taronset if RT < TARTIME: if response == trial2['tarside']: correct = 1 else: correct = 0 elif RT >= TARTIME: RT = '_NR_' correct = 2 ##Give feedback fbstim[correct].draw() disp.flip() wait(FEEDBACKTIME) ###LOGFILE line=[trial2['block'],trial2['tarside'],\ trial2['congr'],response,correct,RT] line = map(str, line) line = '\t'.join(line) line += '\n' log.write(line) line += '\n' log.close() disp.close()
class Test_textbox(_TestColorMixin, _TestUnitsMixin): def setup(self): self.win = Window((128, 128), pos=(50, 50), monitor="testMonitor", allowGUI=False, autoLog=False) self.error = _BaseErrorHandler() self.textbox = TextBox2( self.win, "A PsychoPy zealot knows a smidge of wx, but JavaScript is the question.", "Noto Sans", alignment="top left", lineSpacing=1, pos=(0, 0), size=(1, 1), units='height', letterHeight=0.1, colorSpace="rgb") self.obj = self.textbox # point to textbox for mixin tests # Pixel which is the border color self.borderPoint = (0, 0) self.borderUsed = True # Pixel which is the fill color self.fillPoint = (2, 2) self.fillUsed = True # Textbox foreground is too unreliable due to fonts for pixel analysis self.foreUsed = False def teardown(self): self.win.close() def test_glyph_rendering(self): # Prepare textbox self.textbox.colorSpace = 'rgb' self.textbox.color = 'white' self.textbox.fillColor = (0, 0, 0) self.textbox.borderColor = None self.textbox.opacity = 1 # Add all Noto Sans fonts to cover widest possible base of handles characters for font in [ "Noto Sans", "Noto Sans HK", "Noto Sans JP", "Noto Sans KR", "Noto Sans SC", "Noto Sans TC", "Niramit", "Indie Flower" ]: self.textbox.fontMGR.addGoogleFont(font) # Some exemplar text to test basic TextBox rendering exemplars = [ # An English pangram { "text": "A PsychoPy zealot knows a smidge of wx, but JavaScript is the question.", "font": "Noto Sans", "size": 16, "screenshot": "exemplar_1.png" }, # The same pangram in IPA { "text": "ə saɪkəʊpaɪ zɛlət nəʊz ə smidge ɒv wx, bʌt ˈʤɑːvəskrɪpt ɪz ðə ˈkwɛsʧən", "font": "Noto Sans", "size": 16, "screenshot": "exemplar_2.png" }, # The same pangram in Hangul { "text": "아 프시초피 제알롣 크노W스 아 s믿게 오f wx, 붇 자v앗c립t 잇 테 q왯디온", "font": "Noto Sans KR", "size": 16, "screenshot": "exemplar_3.png" }, # A noticeably non-standard font { "text": "A PsychoPy zealot knows a smidge of wx, but JavaScript is the question.", "font": "Indie Flower", "size": 16, "screenshot": "exemplar_4.png", } ] # Some text which is likely to cause problems if something isn't working tykes = [ # Text which doesn't render properly on Mac (Issue #3203) { "text": "कोशिकायें", "font": "Noto Sans", "size": 16, "screenshot": "tyke_1.png" }, # Thai text which old Text component couldn't handle due to Pyglet { "text": "ขาว แดง เขียว เหลือง ชมพู ม่วง เทา", "font": "Niramit", "size": 16, "screenshot": "tyke_2.png" }, # Text which had the top cut off { "text": "โฬิปื้ด็ลู", "font": "Niramit", "size": 36, "screenshot": "cutoff_top.png" }, ] # Test each case and compare against screenshot for case in exemplars + tykes: self.textbox.reset() self.textbox.fontMGR.addGoogleFont(case['font']) self.textbox.letterHeight = layout.Size(case['size'], "pix", self.win) self.textbox.font = case['font'] self.textbox.text = case['text'] self.win.flip() self.textbox.draw() if case['screenshot']: # Uncomment to save current configuration as desired filename = "textbox_{}_{}".format(self.textbox._lineBreaking, case['screenshot']) #self.win.getMovieFrame(buffer='back').save(Path(utils.TESTS_DATA_PATH) / filename) utils.compareScreenshot(Path(utils.TESTS_DATA_PATH) / filename, self.win, crit=20) def test_colors(self): # Do base tests _TestColorMixin.test_colors(self) # Do own custom tests self.textbox.text = "A PsychoPy zealot knows a smidge of wx, but JavaScript is the question." # Some exemplar text to test basic colors exemplars = [ # White on black in rgb { "color": (1, 1, 1), "fillColor": (-1, -1, -1), "borderColor": (-1, -1, -1), "space": "rgb", "screenshot": "colors_WOB.png" }, # White on black in named { "color": "white", "fillColor": "black", "borderColor": "black", "space": "rgb", "screenshot": "colors_WOB.png" }, # White on black in hex { "color": "#ffffff", "fillColor": "#000000", "borderColor": "#000000", "space": "hex", "screenshot": "colors_WOB.png" }, { "color": "red", "fillColor": "yellow", "borderColor": "blue", "space": "rgb", "screenshot": "colors_exemplar1.png" }, { "color": "yellow", "fillColor": "blue", "borderColor": "red", "space": "rgb", "screenshot": "colors_exemplar2.png" }, { "color": "blue", "fillColor": "red", "borderColor": "yellow", "space": "rgb", "screenshot": "colors_exemplar3.png" }, ] # Some colors which are likely to cause problems if something isn't working tykes = [ # Text only { "color": "white", "fillColor": None, "borderColor": None, "space": "rgb", "screenshot": "colors_tyke1.png" }, # Fill only { "color": None, "fillColor": "white", "borderColor": None, "space": "rgb", "screenshot": "colors_tyke2.png" }, # Border only { "color": None, "fillColor": None, "borderColor": "white", "space": "rgb", "screenshot": "colors_tyke3.png" }, ] # Test each case and compare against screenshot for case in exemplars + tykes: # Raise error if case spec does not contain all necessary keys if not all( key in case for key in ["color", "fillColor", "borderColor", "space", "screenshot"]): raise KeyError( f"Case spec for test_colors in class {self.__class__.__name__} ({__file__}) invalid, test cannot be run." ) # Apply params from case spec self.textbox.colorSpace = case['space'] self.textbox.color = case['color'] self.textbox.fillColor = case['fillColor'] self.textbox.borderColor = case['borderColor'] for lineBreaking in ('default', 'uax14'): self.win.flip() self.textbox.draw() if case['screenshot']: # Uncomment to save current configuration as desired filename = "textbox_{}_{}".format(self.textbox._lineBreaking, case['screenshot']) # self.win.getMovieFrame(buffer='back').save(Path(utils.TESTS_DATA_PATH) / filename) utils.compareScreenshot(Path(utils.TESTS_DATA_PATH) / filename, self.win, crit=20)
class TestGLobalEventKeys(object): @classmethod def setup_class(self): self.win = Window([128, 128], winType='pyglet', pos=[50, 50], autoLog=False) @classmethod def teardown_class(self): self.win.close() def setup_method(self, test_method): # Disable auto-creation of shutdown key. prefs.general['shutdownKey'] = '' def _func(self, *args, **kwargs): return [args, kwargs] def test_shutdownKey_prefs(self): key = 'escape' modifiers = ('ctrl', 'alt') prefs.general['shutdownKey'] = key prefs.general['shutdownKeyModifiers'] = modifiers global_keys = event._GlobalEventKeys() e = list(global_keys)[0] assert key, modifiers == e assert global_keys[e].func == core.quit def test_add(self): key = 'a' func = self._func global_keys = event._GlobalEventKeys() global_keys.add(key=key, func=func) assert global_keys[key, ()].func == func assert global_keys[key, ()].name == func.__name__ def test_add_key_twice(self): key = 'a' func = self._func global_keys = event._GlobalEventKeys() global_keys.add(key=key, func=func) with pytest.raises(ValueError): global_keys.add(key=key, func=func) def test_add_name(self): key = 'a' name = 'foo' func = self._func global_keys = event._GlobalEventKeys() global_keys.add(key=key, func=func, name=name) assert global_keys[key, ()].name == name def test_add_args(self): key = 'a' func = self._func args = (1, 2, 3) global_keys = event._GlobalEventKeys() global_keys.add(key=key, func=func, func_args=args) assert global_keys[key, ()].func_args == args def test_add_kwargs(self): key = 'a' func = self._func kwargs = dict(foo=1, bar=2) global_keys = event._GlobalEventKeys() global_keys.add(key=key, func=func, func_kwargs=kwargs) assert global_keys[key, ()].func_kwargs == kwargs def test_add_args_and_kwargs(self): key = 'a' func = self._func args = (1, 2, 3) kwargs = dict(foo=1, bar=2) global_keys = event._GlobalEventKeys() global_keys.add(key=key, func=func, func_args=args, func_kwargs=kwargs) assert global_keys[key, ()].func_args == args assert global_keys[key, ()].func_kwargs == kwargs def test_add_invalid_key(self): key = 'foo' func = self._func global_keys = event._GlobalEventKeys() with pytest.raises(ValueError): global_keys.add(key=key, func=func) def test_add_invalid_modifiers(self): key = 'a' modifiers = ('foo', 'bar') func = self._func global_keys = event._GlobalEventKeys() with pytest.raises(ValueError): global_keys.add(key=key, modifiers=modifiers, func=func) def test_remove(self): keys = ['a', 'b', 'c'] modifiers = ('ctrl',) func = self._func global_keys = event._GlobalEventKeys() [global_keys.add(key=key, modifiers=modifiers, func=func) for key in keys] global_keys.remove(keys[0], modifiers) with pytest.raises(KeyError): _ = global_keys[keys[0], modifiers] def test_remove_modifiers_list(self): key = 'a' modifiers = ['ctrl', 'alt'] func = self._func global_keys = event._GlobalEventKeys() global_keys.add(key=key, modifiers=modifiers, func=func) global_keys.remove(key, modifiers) with pytest.raises(KeyError): _ = global_keys[key, modifiers] def test_remove_invalid_key(self): key = 'a' global_keys = event._GlobalEventKeys() with pytest.raises(KeyError): global_keys.remove(key) def test_remove_all(self): keys = ['a', 'b', 'c'] func = self._func global_keys = event._GlobalEventKeys() [global_keys.add(key=key, func=func) for key in keys] global_keys.remove('all') assert len(global_keys) == 0 def test_getitem(self): key = 'escape' modifiers = ('ctrl', 'alt') func = self._func global_keys = event._GlobalEventKeys() global_keys.add(key=key, modifiers=modifiers, func=func) assert global_keys[key, modifiers] == global_keys._events[key, modifiers] def test_getitem_string(self): key = 'escape' func = self._func global_keys = event._GlobalEventKeys() global_keys.add(key=key, func=func) assert global_keys[key] == global_keys._events[key, ()] def test_getitem_modifiers_list(self): key = 'escape' modifiers = ['ctrl', 'alt'] func = self._func global_keys = event._GlobalEventKeys() global_keys.add(key=key, modifiers=modifiers, func=func) assert (global_keys[key, modifiers] == global_keys._events[key, tuple(modifiers)]) def test_setitem(self): keys = 'a' modifiers = () global_keys = event._GlobalEventKeys() with pytest.raises(NotImplementedError): global_keys[keys, modifiers] = None def test_delitem(self): key = 'escape' modifiers = ('ctrl', 'alt') func = self._func global_keys = event._GlobalEventKeys() global_keys.add(key=key, modifiers=modifiers, func=func) del global_keys[key, modifiers] with pytest.raises(KeyError): _ = global_keys[key, modifiers] def test_delitem_string(self): key = 'escape' func = self._func global_keys = event._GlobalEventKeys() global_keys.add(key=key, func=func) del global_keys[key] with pytest.raises(KeyError): _ = global_keys[key] def test_len(self): prefs.general['shutdownKey'] = '' key = 'escape' func = self._func global_keys = event._GlobalEventKeys() assert len(global_keys) == 0 global_keys.add(key=key, func=func) assert len(global_keys) == 1 del global_keys[key, ()] assert len(global_keys) == 0 def test_event_processing(self): key = 'a' modifiers = 0 func = self._func args = (1, 2, 3) kwargs = dict(foo=1, bar=2) event.globalKeys.add(key=key, func=func, func_args=args, func_kwargs=kwargs) r = event._process_global_event_key(key, modifiers) assert r[0] == args assert r[1] == kwargs def test_index_keys(self): key = 'escape' modifiers = ('ctrl', 'alt') func = self._func global_keys = event._GlobalEventKeys() global_keys.add(key=key, modifiers=modifiers, func=func) index_key = list(global_keys.keys())[-1] assert index_key.key == key assert index_key.modifiers == modifiers def test_numlock(self): key = 'a' modifiers = ('numlock',) func = self._func global_keys = event._GlobalEventKeys() with pytest.raises(ValueError): global_keys.add(key=key, modifiers=modifiers, func=func)
for keys in getKeys( ): #this allows to kick out of experiment. if keys in ['escape', 'q']: msg = TextStim(win, text="All done!", wrapWidth=15, units='deg', height=1, color='White', pos=[0, 0], alignHoriz='center', alignVert='bottom') msg.draw() win.flip() wait(.5) win.close() quit() if keys in ['x' ]: #this allows to screenshot the stimuli. win.getMovieFrame() win.saveMovieFrames('SampleStim' + str(trial) + '.jpg') (humanResp, humanRT) = displayRecordResponse(a[condition], trialStart, autoResp, StimLength) #Feedback (trialACC, feedback) = decideFeedback(a[condition], humanResp, signal) feedback.draw() win.flip()
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)
class Test_textbox(object): def setup_class(self): self.win = Window([128, 128], pos=[50, 50], allowGUI=False, autoLog=False) def teardown_class(self): self.win.close() def test_glyph_rendering(self): textbox = TextBox2(self.win, "", "Arial", pos=(0, 0), size=(1, 1), letterHeight=0.1, units='height') # Add all Noto Sans fonts to cover widest possible base of handles characters for font in [ "Noto Sans", "Noto Sans HK", "Noto Sans JP", "Noto Sans KR", "Noto Sans SC", "Noto Sans TC", "Niramit", "Indie Flower" ]: textbox.fontMGR.addGoogleFont(font) # Some exemplar text to test basic TextBox rendering exemplars = [ # An English pangram { "text": "A PsychoPy zealot knows a smidge of wx, but JavaScript is the question.", "font": "Noto Sans", "screenshot": "textbox_exemplar_1.png" }, # The same pangram in IPA { "text": "ə saɪkəʊpaɪ zɛlət nəʊz ə smidge ɒv wx, bʌt ˈʤɑːvəskrɪpt ɪz ðə ˈkwɛsʧən", "font": "Noto Sans", "screenshot": "textbox_exemplar_2.png" }, # The same pangram in Hangul { "text": "아 프시초피 제알롣 크노W스 아 s믿게 오f wx, 붇 자v앗c립t 잇 테 q왯디온", "font": "Noto Sans KR", "screenshot": "textbox_exemplar_3.png" }, # A noticeably non-standard font { "text": "A PsychoPy zealot knows a smidge of wx, but JavaScript is the question.", "font": "Indie Flower", "screenshot": "textbox_exemplar_4.png", } ] # Some text which is likely to cause problems if something isn't working tykes = [ # Text which doesn't render properly on Mac (Issue #3203) { "text": "कोशिकायें", "font": "Noto Sans", "screenshot": "textbox_tyke_1.png" }, # Thai text which old Text component couldn't handle due to Pyglet { "text": "ขาว แดง เขียว เหลือง ชมพู ม่วง เทา", "font": "Niramit", "screenshot": "textbox_tyke_2.png" } ] # Test each case and compare against screenshot for case in exemplars + tykes: textbox.reset() textbox.fontMGR.addGoogleFont(case['font']) textbox.font = case['font'] textbox.text = case['text'] self.win.flip() textbox.draw() if case['screenshot']: # Uncomment to save current configuration as desired #self.win.getMovieFrame(buffer='back').save(Path(utils.TESTS_DATA_PATH) / case['screenshot']) utils.compareScreenshot( Path(utils.TESTS_DATA_PATH) / case['screenshot'], self.win) def test_colors(self): textbox = TextBox2(self.win, "", "Consolas", pos=(0, 0), size=(1, 1), letterHeight=0.1, units='height', colorSpace="rgb") textbox.fontMGR.addGoogleFont("Noto Sans") textbox.font = "Noto Sans" textbox.text = "A PsychoPy zealot knows a smidge of wx, but JavaScript is the question." # Some exemplar text to test basic colors exemplars = [ # White on black in rgb { "color": (1, 1, 1), "fillColor": (-1, -1, -1), "borderColor": (-1, -1, -1), "space": "rgb", "screenshot": "textbox_colors_WOB.png" }, # White on black in named { "color": "white", "fillColor": "black", "borderColor": "black", "space": "rgb", "screenshot": "textbox_colors_WOB.png" }, # White on black in hex { "color": "#ffffff", "fillColor": "#000000", "borderColor": "#000000", "space": "hex", "screenshot": "textbox_colors_WOB.png" }, ] # Some colors which are likely to cause problems if something isn't working tykes = [ # Text only { "color": "white", "fillColor": None, "borderColor": None, "space": "rgb", "screenshot": "textbox_colors_tyke1.png" }, # The following will only work when the Color class is implemented (currently opacity is all or nothing) # Fill only # {"color": None, "fillColor": "white", "borderColor": None, "space": "rgb", # "screenshot": "textbox_colors_tyke2.png"}, # Border only # {"color": None, "fillColor": None, "borderColor": "white", "space": "rgb", # "screenshot": "textbox_colors_tyke3.png"}, ] # Test each case and compare against screenshot for case in exemplars + tykes: # Raise error if case spec does not contain all necessary keys if not all( key in case for key in ["color", "fillColor", "borderColor", "space", "screenshot"]): raise KeyError( f"Case spec for test_colors in class {self.__class__.__name__} ({__file__}) invalid, test cannot be run." ) # Apply params from case spec textbox.colorSpace = case['space'] textbox.color = case['color'] textbox.fillColor = case['fillColor'] textbox.borderColor = case['borderColor'] self.win.flip() textbox.draw() if case['screenshot']: # Uncomment to save current configuration as desired # self.win.getMovieFrame(buffer='back').save(Path(utils.TESTS_DATA_PATH) / case['screenshot']) utils.compareScreenshot( Path(utils.TESTS_DATA_PATH) / case['screenshot'], self.win)