def block(self): # Show block message at start of every block header = "Block {0} of {1}".format(P.block_number, P.blocks_per_experiment) if P.practicing: header = "This is a practice block. ({0})".format(header) practice_msg = "During this block you will be given feedback for your responses." msg = message(header + "\n" + practice_msg, align="center", blit_txt=False) else: msg = message(header, blit_txt=False) message_interval = CountDown(1) while message_interval.counting(): ui_request() # Allow quitting during loop fill() blit(msg, 8, (P.screen_c[0], P.screen_y * 0.4)) flip() flush() start_msg = message("Press any key to start.", blit_txt=False) fill() blit(msg, 8, (P.screen_c[0], P.screen_y * 0.4)) blit(start_msg, 5, [P.screen_c[0], P.screen_y * 0.6]) flip() any_key()
def play(self): ui_request() for kf in self.key_frames: if kf.enabled: skip = kf.play() if skip: break
def feedback(self, response): correct_response = True if response == self.tilt_line_location else False # Every 5 trials of a particular payoff, ask anticipated earnings if self.potential_payoff == HIGH: self.high_value_trial_count += 1 if self.high_value_trial_count in [5, 10, 15]: self.query_learning(HIGH) else: self.low_value_trial_count += 1 if self.low_value_trial_count in [5, 10, 15]: self.query_learning(LOW) # Determine payout for trial if correct_response & (self.winning_trial == YES): points = self.payout() msg = message("You won {0} points!".format(points), 'myText', blit_txt=False) else: points = self.penalty msg = message("You lost 5 points!", 'myText', blit_txt=False) # Keep tally of score self.total_score += points feedback = [points, msg] # Present score feedback_exposure = CountDown(self.feedback_exposure_period) fill() blit(feedback[1], location=P.screen_c, registration=5) flip() while feedback_exposure.counting(): ui_request() return feedback[0]
def run(self): if self.use_trial_clock: start = self.evm.trial_time if self.units == TK_S: start *= 0.001 else: start = time() if self.units == TK_MS: start = int(start * 1000) for i in range(0, self.frame_count): if self.frames: for f in self.frames: pass # create a Frame class that knows how to blit itself after loading and rendering assets else: self.frame_draw_cb(*self.frame_draw_cb_args) if self.flip_after_cb: flip() frame_running = True while frame_running: try: self.frame_data_cb(self.frame_draw_cb_args) except TypeError: pass if self.use_trial_clock: elapsed = self.evm.trial_time if self.units == TK_S: elapsed *= 0.001 else: elapsed = time() if self.units == TK_MS: elapsed = int(start * 1000) frame_running = elapsed < i * self.frame_interval ui_request()
def after(self, label, pump_events=False): """Checks if the current trial time is after a given event. .. warning:: Setting 'pump_events' to True will cause the input event queue to be cleared every time the method is called. Please use :func:`~.pump` manually in loops where you will be processing user input. Args: label (str): The label of the event to check. pump_events (bool, optional): If True, a :func:`~.ui_request` is performed when this method is called. Defaults to False. Returns: True if the specified event has already occured within the trial, otherwise False. """ if type(label) is not str: raise ValueError( "Expected 'str' for argument label; got {0}.".format( type(label))) if not self.registered(label): raise NameError( "Event '{0}' not registered with the EventManager.".format( label)) if pump_events: ui_request() return self.__event_issued(label)
def trial(self): # Clear screen and wait for target onset fill() flip() while self.evm.before('target_on'): ui_request() # Start timer and collect response self.rc.collect() response = self.rc.keypress_listener.response() # Stop timer and show for 1sec after response is made elapsed_msg = message(str(int(response.rt)).zfill(4), style='PVT', blit_txt=False) feedback_timer = CountDown(1) while feedback_timer.counting(): fill() blit(elapsed_msg, 5, P.screen_c) flip() # Log trial data to database return { "block_num": P.block_number, "trial_num": P.trial_number, "isi": self.interstim_interval, "rt": response.rt }
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 _generate_figure(self, duration): failures = 0 figure = None gen_start = time.time() while not figure: ui_request() try: figure = TraceLabFigure(animate_time=duration) figure.render() figure.prepare_animation() except RuntimeError as e: print(e) failures += 1 if failures > 10: print( "\nUnable to generate a viable figure after 10 tries, quitting...\n" ) self.quit() # Print warning if load time was particularly slow for a given figure if (time.time() - gen_start) > 0.5: cso("<red>Warning: figure generation took unexpectedly long. " "({0} seconds)</red>".format(round(time.time() - gen_start, 2))) return figure
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 feedback(self, response): if self.winning_bandit == HIGH: winning_bandit_loc = self.high_value_location else: winning_bandit_loc = self.low_value_location if response == winning_bandit_loc: points = self.bandit_payout(value=self.winning_bandit) msg = message("You won {0} points!".format(points), "score up", blit_txt=False) else: points = self.penalty # -5 msg = message("You lost 5 points!", "score down", blit_txt=False) self.total_score += points feedback = [points, msg] feedback_exposure = CountDown(self.feedback_exposure_period) while feedback_exposure.counting(): ui_request() fill() blit(feedback[1], location=P.screen_c, registration=5) flip() return feedback[0]
def present_feedback(self): msg = "Incorrect!" self.blit_msg(msg, 'left') feedback_cd = CountDown(0.5) while feedback_cd.counting(): ui_request()
def trial(self): # Wait 1s before presenting array while self.evm.before("present_fixation", True): ui_request() self.present_fixation() while self.evm.before("search_onset", True): ui_request() if self.search_type == SPACE: self.present_array() self.spatial_rc.collect() if len(self.spatial_rc.cursor_listener.responses): spatial_located, spatial_rt = self.spatial_rc.cursor_listener.response() else: spatial_located = 'miss' spatial_rt = 'NA' else: try: # the display callback "present_stream()" pops an element each pass; when all targets have been shown this bad boy throws an error self.temporal_rc.collect() except IndexError: self.temporal_responses = self.temporal_rc.mousebutton_listener.responses trial_data = { "block_num": P.block_number, "trial_num": P.trial_number, "search_type": "Spatial", "cell_count": self.cell_count, "target_distractor": self.target_distractor, "distractor_distractor": self.distractor_distractor, } if self.search_type == TIME: for tpt in self.temporal_presentation_times: onset_key = 't{0}_onset'.format(self.temporal_presentation_times.index(tpt)+1) onset_val = tpt[0] dist_count_key = 't{0}_distractor_count'.format(self.temporal_presentation_times.index(tpt)+1) dist_count_val = tpt[1] trial_data[onset_key] = onset_val trial_data[dist_count_key] = dist_count_val else: for i in range(0,15): onset_key = 't{0}_onset'.format(i+1) onset_val = 'NA' dist_count_key = 't{0}_distractor_count'.format(i+1) dist_count_val = 'NA' trial_data[onset_key] = onset_val trial_data[dist_count_key] = dist_count_val return trial_data clear()
def key_pressed(self, keysym, queue=None): pressed = False if not queue: queue = pump(True) for e in queue: if e.type == SDL_KEYDOWN: ui_request(e.key.keysym) if e.key.keysym.sym == keysym: pressed = True break return pressed
def render_texture(self, texture_figure, orientation=None): grid_size = (deg_to_px(self.stim_size) + self.stim_pad) * 2 stim_offset = self.stim_pad // 2 dc = Image.new('RGB', (grid_size, grid_size), NEUTRAL_COLOR[:3]) stroke_width = 2 #px grid_cell_size = deg_to_px(self.bg_element_size + self.bg_element_pad) grid_cell_count = grid_size // grid_cell_size stim_offset += (grid_size % grid_cell_size) // 2 # split grid_size %% cells over pad # Visual Representation of the Texture Rendering Logic # <-------G--------> # _______________ ^ # | O | | O = element_offset, ie. 1/2 bg_element_padding # | _____ | | E = element (ie. circle, square, D, etc.) # | | | | | G = one grid length # | O | E | O | G # | |_____| | | # | | | # |_______O_______| | # v element_offset = self.bg_element_pad // 2 # so as to apply padding equally on all sides of bg elements for col in range(0, grid_cell_count): for row in range(0, grid_cell_count): ui_request() top_out = int(row * grid_cell_size + element_offset + stim_offset) top_in = top_out + stroke_width # ie. top_inner left_out = int(col * grid_cell_size + element_offset + stim_offset) left_in = left_out+ stroke_width bottom_out = int(top_out + deg_to_px(self.bg_element_size)) bottom_in = bottom_out - stroke_width right_out = int(left_out + deg_to_px(self.bg_element_size)) right_in = right_out - stroke_width if texture_figure == CIRCLE: ImageDraw.Draw(dc, 'RGB').ellipse((left_out, top_out, right_out, bottom_out), WHITE[:3]) ImageDraw.Draw(dc, 'RGB').ellipse((left_in, top_in, right_in, bottom_in), NEUTRAL_COLOR[:3]) elif texture_figure == SQUARE: ImageDraw.Draw(dc, 'RGB').rectangle((left_out, top_out, right_out, bottom_out), WHITE[:3]) ImageDraw.Draw(dc, 'RGB').rectangle((left_in, top_in, right_in, bottom_in), NEUTRAL_COLOR[:3]) elif texture_figure is False: half_el_width = int(0.5 * deg_to_px(self.bg_element_size)) rect_right = right_out - half_el_width ImageDraw.Draw(dc, 'RGB', ).ellipse((left_out, top_out, right_out, bottom_out), WHITE[:3]) ImageDraw.Draw(dc, 'RGB', ).ellipse((left_in, top_in, right_in, bottom_in), NEUTRAL_COLOR[:3]) ImageDraw.Draw(dc, 'RGB', ).rectangle((left_out, top_out, rect_right, bottom_out), WHITE[:3]) ImageDraw.Draw(dc, 'RGB', ).rectangle((left_in, top_in, rect_right, bottom_in), NEUTRAL_COLOR[:3]) dc = dc.resize((grid_size // 2, grid_size // 2), Image.ANTIALIAS) dc = dc.rotate(orientation) return dc
def present_feedback(self): fill() message("Incorrect!", location=P.screen_c, registration=5, blit_txt=True) flip() feedback_period_cd = CountDown(0.5) # seconds while feedback_period_cd.counting(): ui_request()
def _collect(self): q = pump(True) ui_request(queue=q) for e in q: if e.type == sdl2.SDL_MOUSEBUTTONDOWN: coords = (e.button.x, e.button.y) response = self.which_boundary(coords) if response != None: return response return None
def block(self): # Get response type and feedback type for block self.response_type = self.block_factors[P.block_number - 1]['response_type'] self.feedback_type = self.block_factors[P.block_number - 1]['feedback_type'] # If on first block of session, or response type is different from response type of # previous block, do tutorial animation and practice if self.response_type != self.prev_response_type: # Load instructions for new response type new_instructions = self.instruction_files[self.response_type] instructions_file = os.path.join(P.resources_dir, "Text", new_instructions['text']) inst_txt = io.open(instructions_file, encoding='utf-8').read() self.instructions = message(inst_txt, "instructions", align="center", blit_txt=False) if P.enable_practice: # Load tutorial animation for current condition, play it, and enter practice self.practice_kf = FrameSet(new_instructions['frames'], "assets") if P.block_number == 1: fill() blit(self.practice_instructions, 5, P.screen_c) flip() any_key() else: self.start_trial_button() self.practice() self.prev_response_type = self.response_type for i in range(1, 4): # we do this a few times to avoid block messages being skipped due to duplicate input # from the touch screen we use ui_request() fill() blit(self.instructions, registration=5, location=P.screen_c, flip_x=P.flip_x) flip() any_key() # Indicates if on first trial of block, needed for reloading incomplete sessions self.first_trial = True
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 get_peak_during(self, period, msg=None): """Determines the peak loudness value recorded over a given period. Displays a visual callback that shows the current input volume and the loudest peak encounteredduring the interval so far. Args: period (numeric): the number of seconds to record input for. msg (:obj:`~klibs.KLGraphics.KLNumpySurface.NumpySurface`, optional): a rendered message to display in the top-right corner of the screen during the sampling loop. Returns: int: the loudest peak of all samples recorded during the period. """ local_peak = 0 last_sample = 0 if msg: msg = message(msg, blit_txt=False) flush() self.stream.start() sample_period = CountDown(period + 0.05) while sample_period.counting(): ui_request() sample = self.stream.sample().peak if sample_period.elapsed() < 0.05: # Sometimes 1st or 2nd peaks are extremely high for no reason, so ignore first 50ms continue if sample > local_peak: local_peak = sample sample_avg = (sample + last_sample) / 2 peak_circle = peak(5, int( (local_peak / 32767.0) * P.screen_y * 0.8)) sample_circle = peak( 5, int((sample_avg / 32767.0) * P.screen_y * 0.8)) last_sample = sample fill() blit(Ellipse(peak_circle, fill=[255, 145, 0]), location=P.screen_c, registration=5) blit(Ellipse(sample_circle, fill=[84, 60, 182]), location=P.screen_c, registration=5) if msg: blit(msg, location=[25, 25], registration=7) flip() self.stream.stop() return local_peak
def generate_locations(self): self.n_back_index = self.saccade_count - ( 2 + self.n_back ) # 1 for index, 1 b/c n_back counts from penultimate saccade failed_generations = 0 # generate locations until there are enough for the trial while len(self.disc_locations) < self.saccade_count: ui_request() try: self.disc_locations.append(DiscLocation()) except TrialException: failed_generations += 1 if failed_generations > 10: raise
def block(self): # Determine probe bias for block and generate list of probe locs accordingly if P.block_number > 3: self.probe_bias = "left" nonbiased_loc = "right" else: self.probe_bias = "right" nonbiased_loc = "left" loc_list = [self.probe_bias]*4 + [nonbiased_loc] self.probe_locs = loc_list * int(P.trials_per_block/float(len(loc_list))+1) random.shuffle(self.probe_locs) # At the start of each block, display a start message (block progress if experimental block, # practice message if practice block). After 3000ms, keypress will start first trial. probe_msg = ( "During this block, the colour target will appear more often on the " "{0} and less often on the {1}.".format(self.probe_bias, nonbiased_loc) ) header = "Block {0} of {1}".format(P.block_number, P.blocks_per_experiment) if P.practicing: header = "This is a practice block. ({0})".format(header) if P.block_number > 1: msg = message(header+"\n"+probe_msg, align="center", blit_txt=False) else: msg = message(header, blit_txt=False) message_interval = CountDown(1) while message_interval.counting(): ui_request() # Allow quitting during loop fill() blit(msg, 8, (P.screen_c[0], P.screen_y*0.4)) flip() flush() fill() blit(msg, 8, (P.screen_c[0], P.screen_y*0.4)) message("Press any key to start.", registration=5, location=[P.screen_c[0], P.screen_y*0.6]) flip() any_key() # When running participants, send halfway point and last-block notifications to researcher via Slack if not P.development_mode: if P.block_number == 3: # If participant is halfway done slack_message("Halfway done ({0}/{1})".format(P.block_number, P.blocks_per_experiment))
def present_stream(self): # Each stream item presented for a pre-specified duration cd = CountDown(self.item_duration) sw = Stopwatch() for item in self.rsvp_stream: cd.reset() sw.reset() fill() blit(item, registration=5, location=P.screen_c) flip() #print(cd.elapsed) while cd.counting(): ui_request() print(sw.elapsed()) sw.reset()
def trial(self): while self.evm.before("present_fixation", True): ui_request() self.blit_img(self.fixation, 5, P.screen_c) while self.evm.before("search_onset", True): ui_request() if self.search_type == SPACE: self.rc.collect() else: try: self.target_onset = 'NA' self.target_sw = Stopwatch() self.rc.collect() except IndexError: pass if len(self.rc.keypress_listener.responses): response, rt = self.rc.keypress_listener.response() if response != self.present_absent: self.present_feedback() else: response, rt = 'None', 'NA' clear() return { "practicing": str(P.practicing), "block_num": P.block_number, "trial_num": P.trial_number, "search_type": self.search_type, "stimulus_type": "LINE" if P.condition == 'line' else "COLOUR", "present_absent": self.present_absent, "set_size": self.set_size if self.search_type == SPACE else 'NA', "target_distractor": self.target_distractor, "distractor_distractor": self.distractor_distractor, "target_onset": self.target_onset if self.search_type == TIME else 'NA', "response": response, "rt": rt, "error": str(response != self.present_absent) }
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 get_input_key(self): keys = [] for event in pump(True): if event.type == sdl2.SDL_KEYDOWN: keysym = event.key.keysym if not self.el._quitting: # don't process quit requests while already quitting ui_request(keysym) try: key = self.pylink_keycodes[keysym.sym] except KeyError: key = keysym.sym # don't allow escape to control tracker unless calibrating if key == pylink.ESC_KEY and not self.el.in_setup: key = pylink.JUNK_KEY keys.append(pylink.KeyInput(key, keysym.mod)) return keys
def smart_sleep(interval, units=TK_MS): """Stops and waits for a given amount of time, ensuring that any 'quit' or 'calibrate' key commands issued during the wait interval are processed. Args: interval (float): The number of units of time to pause execution for. units (int, optional): The time unit of 'interval', must be one of `klibs.TK_S` (seconds) or `klibs.TK_MS` (milliseconds). Defaults to milliseconds. """ from klibs.KLUserInterface import ui_request from klibs.KLTime import precise_time if units == TK_MS: interval *= .001 start = precise_time() while precise_time() - start < interval: ui_request()
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()
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 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(self): """ trial_factors: 1 = mask_type, 2 = target_level, 3 = mask_size, 4 = target_shape] """ if P.development_mode: print(self.mask_type, self.target_level, self.mask_size, self.target_shape) self.rc.collect() resp = self.rc.keypress_listener.response() if P.development_mode: print(resp) # handle timeouts if resp.rt == TIMEOUT: feedback = CountDown(1) msg = message("Too slow!", "alert", blit_txt=False) while feedback.counting(): ui_request() fill() blit(msg, 5, P.screen_c) flip() clear() # create readable data as fixation is currrently in (x,y) coordinates if self.fixation == self.fixation_top: initial_fixation = "TOP" elif self.fixation == self.fixation_central: initial_fixation = "CENTRAL" else: initial_fixation = "BOTTOM" return {"practicing": P.practicing, "response": resp.value, "rt": float(resp.rt), "mask_type": self.mask_type, "mask_size": self.mask_size, "local": self.target_shape if self.target_level == LOCAL else "D", "global": self.target_shape if self.target_level == GLOBAL else "D", "d_orientation": self.orientation, "trial_num": P.trial_number, "block_num": P.block_number, "initial_fixation": initial_fixation}