def continue_on(self): while True: if not P.development_mode: if key_pressed(key=sdl2.SDLK_KP_5): break else: if key_pressed(key=sdl2.SDLK_k): break
def jc_wait_time(self): if self.before_target: if lsl(self.el.gaze(), P.screen_c) > self.fixation_boundary: self.log_and_recycle_trial('eye') q = pump(True) if key_pressed(queue=q): if key_pressed(SDLK_SPACE, queue=q): self.log_and_recycle_trial('early') else: self.log_and_recycle_trial('key')
def wait_time(self): # Appropriated verbatim from original code written by John Christie if self.before_target: gaze = self.el.gaze() if not self.bi.within_boundary(label='drift_correct', p=gaze): self.log_and_recycle_trial('eye') q = pump(True) if key_pressed(queue=q): if key_pressed(SDLK_SPACE, queue=q): self.log_and_recycle_trial('early') else: self.log_and_recycle_trial('key')
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 trial(self): if P.development_mode: print(self.image_name, self.mask_type) trial_countdown = CountDown(900) # 15 minutes while trial_countdown.remaining() > 0: q = pump(True) if key_pressed(sdl2.SDLK_DELETE, queue=q): # press 'Del' to skip trial break elif key_pressed(sdl2.SDLK_ESCAPE, queue=q): # Press 'escape' to pause and calibrate trial_countdown.pause() self.el.calibrate() self.el.drift_correct() self.el.start(P.trial_number) trial_countdown.resume() continue if trial_countdown.elapsed() >= self.first_warning_onset and not self.first_warning_played: self.warning_signal.play() self.first_warning_played = True if trial_countdown.elapsed() >= self.second_warning_onset and not self.second_warning_played: self.warning_signal.play() self.second_warning_played = True pos = self.el.gaze() fill() if int(pos[0]) != -32768: # if gaze not lost self.gaze_offscreen = time.time() if (pos[0] < 0) or (pos[0] > P.screen_x) or (pos[1] < 0) or (pos[1] > P.screen_y): pass elif (time.time() - self.gaze_offscreen) < self.gaze_timeout: blit(self.arrangement, 5, P.screen_c) if self.mask is not None and trial_countdown.elapsed() < self.mask_off_time: blit(self.mask, 5, pos) flip() return { "trial_num": P.trial_number, "block_num": P.block_number, "arrangement": self.image_name, "mask_type": self.mask_type }
def trial_clean_up(self): # Provide break 1/2 through experimental block if P.trial_number == P.trials_per_block / 2: txt = "You're 1/2 through, take a break if you like\nand press '5' when you're ready to continue" msg = message(txt, blit_txt=False) fill() blit(msg, location=P.screen_c, registration=5) flip() while True: if key_pressed(key=sdl2.SDLK_KP_5): break
def __validate(self): instruction = ( "Okay, threshold set! " "To ensure its validity, please provide one (and only one) more response." ) fill() message(instruction, location=P.screen_c, registration=5) flip() self.stream.start() validate_counter = CountDown(5) while validate_counter.counting(): ui_request() if self.stream.sample().peak >= self.threshold: validate_counter.finish() self.threshold_valid = True self.stream.stop() if self.threshold_valid: validation_msg = "Great, validation was successful! Press any key to continue." else: validation_msg = ( "Validation wasn't successful. " "Type C to re-calibrate or V to try validation again.") fill() message(validation_msg, location=P.screen_c, registration=5) flip() selection_made = False while not selection_made: q = pump(True) if self.threshold_valid: if key_pressed(queue=q): return else: if key_pressed(SDLK_c, queue=q): self.calibrate() elif key_pressed(SDLK_v, queue=q): self.__validate()
def clean_up(self): # Give the RA the choice of logging or discarding combination of # factors used on last trial done_1 = message("Experiment session complete.", 'q_and_a', blit_txt=False) done_2 = message("Log trial factor set? (Y/N)", 'q_and_a', blit_txt=False) response_made = None while not response_made: fill() blit(done_1, 5, (P.screen_c[0], P.screen_c[1] - 40)) blit(done_2, 5, (P.screen_c[0], P.screen_c[1] + 40)) flip() q = pump(True) if key_pressed(sdl2.SDLK_y, queue=q): response_made = "Y" break elif key_pressed(sdl2.SDLK_n, queue=q): response_made = "N" break if response_made == "Y": with open(self.completed_csv,"a") as f: writer = csv.writer(f) writer.writerow(sum(self.trial_factors, ()))
def present_feedback(self, prime_correct, prime_rt, probe_correct, probe_rt): prime_fb = int(prime_rt) if prime_correct else 'WRONG' probe_fb = int(probe_rt) if probe_correct else 'WRONG' fb_txt = "{0}\n{1}".format(prime_fb, probe_fb) fb = message(fb_txt, align='center', blit_txt=False) fill() blit(fb, location=P.screen_c, registration=5) flip() while True: if key_pressed(key=sdl2.SDLK_SPACE): break
def block(self): # Generate font sizes to use for numbers during the block self.num_sizes = [] while len(self.num_sizes) <= P.trials_per_block: self.num_sizes = self.num_sizes + self.sizes random.shuffle(self.num_sizes) self.num_sizes = self.num_sizes[0:P.trials_per_block] # Generate block message header = "" #"Block {0} of {1}".format(P.block_number, P.blocks_per_experiment) instructions = ( "Please press the space key quickly when a digit other than {0} \nappears on screen, " "and withhold your response when the digit is {0}.".format( P.target)) if P.practicing: header = "This is a practice block." instructions = instructions + "\nYou will be given feedback on your accuracy." msg = message(header + "\n\n" + instructions, 'normal', align="center", blit_txt=False) start_msg = message("Press the [space] key to start.", 'normal', blit_txt=False) # Show block message, and wait for input before staring block if P.block_number in [1, self.first_nonpractice]: message_interval = CountDown(2) while message_interval.counting(): ui_request() # Allow quitting during loop fill() blit(msg, 8, (P.screen_c[0], int(P.screen_y * 0.15))) flip() while True: if key_pressed(' '): break fill() blit(msg, 8, (P.screen_c[0], int(P.screen_y * 0.15))) blit(start_msg, 5, (P.screen_c[0], int(P.screen_y * 0.75))) flip()
def warm_up(self): warmup_timer = CountDown(600, start=True) self.first_signal_played = False self.second_signal_played = False self.third_signal_played = False self.fourth_signal_played = False while warmup_timer.counting(): if key_pressed(sdl2.SDLK_DELETE): break t = warmup_timer.elapsed() if t < 60: tool = 'pencil' elif 60 <= t < 120: if not self.first_signal_played: self.warning_signal.play() self.first_signal_played = True tool = 'charcoal' elif 120 <= t < 180: if not self.second_signal_played: self.warning_signal.play() self.second_signal_played = True tool = 'smudger' elif 180 <= t < 240: if not self.third_signal_played: self.warning_signal.play() self.third_signal_played = True tool = 'eraser' else: if not self.fourth_signal_played: self.warning_signal.play() self.fourth_signal_played = True tool = 'any' fill() blit(self.images['warmup.png'], 5, P.screen_c) blit(self.warmup_txt[tool], 1, (30, P.screen_y-30)) flip()
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.")
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 }
def trial(self): # Draw initial trial stimuli to screen fill() blit(self.accuracy_rect, 5, P.screen_c) blit(self.accuracy_mask, 5, P.screen_c) blit(self.letters[self.letter], 5, P.screen_c) flip() # Immediately start listening for reponses, removing letter after 500ms self.rc.collect() response = self.rc.keypress_listener.response() # Process collected response before writing to database, showing feedback if practicing if response.rt != klibs.TIMEOUT: resp, rt = response correct_resp = 'match' if self.is_target else 'nonmatch' accuracy = int(resp == correct_resp) if P.practicing: self.accuracy_rect.fill = MID_GREEN if accuracy == True else MID_RED self.accuracy_rect.render() while self.evm.before('trial_end'): ui_request() fill() blit(self.accuracy_rect, 5, P.screen_c) blit(self.accuracy_mask, 5, P.screen_c) if self.evm.before('mask_on'): blit(self.letters[self.letter], 5, P.screen_c) else: blit(self.mask, 5, P.screen_c) flip() else: resp, rt, accuracy = ['NA', 'NA', 'NA'] # If probe trial, present MW probe and wait for response + keypress before ending trial if self.probe_trial: probe_resp, probe_rt = self.probe.collect() probe_rt = probe_rt * 1000 # convert seconds to ms resume_msg = message("Press the [n] key to continue.", 'title', blit_txt=False) while True: if key_pressed('n'): break fill() blit(resume_msg, 5, P.screen_c) flip() self.past_letters = [] else: probe_resp, probe_rt = ('NA', 'NA') self.past_letters.append(self.letter) return { "block_num": P.block_number, "trial_num": P.trial_number, "practice": P.practicing, "probe_type": self.probe_condition, "first_nback": self.first_nback, "nback": self.nback, "target_trial": self.is_target, "resp": resp, "accuracy": accuracy, "rt": rt, "probe_resp": probe_resp, "probe_rt": probe_rt }
def block(self): # Initialize/reset list of previous letters presented self.past_letters = [] # Determine nback difficulty for the block even_block = P.block_number % 2 == 0 if self.first_nback == 1: self.nback = 2 if even_block else 1 else: self.nback = 1 if even_block else 2 # Determine positions of thought probes in trial sequence # Probes are pseudo-randomly distributed throughout blocks, with one probe in every set of # 30 trials. Probes cannot appear in the first 8 trials of a set, ensuring that probes are # at minimum 20 seconds apart. self.probe_trials = [] noprobe_span = [False] * P.noprobe_span probe_span = [True] + [False] * P.probe_span while len(self.probe_trials) < P.trials_per_block: random.shuffle(probe_span) self.probe_trials += noprobe_span + probe_span # Generate block message header = "Block {0} of {1}".format(P.block_number, P.blocks_per_experiment) nback_txts = [' ', 'just before it', 'two before it'] instructions = ( "During this block, please press the [m] key when a letter matches\nthe letter " "{0}, and the [n] key when it does not.".format( nback_txts[self.nback])) if P.practicing: header = "This is a practice block. ({0})".format(header) instructions = instructions + "\nYou will be given feedback on your accuracy." msg = message(header + "\n\n" + instructions, 'normal', align="center", blit_txt=False) start_msg = message("Press the [n] key to start.", 'normal', blit_txt=False) # Show block message, and wait for input before staring block message_interval = CountDown(2) while message_interval.counting(): ui_request() # Allow quitting during loop fill() blit(msg, 8, (P.screen_c[0], int(P.screen_y * 0.15))) self.draw_nback_illustration(P.screen_c[1], target=5 if self.nback == 1 else 4) flip() while True: if key_pressed('n'): break fill() blit(msg, 8, (P.screen_c[0], int(P.screen_y * 0.15))) self.draw_nback_illustration(P.screen_c[1], target=5 if self.nback == 1 else 4) blit(start_msg, 5, (P.screen_c[0], int(P.screen_y * 0.75))) flip()
def trial(self): fill() blit(self.digits[self.number][self.num_size], 5, P.screen_c) flip() self.rc.collect() response = self.rc.keypress_listener.response() if response.rt == klibs.TIMEOUT: resp, rt = ['nogo', 'NA'] else: resp, rt = response correct_resp = 'nogo' if self.number == P.target else 'go' accuracy = resp == correct_resp while self.evm.before('trial_end'): ui_request() fill() blit(self.mask_x, 5, P.screen_c) blit(self.mask_ring, 5, P.screen_c) flip() if P.practicing: if accuracy == False: feedback = "Incorrect! " if correct_resp == 'nogo': feedback += "Please withhold responses to the digit {0}.".format( P.target) else: feedback += "Please respond quickly to digits other than {0}.".format( P.target) else: feedback = "Correct response!" feedback_msg = message(feedback, 'normal', blit_txt=False) feedback_timer = CountDown(1.5) while feedback_timer.counting(): ui_request() fill() blit(feedback_msg, 5, P.screen_c) flip() # If probe trial, present MW probe and wait for response + keypress before ending trial if self.probe_trial: probe_resp, probe_rt = self.probe.collect() eff_level, eff_rt = self.get_effort() probe_rt = probe_rt * 1000 # convert seconds to ms eff_rt = eff_rt * 1000 # convert seconds to ms eff_level = eff_level * 100 # scale range to 0% - 100% resume_msg = message("Press the [space] key to resume the task.", 'title', blit_txt=False) while True: if key_pressed(' '): break fill() blit(resume_msg, 5, P.screen_c) flip() else: probe_resp, probe_rt, eff_level, eff_rt = ('NA', 'NA', 'NA', 'NA') return { "practicing": P.practicing, "block_num": P.block_number, "trial_num": P.trial_number, "digit": self.number, "digit_size": self.num_size, "target_digit": P.target, "response": resp, "rt": rt, "accuracy": accuracy, "probe_resp": probe_resp, "probe_rt": probe_rt, "effort_level": eff_level, "effort_rt": eff_rt }