Exemple #1
0
    def trial(self):
        while self.evm.before("present_fixation", True):
            ui_request()

        self.blit_img(self.fixation, 5, P.screen_c)

        while self.evm.before("search_onset", True):
            ui_request()

        if self.search_type == SPACE:
            self.rc.collect()

        else:
            try:
                self.target_onset = 'NA'
                self.target_sw = Stopwatch()
                self.rc.collect()
            except IndexError:

                pass

        if len(self.rc.keypress_listener.responses):
            response, rt = self.rc.keypress_listener.response()
            if response != self.present_absent:
                self.present_feedback()

        else:
            response, rt = 'None', 'NA'

        clear()

        return {
            "practicing": str(P.practicing),
            "block_num": P.block_number,
            "trial_num": P.trial_number,
            "search_type": self.search_type,
            "stimulus_type": "LINE" if P.condition == 'line' else "COLOUR",
            "present_absent": self.present_absent,
            "set_size": self.set_size if self.search_type == SPACE else 'NA',
            "target_distractor": self.target_distractor,
            "distractor_distractor": self.distractor_distractor,
            "target_onset":
            self.target_onset if self.search_type == TIME else 'NA',
            "response": response,
            "rt": rt,
            "error": str(response != self.present_absent)
        }
    def present_stream(self):
        # Each stream item presented for a pre-specified duration
        cd = CountDown(self.item_duration)
        sw = Stopwatch()
        for item in self.rsvp_stream:
            cd.reset()
            sw.reset()
            fill()
            blit(item, registration=5, location=P.screen_c)
            flip()

            #print(cd.elapsed)
            while cd.counting():
                ui_request()
            print(sw.elapsed())
            sw.reset()
Exemple #3
0
    def trial_prep(self):
        if self.search_type == SPACE:
            self.search_stimuli = self.prepare_spatial_array()
            self.rc.display_callback = self.present_spatial_array
            self.rc.display_kwargs = {'spatial_array': self.search_stimuli}

        else:
            self.search_stimuli = self.prepare_temporal_stream()
            self.rc.display_callback = self.present_temporal_stream
            self.rc.display_kwargs = {'temporal_stream': self.search_stimuli}

        events = [[1000, 'present_example_target']]
        events.append([events[-1][0] + 1000, 'present_fixation'])
        events.append([events[-1][0] + 1000, 'search_onset'])

        for e in events:
            self.evm.register_ticket(ET(e[1], e[0]))

        self.trial_sw = Stopwatch()
        self.present_target()
        hide_mouse_cursor()
Exemple #4
0
class SSAT_DISP2020(klibs.Experiment):
    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()

    def block(self):
        self.search_type = SPACE if self.search_type == TIME else TIME

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

        self.target_distractor, self.distractor_distractor = self.condition

        self.create_stimuli(self.target_distractor, self.distractor_distractor)

        self.present_instructions()

    def setup_response_collector(self):
        self.rc.uses(RC_KEYPRESS)

        self.rc.keypress_listener.interrupts = True
        self.rc.keypress_listener.key_map = self.keymap

        timeout = 10 if self.search_type == SPACE else 100
        self.rc.terminate_after = [timeout, TK_S]

    def trial_prep(self):
        if self.search_type == SPACE:
            self.search_stimuli = self.prepare_spatial_array()
            self.rc.display_callback = self.present_spatial_array
            self.rc.display_kwargs = {'spatial_array': self.search_stimuli}

        else:
            self.search_stimuli = self.prepare_temporal_stream()
            self.rc.display_callback = self.present_temporal_stream
            self.rc.display_kwargs = {'temporal_stream': self.search_stimuli}

        events = [[1000, 'present_example_target']]
        events.append([events[-1][0] + 1000, 'present_fixation'])
        events.append([events[-1][0] + 1000, 'search_onset'])

        for e in events:
            self.evm.register_ticket(ET(e[1], e[0]))

        self.trial_sw = Stopwatch()
        self.present_target()
        hide_mouse_cursor()

    def trial(self):
        while self.evm.before("present_fixation", True):
            ui_request()

        self.blit_img(self.fixation, 5, P.screen_c)

        while self.evm.before("search_onset", True):
            ui_request()

        if self.search_type == SPACE:
            self.rc.collect()

        else:
            try:
                self.target_onset = 'NA'
                self.target_sw = Stopwatch()
                self.rc.collect()
            except IndexError:

                pass

        if len(self.rc.keypress_listener.responses):
            response, rt = self.rc.keypress_listener.response()
            if response != self.present_absent:
                self.present_feedback()

        else:
            response, rt = 'None', 'NA'

        clear()

        return {
            "practicing": str(P.practicing),
            "block_num": P.block_number,
            "trial_num": P.trial_number,
            "search_type": self.search_type,
            "stimulus_type": "LINE" if P.condition == 'line' else "COLOUR",
            "present_absent": self.present_absent,
            "set_size": self.set_size if self.search_type == SPACE else 'NA',
            "target_distractor": self.target_distractor,
            "distractor_distractor": self.distractor_distractor,
            "target_onset":
            self.target_onset if self.search_type == TIME else 'NA',
            "response": response,
            "rt": rt,
            "error": str(response != self.present_absent)
        }

    def trial_clean_up(self):
        self.rc.keypress_listener.reset()
        self.search_stimuli = []

    def clean_up(self):
        pass

    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()

    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

    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()

    def prepare_temporal_stream(self):
        stream_length = 16

        temporal_stream = []

        if self.present_absent == PRESENT:
            self.target_time = random.randint(5, 12)
        else:
            self.target_time = -1

        for i in range(stream_length):
            if i == self.target_time:
                temporal_stream.append([self.target_item, True])

            else:
                distractor = random.choice(self.distractors)
                temporal_stream.append([distractor, False])

        return temporal_stream

    def present_temporal_stream(self, temporal_stream):
        duration_cd = CountDown(ITEM_DURATION, start=False)
        mask_cd = CountDown(MASK_DURATION, start=False)

        response_window = CountDown(2, start=False)

        last_item = True if len(temporal_stream) == 1 else False

        item = temporal_stream.pop()

        if item[1]:
            self.target_onset = self.target_sw.elapsed()

        duration_cd.start()
        while duration_cd.counting():
            self.blit_img(item[0], reg=5, loc=P.screen_c)

        mask_cd.start()
        while mask_cd.counting():
            self.blit_img(self.mask, reg=5, loc=P.screen_c)

        if last_item:
            clear()
            response_window.start()
            while response_window.counting():
                fill()
                flip()

    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()

    def present_feedback(self):
        msg = "Incorrect!"

        self.blit_msg(msg, 'left')

        feedback_cd = CountDown(0.5)
        while feedback_cd.counting():
            ui_request()

    def blit_msg(self, msg, align):
        msg = message(msg, align=align, blit_txt=False)

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

    def blit_img(self, img, reg, loc):
        fill()
        blit(img, registration=reg, location=loc)
        flip()

    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))
Exemple #5
0
class SSAT_Color(klibs.Experiment):
    def setup(self):
        self.group = random.choice(['A', 'B'])
        # Stimulus sizes
        fix_size = deg_to_px(0.6)
        fix_thickness = deg_to_px(0.1)
        self.item_size = deg_to_px(0.8)
        self.item_thickness = deg_to_px(.1)

        # Initilize drawbjects
        self.fixation = FixationCross(fix_size, fix_thickness, fill=WHITE)

        # Initialize ResponseCollectors
        self.spatial_rc = ResponseCollector(uses=RC_KEYPRESS)
        self.temporal_pre_rc = ResponseCollector(uses=RC_KEYPRESS,
                                                 flip_screen=True)
        self.temporal_post_rc = ResponseCollector(uses=RC_KEYPRESS,
                                                  flip_screen=True)

        self.group_A_keymap = KeyMap("search_response", ['z', '/'],
                                     ['absent', 'present'],
                                     [sdl2.SDLK_z, sdl2.SDLK_SLASH])
        self.group_B_keymap = KeyMap("search_response", ['z', '/'],
                                     ['present', 'absent'],
                                     [sdl2.SDLK_z, sdl2.SDLK_SLASH])

        self.item_duration = .1  # seconds
        self.isi = .05  # seconds

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

        self.group_A_instuctions = "If you see the target item, please press the '/' key.\nIf you don't see the target item, please press the 'z' key."
        self.group_B_instuctions = "If you see the target item, please press the 'z' key.\nIf you don't see the target item, please press the '/' key."

        self.general_instructions_1 = "In this experiment, you will see a series of items; amongst these items a target item may, or may not, be presented.\n{0}"
        self.general_instructions_2 = self.general_instructions_1.format(
            self.group_A_instuctions if self.group ==
            "A" else self.group_B_instuctions)
        self.general_instructions_3 = (
            "{0}\nThe experiment will begin with a practice round to familiarlize yourself with the task."
            "\n\nBefore every trial, a preview of the target item will be presented."
        )

        self.spatial_instructions_1 = "Searching in Space!\n\nFor these trials, you will see a collection of items arranged in a circle.\n{0}"
        self.temporal_instructions_1 = "Searching in Time!\n\nFor these trials, you will see a series of items presented one at a time, center screen.\n{0}"

        self.group_A_spatial = (
            "If one of the items is the target, press the '/' key as fast as possible.\n"
            "If none of the items are the target, press the 'z' key as fast as possible."
        )

        self.group_B_spatial = (
            "If one of the items is the target, press the 'z' key as fast as possible.\n"
            "If none of the items are the target, press the '/' key as fast as possible."
        )

        self.group_A_temporal = (
            "At any time, if you see the target item, press the '/' key as fast as possible.\n"
            "Once the images stop appearing, if you haven't seen the target item, press the 'z' key as fast as possible."
        )

        self.group_B_temporal = (
            "At any time, if you see the target item, press the 'z' key as fast as possible.\n"
            "Once the images stop appearing, if you haven't seen the target item, press the '/' key as fast as possible."
        )

        self.general_instructions = self.general_instructions_3.format(
            self.general_instructions_2)
        self.spatial_instructions = self.spatial_instructions_1.format(
            self.group_A_spatial if self.group ==
            'A' else self.group_B_spatial)
        self.temporal_instructions = self.temporal_instructions_1.format(
            self.group_A_temporal if self.group ==
            'A' else self.group_B_temporal)

        self.general_instruct_shown = False

        self.spatial_conditions_exp = [[H**O, HETERO], [H**O, H**O],
                                       [HETERO, H**O], [HETERO, HETERO]]
        self.temporal_conditions_exp = [[H**O, HETERO], [H**O, H**O],
                                        [HETERO, H**O], [HETERO, HETERO]]
        self.practice_conditions = [[H**O, HETERO], [H**O, H**O],
                                    [HETERO, H**O], [HETERO, HETERO]]

        random.shuffle(self.spatial_conditions_exp)
        random.shuffle(self.temporal_conditions_exp)
        random.shuffle(self.practice_conditions)

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

        if P.run_practice_blocks:
            self.insert_practice_block(
                block_nums=range(1, 5),
                trial_counts=P.trials_per_practice_block)

    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 setup_response_collector(self):
        self.spatial_rc.terminate_after = [10, TK_S]
        self.spatial_rc.keypress_listener.interrupts = True
        self.spatial_rc.keypress_listener.key_map = self.group_A_keymap if self.group == 'A' else self.group_B_keymap

        self.temporal_pre_rc.terminate_after = [10, TK_S]
        self.temporal_pre_rc.keypress_listener.key_map = self.group_A_keymap if self.group == 'A' else self.group_B_keymap
        self.temporal_pre_rc.keypress_listener.interrupts = True
        self.temporal_pre_rc.display_callback = self.present_stream

        self.temporal_post_rc.terminate_after = [5, TK_S]
        self.temporal_post_rc.keypress_listener.key_map = self.group_A_keymap if self.group == 'A' else self.group_B_keymap
        self.temporal_post_rc.keypress_listener.interrupts = True
        self.temporal_post_rc.display_callback = self.post_stream

    def trial_prep(self):
        self.target_onset = NA

        if self.search_type == SPACE:
            array_radius = deg_to_px(8)
            theta = 360.0 / self.set_size

            self.item_locs = []

            for i in range(0, self.set_size):
                self.item_locs.append(
                    point_pos(origin=P.screen_c,
                              amplitude=array_radius,
                              angle=0,
                              rotation=theta * (i + 1)))

            random.shuffle(self.item_locs)
            if self.present_absent == PRESENT:
                self.target_loc = self.item_locs.pop()

        else:
            self.rsvp_stream = self.prepare_stream()
            self.rsvp_stream.reverse(
            )  # items are extracted via pop() in present_stream()

        events = [[1000, 'present_target']]
        events.append([events[-1][0] + 1000, 'present_fixation'])
        events.append([events[-1][0] + 1000, 'search_onset'])

        for e in events:
            self.evm.register_ticket(ET(e[1], e[0]))

        hide_mouse_cursor()
        self.present_target()

    def trial(self):
        # Wait 1s before presenting array
        while self.evm.before("present_fixation", True):
            ui_request()

        self.present_fixation()

        while self.evm.before("search_onset", True):
            ui_request()

        if self.search_type == SPACE:
            self.present_array()
            self.spatial_rc.collect()

            if len(self.spatial_rc.keypress_listener.responses):
                spatial_response, spatial_rt = self.spatial_rc.keypress_listener.response(
                )

                if spatial_response != self.present_absent:
                    self.present_feedback()

            else:
                spatial_response = "None"
                spatial_rt = 'NA'
        else:

            try:
                self.response_sw = Stopwatch()
                self.stream_sw = Stopwatch()
                self.target_sw = Stopwatch()

                # the display callback "present_stream()" pops an element each pass; when all targets have been shown this bad boy throws an error
                self.temporal_pre_rc.collect()
            except IndexError:
                pass

            self.stream_sw.pause()

            if len(self.temporal_pre_rc.keypress_listener.responses):
                temporal_response, temporal_rt = self.temporal_pre_rc.keypress_listener.response(
                )

            else:
                self.temporal_post_rc.collect()

                if len(self.temporal_post_rc.keypress_listener.responses):
                    temporal_response = self.temporal_post_rc.keypress_listener.response(
                        rt=False)
                    temporal_rt = self.response_sw.elapsed() * 1000

                else:
                    temporal_response = "None"
                    temporal_rt = "NA"

            if temporal_response != self.present_absent:
                self.present_feedback()

        clear()
        return {
            "practicing":
            str(P.practicing),
            "block_num":
            P.block_number,
            "trial_num":
            P.trial_number,
            "search_type":
            self.search_type,
            "stimulus_type":
            'COLOR',
            "present_absent":
            self.present_absent,
            "set_size":
            self.set_size if self.search_type == SPACE else "NA",
            "target_distractor":
            self.target_distractor,
            "distractor_distractor":
            self.distractor_distractor,
            "target_time":
            self.target_time if self.search_type == TIME else "NA",
            "stream_duration":
            self.stream_sw.elapsed() if self.search_type == TIME else "NA",
            "target_onset":
            self.target_onset if self.search_type == TIME else "NA",
            "spatial_response":
            spatial_response if self.search_type == SPACE else "NA",
            "spatial_rt":
            spatial_rt if self.search_type == SPACE else "NA",
            "temporal_response":
            temporal_response if self.search_type == TIME else "NA",
            "temporal_rt":
            temporal_rt if self.search_type == TIME else "NA"
        }

    def trial_clean_up(self):
        self.spatial_rc.keypress_listener.reset()
        self.temporal_pre_rc.keypress_listener.reset()
        self.temporal_post_rc.keypress_listener.reset()

        if not P.practicing:
            if P.trial_number == P.trials_per_block:
                if self.search_type == SPACE:
                    self.search_type = TIME
                else:
                    self.search_type = SPACE
        else:
            if P.trial_number == P.trials_per_practice_block:
                if self.search_type == SPACE:
                    self.search_type = TIME
                else:
                    self.search_type = SPACE

    def clean_up(self):
        pass

    def present_target(self):
        msg = "This is your target!"
        msg_loc = [P.screen_c[0], (P.screen_c[1] - deg_to_px(2))]

        fill()
        message(msg, location=msg_loc, registration=5)
        blit(self.target_item, location=P.screen_c, registration=5)
        flip()

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

    def present_feedback(self):
        fill()
        message("Incorrect!",
                location=P.screen_c,
                registration=5,
                blit_txt=True)
        flip()

        feedback_period_cd = CountDown(0.5)  # seconds
        while feedback_period_cd.counting():
            ui_request()

    def create_stimuli(self):

        # Select distractor colourings
        ref_angle = 0 if self.target_distractor == H**O else 180

        self.distractor_fills = []

        if self.distractor_distractor == H**O:
            pad = random.choice([-20, 20])
            self.distractor_fills.append(
                self.color_selector.color_from_angle(ref_angle + pad))
        else:
            for i in range(1, 3):
                self.distractor_fills.append(
                    self.color_selector.color_from_angle(ref_angle + (20 * i)))
                self.distractor_fills.append(
                    self.color_selector.color_from_angle(ref_angle - (20 * i)))

            # Now that we have our colouring, create stimuli
        self.distractors = []

        for f in self.distractor_fills:
            self.distractors.append(Rectangle(width=self.item_size, fill=f))

    def present_array(self):
        fill()
        blit(self.fixation, registration=5, location=P.screen_c)

        if self.present_absent == PRESENT:
            blit(self.target_item, registration=5, location=self.target_loc)

        for loc in self.item_locs:
            blit(random.choice(self.distractors), registration=5, location=loc)

        flip()

    def prepare_stream(self):
        self.stream_length = 16

        stream_items = []

        if self.present_absent == PRESENT:
            self.target_time = random.randint(5, 12)
        else:
            self.target_time = -1

        for i in range(self.stream_length):
            if i == self.target_time:
                stream_items.append([self.target_item, True])
            else:
                stream_items.append([random.choice(self.distractors), False])

        return stream_items

    def present_stream(self):

        duration_cd = CountDown(self.item_duration, start=False)
        isi_cd = CountDown(self.isi, start=False)

        item = self.rsvp_stream.pop()

        fill()
        blit(item[0], registration=5, location=P.screen_c)
        flip()

        duration_cd.start()
        while duration_cd.counting():
            pass

        fill()

        if item[1]:
            self.target_onset = self.target_sw.elapsed()

        isi_cd.start()
        while isi_cd.counting():
            pass

    def post_stream(self):
        fill()
        flip()
Exemple #6
0
    def trial(self):
        # Wait 1s before presenting array
        while self.evm.before("present_fixation", True):
            ui_request()

        self.present_fixation()

        while self.evm.before("search_onset", True):
            ui_request()

        if self.search_type == SPACE:
            self.present_array()
            self.spatial_rc.collect()

            if len(self.spatial_rc.keypress_listener.responses):
                spatial_response, spatial_rt = self.spatial_rc.keypress_listener.response(
                )

                if spatial_response != self.present_absent:
                    self.present_feedback()

            else:
                spatial_response = "None"
                spatial_rt = 'NA'
        else:

            try:
                self.response_sw = Stopwatch()
                self.stream_sw = Stopwatch()
                self.target_sw = Stopwatch()

                # the display callback "present_stream()" pops an element each pass; when all targets have been shown this bad boy throws an error
                self.temporal_pre_rc.collect()
            except IndexError:
                pass

            self.stream_sw.pause()

            if len(self.temporal_pre_rc.keypress_listener.responses):
                temporal_response, temporal_rt = self.temporal_pre_rc.keypress_listener.response(
                )

            else:
                self.temporal_post_rc.collect()

                if len(self.temporal_post_rc.keypress_listener.responses):
                    temporal_response = self.temporal_post_rc.keypress_listener.response(
                        rt=False)
                    temporal_rt = self.response_sw.elapsed() * 1000

                else:
                    temporal_response = "None"
                    temporal_rt = "NA"

            if temporal_response != self.present_absent:
                self.present_feedback()

        clear()
        return {
            "practicing":
            str(P.practicing),
            "block_num":
            P.block_number,
            "trial_num":
            P.trial_number,
            "search_type":
            self.search_type,
            "stimulus_type":
            'COLOR',
            "present_absent":
            self.present_absent,
            "set_size":
            self.set_size if self.search_type == SPACE else "NA",
            "target_distractor":
            self.target_distractor,
            "distractor_distractor":
            self.distractor_distractor,
            "target_time":
            self.target_time if self.search_type == TIME else "NA",
            "stream_duration":
            self.stream_sw.elapsed() if self.search_type == TIME else "NA",
            "target_onset":
            self.target_onset if self.search_type == TIME else "NA",
            "spatial_response":
            spatial_response if self.search_type == SPACE else "NA",
            "spatial_rt":
            spatial_rt if self.search_type == SPACE else "NA",
            "temporal_response":
            temporal_response if self.search_type == TIME else "NA",
            "temporal_rt":
            temporal_rt if self.search_type == TIME else "NA"
        }
Exemple #7
0
    def trial(self):
        # Wait 1s before presenting array
        while self.evm.before("present_fixation", True):
            ui_request()

        self.present_fixation()

        while self.evm.before("search_onset", True):
            ui_request()

        if self.search_type == SPACE:
            self.present_array()
            self.spatial_rc.collect()

            if len(self.spatial_rc.keypress_listener.responses):
                spatial_response, spatial_rt = self.spatial_rc.keypress_listener.response(
                )

                if spatial_response != self.present_absent:
                    self.present_feedback()
            else:
                spatial_response = "None"
                spatial_rt = 'NA'
        else:

            try:
                self.response_sw = Stopwatch(
                )  # Used as RT stopwatch in cases where responses are made after stream has ended
                self.stream_sw = Stopwatch(
                )  # Records duration of stream, just in case it becomes handy to have
                self.target_sw = Stopwatch(
                )  # Records time that target is presented, again just in case.

                # the display callback "present_stream()" pops an element each pass; when all targets have been shown this bad boy throws an error
                self.temporal_pre_rc.collect()
            except IndexError:
                pass

            # Pause once stream has ended
            self.stream_sw.pause()

            if len(self.temporal_pre_rc.keypress_listener.responses):
                temporal_response, temporal_rt = self.temporal_pre_rc.keypress_listener.response(
                )

            else:
                self.temporal_post_rc.collect()

                if len(self.temporal_post_rc.keypress_listener.responses):
                    temporal_rt = self.response_sw.elapsed(
                    ) * 1000  # Rescale RT to ms and log
                    temporal_response = self.temporal_post_rc.keypress_listener.response(
                        rt=False)

                else:
                    temporal_response = "None"
                    temporal_rt = "NA"

            if temporal_response != self.present_absent:
                self.present_feedback()

        clear()

        return {
            "practicing":
            str(P.practicing),
            "block_num":
            P.block_number,
            "trial_num":
            P.trial_number,
            "search_type":
            self.search_type,
            "stimulus_type":
            'LINE',
            "present_absent":
            self.present_absent,
            "set_size":
            self.set_size if self.search_type == SPACE else 'NA',
            "target_distractor":
            self.target_distractor,
            "distractor_distractor":
            self.distractor_distractor,
            "target_time":
            self.target_time if self.search_type == TIME else "NA",
            "stream_duration":
            self.stream_sw.elapsed() if self.search_type == TIME else "NA",
            "target_onset":
            self.target_onset if self.search_type == TIME else "NA",
            "spatial_response":
            spatial_response if self.search_type == SPACE else "NA",
            "spatial_rt":
            spatial_rt if self.search_type == SPACE else "NA",
            "temporal_response":
            temporal_response if self.search_type == TIME else "NA",
            "temporal_rt":
            temporal_rt if self.search_type == TIME else "NA"
        }
Exemple #8
0
class ABColour_TMTM(klibs.Experiment):
    def setup(self):
        # Stimulus sizes
        fix_thickness = deg_to_px(0.1)
        fix_size = deg_to_px(0.6)
        wheel_size = int(P.screen_y * 0.75)
        cursor_size = deg_to_px(1)
        cursor_thickness = deg_to_px(0.3)
        target_size = deg_to_px(0.8)

        # Initilize drawbjects
        self.fixation = FixationCross(size=fix_size,
                                      thickness=fix_thickness,
                                      fill=WHITE)
        self.t1_wheel = ColorWheel(diameter=wheel_size)
        self.t2_wheel = ColorWheel(diameter=wheel_size)
        self.cursor = Annulus(diameter=cursor_size,
                              thickness=cursor_thickness,
                              fill=BLACK)

        # Create text styles to store target colouring
        self.txtm.add_style(label="T1", font_size=target_size)
        self.txtm.add_style(label="T2", font_size=target_size)

        # Stimulus presentation durations (intervals of 16.7ms refresh rate)
        self.id_target_duration = P.refresh_time * 5  # 83.3ms
        self.id_mask_duration = P.refresh_time
        self.col_target_duration = P.refresh_time * 5  # 167ms
        self.col_mask_duration = P.refresh_time
        self.isi = P.refresh_time  # ISI = inter-stimulus interval (target offset -> mask onset)

        # Colour ResponseCollector needs to be passed an object whose fill (colour)
        # is that of the target colour. W/n trial_prep(), these dummies will be filled
        # w/ the target colour and then passed to their ResponseCollectors, respectively.
        self.t1_dummy = Ellipse(width=1)
        self.t2_dummy = Ellipse(width=1)

        # Experiment messages
        self.anykey_txt = "{0}\nPress any key to continue."
        self.t1_id_request = "What was the first number?"
        self.t2_id_request = "What was the second number?"
        self.t1_col_request = "What was the first colour?"
        self.t2_col_request = "What was the second colour?"
        self.prac_identity_instruct = "\nIn this block, you will be asked to report what number was presented.\nIf you're unsure, make your best guess."
        self.prac_colour_instruct = "\nIn this block, you will be asked to report what colour was presented.\nIf you're unsure, make your best guess."
        self.test_identity_instruct = "\nIn this block, you will be asked to report which two numbers were presented.\nIf you're unsure, make your best guess."
        self.test_colour_instruct = "\nIn this block, you will be asked to report which two colours were presented.\nIf you're unsure, make your best guess."

        # Initialize ResponseCollectors
        self.t1_identity_rc = ResponseCollector(uses=RC_KEYPRESS)
        self.t2_identity_rc = ResponseCollector(uses=RC_KEYPRESS)

        self.t1_colouring_rc = ResponseCollector(uses=RC_COLORSELECT)
        self.t2_colouring_rc = ResponseCollector(uses=RC_COLORSELECT)

        # Initialize ResponseCollector Keymaps
        self.keymap = KeyMap(
            'identity_response', ['1', '2', '3', '4', '5', '6', '7', '8', '9'],
            ['1', '2', '3', '4', '5', '6', '7', '8', '9'], [
                sdl2.SDLK_1, sdl2.SDLK_2, sdl2.SDLK_3, sdl2.SDLK_4,
                sdl2.SDLK_5, sdl2.SDLK_6, sdl2.SDLK_7, sdl2.SDLK_8, sdl2.SDLK_9
            ])

        # Inserting practice blocks requires a pre-defined trial count; but in our case they are of an undefined length,
        # lasting for as long as it takes participants to reach a performance threshold. So, initially they are of length 1
        # but trials are inserted later on depending on participant performance.
        if P.run_practice_blocks:
            self.insert_practice_block([1, 3], trial_counts=1)

        # Randomly select starting condition
        self.block_type = random.choice([IDENTITY, COLOUR])

    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

    def setup_response_collector(self):
        # Configure identity collector
        self.t1_identity_rc.terminate_after = [10,
                                               TK_S]  # Waits 10s for response
        self.t1_identity_rc.display_callback = self.identity_callback  # Continuously draw images to screen
        self.t1_identity_rc.display_kwargs = {
            'target': "T1"
        }  # Passed as arg when identity_callback() is called
        self.t1_identity_rc.keypress_listener.key_map = self.keymap  # Assign key mappings
        self.t1_identity_rc.keypress_listener.interrupts = True  # Terminates listener after valid response

        self.t2_identity_rc.terminate_after = [10, TK_S]
        self.t2_identity_rc.display_callback = self.identity_callback
        self.t2_identity_rc.display_kwargs = {'target': "T2"}
        self.t2_identity_rc.keypress_listener.key_map = self.keymap
        self.t2_identity_rc.keypress_listener.interrupts = True

        # Configure colour collector
        # Because colours are randomly selected on a trial by trial basis
        # most properties of colouring_rc need to be assigned within trial_prep()
        self.t1_colouring_rc.terminate_after = [10, TK_S]
        self.t2_colouring_rc.terminate_after = [10, TK_S]

    def trial_prep(self):
        # Prepare colour wheels
        self.t1_wheel.rotation = random.randrange(
            0, 360)  # Randomly rotate wheel to prevent location biases
        self.t2_wheel.rotation = random.randrange(0, 360)

        while self.t1_wheel.rotation == self.t2_wheel.rotation:  # Ensure unique rotation values
            self.t2_wheel.rotation = random.randrange(0, 360)

        self.t1_wheel.render()
        self.t2_wheel.render()

        # Select target identities
        self.t1_identity = random.sample(numbers,
                                         1)[0]  # Select & assign identity
        self.t2_identity = random.sample(numbers, 1)[0]

        while self.t1_identity == self.t2_identity:  # Ensure that T1 & T2 identities are unique
            self.t2_identity = random.sample(numbers, 1)[0]

        # Select target angles (for selecting colour from wheel)
        self.t1_angle = random.randrange(0, 360)
        self.t2_angle = random.randrange(0, 360)

        while self.t1_angle == self.t2_angle:
            self.t2_angle = random.randrange(0, 360)

        self.t1_colour = self.t1_wheel.color_from_angle(
            self.t1_angle)  # Assign colouring
        self.t2_colour = self.t2_wheel.color_from_angle(self.t2_angle)

        # Dummy objects to serve as reference point when calculating response error
        self.t1_dummy.fill = self.t1_colour
        self.t2_dummy.fill = self.t2_colour

        self.t1_colouring_rc.display_callback = self.wheel_callback
        self.t1_colouring_rc.display_kwargs = {'wheel': self.t1_wheel}

        self.t1_colouring_rc.color_listener.set_wheel(
            self.t1_wheel)  # Set generated wheel as wheel to use
        self.t1_colouring_rc.color_listener.set_target(
            self.t1_dummy)  # Set dummy as target reference point

        self.t2_colouring_rc.display_callback = self.wheel_callback
        self.t2_colouring_rc.display_kwargs = {
            'wheel': self.t2_wheel
        }  # Passed as arg w/ calling wheel_callback()

        self.t2_colouring_rc.color_listener.set_wheel(self.t2_wheel)
        self.t2_colouring_rc.color_listener.set_target(self.t2_dummy)

        if self.block_type == IDENTITY:
            self.target_duration = self.id_target_duration
            self.mask_duration = self.id_mask_duration
        else:
            self.target_duration = self.col_target_duration
            self.mask_duration = self.col_mask_duration

        # Initialize EventManager
        if P.practicing:  # T2 not present during practice blocks
            events = [[self.itoa, "T1_on"]]
            events.append([events[-1][0] + self.target_duration, 'T1_off'])
            events.append([events[-1][0] + self.isi, 'T1_mask_on'])
            events.append([events[-1][0] + self.mask_duration, 'T1_mask_off'])
            events.append([events[-1][0] + 300, 'response_foreperiod'])
        else:
            events = [[self.itoa, 'T1_on']]
            events.append([events[-1][0] + self.target_duration, 'T1_off'])
            events.append([events[-1][0] + self.isi, 'T1_mask_on'])
            events.append([events[-1][0] + self.mask_duration, 'T1_mask_off'])
            events.append([events[-4][0] + self.ttoa,
                           'T2_on'])  # SOA = Time between onset of T1 & T2
            events.append([events[-1][0] + self.target_duration, 'T2_off'])
            events.append([events[-1][0] + self.isi, 'T2_mask_on'])
            events.append([events[-1][0] + self.mask_duration, 'T2_mask_off'])
            events.append([events[-1][0] + 300, 'response_foreperiod'])

        # Stream begins 1000ms after fixation
        for e in events:
            self.evm.register_ticket(ET(e[1], e[0]))

        # Prepare stream
        self.tmtm_stream = self.prep_stream()

        # Present fixation & wait for initiation
        self.present_fixation()

    def trial(self):
        # Hide cursor during trial
        hide_mouse_cursor()

        # Wait some foreperiod before presenting T1
        while self.evm.before('T1_on', True):
            ui_request()

        # Present T1
        fill()
        blit(self.tmtm_stream['t1_target'],
             registration=5,
             location=P.screen_c)
        flip()

        self.t1_sw = Stopwatch()

        # Don't do anything during T1 presentation
        while self.evm.before('T1_off', True):
            ui_request()

        self.t1_isi_sw = Stopwatch()
        # Remove T1
        fill()
        flip()
        self.t1_sw.pause()
        print("T1 duration actual: " + str(self.t1_sw.elapsed()))
        fill()
        flip()

        while self.evm.before('T1_mask_on', True):
            ui_request()

        self.t1_isi_sw.pause()

        print("ISI actual: " + str(self.t1_isi_sw.elapsed()))

        # After one refresh rate (how long it takes to remove T1) present mask
        fill()
        blit(self.tmtm_stream['t1_mask'], registration=5, location=P.screen_c)
        flip()

        self.t1_mask_sw = Stopwatch()

        # Don't do anything during presentation
        while self.evm.before('T1_mask_off', True):
            ui_request()

        # Remove mask
        fill()
        flip()
        self.t1_mask_sw.pause()
        print("T1 mask duration actual: " + str(self.t1_mask_sw.elapsed()))

        # If not practicing, present T2
        if not P.practicing:

            # After TTOA is up, present T2
            while self.evm.before('T2_on', True):
                ui_request()

            fill()
            blit(self.tmtm_stream['t2_target'],
                 registration=5,
                 location=P.screen_c)
            flip()

            self.t2_sw = Stopwatch()

            # Don't do anything during presentation
            while self.evm.before('T2_off', True):
                ui_request()

            self.t2_isi_sw = Stopwatch()

            # Remove T2
            fill()
            flip()

            self.t2_sw.pause()

            fill()
            flip()

            while self.evm.before('T2_mask_on', True):
                ui_request()

            self.t2_isi_sw.pause()

            # After one refresh rate, present mask
            fill()
            blit(self.tmtm_stream['t2_mask'],
                 registration=5,
                 location=P.screen_c)
            flip()

            self.t2_mask_sw = Stopwatch()

            # Don't do anything during presentation
            while self.evm.before('T2_mask_off', True):
                ui_request()

            # Remove mask
            fill()
            flip()

            self.t2_mask_sw.pause()

        # Wait 1/3 second before asking for responses
        while self.evm.before('response_foreperiod', True):
            ui_request()

        # Request & record responses
        if self.block_type == IDENTITY:
            # Not relevant to identity trials
            t1_response_err, t1_response_err_rt, t2_response_err, t2_response_err_rt = [
                'NA', 'NA', 'NA', 'NA'
            ]

            # Collect identity responses
            self.t1_identity_rc.collect()

            if not P.practicing:
                self.t2_identity_rc.collect()

            # Assign to variables returned
            t1_id_response, t1_id_rt = self.t1_identity_rc.keypress_listener.response(
            )

            # No T2 present during practice
            if not P.practicing:
                t2_id_response, t2_id_rt = self.t2_identity_rc.keypress_listener.response(
                )
            else:
                t2_id_response, t2_id_rt = ['NA', 'NA']

            # During practice, keep a tally of T1 performance
            if P.practicing:
                if t1_id_response == self.t1_identity:
                    self.t1_performance += 1

        else:  # Colour block
            # Not relevant to colour trials
            t1_id_response, t1_id_rt, t2_id_response, t2_id_rt = [
                'NA', 'NA', 'NA', 'NA'
            ]

            # Collect colour responses
            self.t1_colouring_rc.collect()

            if not P.practicing:
                self.t2_colouring_rc.collect()

            # Assign to variables returned
            t1_response_err, t1_response_err_rt = self.t1_colouring_rc.color_listener.response(
            )

            # T2 only presented during test blocks
            if not P.practicing:
                t2_response_err, t2_response_err_rt = self.t2_colouring_rc.color_listener.response(
                )
            else:
                t2_response_err, t2_response_err_rt = ['NA', 'NA']

            if P.practicing:
                # As numeric identities have 9 possible values, similarly the colour wheel can
                # be thought of as having 9 'bins' (each 40º wide). Colour responses are labelled
                # as 'correct' if their angular error does not exceed 20º in either direction.

                if (abs(t1_response_err) <= 20):
                    self.t1_performance += 1

        clear()

        print(self.target_duration)
        print(self.mask_duration)

        return {
            "practicing":
            str(P.practicing),
            "block_num":
            P.block_number,
            "trial_num":
            P.trial_number,
            "block_type":
            self.block_type,
            "itoa":
            self.itoa,
            "ttoa":
            self.ttoa,
            "target_duration":
            self.target_duration,
            "mask_duration":
            self.mask_duration,
            "t1_identity":
            self.t1_identity,
            "t2_identity":
            self.t2_identity if not P.practicing else 'NA',
            "t1_identity_response":
            t1_id_response,
            "t1_identity_rt":
            t1_id_rt,
            "t2_identity_response":
            t2_id_response,
            "t2_identity_rt":
            t2_id_rt,
            "t1_colour":
            self.t1_colour,
            "t1_angle":
            self.t1_angle,
            "t1_wheel_rotation":
            self.t1_wheel.rotation,
            "t2_colour":
            self.t2_colour if not P.practicing else 'NA',
            "t2_angle":
            self.t2_angle if not P.practicing else 'NA',
            "t2_wheel_rotation":
            self.t2_wheel.rotation if not P.practicing else 'NA',
            "t1_ang_err":
            t1_response_err,
            "t1_colour_rt":
            t1_response_err_rt,
            "t2_ang_err":
            t2_response_err,
            "t2_colour_rt":
            t2_response_err_rt,
            "t1_performance_practice":
            self.t1_performance if P.practicing else 'NA',
            "t1_duration_actual":
            self.t1_sw.elapsed(),
            "t1_isi_actual":
            self.t1_isi_sw.elapsed(),
            "t1_mask_duration_actual":
            self.t1_mask_sw.elapsed(),
            "t2_duration_actual":
            self.t2_sw.elapsed() if not P.practicing else 'NA',
            "t2_isi_actual":
            self.t2_isi_sw.elapsed() if not P.practicing else 'NA',
            "t2_mask_duration_actual":
            self.t2_mask_sw.elapsed() if not P.practicing else 'NA'
        }

    def trial_clean_up(self):
        # Reset response listeners
        self.t1_identity_rc.keypress_listener.reset()
        self.t2_identity_rc.keypress_listener.reset()

        self.t1_colouring_rc.color_listener.reset()
        self.t2_colouring_rc.color_listener.reset()

        # Performance checks during practice
        if P.practicing:
            if not self.practice_complete:
                # First 10 trials considered an 'introductory' run, where no performance check is performed
                if not self.pre_run_complete:
                    if self.practice_trial_num == 10:

                        self.t1_performance = 0

                        self.pre_run_complete = True

                else:
                    #else:
                    # Every 10 trials, check performance & adjust difficulty as necessary
                    if self.practice_trial_num % 10 == 0:

                        # If subj accuracy above 80%,
                        if self.t1_performance > 8:

                            # Make task harder by adjusting target & mask durations
                            if self.block_type == IDENTITY:
                                if self.id_target_duration > P.refresh_time:
                                    self.id_target_duration -= P.refresh_time
                                    self.id_mask_duration += P.refresh_time

                            else:
                                if self.col_target_duration > P.refresh_time:
                                    self.col_target_duration -= P.refresh_time
                                    self.col_mask_duration += P.refresh_time

                            self.t1_performance = 0

                        # Conversely, if performance is below/at chance, make easier
                        elif self.t1_performance <= 2:
                            if self.block_type == IDENTITY:
                                self.id_target_duration += P.refresh_time
                                self.id_mask_duration -= P.refresh_time

                            else:
                                self.col_target_duration += P.refresh_time
                                self.col_mask_duration -= P.refresh_time
                            self.t1_performance = 0

                        else:
                            self.t1_identity_performance = self.t1_performance
                            self.practice_complete = True

                self.practice_trial_num += 1
        else:
            if P.trial_number == P.trials_per_block and P.block_number == 2:
                self.block_type = COLOUR if self.block_type == IDENTITY else IDENTITY

    def clean_up(self):
        pass

    # --------------------------------- #
    # Project specific helper functions
    # --------------------------------- #

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

        any_key()

    def prep_stream(self):
        # Dynamically assign target colouring
        self.txtm.styles['T1'].color = self.t1_colour
        self.txtm.styles['T2'].color = self.t2_colour

        # Generate unique masks for each target
        self.t1_mask = self.generate_mask()
        self.t2_mask = self.generate_mask()

        stream_items = {
            't1_target':
            message(self.t1_identity,
                    align='center',
                    style='T1',
                    blit_txt=False),
            't1_mask':
            self.t1_mask,
            't2_target':
            message(self.t2_identity,
                    align='center',
                    style='T2',
                    blit_txt=False),
            't2_mask':
            self.t2_mask
        }

        return stream_items

    def wheel_callback(self, wheel):
        # Hide cursor during selection phase
        hide_mouse_cursor()

        # Response request msg
        colour_request_msg = self.t1_col_request if wheel == self.t1_wheel else self.t2_col_request
        message_offset = deg_to_px(1.5)
        message_loc = (P.screen_c[0], (P.screen_c[1] - message_offset))

        fill()

        # Present appropriate wheel
        if wheel == self.t1_wheel:
            blit(self.t1_wheel, registration=5, location=P.screen_c)
        else:
            blit(self.t2_wheel, registration=5, location=P.screen_c)
        # Present response request
        message(colour_request_msg,
                location=message_loc,
                registration=5,
                blit_txt=True)
        # Present annulus drawbject as cursor
        blit(self.cursor, registration=5, location=mouse_pos())

        flip()

    def identity_callback(self, target):
        # Request appropriate identity
        identity_request_msg = self.t1_id_request if target == "T1" else self.t2_id_request

        fill()
        message(identity_request_msg,
                location=P.screen_c,
                registration=5,
                blit_txt=True)
        flip()

    def generate_mask(self):
        # Set mask size
        canvas_size = deg_to_px(1)
        # Set cell size
        cell_size = canvas_size / 5  # Mask comprised of 16 smaller cells arranged 4x4
        # Each cell has a black outline
        cell_outline_width = deg_to_px(.05)

        # Initialize canvas to be painted w/ mask cells
        canvas = Image.new('RGBA', [canvas_size, canvas_size], (0, 0, 0, 0))

        surface = aggdraw.Draw(canvas)

        # Initialize pen to draw cell outlines
        transparent_pen = aggdraw.Pen((0, 0, 0), cell_outline_width)

        # Generate cells, arranged in 4x4 array
        for row in [0, 1, 2, 3, 4]:
            for col in [0, 1, 2, 3, 4]:
                # Randomly select colour for each cell
                cell_colour = const_lum[random.randrange(0, 360)]
                # Brush to apply colour
                colour_brush = aggdraw.Brush(tuple(cell_colour[:3]))
                # Determine cell boundary coords
                top_left = (row * cell_size, col * cell_size)
                bottom_right = ((row + 1) * cell_size, (col + 1) * cell_size)
                # Create cell
                surface.rectangle((top_left[0], top_left[1], bottom_right[0],
                                   bottom_right[1]), transparent_pen,
                                  colour_brush)
        # Apply cells to mask
        surface.flush()

        return np.asarray(canvas)
Exemple #9
0
    def trial(self):
        # Hide cursor during trial
        hide_mouse_cursor()

        # Wait some foreperiod before presenting T1
        while self.evm.before('T1_on', True):
            ui_request()

        # Present T1
        fill()
        blit(self.tmtm_stream['t1_target'],
             registration=5,
             location=P.screen_c)
        flip()

        self.t1_sw = Stopwatch()

        # Don't do anything during T1 presentation
        while self.evm.before('T1_off', True):
            ui_request()

        self.t1_isi_sw = Stopwatch()
        # Remove T1
        fill()
        flip()
        self.t1_sw.pause()
        print("T1 duration actual: " + str(self.t1_sw.elapsed()))
        fill()
        flip()

        while self.evm.before('T1_mask_on', True):
            ui_request()

        self.t1_isi_sw.pause()

        print("ISI actual: " + str(self.t1_isi_sw.elapsed()))

        # After one refresh rate (how long it takes to remove T1) present mask
        fill()
        blit(self.tmtm_stream['t1_mask'], registration=5, location=P.screen_c)
        flip()

        self.t1_mask_sw = Stopwatch()

        # Don't do anything during presentation
        while self.evm.before('T1_mask_off', True):
            ui_request()

        # Remove mask
        fill()
        flip()
        self.t1_mask_sw.pause()
        print("T1 mask duration actual: " + str(self.t1_mask_sw.elapsed()))

        # If not practicing, present T2
        if not P.practicing:

            # After TTOA is up, present T2
            while self.evm.before('T2_on', True):
                ui_request()

            fill()
            blit(self.tmtm_stream['t2_target'],
                 registration=5,
                 location=P.screen_c)
            flip()

            self.t2_sw = Stopwatch()

            # Don't do anything during presentation
            while self.evm.before('T2_off', True):
                ui_request()

            self.t2_isi_sw = Stopwatch()

            # Remove T2
            fill()
            flip()

            self.t2_sw.pause()

            fill()
            flip()

            while self.evm.before('T2_mask_on', True):
                ui_request()

            self.t2_isi_sw.pause()

            # After one refresh rate, present mask
            fill()
            blit(self.tmtm_stream['t2_mask'],
                 registration=5,
                 location=P.screen_c)
            flip()

            self.t2_mask_sw = Stopwatch()

            # Don't do anything during presentation
            while self.evm.before('T2_mask_off', True):
                ui_request()

            # Remove mask
            fill()
            flip()

            self.t2_mask_sw.pause()

        # Wait 1/3 second before asking for responses
        while self.evm.before('response_foreperiod', True):
            ui_request()

        # Request & record responses
        if self.block_type == IDENTITY:
            # Not relevant to identity trials
            t1_response_err, t1_response_err_rt, t2_response_err, t2_response_err_rt = [
                'NA', 'NA', 'NA', 'NA'
            ]

            # Collect identity responses
            self.t1_identity_rc.collect()

            if not P.practicing:
                self.t2_identity_rc.collect()

            # Assign to variables returned
            t1_id_response, t1_id_rt = self.t1_identity_rc.keypress_listener.response(
            )

            # No T2 present during practice
            if not P.practicing:
                t2_id_response, t2_id_rt = self.t2_identity_rc.keypress_listener.response(
                )
            else:
                t2_id_response, t2_id_rt = ['NA', 'NA']

            # During practice, keep a tally of T1 performance
            if P.practicing:
                if t1_id_response == self.t1_identity:
                    self.t1_performance += 1

        else:  # Colour block
            # Not relevant to colour trials
            t1_id_response, t1_id_rt, t2_id_response, t2_id_rt = [
                'NA', 'NA', 'NA', 'NA'
            ]

            # Collect colour responses
            self.t1_colouring_rc.collect()

            if not P.practicing:
                self.t2_colouring_rc.collect()

            # Assign to variables returned
            t1_response_err, t1_response_err_rt = self.t1_colouring_rc.color_listener.response(
            )

            # T2 only presented during test blocks
            if not P.practicing:
                t2_response_err, t2_response_err_rt = self.t2_colouring_rc.color_listener.response(
                )
            else:
                t2_response_err, t2_response_err_rt = ['NA', 'NA']

            if P.practicing:
                # As numeric identities have 9 possible values, similarly the colour wheel can
                # be thought of as having 9 'bins' (each 40º wide). Colour responses are labelled
                # as 'correct' if their angular error does not exceed 20º in either direction.

                if (abs(t1_response_err) <= 20):
                    self.t1_performance += 1

        clear()

        print(self.target_duration)
        print(self.mask_duration)

        return {
            "practicing":
            str(P.practicing),
            "block_num":
            P.block_number,
            "trial_num":
            P.trial_number,
            "block_type":
            self.block_type,
            "itoa":
            self.itoa,
            "ttoa":
            self.ttoa,
            "target_duration":
            self.target_duration,
            "mask_duration":
            self.mask_duration,
            "t1_identity":
            self.t1_identity,
            "t2_identity":
            self.t2_identity if not P.practicing else 'NA',
            "t1_identity_response":
            t1_id_response,
            "t1_identity_rt":
            t1_id_rt,
            "t2_identity_response":
            t2_id_response,
            "t2_identity_rt":
            t2_id_rt,
            "t1_colour":
            self.t1_colour,
            "t1_angle":
            self.t1_angle,
            "t1_wheel_rotation":
            self.t1_wheel.rotation,
            "t2_colour":
            self.t2_colour if not P.practicing else 'NA',
            "t2_angle":
            self.t2_angle if not P.practicing else 'NA',
            "t2_wheel_rotation":
            self.t2_wheel.rotation if not P.practicing else 'NA',
            "t1_ang_err":
            t1_response_err,
            "t1_colour_rt":
            t1_response_err_rt,
            "t2_ang_err":
            t2_response_err,
            "t2_colour_rt":
            t2_response_err_rt,
            "t1_performance_practice":
            self.t1_performance if P.practicing else 'NA',
            "t1_duration_actual":
            self.t1_sw.elapsed(),
            "t1_isi_actual":
            self.t1_isi_sw.elapsed(),
            "t1_mask_duration_actual":
            self.t1_mask_sw.elapsed(),
            "t2_duration_actual":
            self.t2_sw.elapsed() if not P.practicing else 'NA',
            "t2_isi_actual":
            self.t2_isi_sw.elapsed() if not P.practicing else 'NA',
            "t2_mask_duration_actual":
            self.t2_mask_sw.elapsed() if not P.practicing else 'NA'
        }