Ejemplo n.º 1
0
    def block(self):
        if P.block_number == int(math.ceil(P.blocks_per_experiment/2.0))+1:
			util.flush()
			msg_text = "Whew! You're halfway done.\nTake a break, then press any key to continue."
			msg = message(msg_text, align="center", blit_txt=False)
			fill()
			blit(msg, registration=5, location=P.screen_c)
			flip()
			any_key()
Ejemplo n.º 2
0
    def trial(self):
        
        self.trial_time = time.time()        
        print(self.trial_articulations, self.response_articulations, self.duration, self.target_brightness)

        while self.evm.before('lines_on', True):
            fill()
            blit(self.fixation_light, 5, P.screen_c)
            flip()
            
        while self.evm.before('response_circle_on', True):
            self.start_time = time.time()
            target_on = self.evm.after('target_on') and self.evm.before('target_off')
            self.display_refresh(target_on)
            
            # Debug code for target timing, disabled by default
            self.elapsed = time.time() - self.start_time
            if 0.9 < (self.start_time - self.trial_time) <= 1.100 and self.debug_mode:
                print("init: %.3f" % (self.init_time - self.trial_time))
                if lines_on:
                    print("line: %.3f" % (self.line_time - self.trial_time))
                print("total: %.3f" % (self.elapsed))
        
        # Collect localization response and save response RT and response angle
        self.rc.collect()
        self.response_rt = self.rc.color_listener.response(False, True)
        self.deg_err = self.rc.color_listener.response(True, False)
        try:
            self.response_loc = (self.angle + self.deg_err) % 360
            print("Response accuracy: %.1f" % self.deg_err)
        except TypeError:
            self.response_loc = NA
        
        # Require participant to click center of screen to continue to next trial
        self.click_to_continue()

        return {
            "block_num": P.block_number,
            "trial_num": P.trial_number,
            "trial_articulations": self.trial_articulations,
            "response_articulations": self.response_articulations,
            "target_brightness": self.target_brightness,
            "duration": str(self.duration),
            "rt": self.response_rt,
            "target_refreshes": self.target_refreshes,
            "target_loc": self.angle,
            "response_loc": self.response_loc,
            "deg_err": self.deg_err
        }
Ejemplo n.º 3
0
 def click_to_continue(self):        
     util.show_mouse_cursor()
     wait_for_click = True
     while wait_for_click:
         fill()
         blit(self.next_trial_circle, 5, P.screen_c)
         flip()
         events = util.pump(True)
         ui_request(queue=events)
         for e in events:
             if e.type == sdl2.SDL_MOUSEBUTTONUP:
                 pos = (e.button.x, e.button.y)
                 if self.within_boundary("center", pos):
                     fill()
                     flip()
                     wait_for_click = False      
     util.hide_mouse_cursor()
Ejemplo n.º 4
0
 def articulation_callback(self):
     fill()
     blit(self.fixation, 5, P.screen_c)
     if self.response_articulations == True:
         blit(self.articulations, 5, P.screen_c)
     blit(self.response_ring, 5, P.screen_c)
     flip()
Ejemplo n.º 5
0
    def trial_prep(self):
		
        # Define timecourse of events for the trial
             
        target_on = random.choice(range(500, 2050, 50))

        events = [[400-P.refresh_time, 'lines_on']]
        events.append([events[-1][0] + target_on, 'target_on']) 
        events.append([events[-1][0] + self.duration, 'target_off'])
        events.append([events[-1][0] + 1000, 'response_circle_on'])
        for e in events:
            self.evm.register_ticket(ET(e[1], e[0]))
        
        # Randomly select angle of target for the trial, and configure response collector accordingly
        
        self.angle = random.choice(range(0, 360, 1))
        self.response_ring.rotation = self.angle
        self.target_pos = util.point_pos(P.screen_c, self.circle_radius, -90, self.angle)
        self.rc.color_listener.set_target(self.target_pos)
        
        # Initialize target asterisk with random opacity within range
        
        self.target_brightness = random.choice(range(BRIGHTNESS_MIN, BRIGHTNESS_MAX+1, 1))
        target_colour = [self.target_brightness]*3 + [255]
        #self.target = kld.Asterisk(self.target_width, fill=target_colour, thickness=self.default_stroke/2).render()
        self.target = kld.Ellipse(self.target_width, fill=target_colour).render()

        # Reset debug flags before trial starts
        
        self.circle_already_on = False
        self.target_already_on = False
        self.target_refreshes = 0
        
        # Enter trial with screen already at desired state
        
        fill()
        blit(self.fixation_light, 5, P.screen_c)
        flip()
Ejemplo n.º 6
0
 def display_refresh(self, target=False): 
     fill()
     blit(self.fixation, 5, P.screen_c)
     self.init_time = time.time() # for debug
     
     if self.trial_articulations:
         blit(self.articulations, 5, P.screen_c)
         self.line_time = time.time() # for debug
     
     if target:
         blit(self.target, 5, self.target_pos)
         self.target_refreshes += 1
     flip()
     
     if target and not self.target_already_on:
         self.target_ontime = time.time()
         print("target on! %.3f" % (self.target_ontime - self.trial_time))
         self.target_already_on = True   
     if not target and self.target_already_on:
         print("total target on-time: %.3f " % (time.time() - self.target_ontime))
         print("refreshes: {0} \n".format(self.target_refreshes))
         self.target_already_on = False
Ejemplo n.º 7
0
 def toj_callback(self):
     fill()
     blit(self.toj_prompts[self.toj_type], 5, P.screen_c)
     flip()
Ejemplo n.º 8
0
    def block(self):

        halfway_block = (P.blocks_per_experiment / 2) + 1
        if P.run_practice_blocks and P.session_number == 1:
            halfway_block += 3

        if P.run_practice_blocks and P.session_number == 1 and P.block_number <= 3:
            txt = "This is a practice block ({0} of 3)\n\n".format(
                P.block_number)
            if P.block_number == 1:
                txt += (
                    "During this task, arrows will appear either above or below the '+' symbol.\n"
                    "Your job will be to indicate the direction of the middle arrow as quickly\n"
                    "and accurately as possible using the keyboard.\n\n"
                    "( c = left,  m = right )")
            elif P.block_number == 2:
                txt += (
                    "On some trials, the central arrow will be displaced upwards or downwards "
                    "by a large amount.\n"
                    "When this occurs, please press the space bar instead of the 'c' or 'm' keys."
                )
                self.trial_type = 'EV'
                self.ev_offset = 'below'
                demo_arrows = self.generate_arrows()
            else:
                txt += (
                    "On some trials, a large red countdown timer will appear instead of the arrows."
                    "\nWhen this occurs, please press the space bar as quickly as you can."
                )

            instructions = message(txt, align='center', blit_txt=False)
            continue_msg = message('Press any key to begin.',
                                   align='center',
                                   blit_txt=False)
            instruction_time = CountDown(3)
            while True:
                keydown = key_pressed()
                fill()
                blit(instructions, 8, (P.screen_c[0], int(P.screen_y * 0.1)))
                if P.block_number == 2:
                    blit(self.fixation, 5, P.screen_c)
                    for shape, loc in demo_arrows:
                        blit(shape, 5, loc)
                elif P.block_number == 3:
                    elapsed = min(
                        [int(instruction_time.elapsed() * 1000), 367])
                    demo_counter = message(str(elapsed).zfill(4),
                                           "PVT",
                                           blit_txt=False)
                    blit(demo_counter, 5, P.screen_c)
                if P.development_mode or instruction_time.counting() == False:
                    blit(continue_msg, 2,
                         (P.screen_c[0], int(P.screen_y * 0.9)))
                    if keydown == True:
                        break
                flip()

        elif P.run_practice_blocks and P.session_number == 1 and P.block_number == 4:
            self.block_msg(
                "Practice complete! Press any key to begin the task.")

        elif P.block_number == 1:
            self.block_msg("Press any key to begin the task.")

        elif P.block_number == halfway_block:
            self.block_msg(
                "Phew, you're halfway done! Press any key to continue.")
Ejemplo n.º 9
0
	def blit_it(self, it):
		fill()
		blit(it, registration=5, location=P.screen_c)
		flip()
Ejemplo n.º 10
0
 def blit_img(self, img, reg, loc):
     fill()
     blit(img, registration=reg, location=loc)
     flip()
Ejemplo n.º 11
0
    def capture_figures(self):

        txt = (
            "Welcome to the TraceLab figure generator!\n\n"
            "Generated figures will be saved to 'ExpAssets/Resources/figures/'. {0}\n\n"
            "Press any key to begin.")
        auto_txt = "TraceLab will automatically exit when figure generation is complete."
        txt = txt.format(auto_txt if P.auto_generate else "")

        fill()
        message(txt,
                "default",
                registration=5,
                location=P.screen_c,
                align='center',
                blit_txt=True)
        flip()
        any_key()

        if P.development_mode:
            print("Random seed: {0}".format(P.random_seed))

        if P.auto_generate:

            num_figs = P.auto_generate_count
            for i in range(0, num_figs):

                txt = "Generating figure {0} of {1}...".format(i + 1, num_figs)
                msg = message(txt, "default", blit_txt=False)
                fill()
                blit(msg, 5, P.screen_c)
                flip()

                figure = self._generate_figure(duration=5000.0)
                figure.write_out("figure{0}_{1}.tlf".format(
                    i + 1, P.random_seed))

        else:

            done = False
            while not done:

                msg = message("Generating...", "default", blit_txt=False)
                fill()
                blit(msg, 5, P.screen_c)
                flip()

                figure = self._generate_figure(duration=5000.0)
                while True:

                    # Animate figure on screen with dot, then show full rendered shape
                    figure.animate()
                    animation_dur = round(figure.trial_a_frames[-1][2] * 1000,
                                          2)
                    msg = message("Press any key to continue.", blit_txt=False)
                    msg_time = message(
                        "Duration: {0} ms".format(animation_dur),
                        blit_txt=False)
                    fill()
                    figure.draw()
                    blit(msg, 3, (P.screen_x - 30, P.screen_y - 25))
                    blit(msg_time, 7, (30, 25))
                    flip()
                    any_key()

                    # Prompt participants whether they want to (q)uit, (r)eplay animation,
                    # (d)iscard figure and move to next, or (s)ave figure to file
                    resp = query(user_queries.experimental[6])
                    if resp == "r":  # replay
                        continue
                    elif resp == "d":  # discard
                        break
                    elif resp == "q":  # quit
                        done = True
                        break
                    elif resp == "s":  # save
                        f_name = query(user_queries.experimental[7]) + ".tlf"
                        msg = message("Saving... ", blit_txt=False)
                        fill()
                        blit(msg, 5, P.screen_c)
                        flip()
                        figure.write_out(f_name)
                        break
Ejemplo n.º 12
0
 def present_empty_array(self):
     fill()
     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()
Ejemplo n.º 13
0
 def display_refresh(self):
     # Clear the display and draw fixation
     fill()
     blit(self.fixation, 5, P.screen_c)
Ejemplo n.º 14
0
    def init_session(self):

        try:
            cols = [
                'id', 'session_structure', 'session_count',
                'sessions_completed', 'figure_set', 'handedness', 'created'
            ]
            user_data = self.db_select('participants',
                                       cols,
                                       where={'user_id': self.user_id})[0]
            self.restore_session(user_data)
        except IndexError as e:
            if query(uq.experimental[0]) == "y":
                self.user_id = query(uq.experimental[1])
                if self.user_id is None:
                    self.__generate_user_id()
                return self.init_session()
            else:
                fill()
                msg = message("Thanks for participating!",
                              "default",
                              blit_txt=False)
                blit(msg, 5, P.screen_c)
                flip()
                any_key()
                self.exp.quit()

        # Initialize figure paths for current participant/session
        p_dir = "p{0}_{1}".format(P.participant_id, self.exp.created)
        session_dir = "session_" + str(self.exp.session_number)
        self.exp.p_dir = os.path.join(P.data_dir, p_dir)
        self.exp.fig_dir = os.path.join(self.exp.p_dir, session_dir)

        # If any existing trial data for this session+participant, prompt experimenter whether to
        # delete existing data and redo session, continue from start of last completed block,
        # or continue to next session
        start_block, start_trial = self.__check_incomplete_session()

        # Load session structure. If participant already completed all sessions, show message
        # and quit.
        session_structure = P.session_structures[self.exp.session_structure]
        num_sessions = len(session_structure)
        if self.exp.session_number > num_sessions:
            txt1 = "Participant {0} has already completed all {1} sessions of the task."
            msg1 = message(txt1.format(self.user_id, num_sessions),
                           blit_txt=False)
            msg2 = message("Press any key to exit TraceLab.", blit_txt=False)
            fill()
            blit(msg1, 2, (P.screen_c[0], P.screen_c[1] - 30))
            blit(msg2, 8, P.screen_c)
            flip()
            any_key()
            self.exp.quit()

        # Parse block strings for current session
        current_session = session_structure[self.exp.session_number - 1]
        P.blocks_per_experiment = len(current_session)
        for block in current_session:
            cond = block if isinstance(block, str) else block[0]
            resp, fb = self.parse_exp_condition(cond)
            self.exp.block_factors.append({
                'response_type': resp,
                'feedback_type': fb
            })

        # Generate trials and import the figure set specified earlier
        self.init_figure_set()
        self.exp.blocks = self.__generate_blocks(current_session)
        self.exp.trial_factory.blocks = self.exp.blocks
        self.exp.trial_factory.dump()

        # If resuming incomplete session, skip ahead to last completed trial
        trimmed_start_block = self.exp.blocks.blocks[start_block -
                                                     1][(start_trial - 1):]
        self.exp.blocks.blocks[start_block - 1] = trimmed_start_block
        self.exp.blocks.i = start_block - 1  # skips ahead to start_block if specified

        # If needed, create figure folder for session
        if not os.path.exists(self.exp.fig_dir):
            os.makedirs(self.exp.fig_dir)

        # If session number > 1, log runtime info for session in runtime_info table
        if 'session_info' in self.db.table_schemas.keys(
        ) and self.exp.session_number > 1:
            runtime_info = EntryTemplate('session_info')
            for col, value in runtime_info_init().items():
                if col == 'session_number':
                    value = self.exp.session_number
                runtime_info.log(col, value)
            self.db.insert(runtime_info)

        self.db.update('participants', {'initialized': 1})
        self.log_session_init()
Ejemplo n.º 15
0
 def bandit_callback(self, before_go=False):
     fill()
     blit(self.star if before_go else self.star_muted, 5, P.screen_c)
     blit(self.left_bandit, 5, self.left_box_loc)
     blit(self.right_bandit, 5, self.right_box_loc)
Ejemplo n.º 16
0
 def present_neutral_boxes(self):
     fill()
     blit(self.star, 5, P.screen_c)
     blit(self.neutral_box, 5, self.left_box_loc)
     blit(self.neutral_box, 5, self.right_box_loc)
Ejemplo n.º 17
0
 def show_error_message(self, msg_key):
     fill()
     blit(self.err_msgs[msg_key], location=P.screen_c, registration=5)
     flip()
     any_key()
Ejemplo n.º 18
0
    def trial(self):

        if P.development_mode:
            trial_info = (
                "\ntrial_type: '{0}', high_val_loc: '{1}', probe_loc: '{2}', "
                "cue_loc: '{3}', winning_bandit: '{4}'")
            print(
                trial_info.format(self.trial_type, self.high_value_location,
                                  self.probe_location, self.cue_location,
                                  self.winning_bandit))

        while self.evm.before('target_on', True) and not self.err:

            self.confirm_fixation()
            self.present_neutral_boxes()

            if self.evm.between('cue_on', 'cue_off'):
                if self.cue_location in [LEFT, DOUBLE]:
                    blit(self.thick_rect, 5, self.left_box_loc)
                if self.cue_location in [RIGHT, DOUBLE]:
                    blit(self.thick_rect, 5, self.right_box_loc)
            elif self.evm.between('cue_off', 'cueback_off'):
                blit(self.star_cueback, 5, P.screen_c)

            flip()

        self.targets_shown = True  # after bandits or probe shown, don't recycle trial on user error
        if self.trial_type in [BANDIT, BOTH] and not self.err:
            while self.evm.before('nogo_end') and not self.err:
                if key_pressed():
                    self.show_error_message('too_soon')
                    self.err = "early_response"
                    break
                self.confirm_fixation()
                self.bandit_callback(before_go=True)
                flip()

        #  PROBE RESPONSE PERIOD
        if self.trial_type in [PROBE, BOTH] and not self.err:
            self.probe_rc.collect()
            if not self.err:
                if len(self.probe_rc.keypress_listener.responses):
                    self.show_error_message('wrong_response')
                    self.err = 'keypress_on_probe'
                elif len(self.probe_rc.audio_listener.responses) == 0:
                    self.show_error_message('probe_timeout')
                    if self.probe_rc.audio_listener.stream_error:
                        self.err = 'microphone_error'
                    else:
                        self.err = 'probe_timeout'

        #  BANDIT RESPONSE PERIOD
        if self.trial_type in [BANDIT, BOTH] and not self.err:
            self.bandit_rc.collect()
            if self.trial_type == BANDIT and P.ignore_vocal_for_bandits == False:
                if len(self.bandit_rc.audio_listener.responses):
                    self.show_error_message('wrong_response')
                    self.err = 'vocal_on_bandit'

        # Retrieve collected response data before logging to database
        if self.err:
            bandit_choice, bandit_rt, reward = ['NA', 'NA', 'NA']
            probe_rt = 'NA'
        else:
            self.err = 'NA'
            # Retreive responses from RepsponseCollector(s) and record data
            if self.trial_type in [BANDIT, BOTH]:
                bandit_choice = self.bandit_rc.keypress_listener.response(
                    value=True, rt=False)
                bandit_rt = self.bandit_rc.keypress_listener.response(
                    value=False, rt=True)
                if bandit_rt == TIMEOUT:
                    self.show_error_message('bandit_timeout')
                    reward = 'NA'
                else:
                    # determine bandit payout (reward) and display feedback to participant
                    reward = self.feedback(bandit_choice)
            else:
                bandit_choice, bandit_rt, reward = ['NA', 'NA', 'NA']

            if self.trial_type in [PROBE, BOTH]:
                probe_rt = self.probe_rc.audio_listener.response(value=False,
                                                                 rt=True)
            else:
                probe_rt = 'NA'

        # Clear any remaining stimuli from screen before trial end
        clear()

        return {
            "block_num":
            P.block_number,
            "trial_num":
            P.trial_number,
            "trial_type":
            self.trial_type,
            "cue_loc":
            self.cue_location,
            "cotoa":
            self.cotoa,
            "high_value_col":
            self.high_value_color[:3] if self.trial_type != PROBE else "NA",
            "low_value_col":
            self.low_value_color[:3] if self.trial_type != PROBE else "NA",
            "high_value_loc":
            self.high_value_location if self.trial_type != PROBE else "NA",
            "winning_bandit":
            self.winning_bandit if self.trial_type != PROBE else "NA",
            "bandit_choice":
            bandit_choice,
            "bandit_rt":
            bandit_rt,
            "reward":
            reward,
            "probe_loc":
            self.probe_location if self.trial_type != BANDIT else "NA",
            "probe_rt":
            probe_rt,
            "err":
            self.err
        }
Ejemplo n.º 19
0
    def trial(self):

        start_delay = CountDown(P.origin_wait_time)
        while start_delay.counting():
            ui_request()
            fill()
            blit(self.tracker_dot, 5, self.origin_pos)
            flip()

        animate_start = self.evm.trial_time
        self.figure.animate()
        animate_time = self.evm.trial_time - animate_start
        avg_velocity = self.figure.path_length / animate_time

        if self.response_type == PHYS:
            self.physical_trial()
        elif self.response_type == MOTR:
            self.imagery_trial()
        else:
            self.control_trial()

        fill()
        flip()

        if self.__practicing__:
            return

        return {
            "session_num":
            self.session_number,
            "block_num":
            P.block_number,
            "trial_num":
            P.trial_number,
            "response_type":
            self.response_type,
            "feedback_type":
            self.feedback_type,
            "figure_type":
            self.figure_name,
            "figure_file":
            self.file_name + ".tlf",
            "stimulus_gt":
            self.animate_time,  # intended animation time
            "stimulus_mt":
            animate_time,  # actual animation time
            "avg_velocity":
            avg_velocity,  # in pixels per second
            "path_length":
            self.figure.path_length,
            "trace_file":
            (self.file_name + ".tlt") if self.response_type == PHYS else 'NA',
            "rt":
            self.rt,
            "it":
            self.it,
            "control_question":
            self.control_question if self.response_type == CTRL else 'NA',
            "control_response":
            self.control_response,
            "mt":
            self.mt
        }
Ejemplo n.º 20
0
    def present_filled_array(self, display):
        fill()
        for value in self.probe_locs.values():
            blit(self.placeholder, registration=5, location=value[0])

        if display == 'prime':
            blit(self.target, registration=5, location=self.T_prime_loc[0])
            blit(self.distractor, registration=5, location=self.D_prime_loc[0])
        else:
            blit(self.target, registration=5, location=self.T_probe_loc[0])
            blit(self.distractor, registration=5, location=self.D_probe_loc[0])

        blit(self.fixation, location=P.screen_c, registration=5)
        flip()
Ejemplo n.º 21
0
    def blit_msg(self, msg, align):
        msg = message(msg, align=align, blit_txt=False)

        fill()
        blit(msg, registration=5, location=P.screen_c)
        flip()
Ejemplo n.º 22
0
    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()
Ejemplo n.º 23
0
	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)
Ejemplo n.º 24
0
 def present_spatial_array(self, spatial_array):
     fill()
     blit(self.fixation, registration=5, location=P.screen_c)
     for item in spatial_array:
         blit(item[0], registration=5, location=item[1])
     flip()
Ejemplo n.º 25
0
    def trial(self):

        tone_played = False

        while self.evm.before('target_on', pump_events=True):
            fill()
            blit(self.fixation, 5, P.screen_c)
            if self.evm.between('cue_on',
                                'cue_off') and self.cue_location != None:
                loc = self.cue_locations[self.cue_location]
                blit(self.cue, 5, loc)
            if self.tone_trial and self.evm.after('tone_on'):
                if not tone_played:
                    self.warning_tone.play()
                    tone_played = True
            flip()

        if self.trial_type in ['ANTI', 'EV']:
            fill()
            blit(self.fixation, 5, P.screen_c)
            for shape, loc in self.arrows:
                blit(shape, 5, loc)
            flip()
        self.rc.collect()

        # Get response data and preprocess it before logging to database
        response, rt = self.rc.keypress_listener.response()
        if rt == klibs.TIMEOUT:
            response = 'NA'

        # if ANT trial, determine absolute diff. between y of central arrow and nearest flanker
        if self.trial_type != 'AV':
            ylocs = [arrow[1][1] for arrow in self.arrows]
            l_diff = abs(ylocs[2] - ylocs[1])
            r_diff = abs(ylocs[2] - ylocs[3])
            abs_diff = px_to_deg(min([l_diff, r_diff]))
        else:
            abs_diff = 'NA'

        if self.trial_type == 'ANTI':
            self.ev_offset = 'NA'
            accuracy = int(response == self.target_direction)
        elif self.trial_type == 'EV':
            accuracy = int(response == 'detection')
        elif self.trial_type == 'AV':
            self.ev_offset = 'NA'
            self.target_direction = 'NA'
            self.target_location = 'NA'
            self.congruent = 'NA'
            accuracy = int(response == 'detection')

        if response == 'NA':
            accuracy = 'NA'

        if P.practicing and accuracy is not 1:
            # If on a practice block, show feedback if an incorrect response is made.
            if accuracy is 0:
                feedback = "Incorrect response!\n"
                if 'ANTI' in self.trial_type:
                    feedback += "Please press 'c' for left arrows and 'm' for right arrows."
                elif 'EV' in self.trial_type:
                    feedback += "Please press the space bar for displaced arrows."
                else:
                    feedback += "Please press the space bar for countdown timers."
            else:
                feedback = (
                    "No valid response made!\n"
                    "Please press 'c' for left arrows, 'm' for right arrows, "
                    "and space for displaced arrows or countdowns.")

            feedback_msg = message(feedback,
                                   'block_msg',
                                   blit_txt=False,
                                   align='center')
            fill()
            blit(feedback_msg, 5, P.screen_c)
            flip()
            flush()
            any_key()
            # After feedback acknowledged, return to fixation screen
            fill()
            blit(self.fixation, 5, P.screen_c)
            flip()

        else:
            while self.evm.before('trial_end', pump_events=True):
                fill()
                blit(self.fixation, 5, P.screen_c)
                flip()

        return {
            "session_num": P.session_number,
            "block_num": P.block_number,
            "trial_num": P.trial_number,
            "trial_type": self.trial_type,
            "warning_tone": self.tone_trial,
            "cue_type": self.cue_type,
            "target_location": self.target_location,
            "target_direction": self.target_direction,
            "congruent": self.congruent,
            "displacement": self.ev_offset,
            "abs_displacement":
            abs_diff,  # diff. in dva between middle arrow and nearest flanker
            "response": response,
            "accuracy": accuracy,
            "rt": rt
        }
Ejemplo n.º 26
0
 def present_fixation(self):
     fill()
     blit(self.fixation, location=P.screen_c, registration=5)
     flip()
Ejemplo n.º 27
0
    def trial(self):
        
        # Display the stimuli in sequence (which stimuli and in which sequence is
        # determined above in trial_prep).
        
        while self.evm.before('t2_off'):
            ui_request()
            
            fill()
            blit(self.t1_line, 5, self.t1_pos)
            blit(self.t2_line, 5, self.t2_pos)
            
            if self.toj_type == "motion":
                blit(self.t1, self.t1_reg, self.t1_path.position)
                blit(self.t2, self.t2_reg, self.t2_path.position)
            else:
                if self.evm.after('t1_on'):
                    blit(self.t1, 5, self.t1_pos)
                if self.evm.after('t2_on'):
                    blit(self.t2, 5, self.t2_pos)
                
            if self.probe_trial and self.evm.between('t1_on', 'probe_off'):
                blit(self.probe, 5, self.probe_pos)
                
            flip()
        
        # After 2nd target is off, collect either TOJ response or colour wheel response
        # depending on trial type.
        
        if self.probe_trial:
            self.wheel_rc.collect()
        else:
            self.toj_callback()
            self.rc.collect()
        
        # Parse collected response data before writing to the database
        
        if not self.probe_trial:
            toj_response = self.rc.keypress_listener.response(rt=False)
            toj_rt = self.rc.keypress_listener.response(value=False)
            if toj_response == 'NO_RESPONSE':
                toj_response, toj_rt = ['NA', 'timeout']
            response_col, angle_err, wheel_rt = ['NA', 'NA', 'NA']
        else:
            try:
                angle_err, response_col = self.wheel_rc.color_listener.response(rt=False)
                wheel_rt = self.wheel_rc.color_listener.response(value=False)
                response_col = list(response_col) # to be consistent with probe_col
            except ValueError:
                # if no response made (timeout), only one value will be returned
                angle_err, response_col, wheel_rt = ['NA', 'NA', 'timeout']
            toj_response, toj_rt = ['NA', 'NA']

        return {
            "block_num": P.block_number,
            "trial_num": P.trial_number,
            "toj_condition": P.condition,
            "trial_type": 'probe' if self.probe_trial else 'toj',
            "target_type": self.toj_type,
            "t1_location": self.t1_location,
            "t1_type": "white" if self.t1_shape == "a" else "black",
            "upper_target": self.upper_target if self.toj_type == "motion" else 'NA',
            "t1_t2_soa": self.t1_t2_soa,
            "toj_response": toj_response,
            "toj_rt": toj_rt,
            "probe_loc": self.probe_location if self.probe_trial else 'NA',
            "probe_col": str(self.probe.fill_color[:3]) if self.probe_trial else 'NA',
            "response_col": str(response_col[:3]),
            "angle_err": angle_err,
            "wheel_rt": wheel_rt
        }
Ejemplo n.º 28
0
    def display_refresh(self, cue=None, tone=False, target=False):

        fill()

        blit(self.fixation, location=P.screen_c, registration=5)

        if cue is not None:
            if cue == TEMP_CUE:
                self.box_left.stroke = self.cued_stroke
                self.box_right.stroke = self.cued_stroke

            elif cue == VIS_LEFT:
                self.box_left.stroke = self.cued_stroke

            elif cue == VIS_RIGHT:
                self.box_right.stroke = self.cued_stroke

        else:
            self.box_right.stroke = self.uncued_stroke
            self.box_left.stroke = self.uncued_stroke

        blit(self.box_left, registration=5, location=self.locs['top_left'])
        blit(self.box_left, registration=5, location=self.locs['bottom_left'])
        blit(self.box_right, registration=5, location=self.locs['top_right'])
        blit(self.box_right, registration=5, location=self.locs['bottom_right'])

        if target:
            blit(self.target, registration=5, location=self.locs[self.target_loc])

        flip()

        if tone:
            self.audio_tone.play()
Ejemplo n.º 29
0
 def wheel_callback(self):
     fill()
     blit(self.wheel, location=P.screen_c, registration=5)
     flip()
Ejemplo n.º 30
0
    def present_instructions(self):

        msg = "During this experiment, each trial will begin with a cross centre-screen."

        self.anykey_msg(msg)

        fill()
        blit(self.fixation, location=P.screen_c, registration=5)
        flip()
        smart_sleep(1000)

        msg = ("When you see this, press spacebar to start the rest of the trial.\n" +
               "Once you do, four boxes will appear around it.")

        self.anykey_msg(msg)

        fill()
        blit(self.fixation, location=P.screen_c, registration=5)
        flip()

        smart_sleep(500)

        self.display_refresh()
        smart_sleep(1000)

        msg = ("Since you start each trial yourself, if your eyes get tired\n" +
               "you can take a moment before starting the next trial.")

        self.anykey_msg(msg)

        msg = "Shortly after the boxes appear, \nsome number of boxes may 'flash' and a tone might be played."

        self.anykey_msg(msg)

        self.cue_type = TEMP_CUE
        self.display_refresh()
        smart_sleep(250)
        self.display_refresh(cue=True, tone=True)
        smart_sleep(50)
        self.display_refresh()
        smart_sleep(1000)

        msg = "Once the boxes return to normal, a white circle will appear shortly after in one of the boxes."

        self.anykey_msg(msg)

        msg = ("If only the left or right boxes flash, the circle will appear on that side.\n" +
               "If all, or none, of the boxes flash, then the circle could appear on either side."
               )

        self.anykey_msg(msg)

        self.cue_type = VIS_LEFT
        self.target_loc = TOP_LEFT

        self.display_refresh()
        smart_sleep(250)
        self.display_refresh(cue=True, tone=True)
        smart_sleep(50)
        self.display_refresh()
        smart_sleep(250)
        self.display_refresh(target=True)
        smart_sleep(600)

        fill()
        blit(self.fixation, location=P.screen_c, registration=5)
        flip()
        smart_sleep(500)

        self.cue_type = VIS_RIGHT
        self.target_loc = BOTTOM_RIGHT

        self.display_refresh()
        smart_sleep(250)
        self.display_refresh(cue=True)
        smart_sleep(50)
        self.display_refresh()
        smart_sleep(250)
        self.display_refresh(target=True)
        smart_sleep(600)

        fill()
        blit(self.fixation, location=P.screen_c, registration=5)
        flip()
        smart_sleep(500)

        self.cue_type = TEMP_CUE
        self.target_loc = TOP_RIGHT

        self.display_refresh()
        smart_sleep(250)
        self.display_refresh(cue=True, tone=True)
        smart_sleep(50)
        self.display_refresh()
        smart_sleep(250)
        self.display_refresh(target=True)
        smart_sleep(600)

        fill()
        blit(self.fixation, location=P.screen_c, registration=5)
        flip()
        smart_sleep(500)

        self.cue_type = TEMP_CUE
        self.target_loc = BOTTOM_LEFT

        self.display_refresh()
        smart_sleep(250)
        self.display_refresh(cue=True, tone=True)
        smart_sleep(50)
        self.display_refresh()
        smart_sleep(250)
        self.display_refresh(target=True)
        smart_sleep(600)

        msg = ("Once the target appears, you task is to indicate on which side it appeared.\n" +
               "{0} key = left boxes, {1} key = right boxes"
               ).format(self.left_key.upper(), self.right_key.upper())

        self.anykey_msg(msg)

        msg = ("When you make a response, your reaction time will be provided to you.\n" +
               "Otherwise, you'll be asked to respond faster.\nTry to keep this number as" +
               " low as possible, while remaining accurate.")

        self.anykey_msg(msg)

        msg = ("Feedback for correct responses will be provided in white.\n" +
               "For incorrect responses, it will be in red.")

        self.anykey_msg(msg)

        msg = "The experiment will now begin with a few practice rounds to familiarize you with the task."

        self.anykey_msg(msg)
Ejemplo n.º 31
0
    def blit(self):
        if P.development_mode:
            self.exp.log_f.write("\n\tD{0}: blit()".format(self.index))

        if self.allow_blit:
            blit(self.disc, 5, self.x_y_pos)
            if self.using_secondary_pos:
                blit(self.disc, 5, self.secondary_x_y_pos)
            if P.development_mode:
                if P.dm_show_disc_indices:
                    blit(self.index_str, 5, self.x_y_pos)
                    if self.using_secondary_pos:
                        blit(self.secondary_index_str, 5,
                             self.secondary_x_y_pos)
                if P.dm_draw_boundaries:
                    blit(self.debug_boundary, 5, self.x_y_pos)
                    if self.using_secondary_pos:
                        blit(self.debug_boundary, 5, self.secondary_x_y_pos)

            self.initial_blit = True
            if self.first_disc and self.removal_behavior == P.REMOVE_ON_PRESENTATION:
                self.exp.show_dc_target = False  # after the first disc is shown, dc_target should be off
Ejemplo n.º 32
0
    def bandit_callback(self, before_go=False):
        self.confirm_fixation()
        self.present_neutral_boxes()

        blit(self.left_bandit, 5, self.left_box_loc)
        blit(self.right_bandit, 5, self.right_box_loc)
Ejemplo n.º 33
0
    def display_refresh(self, disc_location=None):
        #  handle the removal of background image on absent condition trials
        ui_request()
        fill(GREY)
        if self.bg_state != BG_ABSENT:
            if (disc_location is not None and disc_location.final_disc
                ) and self.bg_state == BG_INTERMITTENT:
                fill(self.bg[2])
            else:
                if P.development_mode:
                    blit(self.bg[4], 7, (50, 50))
                blit(self.bg[1])

        #  show the drift correct target if need be
        if self.show_dc_target:
            blit(drift_correct_target(), position=P.screen_c, registration=5)

        #  blit passed discs if they're allow_blit attribute is set
        if P.development_mode:
            if disc_location:
                disc_args = [disc_location.index, self.evm.trial_time_ms, "F" if disc_location.final_disc else "x", \
                    "P" if disc_location.penultimate_disc else "x", "N" if disc_location.n_back else "x"]
                self.log_f.write(
                    "\n\n*** Current Disc: {0} ({2}{3}{4}) at {1} ***".format(
                        *disc_args))
                for d in [
                        disc_location, disc_location.previous_disc,
                        disc_location.next_disc
                ]:
                    try:
                        try:
                            until_timeout = self.evm.until(
                                d.event_timeout_label
                            ) if d.timing_out else None
                        except EventError:
                            until_timeout = "ELAPSED"
                        try:
                            until_decay = self.evm.until(
                                d.offset_decay_label) if d.decaying else None
                        except EventError:
                            until_decay = "ELAPSED"

                        disc_attrs = [
                            d.initial_blit, d.allow_blit, d.index,
                            d.on_timestamp, d.off_timestamp,
                            d.fixation_timestamp, d.exit_time, until_timeout,
                            until_decay, d.timed_out
                        ]
                        self.log_f.write(
                            "\nD{2}, initial_blit: {0}, allow_blit: {1}, on: {3}, off: {4}, fixation: {5}, exit: {6}, decay_in: {8},  timed_out: {9}, timeout_in: {7}"
                            .format(*disc_attrs))
                        self.log_f.write(" +")
                    except AttributeError:
                        pass
                self.log_f.write("\n")

        if disc_location:
            # display discs
            for d in [
                    disc_location, disc_location.previous_disc,
                    disc_location.next_disc
            ]:
                if isinstance(d, DiscLocation) and d.allow_blit: d.blit()

        flip()

        if disc_location:
            #  log timestamps for discs turning on or off
            for d in [
                    disc_location, disc_location.previous_disc,
                    disc_location.next_disc
            ]:
                if isinstance(d, DiscLocation):
                    timestamp = [self.evm.trial_time, self.el.now()]
                    if d.allow_blit:
                        if not d.on_timestamp:
                            d.record_presentation(timestamp)
                        if d.decaying:
                            d.check_decay()
                    elif d.initial_blit and not d.off_timestamp:
                        d.record_removal(timestamp)
Ejemplo n.º 34
0
 def draw(self):
     blit(self.msg, 5, self.midpoint)
     mp = mouse_pos()
     if self.bounds.within(mp):
         blit(self.hover, 5, self.midpoint)
Ejemplo n.º 35
0
    def setup(self):

        # Set up custom text styles for the experiment
        self.txtm.add_style('instructions', 18, [255, 255, 255, 255])
        self.txtm.add_style('error', 18, [255, 0, 0, 255])
        self.txtm.add_style('tiny', 12, [255, 255, 255, 255])
        self.txtm.add_style('small', 14, [255, 255, 255, 255])

        # Pre-render shape stimuli
        dot_stroke = [P.dot_stroke, P.dot_stroke_col, STROKE_OUTER
                      ] if P.dot_stroke > 0 else None
        self.tracker_dot = Ellipse(P.dot_size,
                                   stroke=dot_stroke,
                                   fill=P.dot_color).render()
        self.origin_active = Ellipse(P.origin_size,
                                     fill=self.origin_active_color).render()
        self.origin_inactive = Ellipse(
            P.origin_size, fill=self.origin_inactive_color).render()

        # If capture figures mode, generate, view, and optionally save some figures
        if P.capture_figures_mode:
            self.fig_dir = os.path.join(P.resources_dir, "figures")
            self.capture_figures()
            self.quit()

        # Initialize participant ID and session options, reloading ID if it already exists
        self.session = TraceLabSession()
        self.user_id = self.session.user_id

        # Once session initialized, show loading screen and finish setup
        self.loading_msg = message("Loading...", "default", blit_txt=False)
        fill()
        blit(self.loading_msg, 5, P.screen_c)
        flip()

        # Scale UI size variables to current screen resolution
        P.btn_s_pad = scale((P.btn_s_pad, 0), (1920, 1080))[0]
        P.y_pad = scale((0, P.y_pad), (1920, 1080))[1]

        # Initialize messages and response buttons for control trials
        control_fail_txt = "Please keep your finger on the start area for the complete duration."
        self.control_fail_msg = message(control_fail_txt,
                                        'error',
                                        blit_txt=False)
        ctrl_buttons = ["1", "2", "3", "4", "5"]
        self.control_bar = ButtonBar(buttons=[(i, P.btn_size, None)
                                              for i in ctrl_buttons],
                                     button_size=P.btn_size,
                                     screen_margins=P.btn_s_pad,
                                     y_offset=P.y_pad,
                                     message_txt=P.control_q)

        # Initialize 'next trial' button
        button_x = 250 if self.handedness == LEFT_HANDED else P.screen_x - 250
        button_y = P.screen_y - 100
        self.next_trial_msg = message(P.next_trial_message,
                                      'default',
                                      blit_txt=False)
        self.next_trial_box = Rectangle(300,
                                        75,
                                        stroke=(2, (255, 255, 255),
                                                STROKE_OUTER))
        self.next_trial_button_loc = (button_x, button_y)
        bounds = [(button_x - 150, button_y - 38),
                  (button_x + 150, button_y + 38)]
        self.add_boundary("next trial button", bounds, RECT_BOUNDARY)

        # Initialize instructions and practice button bar for each condition
        self.instruction_files = {
            PHYS: {
                'text': "physical_group_instructions.txt",
                'frames': "physical_key_frames"
            },
            MOTR: {
                'text': "imagery_group_instructions.txt",
                'frames': "imagery_key_frames"
            },
            CTRL: {
                'text': "control_group_instructions.txt",
                'frames': "control_key_frames"
            }
        }
        self.practice_instructions = message(P.practice_instructions,
                                             "instructions",
                                             align="center",
                                             blit_txt=False)
        practice_buttons = [('Replay', [200, 100], self.practice),
                            ('Practice', [200, 100], self.__practice__),
                            ('Begin', [200, 100], any_key)]
        self.practice_button_bar = ButtonBar(practice_buttons, [200, 100],
                                             P.btn_s_pad,
                                             P.y_pad,
                                             finish_button=False)

        # Import all pre-generated figures needed for the current session
        figures = list(set(self.trial_factory.exp_factors["figure_name"]))
        figures.append(P.practice_figure)
        for f in figures:
            if f != "random":
                ui_request()
                fig_path = os.path.join(P.resources_dir, "figures", f)
                self.test_figures[f] = TraceLabFigure(fig_path)
Ejemplo n.º 36
0
    def trial(self):

        # BANDIT TRIAL
        if P.practicing:
            cotoa, probe_rt = ['NA', 'NA']  # Don't occur in bandit blocks

            # Present placeholders
            while self.evm.before('target_on', True) and not self.err:
                self.confirm_fixation()
                self.present_neutral_boxes()
                flip()

            # BANDIT RESPONSE PERIOD
            self.targets_shown = True  # After bandits shown, don't recycle trial

            # Present bandits and listen for response
            self.bandit_rc.collect()

            # If wrong response made
            if self.err:
                bandit_choice, bandit_rt, reward = ['NA', 'NA', 'NA']
            else:
                self.err = 'NA'
                # Retrieve responses from ResponseCollector(s) & record data
                bandit_choice = self.bandit_rc.keypress_listener.response(
                    value=True, rt=False)
                bandit_rt = self.bandit_rc.keypress_listener.response(
                    value=False, rt=True)

                if bandit_rt == TIMEOUT:
                    self.show_error_message('bandit_timeout')
                    reward = 'NA'
                else:
                    # Determine bandit payout & display
                    reward = self.feedback(bandit_choice)

        # PROBE TRIAL
        else:
            bandit_choice, bandit_rt, reward = [
                'NA', 'NA', 'NA'
            ]  # Don't occur in probe trials

            # Present placeholders & confirm fixation
            while self.evm.before('target_on', True):
                self.confirm_fixation()
                self.present_neutral_boxes()

                # Present cue
                if self.evm.between('cue_on', 'cue_off'):
                    if self.cue_location == LEFT:
                        blit(self.thick_rect, 5, self.left_box_loc)
                    else:
                        blit(self.thick_rect, 5, self.right_box_loc)
                # Present cueback
                elif self.evm.between('cue_off', 'cueback_off'):
                    blit(self.star_cueback, 5, P.screen_c)

                flip()

            # PROBE RESPONSE PERIOD
            self.targets_shown = True  # After probe shown, don't recycle trial
            # Present probes & listen for response
            self.probe_rc.collect()

            # If 'go' trial, check for response
            if self.go_no_go == GO:
                # If wrong response made
                if self.err:
                    probe_rt = 'NA'
                # If correct response OR timeout
                else:
                    self.err = 'NA'
                    probe_rt = self.probe_rc.keypress_listener.response(
                        value=False, rt=True)
                    if probe_rt == TIMEOUT:
                        self.show_error_message('probe_timeout')
                        probe_rt = 'NA'
            # Similarly, for 'nogo' trials
            else:
                probe_rt = 'NA'
                # If response made, penalize
                if len(self.probe_rc.keypress_listener.responses):
                    self.show_error_message('response_on_nogo')
                    self.err = 'response_on_nogo'
                # If no response, continue as normal
                else:
                    self.err = 'NA'
        # Return trial data
        return {
            "block_num": P.block_number,
            "trial_num": P.trial_number,
            "block_type": "BANDIT" if P.practicing else "PROBE",
            "high_value_col":
            self.high_value_colour[:3] if P.practicing else 'NA',
            "high_value_loc":
            self.high_value_location if P.practicing else 'NA',
            "low_value_col":
            self.low_value_colour[:3] if P.practicing else 'NA',
            "low_value_loc": self.low_value_location if P.practicing else 'NA',
            "winning_trial": self.winning_trial if P.practicing else 'NA',
            "bandit_selected": self.bandit_selected if P.practicing else 'NA',
            "bandit_rt": bandit_rt,
            "reward": reward,
            "cue_loc": self.cue_location if not P.practicing else 'NA',
            "cotoa": self.cotoa if not P.practicing else 'NA',
            "probe_loc": self.probe_location if not P.practicing else 'NA',
            "probe_col": self.probe_colour if not P.practicing else 'NA',
            "go_no_go": self.go_no_go if not P.practicing else 'NA',
            "probe_rt": probe_rt,
            "err": self.err
        }
        # Clear remaining stimuli from screen
        clear()