def setup(self): # Stimulus sizes fixation_size = deg_to_px(0.32) fixation_thickness = deg_to_px(0.08) cue_size = deg_to_px(0.64) cue_thickness = deg_to_px(0.08) arrow_tail_len = deg_to_px(0.48) arrow_tail_width = deg_to_px(0.15) arrow_head_len = deg_to_px(0.25) arrow_head_width = deg_to_px(0.45, even=True) arrow_dimensions = [ arrow_tail_len, arrow_tail_width, arrow_head_len, arrow_head_width ] # Stimuli self.warning_tone = Tone(50, 'sine', frequency=2000, volume=0.5) self.fixation = kld.FixationCross(fixation_size, fixation_thickness, fill=WHITE) self.cue = kld.Asterisk(cue_size, thickness=cue_thickness, fill=WHITE, spokes=8) self.arrow_r = kld.Arrow(*arrow_dimensions, fill=WHITE) self.arrow_l = kld.Arrow(*arrow_dimensions, fill=WHITE, rotation=180) self.arrow_r.render() self.arrow_l.render() # Layout self.height_offset = deg_to_px(1.3) self.flanker_offset = arrow_tail_len + arrow_head_len + deg_to_px(0.16) self.above_loc = (P.screen_c[0], P.screen_c[1] - self.height_offset) self.below_loc = (P.screen_c[0], P.screen_c[1] + self.height_offset) self.cue_locations = {'above': self.above_loc, 'below': self.below_loc} # Insert practice block (when applicable) if P.run_practice_blocks: self.insert_practice_block(block_nums=1, trial_counts=24) self.instructions_presented = False
def setup(self): # Stimulus sizes fixation_size = deg_to_px(0.32) fixation_thickness = deg_to_px(0.08) cue_size = deg_to_px(0.64) cue_thickness = deg_to_px(0.08) arrow_tail_len = deg_to_px(0.48) arrow_tail_width = deg_to_px(0.15) arrow_head_len = deg_to_px(0.25) arrow_head_width = deg_to_px(0.45, even=True) arrow_dimensions = [ arrow_tail_len, arrow_tail_width, arrow_head_len, arrow_head_width ] # Stimuli self.warning_tone = Tone(50.1, 'sine', frequency=2000, volume=0.5) self.fixation = kld.FixationCross(fixation_size, fixation_thickness, fill=BLACK) self.cue = kld.Asterisk(cue_size, thickness=cue_thickness, fill=BLACK, spokes=8) self.arrow_r = kld.Arrow(*arrow_dimensions, fill=BLACK) self.arrow_l = kld.Arrow(*arrow_dimensions, fill=BLACK, rotation=180) self.arrow_r.render() self.arrow_l.render() # Layout self.height_offset = deg_to_px(1.3) self.height_jitter = deg_to_px(0.04) self.flanker_offset = arrow_tail_len + arrow_head_len + deg_to_px(0.16) self.ev_offsets = { 'above': -(self.height_jitter * 4), 'below': self.height_jitter * 4 } self.above_loc = (P.screen_c[0], P.screen_c[1] - self.height_offset) self.below_loc = (P.screen_c[0], P.screen_c[1] + self.height_offset) self.cue_locations = {'above': self.above_loc, 'below': self.below_loc} # Add text styles for PVT and block messages self.txtm.add_style("PVT", '1.5deg', color=RED) self.txtm.add_style("block_msg", '0.6deg', color=BLACK) # If it's the first session, insert practice blocks with feedback if P.run_practice_blocks and P.session_number == 1: ANTI_only = ['ANTI-valid', 'ANTI-invalid', 'ANTI-none'] ANTI_EV = copy(ANTI_only) * 2 + ['EV-above', 'EV-below' ] * 3 # 1/2 ANTI, 1/2 EV ANTI_EV_AV = copy(ANTI_EV) + ['AV'] * 6 # 1/3 ANTI, 1/3 EV, 1/3 AV self.insert_practice_block(1, trial_counts=16, factor_mask={'trial_type': ANTI_only}) self.insert_practice_block(2, trial_counts=32, factor_mask={'trial_type': ANTI_EV}) self.insert_practice_block(3, trial_counts=48, factor_mask={'trial_type': ANTI_EV_AV})
def create_stimuli(self, tdSimilarity, ddSimilarity): # Setup target properties if P.condition == 'line': self.target_angle = random.randint(0, 179) self.target_height = deg_to_px(0.1) self.target_fill = WHITE self.target_item = kld.Rectangle(width=self.item_size, height=self.target_height, fill=WHITE, rotation=self.target_angle) if tdSimilarity == HIGH: self.ref_angle = self.target_angle else: self.ref_angle = self.target_angle + 90 self.mask = kld.Asterisk(size=self.item_size, thickness=self.target_height, fill=WHITE, spokes=12) else: self.target_height = self.item_size self.palette = kld.ColorWheel(diameter=self.item_size) self.target_angle = random.randint(0, 359) self.target_colour = self.palette.color_from_angle( self.target_angle) self.target_item = kld.Rectangle(width=self.item_size, height=self.target_height, fill=self.target_colour) if tdSimilarity == HIGH: self.ref_angle = self.target_angle else: self.ref_angle = self.target_angle + 180 self.mask = kld.Rectangle(width=self.item_size, fill=WHITE) # Setup distractor(s) properties if ddSimilarity == HIGH: padding = [random.choice([-20, 20])] else: padding = [-40, -20, 20, 40] self.distractors = [] if P.condition == 'line': for p in padding: rotation = self.ref_angle + p self.distractors.append( kld.Rectangle(width=self.item_size, height=self.target_height, fill=WHITE, rotation=rotation)) else: for p in padding: colour = self.palette.color_from_angle(self.ref_angle + p) self.distractors.append( kld.Rectangle(width=self.item_size, height=self.target_height, fill=colour))
def setup(self): box_size = deg_to_px(1.8) box_thick = deg_to_px(0.05) stim_size = deg_to_px(0.95) stim_thick = deg_to_px(0.1) fix_size = deg_to_px(1) fix_offset = deg_to_px(2.8) box_stroke = [box_thick, WHITE, STROKE_CENTER] self.txtm.add_style(label='greentext', color=GREEN) self.target = kld.Annulus(diameter=stim_size, thickness=stim_thick, fill=WHITE) self.distractor = kld.FixationCross(size=stim_size, thickness=stim_thick, fill=WHITE) # Set the rotation of placeholder boxes & fixation to match that of the display (diamond, square) rotate = 45 if P.condition == 'diamond' else 0 self.placeholder = kld.Rectangle(width=box_size, stroke=box_stroke, rotation=rotate) self.fixation = kld.Asterisk(size=fix_size, thickness=stim_thick, fill=WHITE, rotation=rotate, spokes=8) # Which locations are labelled far or near is dependent on arrangement of display # 'near' locations refer to those that lie at the intersection of 'far' locations if P.condition == 'square': self.far_locs = { 1: [ point_pos(P.screen_c, amplitude=fix_offset, angle=315, clockwise=True), 'NorthEast' ], 2: [ point_pos(P.screen_c, amplitude=fix_offset, angle=45, clockwise=True), 'SouthEast' ], 3: [ point_pos(P.screen_c, amplitude=fix_offset, angle=135, clockwise=True), 'SouthWest' ], 4: [ point_pos(P.screen_c, amplitude=fix_offset, angle=225, clockwise=True), 'NorthWest' ] } self.near_locs = { 5: [midpoint(self.far_locs[4][0], self.far_locs[1][0]), 'North'], 6: [midpoint(self.far_locs[1][0], self.far_locs[2][0]), 'East'], 7: [midpoint(self.far_locs[3][0], self.far_locs[2][0]), 'South'], 8: [midpoint(self.far_locs[3][0], self.far_locs[4][0]), 'West'] } else: # if P.condition == 'diamond' self.far_locs = { 1: [ point_pos(P.screen_c, amplitude=fix_offset, angle=270, clockwise=True), 'North' ], 2: [ point_pos(P.screen_c, amplitude=fix_offset, angle=0, clockwise=True), 'East' ], 3: [ point_pos(P.screen_c, amplitude=fix_offset, angle=90, clockwise=True), 'South' ], 4: [ point_pos(P.screen_c, amplitude=fix_offset, angle=180, clockwise=True), 'West' ] } self.near_locs = { 5: [ midpoint(self.far_locs[4][0], self.far_locs[1][0]), 'NorthWest' ], 6: [ midpoint(self.far_locs[1][0], self.far_locs[2][0]), 'NorthEast' ], 7: [ midpoint(self.far_locs[3][0], self.far_locs[2][0]), 'SouthEast' ], 8: [ midpoint(self.far_locs[3][0], self.far_locs[4][0]), 'SouthWest' ] } if not P.development_mode: self.keymap = KeyMap('directional_response', [ 'North', 'NorthEast', 'East', 'SouthEast', 'South', 'SouthWest', 'West', 'NorthWest' ], [ 'North', 'NorthEast', 'East', 'SouthEast', 'South', 'SouthWest', 'West', 'NorthWest' ], [ sdl2.SDLK_KP_8, sdl2.SDLK_KP_9, sdl2.SDLK_KP_6, sdl2.SDLK_KP_3, sdl2.SDLK_KP_2, sdl2.SDLK_KP_1, sdl2.SDLK_KP_4, sdl2.SDLK_KP_7 ]) else: # Don't have a numpad myself, so I need an alternative when developing self.keymap = KeyMap('directional_response', [ 'North', 'NorthEast', 'East', 'SouthEast', 'South', 'SouthWest', 'West', 'NorthWest' ], [ 'North', 'NorthEast', 'East', 'SouthEast', 'South', 'SouthWest', 'West', 'NorthWest' ], [ sdl2.SDLK_i, sdl2.SDLK_o, sdl2.SDLK_l, sdl2.SDLK_PERIOD, sdl2.SDLK_COMMA, sdl2.SDLK_m, sdl2.SDLK_j, sdl2.SDLK_u ]) # Prime items always presented in far locations self.prime_locs = self.far_locs.copy() # Probe items can be far or near, determined conditionally self.probe_locs = dict(self.near_locs.items() + self.far_locs.items()) # So, to get a practice block of 25, we first need to generate the full set of 2048 # possible permutations, trim that down to the 288 legitimate permutations, # then trim that down to 25... self.insert_practice_block(1, 2048) # Because KLibs auto-generates trials for each product of ind_vars.py, # a lot of 'vestigial' trials are generated that we don't want, this sorts # through the trial list and removes those trials. for ind_b, block in enumerate(self.trial_factory.blocks): for trial in block: # targets & distractors cannot overlap within a given display if trial[1] == trial[2] or trial[3] == trial[4]: self.trial_factory.blocks[ind_b].remove(trial) block.i -= 1 block.length = len(block.trials) continue # For 'near' trials, Ts & Ds cannot appear at 'far' locations if trial[0] == 'near': if trial[3] < 5 or trial[4] < 5: self.trial_factory.blocks[ind_b].remove(trial) block.i -= 1 block.length = len(block.trials) # Conversely, cannot appear at 'near' locations on 'far' trials else: if trial[3] > 4 or trial[4] > 4: self.trial_factory.blocks[ind_b].remove(trial) block.i -= 1 block.length = len(block.trials) # We only want 25 trials for practice, this trims the block # to the appropriate length if ind_b == 0: for trial in block: self.trial_factory.blocks[ind_b].remove(trial) block.i -= 1 block.length = len(block.trials) if block.length == 25: break # Set to True once instructions are provided self.instructed = False
def setup(self): # Stimulus sizes fixation_size = deg_to_px(0.5) fixation_thickness = deg_to_px(0.05) cue_size = deg_to_px(0.5) cue_thickness = deg_to_px(0.05) arrow_tail_len = deg_to_px(0.35) arrow_tail_width = deg_to_px(0.1) arrow_head_len = deg_to_px(0.2) arrow_head_width = deg_to_px(0.3, even=True) # Stimuli self.fixation = kld.FixationCross(fixation_size, fixation_thickness, fill=BLACK) self.cue = kld.Asterisk(cue_size, thickness=cue_thickness, fill=BLACK, spokes=8) self.arrow_l = kld.Arrow(arrow_tail_len, arrow_tail_width, arrow_head_len, arrow_head_width, fill=BLACK, rotation=180) self.arrow_r = kld.Arrow(arrow_tail_len, arrow_tail_width, arrow_head_len, arrow_head_width, fill=BLACK) self.line = kld.Rectangle(arrow_tail_len + arrow_head_len, arrow_tail_width, stroke=[0, BLACK, 1], fill=BLACK) self.arrow_l.render() self.arrow_r.render() # Layout height_offset = deg_to_px(1.06) flanker_offset = arrow_tail_len + arrow_head_len + deg_to_px(0.06) self.above_loc = (P.screen_c[0], P.screen_c[1] - height_offset) self.below_loc = (P.screen_c[0], P.screen_c[1] + height_offset) self.above_flanker_locs = [] self.below_flanker_locs = [] for offset in [-2, -1, 1, 2]: x_pos = P.screen_c[0] + (offset * flanker_offset) self.above_flanker_locs.append((x_pos, self.above_loc[1])) self.below_flanker_locs.append((x_pos, self.below_loc[1])) # Initialize feedback messages for practice block timeout_msg = message('Too slow! Please try to respond more quickly.', blit_txt=False) incorrect_str = ( "Incorrect response!\n" "Please respond to left arrows with the 'z' key and right arrows with the '/' key." ) incorrect_msg = message(incorrect_str, align='center', blit_txt=False) self.feedback_msgs = { 'incorrect': incorrect_msg, 'timeout': timeout_msg } # Set up Response Collector to get keypress responses self.rc.uses(KeyPressResponse) self.rc.terminate_after = [1700, TK_MS ] # response period times out after 1700ms self.rc.keypress_listener.interrupts = True self.rc.keypress_listener.key_map = {'z': 'left', '/': 'right'} # Add practice block of 24 trials to start of experiment if P.run_practice_blocks: self.insert_practice_block(1, trial_counts=24)
def setup(self): # ---------------------------------- # # Setup Stimuli # # ---------------------------------- # # Set stimulus sizes line_length = deg_to_px(2) line_thickness = deg_to_px(0.5) thick_rect_border = deg_to_px(0.7) thin_rect_border = deg_to_px(0.3) fix_size = deg_to_px(0.6) fix_thickness = deg_to_px(0.1) square_size = deg_to_px(3) large_text_size = deg_to_px(0.65) # Stimulus layout box_offset = deg_to_px(8.0) self.left_box_loc = (P.screen_c[0] - box_offset, P.screen_c[1]) self.right_box_loc = (P.screen_c[0] + box_offset, P.screen_c[1]) # Generate target colouring # Select target colours from randomly rotated colourwheel # ensuring those selected are unique and equidistant self.color_selecter = kld.ColorWheel(diameter=1, rotation=random.randrange(0, 360)) self.target_colours = [] for i in (0, 120, 240): self.target_colours.append(self.color_selecter.color_from_angle(i)) # Assign colours to payout valences random.shuffle(self.target_colours) self.high_value_colour = self.target_colours[0] self.low_value_colour = self.target_colours[1] self.neutral_value_colour = self.target_colours[2] # Initialize drawbjects self.thick_rect = kld.Rectangle( square_size, stroke=[thick_rect_border, WHITE, STROKE_CENTER]) self.thin_rect = kld.Rectangle( square_size, stroke=[thin_rect_border, WHITE, STROKE_CENTER]) self.high_val_rect = kld.Rectangle( square_size, stroke=[thin_rect_border, self.high_value_colour, STROKE_CENTER]) self.low_val_rect = kld.Rectangle( square_size, stroke=[thin_rect_border, self.low_value_colour, STROKE_CENTER]) self.fixation = kld.Asterisk(fix_size, fix_thickness, fill=WHITE) self.fix_cueback = kld.Asterisk(fix_size * 2, fix_thickness * 2, fill=WHITE) self.go = kld.FixationCross(fix_size, fix_thickness, fill=BLACK) self.nogo = kld.FixationCross(fix_size, fix_thickness, fill=BLACK, rotation=45) self.flat_line = kld.Rectangle(line_length, line_thickness, fill=BLACK) self.tilt_line = kld.Rectangle(line_length, line_thickness, fill=BLACK, rotation=45) self.probe = kld.Ellipse(int(0.75 * square_size)) # ---------------------------------- # # Setup other experiment factors # # ---------------------------------- # # COTOA = Cue-Offset|Target-Onset Asynchrony self.cotoa = 800 # ms self.feedback_exposure_period = 1.25 # sec # Training block payout variables self.high_payout_baseline = 12 self.low_payout_baseline = 8 self.total_score = None self.penalty = -5 # ---------------------------------- # # Setup Response Collectors # # ---------------------------------- # # Initialize response collectors self.probe_rc = ResponseCollector(uses=RC_KEYPRESS) self.training_rc = ResponseCollector(uses=RC_KEYPRESS) # Initialize ResponseCollector keymaps self.training_keymap = KeyMap( 'training_response', # Name ['z', '/'], # UI labels ["left", "right"], # Data labels [sdl2.SDLK_z, sdl2.SDLK_SLASH] # SDL2 Keysyms ) self.probe_keymap = KeyMap('probe_response', ['spacebar'], ["pressed"], [sdl2.SDLK_SPACE]) # --------------------------------- # # Setup Experiment Messages # # --------------------------------- # # Make default font size larger self.txtm.add_style('myText', large_text_size, WHITE) err_txt = "{0}\n\nPress any key to continue." lost_fixation_txt = err_txt.format( "Eyes moved! Please keep your eyes on the asterisk.") probe_timeout_txt = err_txt.format( "No response detected! Please respond as fast and as accurately as possible." ) training_timeout_txt = err_txt.format("Line response timed out!") response_on_nogo_txt = err_txt.format( "\'nogo\' signal (x) presented\nPlease only respond when you see " "the \'go\' signal (+).") self.err_msgs = { 'fixation': message(lost_fixation_txt, 'myText', align='center', blit_txt=False), 'probe_timeout': message(probe_timeout_txt, 'myText', align='center', blit_txt=False), 'training_timeout': message(training_timeout_txt, 'myText', align='center', blit_txt=False), 'response_on_nogo': message(response_on_nogo_txt, 'myText', align='center', blit_txt=False) } self.rest_break_txt = err_txt.format( "Whew! that was tricky eh? Go ahead and take a break before continuing." ) self.end_of_block_txt = "You're done the first task! Please buzz the researcher to let them know!" # -------------------------------- # # Setup Eyelink boundaries # # -------------------------------- # fix_bounds = [P.screen_c, square_size / 2] self.el.add_boundary('fixation', fix_bounds, CIRCLE_BOUNDARY) # --------------------------------- # # Insert training block (line task) # # --------------------------------- # if P.run_practice_blocks: self.insert_practice_block(1, trial_counts=P.trials_training_block)
def setup(self): # Initialize text styles self.txtm.add_style('normal', '0.7deg') self.txtm.add_style('title', '1.0deg') self.txtm.add_style('stream', '1.5deg') # Stimulus sizes mask_size = deg_to_px(1.5) mask_thick = deg_to_px(0.25) acc_size = deg_to_px(0.5) acc_offset = deg_to_px(3.0) arrow_tail_l = deg_to_px(0.5) arrow_tail_w = deg_to_px(0.2) arrow_head_l = deg_to_px(0.3) arrow_head_w = deg_to_px(0.5, even=True) box_size = deg_to_px(2.0) box_stroke = deg_to_px(0.2) # Generate shape stimuli for instructions self.arrow = kld.Arrow(arrow_tail_l, arrow_tail_w, arrow_head_l, arrow_head_w) self.arrow.fill = P.default_color self.target_box = kld.Rectangle(box_size, stroke=[box_stroke, P.default_color]) # Generate shape stimuli for task self.accuracy_rect = kld.Rectangle(acc_offset, acc_offset + acc_size * 2, fill=P.default_color) self.accuracy_mask = kld.Rectangle(acc_offset + 2, acc_offset, fill=P.default_fill_color) self.mask = kld.Asterisk(mask_size, mask_thick, fill=P.default_color) # Select random letters from the alphabet and render for use in task alphabet = list('ABCDEFGHIJKLMNOPQRSTUVWXYZ') random.shuffle(alphabet) self.letter_set = alphabet[:P.set_size] self.letters = {} for letter in self.letter_set: self.letters[letter] = message(letter, style='stream', blit_txt=False) # Initialize thought probes self.probe_condition = P.condition_map[P.condition] self.probe = self._init_probe(self.probe_condition) # Determine proportion of non-nback trials is_nback = self.trial_factory.exp_factors['is_target'] self.nback_rate = sum(is_nback) / len(is_nback) # Determine order of difficulty levels (counterbalancing) self.first_nback = random.choice([1, 2]) # Show task instructions and example thought probe self.instructions() # Add practice blocks to start of task if P.run_practice_blocks: self.insert_practice_block(1, trial_counts=48) self.insert_practice_block(2, trial_counts=48)
def setup(self): # Initialize stimulus sizes mask_size_x = deg_to_px(3.0) mask_size_ring = deg_to_px(4.0) mask_thick = deg_to_px(0.3) # Initialize text styles self.txtm.add_style('normal', '0.7deg') self.txtm.add_style('title', '1.0deg') self.mask_x = kld.Asterisk(mask_size_x, mask_thick, fill=P.default_color, spokes=8) self.mask_ring = kld.Annulus(mask_size_ring, mask_thick, fill=P.default_color) self.digits = {} digits = [1, 2, 3, 4, 5, 6, 7, 8, 9] for digit in digits: self.digits[digit] = {} self.sizes = ['1.5deg', '2.0deg', '2.5deg', '3.0deg', '3.5deg'] for size in self.sizes: self.txtm.add_style(size, size) for digit in digits: for size in self.sizes: self.digits[digit][size] = message(str(digit), style=size, blit_txt=False) # Initialize thought probes p_title = message(P.probe_question, "title", blit_txt=False) p_responses = P.probe_responses p_order = P.probe_order p_origin = (P.screen_c[0], P.screen_x // 10) self.probe = ThoughtProbe(p_responses, p_title, int(P.screen_x * 0.8), p_origin, p_order) # Initialize effort probes qeffort_text = "How much effort were you putting into staying on-task?" self.effort_q = message(qeffort_text, "title", blit_txt=False) self.mineffort_msg = message("0%", "normal", blit_txt=False) self.maxeffort_msg = message("100%", "normal", blit_txt=False) self.submit_msg = message("Continue", "normal", blit_txt=False) pad = int(self.submit_msg.height * 0.75) self.submit = Button(self.submit_msg, int(self.submit_msg.width + pad * 1.5), self.submit_msg.height + pad, registration=8, location=(P.screen_c[0], int(P.screen_y * 0.8))) # Randomly distribute probes across session, avoiding placing them less than 20 sec. apart self.probe_trials = [] noprobe_span = [False] * P.noprobe_span probe_span = [True] + [False] * (P.probe_span - 1) while len(self.probe_trials) < (P.trials_per_block * P.blocks_per_experiment): random.shuffle(probe_span) self.probe_trials += noprobe_span + probe_span # Show task instructions and example thought probe self.instructions() # Add practice blocks to start of task self.first_nonpractice = 1 if P.run_practice_blocks: num_nonpractice = P.blocks_per_experiment self.insert_practice_block(1, trial_counts=9, factor_mask={'number': range(1, 10)}) self.insert_practice_block(2, trial_counts=9, factor_mask={'number': range(1, 10)}) self.first_nonpractice = P.blocks_per_experiment - num_nonpractice + 1