def whoAmI(response, done, qpos, image): """ Function that will be called to enter the user identification data - **Input**: :response: the user resonse (empty string at beginning) :done: boolean (True / False) -> False at beginning :qpos: text position for user response :image: the stimulus image - **outpu**: :response: the user response """ # --------------------------------------- # # WHILE THE WELCOME MESSAGE IS NOT PASSED # # --------------------------------------- # while len(response) == 0: # while the user has not taped anything yet response = '' # the response written by the user - On commence avec une chaîne vide respstim = TextStim(disp, text='', pos=qpos, height=size, color=color) # stimulus texte qstim = ImageStim(disp, image=image) qstim.draw() # dessiner la question disp.flip( ) # passer au screen au suivant -> on met la question par-dessus core.wait(loadTime ) # delay of 10 seconds before passing to the learning phase done = False # ----------------------------------- # # WHILE THE USER'S ANSWER IS NOT DONE # # Check for keypresses # # ----------------------------------- # while not done: # loop until done == True resplist = waitKeys(maxWait=float('inf'), keyList=None, timeStamped=True) key, presstime = resplist[ 0] # use only the first in the returned list of keypresses -> resplist[0] is the first element in the resplist list if len(key) == 1: # Check si la longeur de la réponse (len) = 1 response += key #Ajouter la lettre tapée à la réponse => on ne tient pas compte des touches pour les majuscules ni de la touche escape elif key == 'space': # Check if key is the space bar response += ' ' # ajoute un espace elif key == 'backspace' and len( response ) > 0: # Check if the key's name was backspace AND si la réponse a au moins une lettre response = response[0: -1] #remove last character of the response if key == 'return': # if the key was non of the above, check si c'est enter done = True # set done to True respstim.setText(response.capitalize( )) # actualiser la user response => 1ère lettre en majuscule qstim.draw() # réafficher la question stimulus (image) respstim.draw() # réafficher la réponse au stimulus disp.flip() # update the monitor core.wait( loadTime) # add a little lag to avoid little freez and/or bug return response.capitalize() # 1ère lettre en majuscule
class Display: def __init__(self, pars): self.win = initializers.setup_window() self.pars = pars self.geom = initializers.setup_geometry(self.win, self.pars) self.rotation_clocks = [None] * self.geom['numtargs'] self.type = ['default'] * self.geom['numtargs'] self.setup_sounds() self.setup_images() self.setup_text() def setup_sounds(self): self.cashsnd = Sound('resources/cash.wav') self.firesnd = Sound('resources/bbhit.wav') self.buzzsnd = Sound('resources/buzz.wav') def setup_images(self): self.notargs = [] self.gotargs = [] self.deftargs = [] self.bkimg = ImageStim(self.win, image='resources/pond.jpg', units='norm', size=(2.0, 2.0)) for targ in range(self.geom['numtargs']): self.notargs.append( ImageStim(self.win, image='resources/rfrog2.jpg', size=self.geom['target_size'], pos=self.geom['target_centers'][targ])) self.gotargs.append( ImageStim(self.win, image='resources/gfrog2.jpg', size=self.geom['target_size'], pos=self.geom['target_centers'][targ])) self.deftargs.append( ImageStim(self.win, image='resources/lilypad.jpg', size=self.geom['target_size'], pos=self.geom['target_centers'][targ])) # set initial target stims to be the defaults self.targets = [] for targ in self.deftargs: self.targets.append(targ) def setup_text(self): self.scoretxt = TextStim(self.win, text="Total Points: ", font='Helvetica', alignHoriz='left', alignVert='top', units='norm', pos=(-1, 1), height=0.2, color=[178, 34, 34], colorSpace='rgb255', wrapWidth=2) self.targtxt = [] for targ in range(self.geom['numtargs']): self.targtxt.append( TextStim(self.win, pos=self.geom['target_centers'][targ], color='White', units='height', height=0.05, text='')) def set_target_image(self, index, value='default'): if value == 'go': self.targets[index] = self.gotargs[index] elif value == 'no': self.targets[index] = self.notargs[index] elif value == 'default': self.targets[index] = self.deftargs[index] def set_target_text(self, index, value): self.targtxt[index].setText(value) def set_score(self, pts): self.scoretxt.setText('Total Points: ' + str(pts)) def onset(self, index, value): self.rotation_clocks[index] = Clock() self.type[index] = value def offset(self, index): self.rotation_clocks[index] = Clock() self.type[index] = 'default' def update(self): # for each target with clock running (i.e., rotating) ... for idx, clk in enumerate(self.rotation_clocks): if clk: rot_time = clk.getTime() rot_dur = self.pars['rot_dur'] if rot_time > rot_dur: rotfrac = -1 # rotation completed else: rotfrac = cos(pi * rot_time / self.pars['rot_dur']) # adjust target size to give illusion of rotation base_size = self.geom['target_size'] self.targets[idx].size = (abs(rotfrac) * base_size[0], base_size[1]) # set correct image on target based on rotation angle if rotfrac < 0: self.set_target_image(idx, self.type[idx]) def draw(self): self.update() self.bkimg.draw() self.scoretxt.draw() for stim in self.targets: stim.draw() for stim in self.targtxt: stim.draw() self.win.flip() def close(self): self.win.close()
class StimulusResource(Resource): should_stop = False def __enter__(self): from psychopy.visual import ImageStim # eats some time from psychopy.visual import TextStim event.clearEvents() win = self.window.instance self.textstim = TextStim(win, text='') # image texture index order is iamge[y, x] self.instance = ImageStim( win=win, units='deg', #, filpVert=True, pos=self.component.position, size=self.component.size) #misc.pix2deg(win.size, win.monitor)*2) try: self.interval = self.window.get_isi() except Exception as e: raise ServiceRuntimeException( 'Could not acquire window object. Please try again') return self @memoized_property def trials(self): from psychopy.data import TrialHandler # eats some time sqf = self.component.square_factor self.image = np.zeros((sqf, sqf)) conditions = [ Condition(x, y, v) for x, y, v in product(range(sqf), range(sqf), [-1, 1]) ] ts = [ Trial(self, cond, self.component.on_duration, self.interval) for cond in conditions ] return TrialHandler( ts, nReps=self.component.repetition, method=('random' if self.component.randomize else 'sequential')) @property def synced(self): self.clock.synchronize(self) return iter(self) def __iter__(self): trials = self.trials clock = self.clock.instance clock.reset() for trial in trials: self.update_trial(trial) logging.msg('Entering trial #%s...' % trials.thisN) trials.addData('on_time', clock.getTime()) yield trial.start() trials.addData('off_time', clock.getTime()) self.flip_blank() core.wait(self.component.off_duration) self.instance.opacity = 1.0 if self.should_stop: logging.msg('UserAbortException raised!') raise UserAbortException() def update_trial(self, trial): cond = trial.condition self.image[cond.y, cond.x] = cond.v self.instance.image = self.image self.instance.draw() self.image[cond.y, cond.x] = 0 # def update_phase(self, trial): # now = trial.tick() # self.instance.phase = np.mod(now * trial.condition.tf, 1) # self.instance.draw() # self.window.flip() def flip_text(self, text): self.textstim.setText(text) self.textstim.draw() self.window.flip() def flip_blank(self): self.instance.opacity = 0.0 self.instance.draw() self.window.flip()
class StimulusResource(Resource): should_stop = False def __enter__(self): from psychopy.visual import GratingStim # eats some time from psychopy.visual import TextStim win = self.window.instance self.textstim = TextStim(win, text='') # for some reason x, y were swapped.. # this may happen if monitor setup was portrait mode instead of landscape. width, height = misc.pix2deg(win.size, win.monitor) if self.component.width: width = self.component.width self.instance = GratingStim( win=win, tex='sin', units='deg', size=(height, width) # size = misc.pix2deg(win.size, win.monitor) ) tf = self.component.tfrequency self.contrast_factor = tf * np.pi * (2 / self.component.ct_period) self.opacity_factor = tf * np.pi * (2 / self.component.op_period) try: self.interval = self.window.get_isi() except Exception as e: raise ServiceRuntimeException( 'Could not acquire window object. Please try again') return self @memoized_property def trials(self): from psychopy.data import TrialHandler # eats some time conditions = [ RevContModCondition( self.component.orientation, self.component.sfrequency, self.component.tfrequency, ) ] ts = [ Trial(self, cond, self.component.on_duration, self.interval) for cond in conditions ] return TrialHandler(ts, nReps=1, method='random') @property def synced(self): self.clock.synchronize(self) return iter(self) def __iter__(self): for trial in self.trials: index = self.trials.thisN logging.msg('Entering trial #%s...' % index) self.update_trial(trial) self.trials.addData('on_time', self.clock.getTime()) yield trial.start() self.trials.addData('off_time', self.clock.getTime()) self.flip_blank() # core.wait(self.component.off_duration) self.instance.opacity = 1.0 def update_trial(self, trial): self.instance.ori = trial.condition.ori self.instance.sf = trial.condition.sf self.instance.tf = trial.condition.tf def update_phase(self, trial): if self.should_stop: logging.msg('UserAbortException raised!') raise UserAbortException() now = trial.tick() cont = np.sin(now * self.contrast_factor) opa = np.cos(now * self.opacity_factor) self.instance.contrast = signal.square(cont) opacity = (opa + 1) / 2 max_contrast = self.component.max_contrast opacity = min(opacity, max_contrast) self.instance.opacity = opacity self.instance.draw() self.window.flip() self.clock.flipped() def flip_text(self, text): self.textstim.setText(text) self.textstim.draw() self.window.flip() def flip_blank(self): self.instance.opacity = 0.0 self.instance.draw() self.window.flip()
class ProjectionsLinesAndCircles(object): """ Test jig for projection warping. Switch between warpings by pressing a key 'S'pherical, 'C'ylindrical, 'N'one, warp'F'ile. Click the mouse to set the eyepoint X, Y. Up / Down arrow or mousewheel to move eyepoint in and out. """ def __init__(self, win, warper): self.win = win self.warper = warper self.stimT = TextStim(self.win, text='Null warper', units = 'pix', pos=(0, -140), alignHoriz='center', height=20) self.bl = -win.size / 2.0 self.tl = (self.bl[0], -self.bl[1]) self.tr = win.size / 2.0 self.stims = [] self.degrees = 120 nLines = 12 for x in range(-nLines, nLines+1): t = GratingStim(win,tex=None,units='deg',size=[2,win.size[1]],texRes=128,color=foregroundColor, pos=[float(x) / nLines * self.degrees,0]) self.stims.append (t) for y in range (-nLines, nLines+1): t = GratingStim(win,tex=None,units='deg',size=[win.size[0],2],texRes=128,color=foregroundColor,pos=[0,float(y)/nLines * self.degrees]) self.stims.append (t) for c in range (1, nLines+1): t = Circle (win, radius=c * 10, edges=128, units='deg', lineWidth=4) self.stims.append (t) self.updateInfo() self.keys = key.KeyStateHandler() win.winHandle.push_handlers(self.keys) self.mouse = event.Mouse(win=self.win) def updateFrame(self): """ Updates frame with any item that is to be modulated per frame. """ for s in self.stims: s.draw() self.stimT.draw() def update_sweep(self): """ Update function for sweeps. Input is in domain units. """ self.updateFrame() self.check_keys() self._handleMouse() self.win.flip() def updateInfo(self): try: self.stimT.setText ("%s \n eyePoint: %.3f, %.3f \n eyeDistance: %.2f\n\nProjection: [s]pherical, [c]ylindrical, [n]one, warp[f]ile\nFlip: [h]orizontal, [v]ertical\nMouse: wheel = eye distance, click to set eyepoint\n[q]uit" % ( self.warper.warp, self.warper.eyepoint[0], self.warper.eyepoint[1], self.warper.dist_cm)) except: pass def check_keys(self): """Checks key input""" for keys in event.getKeys(timeStamped=True): k = keys[0] if k in ['escape', 'q']: self.win.close() sys.exit() elif k in ['space']: for c in range (1,2): t = Circle(self.win, radius=c) self.stims.append (t) #for c in range (1,2): # t = RadialStim(self.win) # self.stims.append(t) # handle projections elif k in ['s']: self.warper.changeProjection ('spherical', None, (0.5,0.5)) elif k in ['c']: self.warper.changeProjection ('cylindrical', None, (0.5,0.5)) elif k in ['n']: self.warper.changeProjection (None, None, (0.5,0.5)) elif k in ['f']: self.warper.changeProjection ('warpfile', r'..\data\sample.meshwarp.data', (0.5,0.5)) # flip horizontal and vertical elif k in ['h']: self.warper.changeProjection(self.warper.warp, self.warper.warpfile, flipHorizontal = not self.warper.flipHorizontal) elif k in ['v']: self.warper.changeProjection(self.warper.warp, self.warper.warpfile, flipVertical = not self.warper.flipVertical) # move eyepoint elif k in ['down']: if (self.warper.dist_cm > 1): self.warper.dist_cm -= 1 self.warper.changeProjection (self.warper.warp, None, self.warper.eyepoint) elif k in ['up']: if (self.warper.dist_cm < 200): self.warper.dist_cm += 1 self.warper.changeProjection (self.warper.warp, None, self.warper.eyepoint) elif k in ['right']: if (self.warper.eyepoint[0] < 0.9): self.warper.eyepoint = (self.warper.eyepoint[0] + 0.1, self.warper.eyepoint[1]) self.warper.changeProjection (self.warper.warp, None, self.warper.eyepoint) elif k in ['left']: if (self.warper.eyepoint[0] > 0.1): self.warper.eyepoint = (self.warper.eyepoint[0] - 0.1, self.warper.eyepoint[1]) self.warper.changeProjection (self.warper.warp, None, self.warper.eyepoint) self.updateInfo() def _handleMouse(self): x,y = self.mouse.getWheelRel() if y != 0: self.warper.dist_cm += y self.warper.dist_cm = max (1, min (200, self.warper.dist_cm)) self.warper.changeProjection (self.warper.warp, self.warper.warpfile, self.warper.eyepoint) self.updateInfo() pos = (self.mouse.getPos() + 1) / 2 leftDown = self.mouse.getPressed()[0] if leftDown: self.warper.changeProjection (self.warper.warp, self.warper.warpfile, pos) self.updateInfo()
# --------------------------------------------------------------- # for pair in LISTE: # pour chaque pair de mot response = '' if firstLetter == True: # Do we give the student the first letter of the translation ? response += pair.translate[ 0] # le mot en français => avec la 1ère lettre du mot comme indice qstim = ImageStim(disp, image='images/Test.gif') # stimulus image qstim2 = TextStim(disp, text=pair.word, pos=qpos3, height=size, color=color) # stimulus texte respstim = TextStim( disp, text=response, pos=qpos, height=size, color=color) # boite réponse (to be completed by the user) respstim.setText( response) # actualiser la user response => 1ère lettre en majuscule qstim.draw() # dessiner la pair de mots (image) qstim2.draw() # dessiner la pair de mots (image) respstim.draw() # Draw the translation response disp.flip( ) # passer au screen au suivant -> on met la pair de mots par-dessus core.wait(loadTime) # delay of 10 seconds before passing to the next pair pair.setTest() # actualize the test property of the pair object response, done = tapeAnswer(response, False, True) # While loop to taping the entire answer # ------------------------------------------------------------------ # # ADD THE USER RESPONSE IN THE PAIR OBJECT # # RESPONSE CHECK - RESPONSE IS WRONG # # ADD FAILURE IN THE WORD PAIR OBJECT # # CHECK IF WE DECIDED TO GIVE A FEEDBACK AFTER THE FIRST TEST OR NOT #
class ContribScreen(Screen): """ This screen lets users select their contribution. """ def __init__(self, disp, text, config_dict, gaze_pos_getter=None, width=None, height=None, margin=None, font_size=None, choice_font_size=100, origin=None ): super(ContribScreen, self).__init__(disp) cfg = config_dict[u'contrib_screen'] self.gaze_pos_getter = gaze_pos_getter self.instr_text_preformat = text self.contrib_choice = '__' self.mouseover_threshold = 0 self.choice_coords = [] if width is not None: self.width = width else: self.width = self.window.hres if height is not None: self.height = height else: self.height = self.window.vres if margin is not None: self.margin = margin else: self.margin = self.window.margin if origin is not None: self.origin = origin if font_size is None: font_size = config_dict[u'contrib_screen'][u'text'][u'font_size'] self.contrib_choices = self.gen_contrib_choices( cfg[u'nrows'], cfg[u'ncols'], font_size=choice_font_size ) self.contrib_instructions = TextStim( self.window, height=font_size, text=self.instr_text_preformat.format(self.contrib_choice), pos=self.coords((self.x_cfg_2_pix(cfg[u'text'][u'x']), self.y_cfg_2_pix(cfg[u'text'][u'y']) )), wrapWidth=self.w_cfg_2_pix(cfg[u'text'][u'width']) ) self.gaze = Circle(self.window, radius=5) self.gaze.fillColor = 'red' def draw(self, debug_mode=False): mouse_x, mouse_y = self.mouse.getPos() for text_stim in self.contrib_choices: if xydist((mouse_x, mouse_y), text_stim.pos) < self.mouseover_threshold: text_stim.color = 'darkorange' if self.mouse.getPressed()[0]: self.contrib_choice = text_stim.text self.contrib_instructions.setText(self.instr_text_preformat.format(self.contrib_choice)) self.continue_button.clickable = True else: text_stim.color = 'white' text_stim.draw() self.continue_button.draw() self.contrib_instructions.draw() if self.gaze_pos_getter is not None: self.gaze.pos = self.coords(self.gaze_pos_getter()) self.gaze.draw() def cleanup(self): self.continue_button.clickable = False self.contrib_instructions.setText(self.instr_text_preformat.format('__')) self.move_on_flag.clear() def gen_contrib_choices(self, nrows, ncols, font_size): h_spacing = (self.width - 2*self.margin)/(ncols-1) v_spacing = h_spacing '''OR: v_spacing = (self.height - 2*self.margin)/(nrows-1)''' self.mouseover_threshold = min(h_spacing, v_spacing)/4 contrib_choices = [] for i in range(0, nrows*ncols): xpos = self.margin + (i % ncols)*h_spacing ''' To move the array to the bottom of the page: ypos = self.height - self.margin - (nrows-floor(1.0*i/ncols)-1)*v_spacing ''' ypos = floor(1.0*i/ncols)*v_spacing + self.margin ''' 1.0*i/ncols gives us a floating-point number. Without the 1.0, python interprets this as int-division and truncates. I could have exploited this for slightly simpler code but it is really stupid that it works that way and who knows what bugs could pop up by relying on something that is in my opinion a bug? We take the floor of that number to give us a "row index." We multiply this number by the vertical spacing. ''' if i < 10: temp = ' {} '.format(i) else: temp = '{}'.format(i) contrib_choices.append(TextStim(win=self.window, text=temp, pos=self.coords((xpos, ypos)), height=font_size)) self.choice_coords.append((xpos, ypos)) return contrib_choices
def __init__(self, disp, num_players, config_dict, gaze_pos_getter=None, width=None, height=None, margin=None, font_size=None, col_x_list=None): super(FeedbackScreen, self).__init__(disp) self.gaze_pos_getter = gaze_pos_getter self.continue_button.clickable = True cfg = config_dict[u'feedback_screen'] if width is not None: self.width = width else: self.width = self.window.hres if height is not None: self.height = height else: self.height = self.window.vres if margin is not None: self.margin = margin else: self.margin = self.window.margin if font_size is not None: self.font_size = font_size else: self.font_size = config_dict[u'feedback_screen'][u'font_size'] self.col_x_list = [] if col_x_list is None: self.col_x_list.append( self.x_cfg_2_pix( config_dict[u'feedback_screen'][u'left_col_x'] )) self.col_x_list.append( self.x_cfg_2_pix( config_dict[u'feedback_screen'][u'midd_col_x'] )) self.col_x_list.append( self.x_cfg_2_pix( config_dict[u'feedback_screen'][u'rite_col_x'] )) else: self.col_x_list = col_x_list if FeedbackScreen.reversed: self.col_x_list.reverse() self.gaze = Circle(self.window, radius=5) self.gaze.fillColor = 'red' self.nrows = num_players + 3 self.SUMROWINDEX = self.nrows - 2 self.AVGROWINDEX = self.nrows - 1 row_spacing = (self.height - 2*self.margin)/(self.nrows-1) self.contr_col = [] self.label_col = [] self.payof_col = [] y_coords = [] for i in range(self.nrows): y = self.window.margin + i*row_spacing y_coords.append(y) temp_contr = TextStim(self.window, height=self.font_size) temp_label = TextStim(self.window, height=self.font_size) temp_payof = TextStim(self.window, height=self.font_size) if i == 0: temp_contr.setText(cfg[u'contributions']) temp_label.setText(cfg[u'players']) temp_payof.setText(cfg[u'payoffs']) else: temp_contr.setText('xx') temp_payof.setText('xx') if i == 1: temp_label.setText(cfg[u'you']) elif i <= num_players: # self.nrows - 3 temp_label.setText(cfg[u'others'][i-2]) elif i == self.SUMROWINDEX: temp_label.setText(cfg[u'sum']) elif i == self.AVGROWINDEX: temp_label.setText(cfg[u'average']) else: logging.error('Error in FeedbackScreen.__init__(). Wrong number of rows?') temp_contr.setPos(self.coords((self.col_x_list[0], y))) self.contr_col.append(temp_contr) temp_label.setPos(self.coords((self.col_x_list[1], y))) self.label_col.append(temp_label) temp_payof.setPos(self.coords((self.col_x_list[2], y))) self.payof_col.append(temp_payof) self.AOI_coords = [] for x in self.col_x_list: for y in y_coords: self.AOI_coords.append((x, y)) self.AOI_coords.remove((self.col_x_list[1], y_coords[0]))
class ProjectionsLinesAndCircles(object): """ Test jig for projection warping. Switch between warpings by pressing a key 'S'pherical, 'C'ylindrical, 'N'one, warp'F'ile. Click the mouse to set the eyepoint X, Y. Up / Down arrow or mousewheel to move eyepoint in and out. """ def __init__(self, win, warper): self.win = win self.warper = warper self.stimT = TextStim(self.win, text='Null warper', units='pix', pos=(0, -140), alignHoriz='center', height=20) self.bl = -win.size / 2.0 self.tl = (self.bl[0], -self.bl[1]) self.tr = win.size / 2.0 self.stims = [] self.degrees = 120 nLines = 12 for x in range(-nLines, nLines + 1): t = GratingStim(win, tex=None, units='deg', size=[2, win.size[1]], texRes=128, color=foregroundColor, pos=[float(x) / nLines * self.degrees, 0]) self.stims.append(t) for y in range(-nLines, nLines + 1): t = GratingStim(win, tex=None, units='deg', size=[win.size[0], 2], texRes=128, color=foregroundColor, pos=[0, float(y) / nLines * self.degrees]) self.stims.append(t) for c in range(1, nLines + 1): t = Circle(win, radius=c * 10, edges=128, units='deg', lineWidth=4) self.stims.append(t) self.updateInfo() self.keys = key.KeyStateHandler() win.winHandle.push_handlers(self.keys) self.mouse = event.Mouse(win=self.win) def updateFrame(self): """ Updates frame with any item that is to be modulated per frame. """ for s in self.stims: s.draw() self.stimT.draw() def update_sweep(self): """ Update function for sweeps. Input is in domain units. """ self.updateFrame() self.check_keys() self._handleMouse() self.win.flip() def updateInfo(self): try: self.stimT.setText( "%s \n eyePoint: %.3f, %.3f \n eyeDistance: %.2f\n\nProjection: [s]pherical, [c]ylindrical, [n]one, warp[f]ile\nFlip: [h]orizontal, [v]ertical\nMouse: wheel = eye distance, click to set eyepoint\n[q]uit" % (self.warper.warp, self.warper.eyepoint[0], self.warper.eyepoint[1], self.warper.dist_cm)) except: pass def check_keys(self): """Checks key input""" for keys in event.getKeys(timeStamped=True): k = keys[0] if k in ['escape', 'q']: self.win.close() sys.exit() elif k in ['space']: for c in range(1, 2): t = Circle(self.win, radius=c) self.stims.append(t) #for c in range (1,2): # t = RadialStim(self.win) # self.stims.append(t) # handle projections elif k in ['s']: self.warper.changeProjection('spherical', None, (0.5, 0.5)) elif k in ['c']: self.warper.changeProjection('cylindrical', None, (0.5, 0.5)) elif k in ['n']: self.warper.changeProjection(None, None, (0.5, 0.5)) elif k in ['f']: self.warper.changeProjection('warpfile', r'..\data\sample.meshwarp.data', (0.5, 0.5)) # flip horizontal and vertical elif k in ['h']: self.warper.changeProjection( self.warper.warp, self.warper.warpfile, flipHorizontal=not self.warper.flipHorizontal) elif k in ['v']: self.warper.changeProjection( self.warper.warp, self.warper.warpfile, flipVertical=not self.warper.flipVertical) # move eyepoint elif k in ['down']: if (self.warper.dist_cm > 1): self.warper.dist_cm -= 1 self.warper.changeProjection(self.warper.warp, None, self.warper.eyepoint) elif k in ['up']: if (self.warper.dist_cm < 200): self.warper.dist_cm += 1 self.warper.changeProjection(self.warper.warp, None, self.warper.eyepoint) elif k in ['right']: if (self.warper.eyepoint[0] < 0.9): self.warper.eyepoint = (self.warper.eyepoint[0] + 0.1, self.warper.eyepoint[1]) self.warper.changeProjection(self.warper.warp, None, self.warper.eyepoint) elif k in ['left']: if (self.warper.eyepoint[0] > 0.1): self.warper.eyepoint = (self.warper.eyepoint[0] - 0.1, self.warper.eyepoint[1]) self.warper.changeProjection(self.warper.warp, None, self.warper.eyepoint) self.updateInfo() def _handleMouse(self): x, y = self.mouse.getWheelRel() if y != 0: self.warper.dist_cm += y self.warper.dist_cm = max(1, min(200, self.warper.dist_cm)) self.warper.changeProjection(self.warper.warp, self.warper.warpfile, self.warper.eyepoint) self.updateInfo() pos = (self.mouse.getPos() + 1) / 2 leftDown = self.mouse.getPressed()[0] if leftDown: self.warper.changeProjection(self.warper.warp, self.warper.warpfile, pos) self.updateInfo()
class StimulusResource(Resource): should_stop = False def __enter__(self): from psychopy.visual import GratingStim # eats some time from psychopy.visual import TextStim event.clearEvents() win = self.window.instance self.textstim = TextStim(win, text='') self.instance = GratingStim( win=win, units='deg', tex='sin', contrast=self.component. contrast, # maybe this still could be used for initial contrast setup. size=misc.pix2deg(win.size, win.monitor) * 2) try: self.interval = self.window.get_isi() except Exception as e: raise ServiceRuntimeException( 'Could not acquire window object. Please try again') return self @memoized_property def trials(self): from psychopy.data import TrialHandler # eats some time # blank comes first # flicker comes later conditions = [ Condition(ori, sf, tf, cnt) for ori, sf, tf, cnt in product( self.component.orientations, self.component.sfrequencies, self.component.tfrequencies, self.component.contrasts, ) ] if self.component.blank: logging.msg('has blank condition...') conditions.append(BlankCondition()) if self.component.flicker: logging.msg('has flicker condition...') conditions.append(FlickerCondition()) ts = [ Trial(self, cond, self.component.on_duration, self.interval) for cond in conditions ] return TrialHandler( ts, nReps=self.component.repetition, method=('random' if self.component.randomize else 'sequential')) @property def synced(self): self.clock.synchronize(self) return iter(self) def __iter__(self): trials = self.trials clock = self.clock.instance clock.reset() for trial in trials: self.update_trial(trial) logging.msg('Entering trial #%s...' % trials.thisN) trials.addData('on_time', clock.getTime()) yield trial.start() trials.addData('off_time', clock.getTime()) self.flip_blank() core.wait(self.component.off_duration) self.instance.opacity = 1.0 if self.should_stop: logging.msg('UserAbortException raised!') raise UserAbortException() def update_trial(self, trial): for key, val in vars(trial.condition).items(): setattr(self.instance, key, val) def update_phase(self, trial): now = trial.tick() self.instance.phase = np.mod(now * trial.condition.tf, 1) # self.instance.draw() self.window.flip() def flip_text(self, text): self.textstim.setText(text) self.textstim.draw() self.window.flip() def flip_blank(self): self.instance.opacity = 0.0 self.instance.draw() self.window.flip()
class Display: def __init__(self, pars): self.win = initializers.setup_window() self.pars = pars self.geom = initializers.setup_geometry(self.win, self.pars) self.rotation_clocks = [None] * self.geom['numtargs'] self.type = ['default'] * self.geom['numtargs'] self.setup_sounds() self.setup_images() self.setup_text() def setup_sounds(self): self.cashsnd = Sound('resources/cash.wav') self.firesnd = Sound('resources/bbhit.wav') self.buzzsnd = Sound('resources/buzz.wav') def setup_images(self): self.notargs = [] self.gotargs = [] self.deftargs = [] self.bkimg = ImageStim(self.win, image='resources/pond.jpg', units='norm', size=(2.0, 2.0)) for targ in range(self.geom['numtargs']): self.notargs.append(ImageStim(self.win, image='resources/rfrog2.jpg', size=self.geom['target_size'], pos=self.geom['target_centers'][targ])) self.gotargs.append(ImageStim(self.win, image='resources/gfrog2.jpg', size=self.geom['target_size'], pos=self.geom['target_centers'][targ])) self.deftargs.append(ImageStim(self.win, image='resources/lilypad.jpg', size=self.geom['target_size'], pos=self.geom['target_centers'][targ])) # set initial target stims to be the defaults self.targets = [] for targ in self.deftargs: self.targets.append(targ) def setup_text(self): self.scoretxt = TextStim(self.win, text="Total Points: ", font='Helvetica', alignHoriz='left', alignVert='top', units='norm', pos=(-1, 1), height=0.2, color=[178, 34, 34], colorSpace='rgb255', wrapWidth=2) self.targtxt = [] for targ in range(self.geom['numtargs']): self.targtxt.append(TextStim(self.win, pos=self.geom['target_centers'][targ], color='White', units='height', height=0.05, text='')) def set_target_image(self, index, value='default'): if value == 'go': self.targets[index] = self.gotargs[index] elif value == 'no': self.targets[index] = self.notargs[index] elif value == 'default': self.targets[index] = self.deftargs[index] def set_target_text(self, index, value): self.targtxt[index].setText(value) def set_score(self, pts): self.scoretxt.setText('Total Points: ' + str(pts)) def onset(self, index, value): self.rotation_clocks[index] = Clock() self.type[index] = value def offset(self, index): self.rotation_clocks[index] = Clock() self.type[index] = 'default' def update(self): # for each target with clock running (i.e., rotating) ... for idx, clk in enumerate(self.rotation_clocks): if clk: rot_time = clk.getTime() rot_dur = self.pars['rot_dur'] if rot_time > rot_dur: rotfrac = -1 # rotation completed else: rotfrac = cos(pi * rot_time / self.pars['rot_dur']) # adjust target size to give illusion of rotation base_size = self.geom['target_size'] self.targets[idx].size = (abs(rotfrac) * base_size[0], base_size[1]) # set correct image on target based on rotation angle if rotfrac < 0: self.set_target_image(idx, self.type[idx]) def draw(self): self.update() self.bkimg.draw() self.scoretxt.draw() for stim in self.targets: stim.draw() for stim in self.targtxt: stim.draw() self.win.flip() def close(self): self.win.close()
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 StimulusResource(Resource): should_stop = False def __enter__(self): win = self.window.instance proj = self.projection.instance try: eyepoint_x = proj.eyepoint[0] except: eyepoint_x = 0.5 self.textstim = TextStim(win, text='') size_overriden = self.component.snp_viewport_override view_size = size_overriden if any(size_overriden) else win.size win_x, win_y = win.size view_x, view_y = view_size afr = 30 # int(win.getActualFrameRate() * 0.5) logging.msg('Fixed frame rate: ' + str(afr)) self.flip_text('Generating stimulus...it may take a few minutes.') try: logging.msg('init movie...') print 'init movie...' width, height = win.monitor.getSizePix() mgen = SweepingNoiseGenerator( max_spat_freq=self.component.snp_max_spat_freq, max_temp_freq=self.component.snp_max_temp_freq, contrast=self.component.snp_contrast, rotation=self.component.snp_rotation, bandfilter=self.component.snp_filter, duration=self.component.snp_duration, bandwidth=self.component.snp_bandwidth, viewwidth=self.component.snp_view_width, imsize=self.component.snp_img_size, binary_threshold=self.component.snp_cont_thresh, framerate=afr, contr_period=self.component.snp_contr_period, screenWidthPix=win_x, screenHeightPix=win_y, viewport_x=view_x, viewport_y=view_y, screenWidthCm=self.window.monitor.component.width, screenHeightCm=self.window.monitor.component.height, screenDistanceCm=self.window.monitor.component.dist, eyepoint_x=eyepoint_x) except Exception as e: print 'got exception', type(e) print e else: print 'generating...' mgen.generate() print 'rotating...' mgen.rotate() print 'viewmasking...' mgen.viewmask() # print 'croping...' # mgen.crop() print 'done!' self.movie = mgen.moviedata print 'shape is', self.movie.shape logging.msg('masking window...') print 'masking window...' # Setting viewport width # screenWidthCm = self.window.monitor.component.width # screenDistanceCm = self.window.monitor.component.dist self.flip_text('Generating stimulus...done!') logging.msg('creating image stim...') print 'creating image stim...' # imagebuffer to play each frame self.instance = ImageStim( win=win, # size = [1440, 900], # prevent stretch # size = [900, 900], # for mbpr with 10cm size=view_size, units='pix', interpolate=True) logging.msg('ImageStim size: ' + str(self.instance.size)) try: logging.msg('getting ISI...') self.interval = self.window.get_isi() except Exception as e: raise ServiceRuntimeException( 'Could not acquire window object. Please try again') return self @memoized_property def trials(self): ts = [ Trial(self, cond, self.component.snp_duration, self.interval) for cond in [self.movie] ] return TrialHandler(ts, nReps=1, method='random') @property def synced(self): self.clock.synchronize(self) return iter(self) def __iter__(self): for trial in self.trials: index = self.trials.thisN logging.msg('Entering trial #%s...' % index) # self.update_trial(trial) # self.trials.addData('on_time', self.clock.getTime()) yield trial.start() # self.trials.addData('off_time', self.clock.getTime()) self.flip_blank() def update_trial(self, trial): pass def update_phase(self, trial): pass def flip_text(self, text): self.textstim.setText(text) self.textstim.draw() self.window.flip() def flip_blank(self): self.instance.opacity = 0.0 self.instance.draw() self.window.flip() def __exit__(self, type, value, tb): self.movie = None return super(StimulusResource, self).__exit__(type, value, tb)
log.write('button_localization\t') if task_disc == True: disc_txt = 'Pay attention to whether the picture is a' disc_txt2 = 'or a' disc_txt3 = '.' log.write('button_discrimination\t') log.write('\n') #after all elements are added to the header row of the log, it goes to the next row (ready to note participants' responses) #If no tasks are chosen, these "task_" objects will remain as initialized: '' #Then, the welcome message will be appended with nothing (i.e., the content of empty objects). welcome_txt.setText(f'Welcome to the experiment! \n\ In this experiment, you will be shown pictures. \n\ \n\ {detec_txt} {disc_cat_A} {disc_txt2} {disc_cat_B} {disc_txt3} \n\ Once the experiment starts, you can between trials by pressing the {button_quit} button.\n\ \n\ Procede to the experiment by pressing the {button_welcome_quit} button. Enjoy!') # ^ in the third row, all appendices will be entered '''Adjust task instructions, based on the selected buttons''' detec_instructions.setText(f'Press {button_detec_pres}, if a picture was shown and {button_detec_abs} if no picture was shown.') local_instructions.setText(f'Press {button_local_left}, if the picture was shown to the left and {button_local_right}, if the picture was shown to the right.') disc_instructions.setText(f'Press {button_disc_A}, if the picture showed a(n) {disc_cat_A} and press {button_disc_A}, if the picture showed a(n) {disc_cat_B}.') '''Sandwich masking (if selected)''' if mask_sandw == True: #Since sandwich masking means that forward + backwards masks are used mask_forw = True #so we set the values up this way mask_backw = True #in the final version, the input will instead be the choice of 3 default values: "forward, backward, sandwich"
class PsychopyCustomDisplay(pylink.EyeLinkCustomDisplay): """ Custom display for Eyelink eyetracker. Modified from the 'pylinkwrapper' package by Nick DiQuattro (https://github.com/ndiquattro/pylinkwrapper). All credits go to him. """ def __init__(self, tracker, win, settings): """ Initializes PsychopyCustomDisplay object. """ super().__init__() self.tracker = tracker self.win = win self.settings = settings # from session self.txtcol = -1 #self.__target_beep__ = sound.Sound(800, secs=.1) # THIS WILL GIVE A SEGFAULT! #self.__target_beep__done__ = sound.Sound(1200, secs=.1) # THIS WILL GIVE A SEGFAULT! #self.__target_beep__error__ = sound.Sound(400, secs=.1) # THIS WILL GIVE A SEGFAULT! self.backcolor = self.win.color dot_size_pix = misc.deg2pix( self.settings['eyetracker'].get('dot_size'), self.win.monitor) self.targetout = Circle(self.win, pos=(0, 0), radius=dot_size_pix, fillColor='black', units='pix', lineColor='black') self.targetin = Circle(self.win, pos=(0, 0), radius=3, fillColor=0, lineColor=0, units='pix', opacity=0) win.flip() def setup_cal_display(self): txt = TextStim( self.win, text= "Please follow the dot. Try not to anticipate its movements.", pos=(0, 100), color='black', units='pix') txt.draw() self.targetout.draw() self.win.flip() def exit_cal_display(self): self.clear_cal_display() def clear_cal_display(self): self.setup_cal_display() def erase_cal_target(self): self.win.flip() def draw_cal_target(self, x, y): # Convert to psychopy coordinates x = x - (self.win.size[0] / 2) y = -(y - (self.win.size[1] / 2)) # Set calibration target position self.targetout.pos = (x, y) self.targetin.pos = (x, y) # Display self.targetout.draw() self.targetin.draw() self.win.flip() def alert_printf(self, msg): print("alert_printf %s" % msg) def play_beep(self, beepid): if beepid == pylink.DC_TARG_BEEP or beepid == pylink.CAL_TARG_BEEP: self.__target_beep__.play() elif beepid == pylink.CAL_ERR_BEEP or beepid == pylink.DC_ERR_BEEP: self.__target_beep__error__.play() else: # CAL_GOOD_BEEP or DC_GOOD_BEEP self.__target_beep__done__.play() def get_input_key(self): ky = [] v = event.getKeys() for key in v: pylink_key = None if len(key) == 1: pylink_key = ord(key) elif key == "escape": pylink_key = pylink.ESC_KEY elif key == "return": pylink_key = pylink.ENTER_KEY elif key == "pageup": pylink_key = pylink.PAGE_UP elif key == "pagedown": pylink_key = pylink.PAGE_DOWN elif key == "up": pylink_key = pylink.CURS_UP elif key == "down": pylink_key = pylink.CURS_DOWN elif key == "left": pylink_key = pylink.CURS_LEFT elif key == "right": pylink_key = pylink.CURS_RIGHT else: print(f'Error! :{key} is not a used key.') return ky.append(pylink.KeyInput(pylink_key, 0)) return ky def record_abort_hide(self): pass def setup_image_display(self, width, height): self.size = (width / 2, height / 2) self.clear_cal_display() self.last_mouse_state = -1 # Create array to hold image data later if self.rgb_index_array is None: self.rgb_index_array = np.zeros((self.size[1], self.size[0]), dtype=np.uint8) def exit_image_display(self): self.clear_cal_display() def image_title(self, text): # Display or update Pupil/CR info on image screen if self.imagetitlestim is None: self.imagetitlestim = TextStim( self.window, text=text, pos=(0, self.window.size[1] / 2 - 15), height=28, color=self.txtcol, alignHoriz='center', alignVert='top', wrapWidth=self.window.size[0] * .8, units='pix') else: self.imagetitlestim.setText(text) def exit_image_display(self): self.clear_cal_display() def draw_image_line(self, width, line, totlines, buff): # Get image info for each line of image for i in range(width): self.rgb_index_array[line - 1, i] = buff[i] # Once all lines are collected turn into an image to display if line == totlines: # Make image image = scipy.misc.toimage(self.rgb_index_array, pal=self.rgb_pallete, mode='P') # Resize Image if self.imgstim_size is None: maxsz = self.sres[0] / 2 mx = 1.0 while (mx + 1) * self.size[0] <= maxsz: mx += 1.0 self.imgstim_size = int(self.size[0] * mx), int( self.size[1] * mx) image = image.resize(self.imgstim_size) # Save image as a temporay file tfile = os.path.join(tempfile.gettempdir(), '_eleye.png') image.save(tfile, 'PNG') # Need this for target distance to show up self.__img__ = image self.draw_cross_hair() self.__img__ = None # Create eye image if self.eye_image is None: self.eye_image = visual.ImageStim(self.window, tfile, size=self.imgstim_size, units='pix') else: self.eye_image.setImage(tfile) # Redraw the Camera Setup Mode graphics self.eye_image.draw() if self.imagetitlestim: self.imagetitlestim.draw() # Display self.window.flip() def set_image_palette(self, r, g, b): # This does something the other image functions need self.clear_cal_display() sz = len(r) self.rgb_pallete = np.zeros((sz, 3), dtype=np.uint8) i = 0 while i < sz: self.rgb_pallete[i:] = int(r[i]), int(g[i]), int(b[i]) i += 1 def dummynote(self): # Draw Text visual.TextStim(self.window, text='Dummy Connection with EyeLink', color=self.txtcol).draw() self.window.flip() # Wait for key press event.waitKeys() self.window.flip()
'They are each talking to the interrogator separately. The interrogator gives each'\ 'person the same deal: \n'\ 'they can choose to vouch for the other person’s innocence (COOPERATING) or rat them out (DEFECTING).\n '\ 'And of course, there’s a twist. If both people vouch for 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\n'\ '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.\n'\ 'You can exit early by pressing Q\nPress space to start' sentCounter = TextStim(win, text='0 months', font="Arial", height=20, color='black', pos = (300, -300)) #counter stating amount of sentence controlinfo = TextStim(win, text = 'Press the left arrow to defect and the right arrow to cooperate', font="Arial", height=20, color='black', pos = (-300, -300), autoDraw = True) #for later in the loop: sentCount = 0 sentCounter.setText(str(sentCount)+'months') #welcome text welcome_txt = TextStim(win, text=welcome_str, font="Arial", height=20, color='black', wrapWidth=400) #avatars: NOT FINISHED YET POSSS image_01 = ImageStim(win, image="im_01.jpg") #check if it's the right picture and name it after ethnicity image_02 = ImageStim(win, image="im_02.jpg") image_03 = ImageStim(win, image="im_03.jpg") image_04 = ImageStim(win, image="im_04.jpg") image_05 = ImageStim(win, image="im_05.jpg") image_06 = ImageStim(win, image="im_06.jpg") image_07 = ImageStim(win, image="im_07.jpg") image_08 = ImageStim(win, image="im_08.jpg")