コード例 #1
0
    def prepare_spatial_array(self):
        spatial_array_locs = list(self.spatial_array_locs)
        random.shuffle(spatial_array_locs)

        self.item_locs = []
        jitter = [x * 0.1 for x in range(-3, 4)]

        for i in range(0, self.set_size):

            x_jitter = deg_to_px(random.choice(jitter))
            y_jitter = deg_to_px(random.choice(jitter))

            loc = spatial_array_locs.pop()
            loc[0], loc[1] = loc[0] + x_jitter, loc[1] + y_jitter

            self.item_locs.append(loc)

        spatial_array = []

        if self.present_absent == PRESENT:
            self.target_loc = self.item_locs.pop()
            spatial_array.append([self.target_item, self.target_loc])

        for loc in self.item_locs:
            distractor = random.choice(self.distractors)
            spatial_array.append([distractor, loc])

        return spatial_array
コード例 #2
0
    def construct_cue(self):

        self.cue_seg_len = deg_to_px(1.7)
        self.cue_seg_thick = deg_to_px(0.4)

        canvas_size = [self.cue_seg_len, self.cue_seg_len]

        canvas = Image.new('RGBA', canvas_size, (0, 0, 0, 0))
        surface = aggdraw.Draw(canvas)

        pen = aggdraw.Pen(WHITE, self.cue_seg_len)

        # Regardless of orientation, two segments remain the same
        xy = [(0, 0, 0, self.cue_seg_len), (0, 0, self.cue_seg_len, 0)]

        # Add missing segment, dependent on orientation
        if self.box_alignment == VERTICAL:
            xy.append((self.cue_seg_len, self.cue_seg_len, 0, self.cue_seg_len))

        else:
            xy.append((self.cue_seg_len, 0, self.cue_seg_len, self.cue_seg_len))

        for seg in xy:
            surface.line(seg, pen)

        surface.flush()

        return np.asarray(canvas)
コード例 #3
0
    def setup(self):

        # Generate messages to be displayed during experiment
        self.err_msgs = {}
        if P.saccade_response_cond:
            self.err_msgs['eye'] = "Moved eyes too soon!"
            self.err_msgs['key'] = "Please respond with eye movements only."
            self.err_msgs['early'] = self.err_msgs['key']  # for convenience in logic
        else:
            self.err_msgs['eye'] = "Moved eyes!"
            self.err_msgs['key'] = "Please respond with the spacebar only."
            self.err_msgs['early'] = "Responded too soon!"

        # Stimulus sizes
        self.target_diameter = deg_to_px(1.0)

        self.cue_seg_len = deg_to_px(1.7)
        self.cue_seg_thick = deg_to_px(0.4)

        self.rect_long_side = deg_to_px(11.4)
        self.rect_short_side = deg_to_px(1.7)
        self.rect_thickness = deg_to_px(0.2)

        self.fix_width = deg_to_px(1.0)
        self.fix_thickness = deg_to_px(0.2)

        # Stimulus drawbjects
        # NOTE: too many properties of placeholders & cue vary trial-by-trial, easier to construct during trial_prep()
        self.fix = FixationCross(size=self.fix_width, thickness=self.fix_thickness, fill=RED)
        self.target = Circle(diameter=self.target_diameter, fill=WHITE)

        # Offset between center of placeholders & fixation
        offset = deg_to_px(4.8)

        # Use offsets to establish possible stimulus locations
        self.locations = {
            "left": [P.screen_c[0] - offset, P.screen_c[1]],
            'right': [P.screen_c[0] + offset, P.screen_c[1]],
            'top': [P.screen_c[0], P.screen_c[1] - offset],
            'bottom': [P.screen_c[0], P.screen_c[1] + offset],
            'top_left': [P.screen_c[0] - offset, P.screen_c[1] - offset],
            'top_right': [P.screen_c[0] + offset, P.screen_c[1] - offset],
            'bottom_left': [P.screen_c[0] - offset, P.screen_c[1] + offset],
            'bottom_right': [P.screen_c[0] + offset, P.screen_c[1] + offset]
        }

        # Define keymap for ResponseCollector
        self.keymap = KeyMap(
            "speeded response",  # Name
            ["spacebar"],  # UI Label
            ["spacebar"],  # Data Label
            [SDLK_SPACE]  # SDL2 Keycode
        )

        # Instantiate boundary inspector to handle drift checks & target acquisitions (for saccade responses)
        self.bi = BoundaryInspector()
        self.gaze_boundary = deg_to_px(3.0)  # Radius of 3.0º of visual angle
        self.bi.add_boundary(label="drift_correct", bounds=[P.screen_c, self.gaze_boundary], shape="Circle")
コード例 #4
0
    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
コード例 #5
0
    def present_spatial_array(self, spatial_array):
        fill()
        blit(self.fixation, registration=5, location=P.screen_c)

        if P.development_mode:
            c = kld.Annulus(deg_to_px(1.0),
                            deg_to_px(0.1),
                            fill=(255, 0, 0, 255))
            blit(c, registration=5, location=self.target_loc)
        for item in spatial_array:
            blit(item[0], registration=5, location=item[1])
        flip()
コード例 #6
0
    def present_target(self):
        msg = "This is your target!"
        msg_loc = [P.screen_c[0], P.screen_c[1] - deg_to_px(2.0)]

        fill()
        message(msg, location=msg_loc, registration=5)
        blit(self.target_item, location=P.screen_c, registration=5)
        flip()
コード例 #7
0
    def setup(self):

        # Stimulus sizes
        fixation_size = deg_to_px(0.32)
        fixation_thickness = deg_to_px(0.08)
        cue_size = deg_to_px(0.64)
        cue_thickness = deg_to_px(0.08)
        arrow_tail_len = deg_to_px(0.48)
        arrow_tail_width = deg_to_px(0.15)
        arrow_head_len = deg_to_px(0.25)
        arrow_head_width = deg_to_px(0.45, even=True)
        arrow_dimensions = [
            arrow_tail_len, arrow_tail_width, arrow_head_len, arrow_head_width
        ]

        # Stimuli
        self.warning_tone = Tone(50, 'sine', frequency=2000, volume=0.5)
        self.fixation = kld.FixationCross(fixation_size,
                                          fixation_thickness,
                                          fill=WHITE)
        self.cue = kld.Asterisk(cue_size,
                                thickness=cue_thickness,
                                fill=WHITE,
                                spokes=8)

        self.arrow_r = kld.Arrow(*arrow_dimensions, fill=WHITE)
        self.arrow_l = kld.Arrow(*arrow_dimensions, fill=WHITE, rotation=180)
        self.arrow_r.render()
        self.arrow_l.render()

        # Layout
        self.height_offset = deg_to_px(1.3)
        self.flanker_offset = arrow_tail_len + arrow_head_len + deg_to_px(0.16)
        self.above_loc = (P.screen_c[0], P.screen_c[1] - self.height_offset)
        self.below_loc = (P.screen_c[0], P.screen_c[1] + self.height_offset)
        self.cue_locations = {'above': self.above_loc, 'below': self.below_loc}

        # Insert practice block (when applicable)
        if P.run_practice_blocks:
            self.insert_practice_block(block_nums=1, trial_counts=24)

        self.instructions_presented = False
コード例 #8
0
    def setup(self):
        
        # Stimulus sizes
        
        self.stim_pad = deg_to_px(self.stim_pad)
        self.txtm.add_style('q_and_a', 48, WHITE)
    
        
        # Generate masks, stimuli, and fixations for the experiment
        
        self.__generate_masks()
        self.__generate_stimuli()
        self.__generate_fixations()

        self.trial_start_msg = message("Press any key to advance...", 'default', blit_txt=False)
コード例 #9
0
ファイル: KLText.py プロジェクト: a-hurst/klibs
 def font_size(self, size):
     if isinstance(size, str):
         unit = ''.join([i for i in size if not (i.isdigit() or i == '.')])
         if len(unit):
             if unit not in ['pt', 'px', 'deg']:
                 raise ValueError(
                     "Font size unit must be either 'pt', 'px', or 'deg'")
             self.__font_size_units = unit
             size = float(''.join(
                 [i for i in size if (i.isdigit() or i == '.')]))
         else:
             size = float(size)
     if self.__font_size_units == 'px':
         self._font_size = int(size * self.scale_factor)
     elif self.__font_size_units == 'deg':
         self._font_size = int(deg_to_px(size) * self.scale_factor)
     else:
         self._font_size = int(size)
     TTF_CloseFont(self.__font)
     self.__font = TTF_OpenFont(self.__fontpath, self._font_size)
コード例 #10
0
    def draw_nback_illustration(self, yloc, target):

        stims = {
            "A": message("A", 'stream', blit_txt=False),
            "B": message("B", 'stream', blit_txt=False),
            "C": message("C", 'stream', blit_txt=False),
            "->": self.arrow.render()
        }
        stim_sequence = ['A', '->', 'B', '->', 'C', '->', 'B', '->', 'B']
        stim_spacing = deg_to_px(2.5)

        for i in range(0, len(stim_sequence)):
            s = stim_sequence[i]
            midpoint = len(stim_sequence) / 2.0 + 0.5
            x_offset = int(stim_spacing * (i + 1 - midpoint))
            y_offset = 4 if s == '->' else 0  # fixes arrow offsets
            blit(stims[s], 5, (P.screen_c[0] + x_offset, yloc + y_offset))
            if (i + 2) / 2.0 == target:
                blit(self.target_box, 5,
                     (P.screen_c[0] + x_offset + 1, yloc + 3))
コード例 #11
0
    def __generate_masks(self):
        smaller_than_screen = True
        while smaller_than_screen:
            self.maximum_mask_size += 1
            new_max_mask_px = deg_to_px(self.maximum_mask_size) + self.mask_blur_width * 4 + 2
            if new_max_mask_px > P.screen_y:
                smaller_than_screen = False
                self.maximum_mask_size -= 1
        for size in self.trial_factory.exp_factors['mask_size']:
            if size > self.maximum_mask_size:
                e_str = "The maximum mask size this monitor can support is {0} degrees.".format(self.maximum_mask_size)
                raise ValueError(e_str)

        clear()
        msg = message("Rendering masks...", "q_and_a", blit_txt=False)
        blit(msg, 5, P.screen_c)
        flip()

        self.masks = {}
        for size in self.trial_factory.exp_factors['mask_size']:
            ui_request()
            self.masks["{0}_{1}".format(CENTRAL, size)] = self.render_mask(size, CENTRAL).render()
            self.masks["{0}_{1}".format(PERIPHERAL, size)] = self.render_mask(size, PERIPHERAL).render()
コード例 #12
0
    def render_mask(self, diameter, mask_type):
        MASK_COLOR = NEUTRAL_COLOR
        diameter = deg_to_px(diameter)
        blur_width = self.mask_blur_width

        if mask_type != "none":
            
            if mask_type == PERIPHERAL:
                bg_width  = P.screen_x * 2
                bg_height = P.screen_y * 2
                inner_fill = TRANSPARENT
                outer_fill = OPAQUE
                
            elif mask_type == CENTRAL:
                bg_width  = diameter + blur_width * 4 + 2
                bg_height = bg_width
                inner_fill = OPAQUE
                outer_fill = TRANSPARENT
                
            # Create solid background
            bg = Image.new('RGB', (bg_width, bg_height), MASK_COLOR[:3])
    
            # Create an alpha mask
            r  = diameter // 2
            x1 = (bg_width  // 2) - r
            y1 = (bg_height // 2) - r
            x2 = (bg_width  // 2) + r
            y2 = (bg_height // 2) + r
            alpha_mask = Image.new('L', (bg_width, bg_height), outer_fill)
            ImageDraw.Draw(alpha_mask).ellipse((x1, y1, x2, y2), fill=inner_fill)
            alpha_mask = alpha_mask.filter( ImageFilter.GaussianBlur(blur_width) )
    
            # Apply mask to background and render
            bg.putalpha(alpha_mask)
            mask = aggdraw_to_numpy_surface(bg)

        return mask
コード例 #13
0
    def render_figure(self, figure_shape, orientation):

        stim_size_px = deg_to_px(self.stim_size)
        half_pad = self.stim_pad
        pad = 2 * self.stim_pad
        tl_fig = pad
        br_fig = 2 * stim_size_px
        rect_right = br_fig - stim_size_px
        dc_size = (stim_size_px + half_pad) * 2
        dc = Image.new('L', (dc_size, dc_size), 0)

        if figure_shape == CIRCLE:
            ImageDraw.Draw(dc, 'L').ellipse((tl_fig, tl_fig, br_fig, br_fig), 255)
        if figure_shape == SQUARE:
            ImageDraw.Draw(dc, 'L').rectangle((tl_fig, tl_fig, br_fig, br_fig), 255)
        if figure_shape is False:
            ImageDraw.Draw(dc, 'L').ellipse((tl_fig, tl_fig, br_fig, br_fig), 255)
            ImageDraw.Draw(dc, 'L').rectangle((tl_fig, tl_fig, rect_right, br_fig), 255)

        if orientation > 0:
            dc = dc.rotate(orientation)
        cookie_cutter = dc.resize((dc_size // 2, dc_size // 2), Image.ANTIALIAS)

        return cookie_cutter
コード例 #14
0
ファイル: experiment.py プロジェクト: TheKleinLab/TOJ_Motion
    def trial_prep(self):
        
        # Determing the starting locations of the two target shapes
    
        if self.t1_location == "left":
            t1_x = self.left_x
            t2_x = self.right_x
        else:
            t1_x = self.right_x
            t2_x = self.left_x
            
        # Set shapes for t1 and t2
        
        if self.t1_shape == "a":
            self.t1 = self.diamond_a
            self.t2 = self.diamond_b
            self.t1_line = self.line_b
            self.t2_line = self.line_a
        else:
            self.t1 = self.diamond_b
            self.t2 = self.diamond_a
            self.t1_line = self.line_a
            self.t2_line = self.line_b
        
        self.t1_pos = (t1_x, P.screen_c[1])
        self.t2_pos = (t2_x, P.screen_c[1])

        # Initialize start/end positions and animation paths
        
        if self.toj_type == "motion":
            self.start_offset = P.screen_y/4 + deg_to_px(random.uniform(-2, 2))
            end_offset = deg_to_px(5.0)
            
            if self.upper_target == "t2":
                self.start_offset *= -1
                end_offset *= -1
                self.t1_reg = 8
                self.t2_reg = 2
            else:
                self.t1_reg = 2
                self.t2_reg = 8
                
            t1_start = (t1_x, P.screen_c[1]-self.start_offset)
            t1_end = (t1_x, P.screen_c[1]+end_offset)
            self.t1_path = Animation(t1_start, t1_end, self.motion_duration)
            
            t2_offset = self.t1_path.motion_per_ms[1] * self.t1_t2_soa
            t2_start = (t2_x, P.screen_c[1]+self.start_offset+t2_offset)
            t2_end = (t2_x, P.screen_c[1]-end_offset+t2_offset)
            self.t2_path = Animation(t2_start, t2_end, self.motion_duration)
            
            print(self.upper_target, self.t1_location, self.t1_t2_soa, self.start_offset, t2_offset)
            print("t1 start: {0} end: {1}".format(t1_start, t1_end))
            print("t2 start: {0} end: {1}".format(t2_start, t2_end))

        # Set up colour probe and colour wheel

        self.wheel.rotation = random.randrange(0, 360, 1)
        self.wheel.render()
        self.probe.fill = self.wheel.color_from_angle(random.randrange(0, 360, 1))
        self.probe.render()
        
        # Determine the probe location for the trial
        
        self.probe_location = self.probe_locs.pop()
        self.probe_pos = self.probe_positions[self.probe_location]
        
        # Calculate when t1 onset and t2 off are going to be based on motion
        
        if self.toj_type == "motion":
            self.t1_on = (1/self.t1_path.motion_per_ms[1])*self.start_offset
            self.t2_off = (1/self.t1_path.motion_per_ms[1])*(self.start_offset+end_offset)
        else:
            self.t1_on = self.random_interval(700, 1200)
            self.t2_off = self.t1_on + self.t1_t2_soa-1 + 300
        
        # Add timecourse of events to EventManager
        
        events = []
        events.append([self.t1_on, 't1_on'])
        events.append([events[-1][0] + 200, 'probe_off'])
        events.append([events[-2][0] + self.t1_t2_soa-1, 't2_on'])
        events.append([self.t2_off, 't2_off'])
        for e in events:
            self.evm.register_ticket(ET(e[1], e[0]))
コード例 #15
0
ファイル: experiment.py プロジェクト: a-hurst/ANTI_VEA
    def setup(self):

        # Stimulus sizes

        fixation_size = deg_to_px(0.32)
        fixation_thickness = deg_to_px(0.08)
        cue_size = deg_to_px(0.64)
        cue_thickness = deg_to_px(0.08)
        arrow_tail_len = deg_to_px(0.48)
        arrow_tail_width = deg_to_px(0.15)
        arrow_head_len = deg_to_px(0.25)
        arrow_head_width = deg_to_px(0.45, even=True)
        arrow_dimensions = [
            arrow_tail_len, arrow_tail_width, arrow_head_len, arrow_head_width
        ]

        # Stimuli

        self.warning_tone = Tone(50.1, 'sine', frequency=2000, volume=0.5)
        self.fixation = kld.FixationCross(fixation_size,
                                          fixation_thickness,
                                          fill=BLACK)
        self.cue = kld.Asterisk(cue_size,
                                thickness=cue_thickness,
                                fill=BLACK,
                                spokes=8)
        self.arrow_r = kld.Arrow(*arrow_dimensions, fill=BLACK)
        self.arrow_l = kld.Arrow(*arrow_dimensions, fill=BLACK, rotation=180)
        self.arrow_r.render()
        self.arrow_l.render()

        # Layout

        self.height_offset = deg_to_px(1.3)
        self.height_jitter = deg_to_px(0.04)
        self.flanker_offset = arrow_tail_len + arrow_head_len + deg_to_px(0.16)
        self.ev_offsets = {
            'above': -(self.height_jitter * 4),
            'below': self.height_jitter * 4
        }

        self.above_loc = (P.screen_c[0], P.screen_c[1] - self.height_offset)
        self.below_loc = (P.screen_c[0], P.screen_c[1] + self.height_offset)

        self.cue_locations = {'above': self.above_loc, 'below': self.below_loc}

        # Add text styles for PVT and block messages

        self.txtm.add_style("PVT", '1.5deg', color=RED)
        self.txtm.add_style("block_msg", '0.6deg', color=BLACK)

        # If it's the first session, insert practice blocks with feedback

        if P.run_practice_blocks and P.session_number == 1:
            ANTI_only = ['ANTI-valid', 'ANTI-invalid', 'ANTI-none']
            ANTI_EV = copy(ANTI_only) * 2 + ['EV-above', 'EV-below'
                                             ] * 3  # 1/2 ANTI, 1/2 EV
            ANTI_EV_AV = copy(ANTI_EV) + ['AV'] * 6  # 1/3 ANTI, 1/3 EV, 1/3 AV
            self.insert_practice_block(1,
                                       trial_counts=16,
                                       factor_mask={'trial_type': ANTI_only})
            self.insert_practice_block(2,
                                       trial_counts=32,
                                       factor_mask={'trial_type': ANTI_EV})
            self.insert_practice_block(3,
                                       trial_counts=48,
                                       factor_mask={'trial_type': ANTI_EV_AV})
コード例 #16
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()
コード例 #17
0
    def create_stimuli(self, tdSimilarity, ddSimilarity):
        # Setup target properties
        if P.condition == 'line':
            self.target_angle = random.randint(0, 179)
            self.target_height = deg_to_px(0.1)
            self.target_fill = WHITE

            self.target_item = kld.Rectangle(width=self.item_size,
                                             height=self.target_height,
                                             fill=WHITE,
                                             rotation=self.target_angle)

            if tdSimilarity == HIGH:
                self.ref_angle = self.target_angle
            else:
                self.ref_angle = self.target_angle + 90

            self.mask = kld.Asterisk(size=self.item_size,
                                     thickness=self.target_height,
                                     fill=WHITE,
                                     spokes=12)

        else:
            self.target_height = self.item_size
            self.palette = kld.ColorWheel(diameter=self.item_size)
            self.target_angle = random.randint(0, 359)
            self.target_colour = self.palette.color_from_angle(
                self.target_angle)

            self.target_item = kld.Rectangle(width=self.item_size,
                                             height=self.target_height,
                                             fill=self.target_colour)

            if tdSimilarity == HIGH:
                self.ref_angle = self.target_angle
            else:
                self.ref_angle = self.target_angle + 180

            self.mask = kld.Rectangle(width=self.item_size, fill=WHITE)

        # Setup distractor(s) properties
        if ddSimilarity == HIGH:
            padding = [random.choice([-20, 20])]
        else:
            padding = [-40, -20, 20, 40]

        self.distractors = []

        if P.condition == 'line':
            for p in padding:
                rotation = self.ref_angle + p

                self.distractors.append(
                    kld.Rectangle(width=self.item_size,
                                  height=self.target_height,
                                  fill=WHITE,
                                  rotation=rotation))
        else:
            for p in padding:
                colour = self.palette.color_from_angle(self.ref_angle + p)
                self.distractors.append(
                    kld.Rectangle(width=self.item_size,
                                  height=self.target_height,
                                  fill=colour))
コード例 #18
0
    def setup(self):

        # Initialize text styles

        self.txtm.add_style('normal', '0.7deg')
        self.txtm.add_style('title', '1.0deg')
        self.txtm.add_style('stream', '1.5deg')

        # Stimulus sizes

        mask_size = deg_to_px(1.5)
        mask_thick = deg_to_px(0.25)
        acc_size = deg_to_px(0.5)
        acc_offset = deg_to_px(3.0)
        arrow_tail_l = deg_to_px(0.5)
        arrow_tail_w = deg_to_px(0.2)
        arrow_head_l = deg_to_px(0.3)
        arrow_head_w = deg_to_px(0.5, even=True)
        box_size = deg_to_px(2.0)
        box_stroke = deg_to_px(0.2)

        # Generate shape stimuli for instructions

        self.arrow = kld.Arrow(arrow_tail_l, arrow_tail_w, arrow_head_l,
                               arrow_head_w)
        self.arrow.fill = P.default_color
        self.target_box = kld.Rectangle(box_size,
                                        stroke=[box_stroke, P.default_color])

        # Generate shape stimuli for task

        self.accuracy_rect = kld.Rectangle(acc_offset,
                                           acc_offset + acc_size * 2,
                                           fill=P.default_color)
        self.accuracy_mask = kld.Rectangle(acc_offset + 2,
                                           acc_offset,
                                           fill=P.default_fill_color)
        self.mask = kld.Asterisk(mask_size, mask_thick, fill=P.default_color)

        # Select random letters from the alphabet and render for use in task

        alphabet = list('ABCDEFGHIJKLMNOPQRSTUVWXYZ')
        random.shuffle(alphabet)
        self.letter_set = alphabet[:P.set_size]
        self.letters = {}
        for letter in self.letter_set:
            self.letters[letter] = message(letter,
                                           style='stream',
                                           blit_txt=False)

        # Initialize thought probes

        self.probe_condition = P.condition_map[P.condition]
        self.probe = self._init_probe(self.probe_condition)

        # Determine proportion of non-nback trials

        is_nback = self.trial_factory.exp_factors['is_target']
        self.nback_rate = sum(is_nback) / len(is_nback)

        # Determine order of difficulty levels (counterbalancing)

        self.first_nback = random.choice([1, 2])

        # Show task instructions and example thought probe

        self.instructions()

        # Add practice blocks to start of task

        if P.run_practice_blocks:
            self.insert_practice_block(1, trial_counts=48)
            self.insert_practice_block(2, trial_counts=48)
コード例 #19
0
ファイル: experiment.py プロジェクト: jmwmulle/WaldoMkIII
    def setup(self):
        self.log_f = open(
            join(P.local_dir, "logs",
                 "P{0}_log_f.txt".format(P.participant_id)), "w+")
        header = {
            "target_presentation_behavior": P.target_presentation_behavior,
            "target_removal_behavior": P.target_removal_behavior,
            "fixation_interval": P.fixation_interval,
            "disc_timeout_interval": P.disc_timeout_interval,
            "drift_correct_initial_persist": P.drift_correct_initial_persist,
            "final_disc_timeout_interval": P.final_disc_timeout_interval
        }

        if P.development_mode:
            for k in header:
                self.log_f.write("{0}: {1}\n".format(k, header[k]))

        self.max_amplitude = deg_to_px(self.max_amplitude_deg)
        self.min_amplitude = deg_to_px(self.min_amplitude_deg)
        self.disc_diameter = deg_to_px(self.disc_diameter_deg)
        self.display_margin = int(self.disc_diameter * 1.5)
        self.search_disc_proto = Annulus(self.disc_diameter,
                                         int(self.disc_diameter * 0.25),
                                         (2, WHITE), BLACK)
        # if P.inter_disc_interval and P.persist_to_exit_saccade:
        # 	raise RuntimeError("P.inter_disc_interval and P.persist_to_exit_saccade cannot both be set.")
        r = drift_correct_target().width * self.fixation_boundary_tolerance
        self.el.add_boundary(INITIAL_FIXATION, [P.screen_c, r],
                             CIRCLE_BOUNDARY)
        fill(P.default_fill_color)
        self.txtm.add_style("msg", 32, WHITE)
        self.txtm.add_style("err", 64, WHITE)
        self.txtm.add_style("disc test", 48, (245, 165, 5))
        self.txtm.add_style("tny", 12)
        self.looked_away_msg = message("Looked away too soon.",
                                       "err",
                                       blit_txt=False)
        message("Loading, please hold...", "msg", flip_screen=True)
        if not P.testing:
            scale_images = False
            # for i in range(1, self.trial_factory.num_values("bg_image")):
            for i in range(1, 10):
                ui_request()
                image_key = "wally_0{0}".format(i)
                #  there are 3 sizes of image included by default; if none match the screen res, choose 1080p then scale
                image_f = join(P.image_dir, image_key,
                               "{0}x{1}.jpg".format(*P.screen_x_y))
                for k, v in imp.load_source(
                        "*", join(P.image_dir, image_key,
                                  "average_color.txt")).__dict__.iteritems():
                    if k == "avg_color":
                        avg_color = v
                    if k == "avg_luminance":
                        avg_luminance = v
                with open(join(P.image_dir, image_key,
                               "average_color.txt")) as color_f:
                    avg_color = eval(color_f.read())
                if not isfile(image_f):
                    image_f = join(P.image_dir, image_key, "1920x1080.jpg")
                    scale_images = True
                img_ns = NpS(image_f)
                self.backgrounds[image_key] = ([
                    image_key, img_ns, avg_color, avg_luminance,
                    message(image_key, blit_txt=False)
                ])
                if scale_images:
                    self.backgrounds[image_key][1].scale(P.screen_x_y)
                self.backgrounds[image_key][1] = self.backgrounds[image_key][
                    1].render()
コード例 #20
0
    def setup(self):
        # Font styles for feedback
        self.txtm.add_style(label='correct', color=WHITE)
        self.txtm.add_style(label='incorrect', color=RED)

        # Stimulus properties #

        # Placeholder(s)
        placeholder_size = deg_to_px(2.0)
        uncued_thick = deg_to_px(0.2)
        cued_thick = deg_to_px(0.5)
        self.cued_stroke = [cued_thick, WHITE, STROKE_CENTER]
        self.uncued_stroke = [uncued_thick, WHITE, STROKE_CENTER]

        # Tone
        tone_type = 'sine'
        tone_duration = 50  # ms
        tone_volume = 0.6   # moderate volume

        # Fixation
        fix_size = deg_to_px(0.8)
        fix_thick = deg_to_px(0.1)

        # Target
        target_size = deg_to_px(0.8)

        # Stimlus construction
        self.fixation = kld.FixationCross(size=fix_size, thickness=fix_thick, fill=WHITE)
        self.audio_tone = Tone(duration=tone_duration, wave_type=tone_type, volume=tone_volume)
        self.target = kld.Circle(diameter=target_size, fill=WHITE)

        self.box_left = kld.Rectangle(width=placeholder_size, stroke=self.uncued_stroke)
        self.box_right = kld.Rectangle(width=placeholder_size, stroke=self.uncued_stroke)

        # Stimulus locations
        offset = deg_to_px(4.0) if P.development_mode else deg_to_px(8.0)  # Normal offset too wide for my laptop
        self.locs = {
            TOP_LEFT: [P.screen_c[0] - offset, P.screen_c[1] - offset],
            TOP_RIGHT: [P.screen_c[0] + offset, P.screen_c[1] - offset],
            BOTTOM_LEFT: [P.screen_c[0] - offset, P.screen_c[1] + offset],
            BOTTOM_RIGHT: [P.screen_c[0] + offset, P.screen_c[1] + offset]
        }

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

        if coinflip:
            self.left_key, self.right_key = 'up', 'down'
            self.keymap = KeyMap(
                "response", ['left', 'right'],
                [LEFT, RIGHT],
                [sdl2.SDLK_UP, sdl2.SDLK_DOWN]
            )

        else:
            self.left_key, self.right_key = 'down', 'up'
            self.keymap = KeyMap(
                "response", ['left', 'right'],
                [LEFT, RIGHT],
                [sdl2.SDLK_DOWN, sdl2.SDLK_UP]
            )

        factor_mask = {'cue_type': ['vis_left', 'vis_right', 'temporal', 'temporal', 'no_cue', 'no_cue'],
                       'tone_trial': [True, False]}

        self.ctoa_practice = [100, 250, 850]
        self.ctoa_testing = [100, 250, 850]

        random.shuffle(self.ctoa_practice)
        random.shuffle(self.ctoa_testing)


        self.insert_practice_block(block_nums=[1, 2, 3], trial_counts=12, factor_mask=factor_mask)
コード例 #21
0
ファイル: experiment.py プロジェクト: brettfeltmate/NP_IOR
    def setup(self):

        box_size = deg_to_px(1.8)
        box_thick = deg_to_px(0.05)
        stim_size = deg_to_px(0.95)
        stim_thick = deg_to_px(0.1)
        fix_size = deg_to_px(1)
        fix_offset = deg_to_px(2.8)

        box_stroke = [box_thick, WHITE, STROKE_CENTER]

        self.txtm.add_style(label='greentext', color=GREEN)

        self.target = kld.Annulus(diameter=stim_size,
                                  thickness=stim_thick,
                                  fill=WHITE)
        self.distractor = kld.FixationCross(size=stim_size,
                                            thickness=stim_thick,
                                            fill=WHITE)

        # Set the rotation of placeholder boxes & fixation to match that of the display (diamond, square)
        rotate = 45 if P.condition == 'diamond' else 0
        self.placeholder = kld.Rectangle(width=box_size,
                                         stroke=box_stroke,
                                         rotation=rotate)
        self.fixation = kld.Asterisk(size=fix_size,
                                     thickness=stim_thick,
                                     fill=WHITE,
                                     rotation=rotate,
                                     spokes=8)

        # Which locations are labelled far or near is dependent on arrangement of display
        # 'near' locations refer to those that lie at the intersection of 'far' locations
        if P.condition == 'square':
            self.far_locs = {
                1: [
                    point_pos(P.screen_c,
                              amplitude=fix_offset,
                              angle=315,
                              clockwise=True), 'NorthEast'
                ],
                2: [
                    point_pos(P.screen_c,
                              amplitude=fix_offset,
                              angle=45,
                              clockwise=True), 'SouthEast'
                ],
                3: [
                    point_pos(P.screen_c,
                              amplitude=fix_offset,
                              angle=135,
                              clockwise=True), 'SouthWest'
                ],
                4: [
                    point_pos(P.screen_c,
                              amplitude=fix_offset,
                              angle=225,
                              clockwise=True), 'NorthWest'
                ]
            }

            self.near_locs = {
                5:
                [midpoint(self.far_locs[4][0], self.far_locs[1][0]), 'North'],
                6:
                [midpoint(self.far_locs[1][0], self.far_locs[2][0]), 'East'],
                7:
                [midpoint(self.far_locs[3][0], self.far_locs[2][0]), 'South'],
                8:
                [midpoint(self.far_locs[3][0], self.far_locs[4][0]), 'West']
            }

        else:  # if P.condition == 'diamond'
            self.far_locs = {
                1: [
                    point_pos(P.screen_c,
                              amplitude=fix_offset,
                              angle=270,
                              clockwise=True), 'North'
                ],
                2: [
                    point_pos(P.screen_c,
                              amplitude=fix_offset,
                              angle=0,
                              clockwise=True), 'East'
                ],
                3: [
                    point_pos(P.screen_c,
                              amplitude=fix_offset,
                              angle=90,
                              clockwise=True), 'South'
                ],
                4: [
                    point_pos(P.screen_c,
                              amplitude=fix_offset,
                              angle=180,
                              clockwise=True), 'West'
                ]
            }

            self.near_locs = {
                5: [
                    midpoint(self.far_locs[4][0], self.far_locs[1][0]),
                    'NorthWest'
                ],
                6: [
                    midpoint(self.far_locs[1][0], self.far_locs[2][0]),
                    'NorthEast'
                ],
                7: [
                    midpoint(self.far_locs[3][0], self.far_locs[2][0]),
                    'SouthEast'
                ],
                8: [
                    midpoint(self.far_locs[3][0], self.far_locs[4][0]),
                    'SouthWest'
                ]
            }

        if not P.development_mode:
            self.keymap = KeyMap('directional_response', [
                'North', 'NorthEast', 'East', 'SouthEast', 'South',
                'SouthWest', 'West', 'NorthWest'
            ], [
                'North', 'NorthEast', 'East', 'SouthEast', 'South',
                'SouthWest', 'West', 'NorthWest'
            ], [
                sdl2.SDLK_KP_8, sdl2.SDLK_KP_9, sdl2.SDLK_KP_6, sdl2.SDLK_KP_3,
                sdl2.SDLK_KP_2, sdl2.SDLK_KP_1, sdl2.SDLK_KP_4, sdl2.SDLK_KP_7
            ])

        else:  # Don't have a numpad myself, so I need an alternative when developing
            self.keymap = KeyMap('directional_response', [
                'North', 'NorthEast', 'East', 'SouthEast', 'South',
                'SouthWest', 'West', 'NorthWest'
            ], [
                'North', 'NorthEast', 'East', 'SouthEast', 'South',
                'SouthWest', 'West', 'NorthWest'
            ], [
                sdl2.SDLK_i, sdl2.SDLK_o, sdl2.SDLK_l, sdl2.SDLK_PERIOD,
                sdl2.SDLK_COMMA, sdl2.SDLK_m, sdl2.SDLK_j, sdl2.SDLK_u
            ])

        # Prime items always presented in far locations
        self.prime_locs = self.far_locs.copy()
        # Probe items can be far or near, determined conditionally
        self.probe_locs = dict(self.near_locs.items() + self.far_locs.items())

        # So, to get a practice block of 25, we first need to generate the full set of 2048
        # possible permutations, trim that down to the 288 legitimate permutations,
        # then trim that down to 25...
        self.insert_practice_block(1, 2048)

        # Because KLibs auto-generates trials for each product of ind_vars.py,
        # a lot of 'vestigial' trials are generated that we don't want, this sorts
        # through the trial list and removes those trials.
        for ind_b, block in enumerate(self.trial_factory.blocks):
            for trial in block:

                # targets & distractors cannot overlap within a given display
                if trial[1] == trial[2] or trial[3] == trial[4]:

                    self.trial_factory.blocks[ind_b].remove(trial)
                    block.i -= 1
                    block.length = len(block.trials)
                    continue
                # For 'near' trials, Ts & Ds cannot appear at 'far' locations
                if trial[0] == 'near':
                    if trial[3] < 5 or trial[4] < 5:

                        self.trial_factory.blocks[ind_b].remove(trial)
                        block.i -= 1
                        block.length = len(block.trials)

                # Conversely, cannot appear at 'near' locations on 'far' trials
                else:
                    if trial[3] > 4 or trial[4] > 4:

                        self.trial_factory.blocks[ind_b].remove(trial)
                        block.i -= 1
                        block.length = len(block.trials)

            # We only want 25 trials for practice, this trims the block
            # to the appropriate length
            if ind_b == 0:
                for trial in block:
                    self.trial_factory.blocks[ind_b].remove(trial)
                    block.i -= 1
                    block.length = len(block.trials)

                    if block.length == 25:
                        break

        # Set to True once instructions are provided
        self.instructed = False
コード例 #22
0
    def setup(self):

        # Stimulus sizes

        fixation_size = deg_to_px(0.5)
        fixation_thickness = deg_to_px(0.05)
        cue_size = deg_to_px(0.5)
        cue_thickness = deg_to_px(0.05)
        arrow_tail_len = deg_to_px(0.35)
        arrow_tail_width = deg_to_px(0.1)
        arrow_head_len = deg_to_px(0.2)
        arrow_head_width = deg_to_px(0.3, even=True)

        # Stimuli

        self.fixation = kld.FixationCross(fixation_size,
                                          fixation_thickness,
                                          fill=BLACK)
        self.cue = kld.Asterisk(cue_size,
                                thickness=cue_thickness,
                                fill=BLACK,
                                spokes=8)
        self.arrow_l = kld.Arrow(arrow_tail_len,
                                 arrow_tail_width,
                                 arrow_head_len,
                                 arrow_head_width,
                                 fill=BLACK,
                                 rotation=180)
        self.arrow_r = kld.Arrow(arrow_tail_len,
                                 arrow_tail_width,
                                 arrow_head_len,
                                 arrow_head_width,
                                 fill=BLACK)
        self.line = kld.Rectangle(arrow_tail_len + arrow_head_len,
                                  arrow_tail_width,
                                  stroke=[0, BLACK, 1],
                                  fill=BLACK)
        self.arrow_l.render()
        self.arrow_r.render()

        # Layout

        height_offset = deg_to_px(1.06)
        flanker_offset = arrow_tail_len + arrow_head_len + deg_to_px(0.06)
        self.above_loc = (P.screen_c[0], P.screen_c[1] - height_offset)
        self.below_loc = (P.screen_c[0], P.screen_c[1] + height_offset)
        self.above_flanker_locs = []
        self.below_flanker_locs = []
        for offset in [-2, -1, 1, 2]:
            x_pos = P.screen_c[0] + (offset * flanker_offset)
            self.above_flanker_locs.append((x_pos, self.above_loc[1]))
            self.below_flanker_locs.append((x_pos, self.below_loc[1]))

        # Initialize feedback messages for practice block

        timeout_msg = message('Too slow! Please try to respond more quickly.',
                              blit_txt=False)
        incorrect_str = (
            "Incorrect response!\n"
            "Please respond to left arrows with the 'z' key and right arrows with the '/' key."
        )
        incorrect_msg = message(incorrect_str, align='center', blit_txt=False)

        self.feedback_msgs = {
            'incorrect': incorrect_msg,
            'timeout': timeout_msg
        }

        # Set up Response Collector to get keypress responses

        self.rc.uses(KeyPressResponse)
        self.rc.terminate_after = [1700, TK_MS
                                   ]  # response period times out after 1700ms
        self.rc.keypress_listener.interrupts = True
        self.rc.keypress_listener.key_map = {'z': 'left', '/': 'right'}

        # Add practice block of 24 trials to start of experiment

        if P.run_practice_blocks:
            self.insert_practice_block(1, trial_counts=24)
コード例 #23
0
    def setup(self):
        # ---------------------------------- #
        # 		  Setup Stimuli      		 #
        # ---------------------------------- #

        # Set stimulus sizes
        line_length = deg_to_px(2)
        line_thickness = deg_to_px(0.5)
        thick_rect_border = deg_to_px(0.7)
        thin_rect_border = deg_to_px(0.3)
        fix_size = deg_to_px(0.6)
        fix_thickness = deg_to_px(0.1)
        square_size = deg_to_px(3)
        large_text_size = deg_to_px(0.65)

        # Stimulus layout
        box_offset = deg_to_px(8.0)
        self.left_box_loc = (P.screen_c[0] - box_offset, P.screen_c[1])
        self.right_box_loc = (P.screen_c[0] + box_offset, P.screen_c[1])

        # Generate target colouring
        # Select target colours from randomly rotated colourwheel
        # ensuring those selected are unique and equidistant
        self.color_selecter = kld.ColorWheel(diameter=1,
                                             rotation=random.randrange(0, 360))
        self.target_colours = []
        for i in (0, 120, 240):
            self.target_colours.append(self.color_selecter.color_from_angle(i))

        # Assign colours to payout valences
        random.shuffle(self.target_colours)
        self.high_value_colour = self.target_colours[0]
        self.low_value_colour = self.target_colours[1]
        self.neutral_value_colour = self.target_colours[2]

        # Initialize drawbjects
        self.thick_rect = kld.Rectangle(
            square_size, stroke=[thick_rect_border, WHITE, STROKE_CENTER])
        self.thin_rect = kld.Rectangle(
            square_size, stroke=[thin_rect_border, WHITE, STROKE_CENTER])

        self.high_val_rect = kld.Rectangle(
            square_size,
            stroke=[thin_rect_border, self.high_value_colour, STROKE_CENTER])
        self.low_val_rect = kld.Rectangle(
            square_size,
            stroke=[thin_rect_border, self.low_value_colour, STROKE_CENTER])

        self.fixation = kld.Asterisk(fix_size, fix_thickness, fill=WHITE)
        self.fix_cueback = kld.Asterisk(fix_size * 2,
                                        fix_thickness * 2,
                                        fill=WHITE)

        self.go = kld.FixationCross(fix_size, fix_thickness, fill=BLACK)
        self.nogo = kld.FixationCross(fix_size,
                                      fix_thickness,
                                      fill=BLACK,
                                      rotation=45)

        self.flat_line = kld.Rectangle(line_length, line_thickness, fill=BLACK)
        self.tilt_line = kld.Rectangle(line_length,
                                       line_thickness,
                                       fill=BLACK,
                                       rotation=45)

        self.probe = kld.Ellipse(int(0.75 * square_size))

        # ---------------------------------- #
        #   Setup other experiment factors   #
        # ---------------------------------- #

        # COTOA = Cue-Offset|Target-Onset Asynchrony
        self.cotoa = 800  # ms
        self.feedback_exposure_period = 1.25  # sec

        # Training block payout variables
        self.high_payout_baseline = 12
        self.low_payout_baseline = 8
        self.total_score = None
        self.penalty = -5

        # ---------------------------------- #
        #     Setup Response Collectors      #
        # ---------------------------------- #

        # Initialize response collectors
        self.probe_rc = ResponseCollector(uses=RC_KEYPRESS)
        self.training_rc = ResponseCollector(uses=RC_KEYPRESS)

        # Initialize ResponseCollector keymaps
        self.training_keymap = KeyMap(
            'training_response',  # Name
            ['z', '/'],  # UI labels
            ["left", "right"],  # Data labels
            [sdl2.SDLK_z, sdl2.SDLK_SLASH]  # SDL2 Keysyms
        )
        self.probe_keymap = KeyMap('probe_response', ['spacebar'], ["pressed"],
                                   [sdl2.SDLK_SPACE])

        # --------------------------------- #
        #     Setup Experiment Messages     #
        # --------------------------------- #

        # Make default font size larger
        self.txtm.add_style('myText', large_text_size, WHITE)

        err_txt = "{0}\n\nPress any key to continue."
        lost_fixation_txt = err_txt.format(
            "Eyes moved! Please keep your eyes on the asterisk.")
        probe_timeout_txt = err_txt.format(
            "No response detected! Please respond as fast and as accurately as possible."
        )
        training_timeout_txt = err_txt.format("Line response timed out!")
        response_on_nogo_txt = err_txt.format(
            "\'nogo\' signal (x) presented\nPlease only respond when you see "
            "the \'go\' signal (+).")

        self.err_msgs = {
            'fixation':
            message(lost_fixation_txt,
                    'myText',
                    align='center',
                    blit_txt=False),
            'probe_timeout':
            message(probe_timeout_txt,
                    'myText',
                    align='center',
                    blit_txt=False),
            'training_timeout':
            message(training_timeout_txt,
                    'myText',
                    align='center',
                    blit_txt=False),
            'response_on_nogo':
            message(response_on_nogo_txt,
                    'myText',
                    align='center',
                    blit_txt=False)
        }

        self.rest_break_txt = err_txt.format(
            "Whew! that was tricky eh? Go ahead and take a break before continuing."
        )
        self.end_of_block_txt = "You're done the first task! Please buzz the researcher to let them know!"

        # -------------------------------- #
        #     Setup Eyelink boundaries     #
        # -------------------------------- #
        fix_bounds = [P.screen_c, square_size / 2]
        self.el.add_boundary('fixation', fix_bounds, CIRCLE_BOUNDARY)

        # --------------------------------- #
        # Insert training block (line task) #
        # --------------------------------- #
        if P.run_practice_blocks:
            self.insert_practice_block(1, trial_counts=P.trials_training_block)
コード例 #24
0
ファイル: experiment.py プロジェクト: TheKleinLab/TOJ_Motion
    def setup(self):
        
        # Stimulus Sizes
        
        target_size = deg_to_px(3.0)
        diamond_size = sqrt(target_size**2/2.0)
        probe_diameter = deg_to_px(1.0)
        wheel_diameter = deg_to_px(16.0)
        
        # Stimulus Drawbjects
        
        self.line_a = kld.Rectangle(width=P.screen_x/2, height=2, fill=WHITE)
        self.line_b = kld.Rectangle(width=P.screen_x/2, height=2, fill=BLACK)
        self.diamond_a = kld.Rectangle(diamond_size, fill=WHITE, rotation=45)
        self.diamond_b = kld.Rectangle(diamond_size, fill=BLACK, rotation=45)
        self.probe = kld.Ellipse(probe_diameter, fill=None)
        self.wheel = kld.ColorWheel(wheel_diameter)
        
        self.line_a.render()
        self.line_b.render()
        self.diamond_a.render()
        self.diamond_b.render()
        
        # Layout
        
        self.left_x = P.screen_x/4
        self.right_x = 3*P.screen_x/4
        self.probe_positions = {
            "left": (self.left_x, P.screen_c[1]),
            "right": (self.right_x, P.screen_c[1])
        }
    
        self.start_baseline = P.screen_y/4
        self.end_offset = deg_to_px(5.0)
        self.left_start = [self.left_x, P.screen_y/4]
        self.right_start = [self.right_x, 3*P.screen_y/4]
        self.left_end = [self.left_x, P.screen_c[1]+self.end_offset]
        self.right_end = [self.right_x, P.screen_c[1]-self.end_offset]
        
        # Timing
        
        self.motion_duration = 1.5 # seconds
        
        # Experiment Messages
        
        if not P.condition:
            P.condition = P.default_condition
            
        toj_string = "Which shape {0} {1}?\n(White = 8   Black = 2)"
        stationary_string = toj_string.format("appeared", P.condition)
        motion_string = toj_string.format("touched the line", P.condition)
        self.toj_prompts = {
            'stationary': message(stationary_string, align="center", blit_txt=False),
            'motion': message(motion_string, align="center", blit_txt=False)
        }
        
        # Initialize ResponseCollector keymaps

        if P.use_numpad:
            keysyms = [sdl2.SDLK_KP_8, sdl2.SDLK_KP_2]
        else:
            keysyms = [sdl2.SDLK_8, sdl2.SDLK_2]

        self.toj_keymap = KeyMap(
            "toj_responses", # Name
            ['8', '2'], # UI labels
            ['white', 'black'], # Data labels
            keysyms # SDL2 Keysyms
        )

        # Initialize second ResponseCollector object for colour wheel responses

        self.wheel_rc = ResponseCollector()
        
        # Generate practice blocks
        
        default_soas = self.trial_factory.exp_factors['t1_t2_soa']
        toj_soas = [soa for soa in default_soas if soa!=0.0]
        toj_only = {"t1_t2_soa": toj_soas}
        probe_only = {"t1_t2_soa": [0.0]}
        
        if P.run_practice_blocks:
            num = P.trials_per_practice_block
            self.insert_practice_block(1, trial_counts=num, factor_mask=toj_only)
            self.insert_practice_block((2,4), trial_counts=num, factor_mask=probe_only)
        self.trial_factory.dump()
コード例 #25
0
    def setup(self):

        # Bandit Variables

        self.high_payout_baseline = 12
        self.low_payout_baseline = 8
        self.total_score = None
        self.penalty = -5

        # Stimulus Sizes

        thick_rect_border = deg_to_px(0.5)
        thin_rect_border = deg_to_px(0.1)
        star_size = deg_to_px(0.6)
        star_thickness = deg_to_px(0.1)
        square_size = deg_to_px(3)
        text_size = deg_to_px(0.65)
        large_text_size = deg_to_px(0.85)

        # Generate bandit colours from colour wheel

        self.bandit_colour_combos = []
        if P.blocks_per_experiment > 4:
            msg = (
                "Only 4 sets of colours available, experiment script must be modified if more"
                "than 4 blocks total are wanted.")
            raise RuntimeError(msg)
        for angle in [0, 45, 90, 135]:
            combo = [const_lum[angle], const_lum[angle + 180]]
            self.bandit_colour_combos.append(combo)
        random.shuffle(self.bandit_colour_combos)

        # Stimulus Drawbjects

        self.thick_rect = Rectangle(
            square_size, stroke=[thick_rect_border, WHITE, STROKE_CENTER])
        self.thin_rect = Rectangle(
            square_size, stroke=[thin_rect_border, WHITE, STROKE_CENTER])
        self.left_bandit = Rectangle(
            square_size, stroke=[thin_rect_border, WHITE, STROKE_CENTER])
        self.right_bandit = Rectangle(
            square_size, stroke=[thin_rect_border, WHITE, STROKE_CENTER])
        self.neutral_box = self.thin_rect.render()
        self.star = Asterisk(star_size, star_thickness, fill=WHITE)
        self.star_cueback = Asterisk(star_size * 2,
                                     star_thickness * 2,
                                     fill=WHITE)
        self.star_muted = Asterisk(star_size, star_thickness, fill=GREY)
        self.probe = Ellipse(int(0.75 * square_size), fill=WHITE).render()

        # Layout

        box_offset = deg_to_px(8.0)
        self.left_box_loc = (P.screen_c[0] - box_offset, P.screen_c[1])
        self.right_box_loc = (P.screen_c[0] + box_offset, P.screen_c[1])

        # Timing

        # Note: cotoa = cue-offset target-onset asynchrony
        self.cotoa_min = 700  # ms
        self.cotoa_max = 1000  # ms
        self.feedback_exposure_period = 1.25  # sec

        # EyeLink Boundaries

        fix_bounds = [P.screen_c, square_size / 2]
        self.el.add_boundary('fixation', fix_bounds, CIRCLE_BOUNDARY)

        # Experiment Messages

        self.txtm.styles[
            'default'].font_size = text_size  # re-define default font size in degrees
        self.txtm.add_style("score up", large_text_size, PASTEL_GREEN)
        self.txtm.add_style("score down", large_text_size, PASTEL_RED)
        self.txtm.add_style("timeout", large_text_size, WHITE)

        err_txt = "{0}\n\nPress any key to continue."
        lost_fixation_txt = err_txt.format(
            "Eyes moved! Please keep your eyes on the asterisk.")
        too_soon_txt = err_txt.format(
            "Responded too soon! Please wait until the 'go' signal to "
            "make a response.")
        probe_timeout_txt = err_txt.format(
            "No response detected! Please answer louder or faster.")
        bandit_timeout_txt = err_txt.format("Bandit selection timed out!")
        wrong_response_txt = err_txt.format(
            "Wrong response type!\nPlease make vocal responses "
            "to probes and keypress responses to bandits.")

        self.err_msgs = {
            'fixation':
            message(lost_fixation_txt, align='center', blit_txt=False),
            'too_soon':
            message(too_soon_txt, align='center', blit_txt=False),
            'probe_timeout':
            message(probe_timeout_txt,
                    'timeout',
                    align='center',
                    blit_txt=False),
            'bandit_timeout':
            message(bandit_timeout_txt,
                    'timeout',
                    align='center',
                    blit_txt=False),
            'wrong_response':
            message(wrong_response_txt, align='center', blit_txt=False)
        }

        # Initialize separate ResponseCollectors for probe and bandit responses

        self.probe_rc = ResponseCollector(uses=[RC_AUDIO, RC_KEYPRESS])
        self.bandit_rc = ResponseCollector(uses=[RC_AUDIO, RC_KEYPRESS])

        # Initialize ResponseCollector keymap

        self.keymap = KeyMap(
            'bandit_response',  # Name
            ['z', '/'],  # UI labels
            ["left", "right"],  # Data labels
            [sdl2.SDLK_z, sdl2.SDLK_SLASH]  # SDL2 Keysyms
        )

        # Add practice block of 20 trials to start of experiment

        if P.run_practice_blocks:
            self.insert_practice_block(1, trial_counts=20)
コード例 #26
0
    def setup(self):

        # Stimulus sizes
        thick_rect_border = deg_to_px(0.5)
        thin_rect_border = deg_to_px(0.1)
        star_size = deg_to_px(0.6)
        star_thickness = deg_to_px(0.1)
        square_size = deg_to_px(3)
        large_text_size = 0.65

        # Stimulus drawbjects
        self.thick_rect = Rectangle(
            square_size, stroke=[thick_rect_border, WHITE, STROKE_CENTER])
        self.thin_rect = Rectangle(
            square_size, stroke=[thin_rect_border, WHITE, STROKE_CENTER])
        self.neutral_box = self.thin_rect.render()

        self.star = Asterisk(star_size, star_thickness, fill=WHITE)
        self.star_cueback = Asterisk(star_size * 2,
                                     star_thickness * 2,
                                     fill=WHITE)

        self.go = FixationCross(star_size, star_thickness, fill=BLACK)
        self.go.render()
        self.nogo = FixationCross(star_size,
                                  star_thickness,
                                  fill=BLACK,
                                  rotation=45)
        self.nogo.render()

        self.left_bandit = Ellipse(int(0.75 * square_size))
        self.right_bandit = Ellipse(int(0.75 * square_size))
        self.probe = Ellipse(int(0.75 * square_size))

        # Layout
        box_offset = deg_to_px(8.0)
        self.left_box_loc = (P.screen_c[0] - box_offset, P.screen_c[1])
        self.right_box_loc = (P.screen_c[0] + box_offset, P.screen_c[1])

        # Set cotoa
        self.cotoa = 800  # ms

        self.feedback_exposure_period = 1.25  # sec

        # Bandit payout variables
        self.high_payout_baseline = 12
        self.low_payout_baseline = 8
        self.total_score = None
        self.penalty = -5

        # Generate colours from colour wheel
        self.target_colours = [const_lum[0], const_lum[120], const_lum[240]]
        random.shuffle(self.target_colours)

        # Assign to bandits & neutral probe
        self.high_value_colour = self.target_colours[0]
        self.low_value_colour = self.target_colours[1]
        self.neutral_value_colour = self.target_colours[2]

        # EyeLink Boundaries
        fix_bounds = [P.screen_c, square_size / 2]
        self.el.add_boundary('fixation', fix_bounds, CIRCLE_BOUNDARY)

        # Initialize response collectors
        self.probe_rc = ResponseCollector(uses=RC_KEYPRESS)
        self.bandit_rc = ResponseCollector(uses=RC_KEYPRESS)

        # Initialize ResponseCollector keymaps
        self.bandit_keymap = KeyMap(
            'bandit_response',  # Name
            ['z', '/'],  # UI labels
            ["left", "right"],  # Data labels
            [sdl2.SDLK_z, sdl2.SDLK_SLASH]  # SDL2 Keysyms
        )
        self.probe_keymap = KeyMap('probe_response', ['spacebar'], ["pressed"],
                                   [sdl2.SDLK_SPACE])

        # Experiment Messages
        self.txtm.add_style("payout", large_text_size, WHITE)
        self.txtm.add_style("timeout", large_text_size, WHITE)

        err_txt = "{0}\n\nPress any key to continue."
        lost_fixation_txt = err_txt.format(
            "Eyes moved! Please keep your eyes on the asterisk.")
        probe_timeout_txt = err_txt.format(
            "No response detected! Please respond as fast and as accurately as possible."
        )
        bandit_timeout_txt = err_txt.format("Bandit selection timed out!")
        response_on_nogo_txt = err_txt.format(
            "\'nogo\' signal (x) presented\nPlease only respond when you see "
            "the \'go\' signal (+).")

        self.err_msgs = {
            'fixation':
            message(lost_fixation_txt, align='center', blit_txt=False),
            'probe_timeout':
            message(probe_timeout_txt,
                    'timeout',
                    align='center',
                    blit_txt=False),
            'bandit_timeout':
            message(bandit_timeout_txt,
                    'timeout',
                    align='center',
                    blit_txt=False),
            'response_on_nogo':
            message(response_on_nogo_txt, align='center', blit_txt=False)
        }

        self.rest_break_txt = err_txt.format(
            "Whew! that was tricky eh? Go ahead and take a break before continuing."
        )
        self.end_of_block_txt = "You're done the first task! Please buzz the researcher to let them know!"

        # Insert bandit block
        if P.run_practice_blocks:
            self.insert_practice_block(1, trial_counts=P.trials_bandit_block)
コード例 #27
0
    def setup(self):

        # Generate messages to be displayed during experiment
        self.err_msgs = {}
        if P.saccade_response_cond:
            self.err_msgs['eye'] = "Moved eyes too soon!"
            self.err_msgs['key'] = "Please respond with eye movements only."
            self.err_msgs['early'] = self.err_msgs[
                'key']  # for convenience in logic
        else:
            self.err_msgs['eye'] = "Moved eyes!"
            self.err_msgs['key'] = "Please respond with the spacebar only."
            self.err_msgs['early'] = "Responded too soon!"

        # Stimulus sizes
        self.target_width = deg_to_px(
            0.5, even=True)  # diameter of target circle (0.5 degrees)
        self.cue_size = deg_to_px(
            0.5, even=True)  # size of asterisk/fixations (0.5 degrees)
        self.box_size = deg_to_px(
            0.8, even=True)  # size of placeholder boxes (0.8 degrees)

        # Stimulus Drawbjects
        self.box = Rectangle(self.box_size, stroke=(2, WHITE)).render()
        self.cross_r = FixationCross(self.cue_size, 2, fill=RED).render()
        self.cross_w = FixationCross(self.cue_size, 2, fill=WHITE).render()
        self.circle = Circle(self.target_width, fill=WHITE).render()
        self.asterisk = SquareAsterisk(self.cue_size, 2, fill=WHITE).render()

        # Layout of stimuli

        # offset between centre of boxes and centre of screen, in degrees
        offset_size_deg = P.dm_offset_size if P.development_mode else 7.0
        self.offset_size = deg_to_px(offset_size_deg)
        self.target_locs = {
            TOP: (P.screen_c[0], P.screen_c[1] - self.offset_size),
            RIGHT: (P.screen_c[0] + self.offset_size, P.screen_c[1]),
            BOTTOM: (P.screen_c[0], P.screen_c[1] + self.offset_size),
            LEFT: (P.screen_c[0] - self.offset_size, P.screen_c[1])
        }

        # prepare all animation locations for both rotation directions and starting axes
        self.animation_frames = 15
        animation_duration = 300  # ms
        self.frame_duration = animation_duration / self.animation_frames
        rotation_increment = (pi / 2) / self.animation_frames
        cx, cy = P.screen_c
        self.frames = {
            V_START_AXIS: {
                ROT_CW: [],
                ROT_CCW: []
            },
            H_START_AXIS: {
                ROT_CW: [],
                ROT_CCW: []
            }
        }
        for i in range(0, self.animation_frames):
            l_x_cw = -self.offset_size * cos(i * rotation_increment)
            l_y_cw = self.offset_size * sin(i * rotation_increment)
            l_x_ccw = self.offset_size * cos(i * rotation_increment)
            l_y_ccw = -self.offset_size * sin(i * rotation_increment)
            cw_locs = [(cx + l_x_cw, cy + l_y_cw), (cx - l_x_cw, cy - l_y_cw)]
            ccw_locs = [(cx + l_x_ccw, cy - l_y_ccw),
                        (cx - l_x_ccw, cy + l_y_ccw)]
            self.frames[H_START_AXIS][ROT_CW].append(ccw_locs)
            self.frames[H_START_AXIS][ROT_CCW].append(cw_locs)
            self.frames[V_START_AXIS][ROT_CW].insert(0, cw_locs)
            self.frames[V_START_AXIS][ROT_CCW].insert(0, ccw_locs)

        # Define keymap for ResponseCollector
        self.keymap = KeyMap(
            "speeded response",  # Name
            ["spacebar"],  # UI Label
            ["spacebar"],  # Data Label
            [SDLK_SPACE]  # SDL2 Keycode
        )

        # Boundaries for stimuli
        self.fixation_boundary = deg_to_px(
            3.0)  # radius of 3 degrees of visual angle
        self.el.add_boundary("drift_correct",
                             [P.screen_c, self.fixation_boundary],
                             CIRCLE_BOUNDARY)
コード例 #28
0
    def setup(self):

        # Initialize stimulus sizes

        mask_size_x = deg_to_px(3.0)
        mask_size_ring = deg_to_px(4.0)
        mask_thick = deg_to_px(0.3)

        # Initialize text styles
        self.txtm.add_style('normal', '0.7deg')
        self.txtm.add_style('title', '1.0deg')

        self.mask_x = kld.Asterisk(mask_size_x,
                                   mask_thick,
                                   fill=P.default_color,
                                   spokes=8)
        self.mask_ring = kld.Annulus(mask_size_ring,
                                     mask_thick,
                                     fill=P.default_color)

        self.digits = {}
        digits = [1, 2, 3, 4, 5, 6, 7, 8, 9]
        for digit in digits:
            self.digits[digit] = {}

        self.sizes = ['1.5deg', '2.0deg', '2.5deg', '3.0deg', '3.5deg']
        for size in self.sizes:
            self.txtm.add_style(size, size)

        for digit in digits:
            for size in self.sizes:
                self.digits[digit][size] = message(str(digit),
                                                   style=size,
                                                   blit_txt=False)

        # Initialize thought probes

        p_title = message(P.probe_question, "title", blit_txt=False)
        p_responses = P.probe_responses
        p_order = P.probe_order
        p_origin = (P.screen_c[0], P.screen_x // 10)
        self.probe = ThoughtProbe(p_responses, p_title, int(P.screen_x * 0.8),
                                  p_origin, p_order)

        # Initialize effort probes

        qeffort_text = "How much effort were you putting into staying on-task?"
        self.effort_q = message(qeffort_text, "title", blit_txt=False)
        self.mineffort_msg = message("0%", "normal", blit_txt=False)
        self.maxeffort_msg = message("100%", "normal", blit_txt=False)
        self.submit_msg = message("Continue", "normal", blit_txt=False)
        pad = int(self.submit_msg.height * 0.75)
        self.submit = Button(self.submit_msg,
                             int(self.submit_msg.width + pad * 1.5),
                             self.submit_msg.height + pad,
                             registration=8,
                             location=(P.screen_c[0], int(P.screen_y * 0.8)))

        # Randomly distribute probes across session, avoiding placing them less than 20 sec. apart

        self.probe_trials = []
        noprobe_span = [False] * P.noprobe_span
        probe_span = [True] + [False] * (P.probe_span - 1)
        while len(self.probe_trials) < (P.trials_per_block *
                                        P.blocks_per_experiment):
            random.shuffle(probe_span)
            self.probe_trials += noprobe_span + probe_span

        # Show task instructions and example thought probe

        self.instructions()

        # Add practice blocks to start of task

        self.first_nonpractice = 1
        if P.run_practice_blocks:
            num_nonpractice = P.blocks_per_experiment
            self.insert_practice_block(1,
                                       trial_counts=9,
                                       factor_mask={'number': range(1, 10)})
            self.insert_practice_block(2,
                                       trial_counts=9,
                                       factor_mask={'number': range(1, 10)})
            self.first_nonpractice = P.blocks_per_experiment - num_nonpractice + 1