def start_trial_button(self): fill() blit(self.next_trial_box, 5, self.next_trial_button_loc, flip_x=P.flip_x) blit(self.next_trial_msg, 5, self.next_trial_button_loc, flip_x=P.flip_x) flip() if P.demo_mode or P.dm_always_show_cursor: show_mouse_cursor() flush() clicked = False while not clicked: event_queue = pump(True) for e in event_queue: if e.type == SDL_MOUSEBUTTONDOWN: clicked = self.within_boundary("next trial button", [e.button.x, e.button.y]) elif e.type == SDL_KEYDOWN: ui_request(e.key.keysym) if not (P.demo_mode or P.dm_always_show_cursor): hide_mouse_cursor()
def imagery_trial(self): fill() blit(self.origin_inactive, 5, self.origin_pos, flip_x=P.flip_x) flip() start = self.evm.trial_time if P.demo_mode or P.dm_always_show_cursor: show_mouse_cursor() at_origin = False while not at_origin: x, y, button = mouse_pos(return_button_state=True) left_button_down = button == 1 if self.within_boundary('origin', (x, y)) and left_button_down: at_origin = True self.rt = self.evm.trial_time - start ui_request() fill() blit(self.origin_active, 5, self.origin_pos, flip_x=P.flip_x) flip() while at_origin: x, y, button = mouse_pos(return_button_state=True) left_button_down = button == 1 if not (self.within_boundary('origin', (x, y)) and left_button_down): at_origin = False self.mt = self.evm.trial_time - (self.rt + start) if P.demo_mode: hide_mouse_cursor()
def trial_prep(self): # Grab locations (and their cardinal labels) for each T & D self.T_prime_loc = list(self.prime_locs[self.prime_target]) self.D_prime_loc = list(self.prime_locs[self.prime_distractor]) self.T_probe_loc = list(self.probe_locs[self.probe_target]) self.D_probe_loc = list(self.probe_locs[self.probe_distractor]) # Grab distance between each item pair self.T_prime_to_T_probe = line_segment_len(self.T_prime_loc[0], self.T_probe_loc[0]) self.T_prime_to_D_probe = line_segment_len(self.T_prime_loc[0], self.D_probe_loc[0]) self.D_prime_to_T_probe = line_segment_len(self.D_prime_loc[0], self.T_probe_loc[0]) self.D_prime_to_D_probe = line_segment_len(self.D_prime_loc[0], self.D_probe_loc[0]) # Once locations selected, determine which trial type this trial would fall under. self.trial_type = self.determine_trial_type() # Hide mouse cursor throughout trial hide_mouse_cursor() # Present fixation & start trial self.present_fixation()
def trial(self): # Set to true once played (to avoid repeats) tone_played = False # Prior to target onset, present fixation, tone & cue (when applicable) while self.evm.before('target_on'): fill() blit(self.fixation, registration=5, location=P.screen_c) # Tone if self.tone_trial and self.evm.between('tone_on', 'tone_off'): if not tone_played: self.warning_tone.play() tone_played = True # Cue if self.cue_location is not None and self.evm.between( 'cue_on', 'cue_off'): loc = self.cue_locations[self.cue_location] blit(self.cue, registration=5, location=loc) flip() # For some reason the cursor reappears here... hide_mouse_cursor() # Present target fill() blit(self.fixation, registration=5, location=P.screen_c) for shape, loc in self.arrows: blit(shape, registration=5, location=loc) flip() # Listen for response self.rc.collect() # Record response value & rt response, rt = self.rc.keypress_listener.response() # If no (valid) response made before timeout if rt == klibs.TIMEOUT: response = 'na' # Label response as correct/incorrect accuracy = int(response == self.target_direction) # Record trial data to database return { "block_num": P.block_number, "trial_num": P.trial_number, "practicing": P.practicing, "tone_trial": self.tone_trial, "tone_onset": self.tone_onset, "cue_type": self.cue_type, "congruent": self.congruent, "target_location": self.target_location, "target_direction": self.target_direction, "accuracy": accuracy, "response": response, "rt": rt }
def trial_prep(self): # Establish cue condition based on cue validity if self.cue_type == 'valid': self.cue_location = self.target_location elif self.cue_type == 'none': self.cue_location = None else: self.cue_location = 'above' if self.target_location == 'below' else 'below' # Establish remaining trial factors self.target_direction = random.choice(['left', 'right']) self.tone_onset = random.randrange(400, 1650, 50) # Generate target & flanker arrows self.arrows = self.generate_arrows() # Establish sequence of events events = [] events.append(['tone_on', self.tone_onset]) events.append(['tone_off', events[-1][1] + 50]) events.append(['cue_on', events[-1][1] + 350]) events.append(['cue_off', events[-1][1] + 50]) events.append(['target_on', events[-1][1] + 50]) # Register sequence w/ event manager for e in events: self.evm.register_ticket(e) # Hide cursor during trial hide_mouse_cursor()
def get_effort(self): slider_loc = (P.screen_c[0], int(P.screen_y * 0.55)) slider_cols = {'line': WHITE, 'slider': TRANSLUCENT_WHITE} scale = Slider(int(P.screen_x * 0.75), ticks=5, location=slider_loc, fills=slider_cols) label_pad = scale.tick.surface_height show_mouse_cursor() onset = time.time() while True: sq = pump(True) ui_request(queue=sq) fill() blit(self.effort_q, 5, (P.screen_c[0], int(P.screen_y * 0.3))) blit(self.mineffort_msg, 8, (scale.xmin, slider_loc[1] + label_pad)) blit(self.maxeffort_msg, 8, (scale.xmax, slider_loc[1] + label_pad)) scale.draw() if scale.pos != None: self.submit.draw() flip() scale.listen(sq) if scale.pos != None: if self.submit.listen(sq) or key_pressed('Return', queue=sq): rt = time.time() - onset hide_mouse_cursor() return (scale.pos, rt)
def drift_correct(self, location=None, target=None, fill_color=None, draw_target=True): """Checks the accuracy of the eye tracker's calibration by presenting a fixation stimulus and requiring the participant to press the space bar while looking directly at it. If there is a large difference between the gaze location at the time the key was pressed and the true location of the fixation, it indicates that there has been drift in the calibration. In TryLink mode, drift correct targets are still displayed the same as with a hardware eye tracker. Simulated drift corrects are performed by clicking the drift correct target with the mouse. Args: location (Tuple(int, int), optional): The (x,y) pixel coordinates where the drift correct target should be located. Defaults to the center of the screen. target: A :obj:`Drawbject` or other :func:`KLGraphics.blit`-able shape to use as the drift correct target. Defaults to a circular :func:`drift_correct_target`. fill_color: A :obj:`List` or :obj:`Tuple` containing an RGBA colour to use for the background for the drift correct screen. Defaults to the value of ``P.default_fill_color``. draw_target (bool, optional): A flag indicating whether the function should draw the drift correct target itself (True), or whether it should leave it to the programmer to draw the target before :meth:`drift_correct` is called (False). Defaults to True. """ show_mouse_cursor() target = drift_correct_target() if target is None else target draw_target = EL_TRUE if draw_target in [EL_TRUE, True] else EL_FALSE location = P.screen_c if location is None else location if not iterable(location): raise ValueError("'location' must be a pair of (x,y) pixel coordinates.") dc_boundary = CircleBoundary('drift_correct', location, P.screen_y // 30) while True: event_queue = pump(True) ui_request(queue=event_queue) if draw_target == EL_TRUE: fill(P.default_fill_color if not fill_color else fill_color) blit(target, 5, location) flip() else: SDL_Delay(2) # required for pump() to reliably return mousebuttondown events for e in event_queue: if e.type == SDL_MOUSEBUTTONDOWN and dc_boundary.within([e.button.x, e.button.y]): hide_mouse_cursor() if draw_target == EL_TRUE: fill(P.default_fill_color if not fill_color else fill_color) flip() return 0
def collect(self): show_mouse_cursor() response = None onset = time.time() while response == None: fill() self._render() flip() response = self._collect() rt = time.time() - onset hide_mouse_cursor() return Response(response, rt)
def __trial__(self, trial, practice): """ Private method; manages a trial. """ from klibs.KLUtilities import pump, show_mouse_cursor, hide_mouse_cursor # At start of every trial, before setup_response_collector or trial_prep are run, retrieve # the values of the independent variables (factors) for that trial (as generated earlier by # TrialFactory) and set them as attributes of the experiment object. factors = list(self.trial_factory.exp_factors.keys()) for iv in factors: iv_value = trial[factors.index(iv)] setattr(self, iv, iv_value) pump() self.setup_response_collector() self.trial_prep() tx = None try: if P.development_mode and (P.dm_trial_show_mouse or (P.eye_tracking and not P.eye_tracker_available)): show_mouse_cursor() self.evm.start_clock() if P.eye_tracking and not P.manual_eyelink_recording: self.el.start(P.trial_number) P.in_trial = True self.__log_trial__(self.trial()) P.in_trial = False if P.eye_tracking and not P.manual_eyelink_recording: self.el.stop() if P.development_mode and (P.dm_trial_show_mouse or (P.eye_tracking and not P.eye_tracker_available)): hide_mouse_cursor() self.evm.stop_clock() self.trial_clean_up() except TrialException as e: P.trial_id = False self.trial_clean_up() self.evm.stop_clock() tx = e if P.eye_tracking and not P.manual_eyelink_recording: # todo: add a warning, here, if the recording hasn't been stopped when under manual control self.el.stop() if tx: raise tx
def collect_response(self): self.start = time.time() finished = False selection = None last_selected = None flush() mt_start = None while not finished: show_mouse_cursor() events = pump(True) for e in events: if e.type == sdl2.SDL_KEYDOWN: ui_request(e.key.keysym) elif e.type == sdl2.SDL_MOUSEBUTTONDOWN: selection = None for b in self.buttons: if self.within_boundary(b.button_text, [e.button.x, e.button.y]): self.toggle(b) if not self.rt: self.rt = time.time() - self.start mt_start = time.time() if b.active: selection = b last_selected = b if callable(b.callback): if self.finish_b is None: return b.callback else: b.callback() try: if self.finish_b.active and self.within_boundary( "Done", [e.button.x, e.button.y]): self.response = int(last_selected.button_text) self.mt = time.time() - mt_start finished = True except AttributeError: pass try: self.finish_b.active = selection is not None except AttributeError: pass self.render() fill() flip() hide_mouse_cursor()
def collect(self): show_mouse_cursor() onset = time.time() while self.scale.response == None: q = pump(True) ui_request(queue=q) fill() blit(self.q, location=self.origin, registration=8) self.scale.response_listener(q) flip() response = self.scale.response rt = time.time() - onset hide_mouse_cursor() self.scale.response = None # reset for next time return Response(response, rt)
def trial_prep(self): if self.search_type == SPACE: self.search_stimuli = self.prepare_spatial_array() self.rc.display_callback = self.present_spatial_array self.rc.display_kwargs = {'spatial_array': self.search_stimuli} else: self.search_stimuli = self.prepare_temporal_stream() self.rc.display_callback = self.present_temporal_stream self.rc.display_kwargs = {'temporal_stream': self.search_stimuli} events = [[1000, 'present_example_target']] events.append([events[-1][0] + 1000, 'present_fixation']) events.append([events[-1][0] + 1000, 'search_onset']) for e in events: self.evm.register_ticket(ET(e[1], e[0])) self.trial_sw = Stopwatch() self.present_target() hide_mouse_cursor()
def trial(self): hide_mouse_cursor() while self.evm.before('cue_on'): ui_request() self.display_refresh(cue=self.cue_type, tone=self.tone_trial) while self.evm.before('cue_off'): ui_request() self.display_refresh() while self.evm.before('target_on'): ui_request() self.rc.collect() if self.rc.keypress_listener.response_count != 0: response, rt = self.rc.keypress_listener.response() else: response, rt = NA, NA self.present_feedback(rt=rt, response=response) clear() return { "block_num": P.block_number, "trial_num": P.trial_number, "practicing": str(P.practicing), 'cue_type': self.cue_type, 'ctoa': self.ctoa, 'target_loc': self.target_loc, 'target_side': self.target_side, 'tone_trial': str(self.tone_trial), 'response': response, 'rt': rt }
def drift_correct(self, location=None, target=None, fill_color=None, draw_target=True): """Checks the accuracy of the EyeLink's calibration by presenting a fixation stimulus and requiring the participant to press the space bar while looking directly at it. If there is a large difference between the gaze location at the time the key was pressed and the true location of the fixation, it indicates that there has been drift in the calibration. On older EyeLink models (EyeLink I & II), the recorded drift is used to adjust the calibration for improved accuracy on future trials. On recent models (EyeLink 1000 and up), drift corrections will *check* for drift and prompt the participant to try again if the drift is large, but they do not affect the tracker's calibration. Args: location (Tuple(int, int), optional): The (x,y) pixel coordinates where the drift correct target should be located. Defaults to the center of the screen. target: A :obj:`Drawbject` or other :func:`KLGraphics.blit`-able shape to use as the drift correct target. Defaults to a circular :func:`drift_correct_target`. fill_color: A :obj:`List` or :obj:`Tuple` containing an RGBA colour to use for the background for the drift correct screen. Defaults to the value of ``P.default_fill_color``. draw_target (bool, optional): A flag indicating whether the function should draw the drift correct target itself (True), or whether it should leave it to the programmer to draw the target before :meth:`drift_correct` is called (False). Defaults to True. Raises: TrialException: If repeated EyeLink errors are encountered while attempting to perform the drift correct. """ hide_mouse_cursor() target = drift_correct_target() if target is None else target draw_target = EL_TRUE if draw_target in [EL_TRUE, True] else EL_FALSE location = P.screen_c if location is None else location if not valid_coords(location): raise ValueError("'location' must be a pair of (x,y) pixel coordinates.") try: while True: if draw_target == EL_TRUE: fill(P.default_fill_color if not fill_color else fill_color) blit(target, 5, location) flip() ret = self.doDriftCorrect(location[0], location[1], draw_target, EL_TRUE) if ret != 27: # 27 means we hit Esc to enter calibration, so redo drift correct break if draw_target == EL_TRUE: fill(P.default_fill_color if not fill_color else fill_color) flip() return self.applyDriftCorrect() except RuntimeError: try: self.setOfflineMode() except RuntimeError: self._unresolved_exceptions += 1 if self._unresolved_exceptions > 5: cso("\n<red>*** Fatal Error: Unresolvable EyeLink Error ***</red>") print(full_trace()) self._unresolved_exceptions = 0 raise TrialException("EyeLink not ready.") return self.drift_correct(location, target, fill_color, draw_target)
def give_instructions(self): button_map = { 'North': message("8", align='center', blit_txt=False, style='greentext'), 'East': message("6", align='center', blit_txt=False, style='greentext'), 'South': message("2", align='center', blit_txt=False, style='greentext'), 'West': message("4", align='center', blit_txt=False, style='greentext'), 'NorthEast': message("9", align='center', blit_txt=False, style='greentext'), 'NorthWest': message("7", align='center', blit_txt=False, style='greentext'), 'SouthWest': message("1", align='center', blit_txt=False, style='greentext'), 'SouthEast': message("3", align='center', blit_txt=False, style='greentext') } hide_mouse_cursor() txt = ( "In this experiment, your task is to indicate the location of the target 'o'\n" "while ignoring the distractor '+'." "\n\n(press the '5' on the numpad to continue past each message)") instruction_msg = message(txt, align='center', blit_txt=False) fill() blit(instruction_msg, location=P.screen_c, registration=5) flip() self.continue_on() txt = ( "Each trial will begin with a fixation cross, when you see this\n" "you may begin the trial by pressing the '5' key on the numpad.\n" "Shortly after which an array will appear") instruction_msg = message(txt, align='center', blit_txt=False) fill() blit(instruction_msg, location=(P.screen_c[0], int(P.screen_c[1] * 0.3)), registration=5) blit(self.fixation, location=P.screen_c, registration=5) flip() self.continue_on() fill() blit(instruction_msg, location=(P.screen_c[0], int(P.screen_c[1] * 0.3)), registration=5) for value in self.probe_locs.values(): blit(self.placeholder, registration=5, location=value[0]) blit(self.fixation, location=P.screen_c, registration=5) flip() self.continue_on() txt = ( "Shortly after the array appears, both the target 'o' and distractor '+'\n" "will appear in random locations within the array...") instruction_msg = message(txt, align='center', blit_txt=False) t_loc = self.prime_locs[1][0] d_loc = self.prime_locs[3][0] fill() blit(instruction_msg, location=(P.screen_c[0], int(P.screen_c[1] * 0.3)), registration=5) for value in self.probe_locs.values(): blit(self.placeholder, registration=5, location=value[0]) blit(self.target, location=t_loc, registration=5) blit(self.distractor, location=d_loc, registration=5) blit(self.fixation, location=P.screen_c, registration=5) flip() self.continue_on() txt = ( "Once they appear, please indicate the location of the 'o' as quickly and\n" "accurately as possible, using the numpad ('8' for North, '9' for Northeast, etc.,)\n" "Each trial will actually consist of two displays, each requiring their own response,\n" "one after the other") instruction_msg = message(txt, align='center', blit_txt=False) fill() blit(instruction_msg, location=(P.screen_c[0], int(P.screen_c[1] * 0.3)), registration=5) for value in self.probe_locs.values(): blit(self.placeholder, registration=5, location=value[0]) blit(button_map[value[1]], registration=5, location=value[0]) blit(self.target, location=t_loc, registration=5) blit(self.distractor, location=d_loc, registration=5) blit(self.fixation, location=P.screen_c, registration=5) flip() self.continue_on() txt = ( "Once you have made both responses, you will be provided with feedback,\n" "the upper and lower line referring to your performance\n" "in the first and second display, respectively.\n" "Please press spacebar to skip past the feedback display") instruction_msg = message(txt, align='center', blit_txt=False) fill() blit(instruction_msg, location=P.screen_c, registration=5) flip() self.continue_on() txt = "For correct responses, your reaction time will be provided to you." fb_txt = "360\n412" instruction_msg = message(txt, align='center', blit_txt=False) fb_msg = message(fb_txt, align='center', blit_txt=False) fill() blit(instruction_msg, location=(P.screen_c[0], int(P.screen_c[1] * 0.3)), registration=5) blit(fb_msg, location=P.screen_c, registration=5) flip() self.continue_on() txt = "For incorrect responses, your reaction time will be replaced by the word WRONG." fb_txt = "323\nWRONG" instruction_msg = message(txt, align='center', blit_txt=False) fb_msg = message(fb_txt, align='center', blit_txt=False) fill() blit(instruction_msg, location=(P.screen_c[0], int(P.screen_c[1] * 0.3)), registration=5) blit(fb_msg, location=P.screen_c, registration=5) flip() self.continue_on() continue_txt = ( "Throughout the task, please keep your fingers rested on the numpad,\n" "with your middle finger resting on the '5' key\n\n" "The experiment will begin with a short practice round to familiarize you with the task\n\n" "When you're ready, press the '5' key to begin...") continue_msg = message(continue_txt, align='center', blit_txt=False) fill() blit(continue_msg, location=P.screen_c, registration=5) flip() self.continue_on()
def trial(self): hide_mouse_cursor() # Begin with empty array... self.present_empty_array() smart_sleep(500) # 500ms later present prime array & record response self.prime_rc.collect() # If response, log, otherwise NA response_prime, rt_prime = 'NA', 'NA' if len(self.prime_rc.keypress_listener.response()): response_prime, rt_prime = self.prime_rc.keypress_listener.response( ) # Reset to empty array following response self.present_empty_array() smart_sleep(300) # 300ms later present probe array self.probe_rc.collect() response_probe, rt_probe = 'NA', 'NA' if len(self.probe_rc.keypress_listener.response()): response_probe, rt_probe = self.probe_rc.keypress_listener.response( ) # Determine accuracy of responses (i.e., whether target selected) prime_correct = response_prime == self.T_prime_loc[1] probe_correct = response_probe == self.T_probe_loc[1] # Present feedback on performance (mean RT for correct, 'WRONG' for incorrect) self.present_feedback(prime_correct, rt_prime, probe_correct, rt_probe) prime_choice, probe_choice = 'NA', 'NA' if response_prime == self.T_prime_loc[1]: prime_choice = 'target' elif response_prime == self.D_prime_loc[1]: prime_choice = 'distractor' else: prime_choice = "empty_cell" if response_probe == self.T_probe_loc[1]: probe_choice = 'target' elif response_probe == self.D_probe_loc[1]: probe_choice = 'distractor' else: probe_choice = "empty_cell" return { "block_num": P.block_number, "trial_num": P.trial_number, "practicing": str(P.practicing), "far_near": self.far_or_near, 'trial_type': self.trial_type, 'prime_rt': rt_prime, 'probe_rt': rt_probe, 'prime_correct': str(prime_correct), 'probe_correct': str(probe_correct), 't_prime_to_t_probe': self.T_prime_to_T_probe, 't_prime_to_d_probe': self.T_prime_to_D_probe, 'd_prime_to_t_probe': self.D_prime_to_T_probe, 'd_prime_to_d_probe': self.D_prime_to_D_probe, 'prime_choice': prime_choice, 'probe_choice': probe_choice, 'prime_response': response_prime, 'probe_response': response_probe, 't_prime_loc': self.T_prime_loc[1], 'd_prime_loc': self.D_prime_loc[1], 't_probe_loc': self.T_probe_loc[1], 'd_probe_loc': self.D_probe_loc[1] }