示例#1
0
    def anykey_msg(self, msg):
        msg += "\n\n{press any key to continue}"

        fill()
        message(msg, location=P.screen_c, registration=5, align='center', blit_txt=True)
        flip()

        any_key()
示例#2
0
 def clean_up(self):
     # Let Ss know when experiment is over
     self.all_done_text = "You're all done! Now I get to take a break.\nPlease buzz the researcher to let them know you're done!"
     fill()
     msg = message(self.all_done_text, 'timeout', blit_txt=False)
     blit(msg, 5, P.screen_c)
     flip()
     any_key()
示例#3
0
文件: experiment.py 项目: a-hurst/PVT
    def block(self):

        # Show block start message and wait for input before starting block
        start_msg = message("Press any key to start.", blit_txt=False)
        fill()
        blit(start_msg, 5, P.screen_c)
        flip()

        any_key()  # wait for keypress before continuing
    def clean_up(self):
        # Inform Ss that they have completed the experiment
        all_done_txt = "Whew! You're all done!\nPlease buzz the researcher to let them know."
        all_done_msg = message(all_done_txt, align="center", blit_txt=False)

        fill()
        blit(all_done_msg, 5, P.screen_c)
        flip()
        any_key()
示例#5
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()
示例#6
0
    def trial_prep(self):
        # Reset error flag
        self.targets_shown = False
        self.err = None

        # TRAINING PROPERTIES
        if P.practicing:
            self.cotoa = 'NA'  # No cue, so no COTOA
            # Establish location of target line
            if self.tilt_line_location == LEFT:
                self.tilt_line_loc = self.left_box_loc
                self.flat_line_loc = self.right_box_loc
            else:
                self.tilt_line_loc = self.right_box_loc
                self.flat_line_loc = self.left_box_loc

        # PROBE PROPERTIES
        else:
            # Rest breaks
            if P.trial_number % (P.trials_per_block / P.breaks_per_block) == 0:
                if P.trial_number < P.trials_per_block:
                    fill()
                    msg = message(self.rest_break_txt,
                                  'myText',
                                  blit_txt=False)
                    blit(msg, 5, P.screen_c)
                    flip()
                    any_key()

            # Establish & assign probe location
            self.probe_loc = self.right_box_loc if self.probe_location == RIGHT else self.left_box_loc
            # go/nogo signal always presented w/probe
            self.go_nogo_loc = self.probe_loc

            # Establish & assign probe colour
            if self.probe_colour == HIGH:
                self.probe.fill = self.high_value_colour
            elif self.probe_colour == LOW:
                self.probe.fill = self.low_value_colour
            else:
                self.probe.fill = self.neutral_value_colour

        # Add timecourse of events to EventManager
        if P.practicing:  # training trials
            events = [[1000, 'target_on']]
        else:  # Probe trials
            events = [[1000, 'cue_on']]
            events.append([events[-1][0] + 200, 'cue_off'])
            events.append([events[-1][0] + 200, 'cueback_off'])
            events.append([events[-2][0] + 800, 'target_on'])
        for e in events:
            self.evm.register_ticket(ET(e[1], e[0]))

        # Perform drift correct on Eyelink before trial start
        self.el.drift_correct()
示例#7
0
    def trial(self):

        # Before target onset, show fixation and draw cues during cue period
        while self.evm.before('target_on', pump_events=True):
            self.display_refresh()
            if self.evm.between('cue_on', 'cue_off'):
                self.draw_cues()
            flip()

        # Draw target stimuli/flankers and enter response collection loop
        fill()
        blit(self.fixation, 5, P.screen_c)
        blit(self.target, 5, self.target_loc)
        for loc in self.flanker_locs:
            blit(self.flanker, 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 == TIMEOUT:
            response = 'NA'

        # If practice trial, show participant feedback for bad responses
        if P.practicing and response != self.target_direction:
            fill()
            if response == 'NA':
                blit(self.feedback_msgs['timeout'], 5, P.screen_c)
            else:
                blit(self.feedback_msgs['incorrect'], 5, P.screen_c)
            flip()
            any_key()

        # Otherwise, clear screen immediately after response and wait for trial end
        else:
            self.display_refresh()
            flip()

            while self.evm.before('trial_end', pump_events=True):
                self.display_refresh()
                flip()

        # Log recorded trial data to database
        return {
            "block_num": P.block_number,
            "trial_num": P.trial_number,
            "cue_type": self.cue_type,
            "cue_onset": self.cue_onset if self.cue_type != 'none' else 'NA',
            "target_direction": self.target_direction,
            "target_loc": self.target_location,
            "flanker_type": self.flanker_type,
            "response": response,
            "rt": rt
        }
示例#8
0
 def show_logo(self):
     from klibs.KLUtilities import flush
     from klibs.KLUserInterface import any_key
     from klibs.KLGraphics import fill, blit, flip
     from klibs.KLGraphics import NumpySurface as NpS
     logo = NpS(P.logo_file_path)
     flush()
     for i in (1, 2):
         fill()
         blit(logo, 5, P.screen_c)
         flip()
     any_key()
示例#9
0
    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
示例#10
0
    def __verify_session_structures(self):

        error_strings = {
            "bad_format":
            "Response type and feedback type must be separated by a single hyphen.",
            "bad_condition":
            ("Response type must be either 'PP' (physical), 'MI' (imagery), "
             "or 'CC' (control)."),
            "bad_feedback":
            ("Feedback type must be one or two characters long, and be "
             "a combination of the letters 'V', 'R' and / or 'X'."),
            "bad_trialcount":
            ("Custom trial counts must be specified in (blocktype, trials) "
             "format, where 'trials' is a positive integer."),
        }

        # Validate specified session structure to use, return informative error if formatted wrong
        session_num, block_num = (0, 0)
        e = "Error encountered parsing Block {0} of Session {1} in session structure '{2}' ({3}):"
        for structure_key, session_structure in P.session_structures.items():
            for session in session_structure:
                session_num += 1
                for block in session:
                    block_num += 1
                    if type(block) in [tuple, list]:
                        if isinstance(block[1], int) and block[1] > 0:
                            err = self.validate_block_condition(block[0])
                        else:
                            err = "bad_trialcount"
                    else:
                        err = self.validate_block_condition(block)
                    if err:
                        if isinstance(block, tuple):
                            block = list(block)
                        err_txt1 = e.format(block_num, session_num,
                                            structure_key, str(block))
                        err_txt1 += "\n" + error_strings[err]
                        msg1 = message(err_txt1,
                                       "error",
                                       align="center",
                                       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()
示例#11
0
    def present_instructions(self):
        block_txt = "Block {0} of {1}\n\n".format(P.block_number,
                                                  P.blocks_per_experiment)

        if self.search_type == SPACE:
            block_txt += self.spatial_instructions
        else:
            block_txt += self.temporal_instructions

        if P.practicing:
            block_txt += "\n\n(This is a practice block)"

        block_txt = self.anykey_txt.format(block_txt)
        self.blit_msg(block_txt, "left")
        any_key()
示例#12
0
    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))
示例#13
0
    def clean_up(self):

        if self.session_number == self.session_count and P.enable_learned_figures_querying:
            self.fig_dir = os.path.join(self.p_dir, "learned")
            if not os.path.exists(self.fig_dir):
                os.makedirs(self.fig_dir)

            learned_fig_num = 1
            if query(user_queries.experimental[3]) == "y":
                self.origin_pos = (P.screen_c[0], int(P.screen_y * 0.8))
                self.origin_boundary = [self.origin_pos, P.origin_size // 2]
                while True:
                    self.setup_response_collector()
                    self.rc.draw_listener.add_boundaries([
                        ('start', self.origin_boundary, CIRCLE_BOUNDARY),
                        ('stop', self.origin_boundary, CIRCLE_BOUNDARY)
                    ])
                    self.start_trial_button()
                    self.capture_learned_figure(learned_fig_num)
                    if query(user_queries.experimental[4]) == "y":
                        learned_fig_num += 1
                    else:
                        break

        # if the entire experiment is successfully completed, update the sessions_completed column
        q_str = "UPDATE `participants` SET `sessions_completed` = ? WHERE `id` = ?"
        self.db.query(q_str,
                      QUERY_UPD,
                      q_vars=[self.session_number, P.participant_id])

        # log session data to database
        session_data = {
            'participant_id': P.participant_id,
            'user_id': self.user_id,
            'session_number': self.session_number,
            'completed': now(True)
        }
        self.db.insert(session_data, "sessions")

        # show 'experiment complete' message before exiting experiment
        msg = message(P.experiment_complete_message,
                      "instructions",
                      blit_txt=False)
        flush()
        fill()
        blit(msg, registration=5, location=P.screen_c)
        flip()
        any_key()
示例#14
0
    def practice(self, play_key_frames=True, callback=None):

        self.__practicing__ = True

        if callback == self.__practice__:
            play_key_frames = False
            self.__practice__()
        elif callback == self.practice:
            play_key_frames = True
        elif callback == any_key:
            self.__practicing__ = False
            return any_key()

        if play_key_frames:
            self.practice_kf.play()

        self.practice_button_bar.reset()
        self.practice_button_bar.render()
        self.evm.start_clock()
        cb = self.practice_button_bar.collect_response()
        self.evm.stop_clock()

        self.__practicing__ = False

        return self.practice(callback=cb)
示例#15
0
    def block(self):

        if not P.practicing:
            if self.search_type == SPACE:
                self.condition = self.spatial_conditions_exp.pop()
            else:
                self.condition = self.temporal_conditions_exp.pop()
        else:
            self.condition = self.practice_conditions.pop()

        self.target_distractor, self.distractor_distractor = self.condition

        # Generate wheel to select colors from
        self.color_selector = ColorWheel(deg_to_px(1),
                                         rotation=random.randrange(0, 360))

        # Select target colouring
        self.target_color = self.color_selector.color_from_angle(0)
        self.target_item = Rectangle(width=self.item_size,
                                     fill=self.target_color)

        self.create_stimuli()

        if not self.general_instruct_shown:
            self.general_instruct_shown = True

            general_text = self.anykey_text.format(self.general_instructions)
            general_msg = message(general_text, align='left', blit_txt=False)

            fill()
            blit(general_msg, 5, P.screen_c)
            flip()
            any_key()

        block_txt = "Block {0} of {1}".format(P.block_number,
                                              P.blocks_per_experiment)
        progress_txt = self.anykey_text.format(block_txt)

        if P.practicing:
            progress_txt += "\n(This is a practice block)"

        progress_msg = message(progress_txt, align='center', blit_txt=False)

        fill()
        blit(progress_msg, 5, P.screen_c)
        flip()
        any_key()

        if self.search_type == SPACE:
            block_type_txt = self.anykey_text.format(self.spatial_instructions)
        else:
            block_type_txt = self.anykey_text.format(
                self.temporal_instructions)

        block_type_msg = message(block_type_txt, align='left', blit_txt=False)

        fill()
        blit(block_type_msg, 5, P.screen_c)
        flip()
        any_key()
    def trial_prep(self):
        clear()
        # choose randomly varying parts of trial
        self.orientation = random.choice(self.orientations)
        self.fixation = tuple(random.choice(self.exp_meta_factors['fixation']))

        # infer which mask & stim to use and retrieve them
        self.figure = self.target_shape if self.target_level == LOCAL else False

        if self.figure:
            stim_label = "{0}_D_{1}".format(self.target_shape, self.orientation)
        else:
            stim_label = "D_{0}_{1}".format(self.orientation, self.target_shape)
        self.figure = self.stimuli[stim_label]
        self.mask_label = "{0}_{1}".format(self.mask_type, self.mask_size)
        try:
            self.mask = self.masks[self.mask_label]
        except KeyError:
            self.mask = None  # for the no mask condition, easier than creating empty keys in self.masks
        blit(self.trial_start_msg, 5, P.screen_c)
        flip()
        any_key()
        self.el.drift_correct(self.fixation)
示例#17
0
    def block(self):
        # Grab 1/2 mark
        halfway_block = P.blocks_per_experiment / 2 + 1

        # Present task instructions prior to 1st block
        if not self.instructions_presented:
            self.instructions_presented = True
            halfway_block += 1
            txt = (
                "During this task arrows will appear either above or below fixation.\n"
                "Your job is to indicate in which direction the middle arrow is pointing,\n"
                "both as quickly and accurately as possible.\n"
                "( 'c' = Left, 'm' = Right )\n\n"
                "Press any key to continue...")

            msg = message(txt, align='center', blit_txt=False)

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

            # Instructions will hang until keypress
            any_key()

        # Provide break at 1/2 mark
        if P.block_number == halfway_block:
            txt = ("You're half way through! Take a break and\n"
                   "press any key when you're ready to continue.")

            msg = message(txt, align='center', blit_txt=False)

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

            any_key()
示例#18
0
    def block(self):
        if not P.practicing:
            if self.search_type == SPACE:
                self.condition = self.spatial_conditions_exp.pop()
            else:
                self.condition = self.temporal_conditions_exp.pop()
        else:
            self.condition = self.practice_conditions.pop()

        self.target_distractor, self.distractor_distractor = self.condition

        self.target_tilt = random.randint(0, 179)
        self.target_item = Rectangle(self.item_size,
                                     self.item_thickness,
                                     fill=WHITE,
                                     rotation=self.target_tilt)

        self.create_stimuli()

        if not self.general_instruct_shown:
            self.general_instruct_shown = True

            general_text = self.anykey_text.format(self.general_instructions)
            general_msg = message(general_text, align='left', blit_txt=False)

            fill()
            blit(general_msg, 5, P.screen_c)
            flip()
            any_key()

        block_txt = "Block {0} of {1}".format(P.block_number,
                                              P.blocks_per_experiment)
        progress_txt = self.anykey_text.format(block_txt)

        if P.practicing:
            progress_txt += "\n(This is a practice block)"

        progress_msg = message(progress_txt, align='center', blit_txt=False)

        fill()
        blit(progress_msg, 5, P.screen_c)
        flip()
        any_key()

        if self.search_type == SPACE:
            block_type_txt = self.anykey_text.format(self.spatial_instructions)
        else:
            block_type_txt = self.anykey_text.format(
                self.temporal_instructions)

        block_type_msg = message(block_type_txt, align='left', blit_txt=False)

        fill()
        blit(block_type_msg, 5, P.screen_c)
        flip()
        any_key()
示例#19
0
    def instructions(self):

        p1 = (
            "During this task, you will presented with a sequence of numbers in the middle of "
            "the screen.\n\nPress any key to see an example.")
        p2 = (
            "Your task will be to press the [space] key as quickly as possible whenever a "
            "number other than {0} appears, and to withhold your response whenever the number "
            "is {0}.\n\nPress any key to continue.".format(P.target))
        p3 = (
            "Occasionally, the task will be interrupted by screens asking you about your "
            "focus just prior.\nWhen this happens, please select the most accurate "
            "response using the mouse cursor.\n\nPress any key to see an example."
        )
        p4 = (
            "After responding, you will be asked how much effort you "
            "were putting into staying focused on the task before the interruption.\n\n"
            "When this happens, please answer using the scale from 0% to 100% that will appear "
            "on screen. Your responses will be anonymous so please answer honestly.\n\n"
            "Press any key to see an example.")

        msg1 = message(p1, 'normal', blit_txt=False, align='center')
        msg2 = message(p2,
                       'normal',
                       blit_txt=False,
                       align='center',
                       wrap_width=int(P.screen_x * 0.8))
        msg3 = message(p3,
                       'normal',
                       blit_txt=False,
                       align='center',
                       wrap_width=int(P.screen_x * 0.8))
        msg4 = message(p4,
                       'normal',
                       blit_txt=False,
                       align='center',
                       wrap_width=int(P.screen_x * 0.8))

        # First page of instructions

        flush()
        fill()
        blit(msg1, 5, P.screen_c)
        flip()
        any_key(allow_mouse_click=False)

        # Example stimuli

        numlist = [1, 2, 3, 4, 5, 6, 7, 8, 9]
        random.shuffle(numlist)
        for n in numlist[1:5]:
            trialtime = CountDown(1.125)
            numsize = random.choice(self.sizes)
            while trialtime.counting():
                ui_request()
                fill()
                mask_on = trialtime.elapsed() > 0.25
                if mask_on:
                    blit(self.mask_x, 5, P.screen_c)
                    blit(self.mask_ring, 5, P.screen_c)
                else:
                    blit(self.digits[n][numsize], 5, P.screen_c)
                flip()

        # Task explanation/illustration

        flush()
        fill()
        blit(msg2, 5, P.screen_c)
        flip()
        any_key(allow_mouse_click=False)

        # Probe explanation + example probe

        fill()
        blit(msg3, 5, P.screen_c)
        flip()
        any_key(allow_mouse_click=False)
        self.probe.collect()

        # Slider explanation + example slider

        fill()
        blit(msg4, 5, P.screen_c)
        flip()
        any_key(allow_mouse_click=False)
        self.get_effort()
示例#20
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
        }
示例#21
0
	def present_fixation(self):
		self.blit_it(self.fixation)
		any_key()
示例#22
0
	def present_txt(self, txt):
		msg = message(txt, align='center', blit_txt=False)
		self.blit_it(msg)
		any_key()
示例#23
0
 def show_error_message(self, msg_key):
     fill()
     blit(self.err_msgs[msg_key], location=P.screen_c, registration=5)
     flip()
     any_key()
示例#24
0
    def setup(self):

        self.fix_size = deg_to_px(0.8)
        self.fix_thickness = deg_to_px(0.1)

        self.item_size = deg_to_px(0.8)

        self.fixation = kld.FixationCross(size=self.fix_size,
                                          thickness=self.fix_thickness,
                                          fill=WHITE)

        # Create array of possible stimulus locations for spatial search
        # When it comes time to present stimuli, a random jitter will be applied
        # to each item presented.

        locs = []
        offset = deg_to_px(3.0)

        for x in range(0, 3):
            for y in range(0, 3):

                locs.append(
                    [P.screen_c[0] + offset * x, P.screen_c[1] + offset * y])
                locs.append(
                    [P.screen_c[0] - offset * x, P.screen_c[1] - offset * y])
                locs.append(
                    [P.screen_c[0] + offset * x, P.screen_c[1] - offset * y])
                locs.append(
                    [P.screen_c[0] - offset * x, P.screen_c[1] + offset * y])

        locs.sort()
        self.spatial_array_locs = list(loc
                                       for loc, _ in itertools.groupby(locs))
        self.spatial_array_locs.remove([P.screen_c[0], P.screen_c[1]])

        coinflip = random.choice([True, False])

        if coinflip:
            self.present_key, self.absent_key = 'z', '/'
            self.keymap = KeyMap("response", ['z', '/'], [PRESENT, ABSENT],
                                 [sdl2.SDLK_z, sdl2.SDLK_SLASH])

        else:
            self.present_key, self.absent_key = '/', 'z'
            self.keymap = KeyMap("response", ['/', 'z'], [PRESENT, ABSENT],
                                 [sdl2.SDLK_SLASH, sdl2.SDLK_z])

        self.anykey_txt = "{0}\n\nPress any key to continue..."

        self.general_instructions = (
            "In this experiment you will see a series of items, amongst these items\n"
            "a target item may, or may not, be presented.\n If you see the target item "
            "press the '{0}' key, if the target item wasn't presented press the '{1}' instead.\n\n"
            "The experiment will begin with a few practice rounds to give you a sense of the task."
        ).format(self.present_key, self.absent_key)

        self.spatial_instructions = (
            "Searching in Space!\n\nIn these trials you will see a bunch of items scattered"
            "around the screen.\nIf one of them is the target, press the '{0}' key as fast as possible."
            "\nIf none of them are the target, press the '{1}' key as fast as possible."
        ).format(self.present_key, self.absent_key)

        self.temporal_instructions = (
            "Searching in Time!\n\nIn these trials you will see a series of items presented one\n"
            "at a time, centre-screen. If one of these items is the target, press the '{0}' key.\n"
            "If, by the end, none of them was the target, press the '{1}' key instead."
        ).format(self.present_key, self.absent_key)

        # Setup search conditions (blocked)
        self.spatial_conditions = [[HIGH, LOW], [HIGH, HIGH], [LOW, HIGH],
                                   [LOW, LOW]]
        random.shuffle(self.spatial_conditions)

        self.temporal_conditions = [[HIGH, LOW], [HIGH, HIGH], [LOW, HIGH],
                                    [LOW, LOW]]
        random.shuffle(self.temporal_conditions)

        self.practice_conditions = [[HIGH, LOW], [HIGH, HIGH], [LOW, HIGH],
                                    [LOW, LOW]]
        random.shuffle(self.practice_conditions)

        self.search_type = random.choice([SPACE, TIME])

        if P.run_practice_blocks:
            self.insert_practice_block(
                block_nums=[1, 2, 3, 4],
                trial_counts=P.trials_per_practice_block)

        general_msg = self.anykey_txt.format(self.general_instructions)
        self.blit_msg(general_msg, "left")
        any_key()
示例#25
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
示例#26
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()
示例#27
0
    def block(self):
        if not P.practicing:
            if P.trial_number % 60 == 0:
                rest_txt = "Whew, go ahead a take a break!\nPress any key when you're ready to continue."
                rest_msg = message(rest_txt, align='center', blit_txt=False)
                fill()
                blit(rest_msg, 5, P.screen_c)
                flip()
                any_key()

        self.t1_performance = 0

        # Present block progress
        block_txt = "Block {0} of {1}".format(P.block_number,
                                              P.blocks_per_experiment)
        progress_txt = self.anykey_txt.format(block_txt)

        if P.practicing:
            progress_txt += "\n(This is a practice block)"

        progress_msg = message(progress_txt, align='center', blit_txt=False)

        fill()
        blit(progress_msg, 5, P.screen_c)
        flip()
        any_key()

        # Inform as to block type
        if self.block_type == COLOUR:
            if P.practicing:
                block_type_txt = self.anykey_txt.format(
                    self.prac_colour_instruct)
            else:
                block_type_txt = self.anykey_txt.format(
                    self.test_colour_instruct)
        else:
            if P.practicing:
                block_type_txt = self.anykey_txt.format(
                    self.prac_identity_instruct)
            else:
                block_type_txt = self.anykey_txt.format(
                    self.test_identity_instruct)

        block_type_msg = message(block_type_txt,
                                 align='center',
                                 blit_txt=False)

        fill()
        blit(block_type_msg, 5, P.screen_c)
        flip()
        any_key()

        # Pre-run: First 10 practice trials, no performance adjustments
        self.pre_run_complete = False
        # Practice: Subsequent practice trials wherein performance is adjusted
        self.practice_complete = False
        self.practice_trial_num = 1
        # Reset T1 performance each practice block
        self.t1_performance = 0

        # The following block manually inserts trials one at a time
        # during which performance is checked and adjusted for.
        if P.practicing:
            while P.practicing:
                self.itoa = random.choice([100, 200, 300])
                self.ttoa = random.choice([120, 240, 360, 480, 600])

                self.setup_response_collector()
                self.trial_prep()
                self.evm.start_clock()

                try:
                    self.trial()
                except TrialException:
                    pass

                self.evm.stop_clock()
                self.trial_clean_up()
                # Once practice is complete, the loop is exited
                if self.practice_complete:
                    P.practicing = False
示例#28
0
def collect_demographics(anonymous=False):
    '''Collects participant demographics and writes them to the 'participants' table in the
	experiment's database, based on the queries in the "demographic" section of the project's
	user_queries.json file.
	
	If P.manual_demographics_collection = True, this function should be called at some point during
	the setup() section of your experiment class. Otherwise, this function will be run
	automatically when the experiment is launched.

	Args:
		anonymous (bool, optional): If True, this function will log all of the anonymous values for
			the experiment's demographic queries to the database immediately without prompting the
			user for input.

	'''
    from klibs.KLEnvironment import exp, db

    # ie. demographic questions aren't being asked for this experiment
    if not P.collect_demographics and not anonymous: return

    # first insert required, automatically-populated fields
    demographics = EntryTemplate('participants')
    demographics.log('created', now(True))
    try:
        # columns moved to session_info in newer templates
        demographics.log("random_seed", P.random_seed)
        demographics.log("klibs_commit", P.klibs_commit)
    except ValueError:
        pass

    # collect a response and handle errors for each question
    for q in user_queries.demographic:
        if q.active:
            # if querying unique identifier, make sure it doesn't already exist in db
            if q.database_field == P.unique_identifier:
                # TODO: fix this to work with multi-user mode
                existing = db.query("SELECT `{0}` FROM `participants`".format(
                    q.database_field))
                while True:
                    value = query(q, anonymous=anonymous)
                    if utf8(value) in [utf8(val[0]) for val in existing]:
                        err = ("A participant with that ID already exists!\n"
                               "Please try a different identifier.")
                        fill()
                        blit(
                            message(err,
                                    "alert",
                                    align='center',
                                    blit_txt=False), 5, P.screen_c)
                        flip()
                        any_key()
                    else:
                        break
            else:
                value = query(q, anonymous=anonymous)
            demographics.log(q.database_field, value)

    # typical use; P.collect_demographics is True and called automatically by klibs
    if not P.demographics_collected:
        P.participant_id = db.insert(demographics)
        P.p_id = P.participant_id
        P.demographics_collected = True
        # Log info about current runtime environment to database
        if 'session_info' in db.table_schemas.keys():
            runtime_info = EntryTemplate('session_info')
            for col, value in runtime_info_init().items():
                runtime_info.log(col, value)
            db.insert(runtime_info)
        # Save copy of experiment.py and config files as they were for participant
        if not P.development_mode:
            pid = P.random_seed if P.multi_user else P.participant_id  # pid set at end for multiuser
            P.version_dir = join(P.versions_dir,
                                 "p{0}_{1}".format(pid, now(True)))
            os.mkdir(P.version_dir)
            copyfile("experiment.py", join(P.version_dir, "experiment.py"))
            copytree(P.config_dir, join(P.version_dir, "Config"))
    else:
        #  The context for this is: collect_demographics is set to false but then explicitly called later
        db.update(demographics.table, demographics.defined)

    if P.multi_session_project and not P.manual_demographics_collection:
        try:
            exp.init_session()
        except:
            pass
示例#29
0
    def present_fixation(self):
        fill()
        blit(self.fixation, location=P.screen_c, registration=5)
        flip()

        any_key()
 def show_instruction(self, instruction, registration, location):
     fill()
     blit(instruction, registration, location)
     flip()
     smart_sleep(500)
     any_key()
示例#31
0
    def instructions(self):

        p1 = (
            "During this task, you will presented with a sequence of letters in the middle of "
            "the screen.\n\nPress any key to see an example.")
        p2a = (
            "For some blocks, your task will be to indicate whether each letter matches "
            "the letter just before it:")
        p2b = (
            "For others, your task will be to indicate whether each letter matches "
            "the letter two before it:")
        p2c = "(matches for each block type are highlighted)"
        p3 = (
            "Occasionally, the task will be interrupted by screens asking you about your "
            "focus just prior.\nWhen this happens, please select the most accurate "
            "response using the mouse cursor.\n\nPress any key to see an example."
        )

        msg1 = message(p1, 'normal', blit_txt=False, align='center')
        msg2a = message(p2a,
                        'normal',
                        blit_txt=False,
                        wrap_width=int(P.screen_x * 0.8))
        msg2b = message(p2b,
                        'normal',
                        blit_txt=False,
                        wrap_width=int(P.screen_x * 0.8))
        msg2c = message(p2c, 'normal', blit_txt=False)
        msg3 = message(p3,
                       'normal',
                       blit_txt=False,
                       align='center',
                       wrap_width=int(P.screen_x * 0.8))

        # First page of instructions

        flush()
        fill()
        blit(msg1, 5, P.screen_c)
        flip()
        any_key(allow_mouse_click=False)

        # Example stimuli

        for letter in (1, 2, 1):
            trialtime = CountDown(2.5)
            while trialtime.counting():
                mask_on = trialtime.elapsed() > 0.5
                stim = self.mask if mask_on else self.letters[
                    self.letter_set[letter]]
                ui_request()
                fill()
                blit(self.accuracy_rect, 5, P.screen_c)
                blit(self.accuracy_mask, 5, P.screen_c)
                blit(stim, 5, P.screen_c)
                flip()

        # Task explanation/illustration

        fill()
        blit(msg2a, 8, (P.screen_c[0], int(P.screen_y * 0.15)))
        blit(msg2b, 8, (P.screen_c[0], int(P.screen_y * 0.45)))
        blit(msg2c, 2, (P.screen_c[0], int(P.screen_y * 0.8)))
        self.draw_nback_illustration(int(P.screen_y * 0.3), target=5)
        self.draw_nback_illustration(int(P.screen_y * 0.6), target=4)
        flip()
        any_key(allow_mouse_click=False)

        # Probe explanation + example probe

        fill()
        blit(msg3, 5, P.screen_c)
        flip()
        any_key()
        self.probe.collect()