def run_session(self):

        path = os.path.join(self.res_dir, self.subject)
        if not os.path.exists(path):
            os.makedirs(path)
        psydat_path = os.path.join(path, 'psydat')
        if not os.path.exists(psydat_path):
            os.makedirs(psydat_path)

        # welcome
        msg = visual.TextStim(self.win,
                              'Welcome!' + '\n' +
                              ' Press any key to start this session :)',
                              color='black',
                              units='deg',
                              pos=(0, 0),
                              height=0.8)
        msg.draw()
        self.win.mouseVisible = False
        self.win.flip()
        event.waitKeys()

        # read staircase parameters
        conditions = [
            dict({'stimulus': key}, **value)
            for key, value in self.param.items() if key.startswith('stimulus')
        ]

        if conditions[0]['stairType'] == 'simple':
            stairs = data.MultiStairHandler(stairType='simple',
                                            conditions=conditions,
                                            nTrials=self.trial_nmb,
                                            method='sequential')
        elif conditions[0]['stairType'] == 'quest':
            stairs = []
            for cond in conditions:
                if self.priors_file_path:
                    prior_file = self.priors_file_path + cond[
                        'label'] + '.psydat'
                    print(prior_file)
                    prior_handler = misc.fromFile(prior_file)
                else:
                    prior_handler = None
                cur_handler = data.QuestHandler(cond['startVal'],
                                                cond['startValSd'],
                                                pThreshold=cond['pThreshold'],
                                                nTrials=self.trial_nmb,
                                                minVal=cond['min_val'],
                                                maxVal=cond['max_val'],
                                                staircase=prior_handler,
                                                extraInfo=cond,
                                                grain=0.02)
                stairs.append(cur_handler)
        elif conditions[0]['stairType'] == 'psi':
            stairs = []
            for cond in conditions:
                if self.priors_file_path:
                    prior_file = self.priors_file_path + cond['label'] + '.npy'
                else:
                    prior_file = None
                print(prior_file)
                cur_handler = data.PsiHandler(nTrials=self.trial_nmb,
                                              intensRange=[1, 10],
                                              alphaRange=[1, 10],
                                              betaRange=[0.01, 10],
                                              intensPrecision=0.1,
                                              alphaPrecision=0.1,
                                              betaPrecision=0.01,
                                              delta=0.01,
                                              extraInfo=cond,
                                              prior=prior_file,
                                              fromFile=(prior_file
                                                        is not None))
                stairs.append(cur_handler)

        # write configuration files
        xpp = config_tools.WriteXpp(self.subject, self.idx)
        xpp_file = xpp.head(self.cfg_file, self.par_file)
        config_tools.write_xrl(self.subject,
                               cfg_file=self.cfg_file,
                               par_file=self.par_file,
                               xpp_file=xpp_file)

        xlsname = path + '/' + self.idx + self.param[
            'noise_condition'] + '.xlsx'
        """ running staircase """

        if isinstance(stairs, data.MultiStairHandler):
            # start running the staircase using the MultiStairHandler for the up-down method
            count = 0

            for rot, cond in stairs:
                count += 1
                direction = (-1)**(cond['label'].endswith('m')
                                   )  # direction as -1 if for minus stim
                rot = rot * direction  # rotation for this trial
                judge, react_time, trial_time_start = self.run_trial(
                    rot, cond, cond['std'], count)

                # check whether the theta is valid - if not, the rotation given by staircase should be corrected by
                # realizable values
                valid_theta = np.round(np.load(self.hue_list), decimals=1)

                disp_standard = self.take_closest(
                    valid_theta, cond['standard'])  # theta actually displayed
                stair_test = cond[
                    'standard'] + stairs._nextIntensity * direction
                if stair_test < 0:
                    stair_test += 360
                disp_test = self.take_closest(valid_theta, stair_test)
                disp_intensity = disp_test - disp_standard
                if disp_intensity > 300:
                    disp_intensity = (disp_test + disp_standard) - 360
                stairs.addResponse(judge, abs(disp_intensity))

                xpp.task(count, cond, rot, float(disp_intensity), judge,
                         react_time, trial_time_start)

                if 'escape' in event.waitKeys():
                    config_tools.write_xrl(self.subject,
                                           break_info='userbreak')
                    core.quit()

            config_tools.write_xrl(self.subject, xls_file=xlsname)
            stairs.saveAsExcel(xlsname)  # save results
            misc.toFile(
                os.path.join(
                    psydat_path,
                    self.idx + self.param['noise_condition'] + '.psydat'),
                stairs)

        elif isinstance(stairs, list):
            # start running the staircase using custom interleaving stairs for the quest and psi methods
            count = 0
            rot_all = []
            rot_all_disp = []
            judge_all = []
            estimates = {s.extraInfo['label']: [] for s in stairs}

            for trial_n in range(self.trial_nmb):
                for handler_idx, cur_handler in enumerate(stairs):
                    count += 1
                    direction = (-1)**(
                        cur_handler.extraInfo['label'].endswith('m')
                    )  # direction as -1 if for minus stim

                    if cur_handler._nextIntensity >= 10.0:
                        sys.exit(
                            "Hue difference is out of range! Please enlarge the testing range or take more training!"
                        )

                    rot = cur_handler._nextIntensity * direction  # rotation for this trial
                    if trial_n >= 5:  # avoid repeating an intensity more than 3 times
                        last_rots = [
                            np.round(r, decimals=1) for r in [
                                rot_all_disp[handler_idx][trial_n - 1],
                                rot_all_disp[handler_idx][trial_n - 2],
                                rot_all_disp[handler_idx][trial_n - 3]
                            ]
                        ]
                        last_resp = [
                            judge_all[handler_idx][trial_n - 1],
                            judge_all[handler_idx][trial_n - 2],
                            judge_all[handler_idx][trial_n - 3]
                        ]
                        if last_rots[0] == last_rots[1] == last_rots[2] \
                                and last_resp[0] == last_resp[1] == last_resp[2]:
                            if cur_handler._nextIntensity > 0.5:
                                rot = (cur_handler._nextIntensity -
                                       0.5) * direction
                                print('Intensity decreases by 0.5!')
                            if cur_handler._nextIntensity <= 0.5:
                                rot = (cur_handler._nextIntensity +
                                       0.5) * direction
                                print('Intensity increases by 0.5!')
                    cond = cur_handler.extraInfo
                    judge, react_time, trial_time_start = self.run_trial(
                        rot, cond, cond['std'], count)

                    if len(rot_all) <= handler_idx:
                        rot_all.append([])
                    rot_all[handler_idx].append(rot)

                    if len(judge_all) <= handler_idx:
                        judge_all.append([])
                    judge_all[handler_idx].append(judge)

                    valid_theta = np.round(np.load(self.hue_list), decimals=1)
                    disp_standard = self.take_closest(valid_theta,
                                                      cond['standard'])
                    stair_test = cond[
                        'standard'] + rot  # calculated test hue for this trial
                    if stair_test < 0:
                        stair_test += 360
                    disp_test = self.take_closest(
                        valid_theta,
                        stair_test)  # actual displayed test hue for this trial

                    disp_intensity = disp_test - disp_standard  # actual displayed hue difference
                    if disp_intensity > 300:
                        disp_intensity = (disp_test + disp_standard) - 360

                    cur_handler.addResponse(
                        judge, abs(disp_intensity)
                    )  # only positive number is accepted by addResponse

                    if len(rot_all_disp
                           ) <= handler_idx:  # add displayed intensities
                        rot_all_disp.append([])
                    rot_all_disp[handler_idx].append(disp_intensity)

                    if isinstance(cur_handler, data.PsiHandler):
                        estimates[cur_handler.extraInfo['label']].append([
                            cur_handler.estimateLambda()[0],  # location
                            cur_handler.estimateLambda768()[1],  # slope
                            cur_handler.estimateThreshold(0.75)
                        ])
                    elif isinstance(cur_handler, data.QuestHandler):
                        estimates[cur_handler.extraInfo['label']].append([
                            cur_handler.mean(),
                            cur_handler.mode(),
                            cur_handler.quantile(0.5)
                        ])

                    xpp.task(count, cond, rot, disp_intensity, judge,
                             react_time, trial_time_start)

                    if 'escape' in event.waitKeys():
                        config_tools.write_xrl(self.subject,
                                               break_info='userbreak')
                        core.quit()

            config_tools.write_xrl(self.subject, xls_file=xlsname)

            # save results in xls-file
            workbook = xlsxwriter.Workbook(xlsname)
            for handler_idx, cur_handler in enumerate(stairs):
                worksheet = workbook.add_worksheet(
                    cur_handler.extraInfo['label'])
                worksheet.write('A1', 'Reversal Intensities')
                worksheet.write('B1', 'Reversal Indices')
                worksheet.write('C1', 'All Intensities')
                worksheet.write('D1', 'All Responses')
                for i in range(len(rot_all[handler_idx])):
                    # worksheet.write('C' + str(i + 2), rot_all[handler_idx][i])
                    worksheet.write('C' + str(i + 2),
                                    rot_all_disp[handler_idx][i])
                    worksheet.write('D' + str(i + 2),
                                    judge_all[handler_idx][i])
            workbook.close()

            # print resulting parameters and estimates for each step
            res_file_path = os.path.join(path, self.idx + '_estimates.csv')
            res_writer = csv.writer(open(res_file_path, 'w'))
            for res_stim, res_vals in estimates.items():
                for res_val_id, res_val in enumerate(res_vals):
                    res_writer.writerow([
                        res_stim, res_val_id, res_val[0], res_val[1],
                        res_val[2]
                    ])

            # save each handler into a psydat-file and save posterior into a numpy-file
            for cur_handler in stairs:
                file_name = os.path.join(
                    psydat_path, self.idx + self.param['noise_condition'] +
                    cur_handler.extraInfo['label'])
                misc.toFile(file_name + '.psydat', cur_handler)
                if isinstance(cur_handler, data.PsiHandler):
                    cur_handler.savePosterior(file_name + '.npy')
    def run_trial(self, rot, cond, std, count):
        # set two reference
        leftRef = self.patch_ref(theta=cond['leftRef'],
                                 pos=self.cfg['leftRef.pos'])
        rightRef = self.patch_ref(theta=cond['rightRef'],
                                  pos=self.cfg['rightRef.pos'])

        # randomly assign patch positions: upper (+) or lower (-)
        patchpos = [self.cfg['standard.ylim'], self.cfg['test.ylim']]
        rndpos = patchpos.copy()
        np.random.shuffle(rndpos)

        sPatch = self.patch_stim(self.cfg['standard.xlim'], rndpos[0])
        tPatch = self.patch_stim(self.cfg['test.xlim'], rndpos[1])

        # set colors of two stimuli
        standard = cond['standard']  # standard should be fixed
        test = standard + rot
        sPatch.colors, tPatch.colors = self.choose_con(standard, test, std)

        # fixation cross
        fix = visual.TextStim(self.win,
                              text="+",
                              units='deg',
                              pos=[0, 0],
                              height=0.5,
                              color='black',
                              colorSpace=self.ColorSpace)
        # number of trial
        num = visual.TextStim(self.win,
                              text="trial " + str(count),
                              units='deg',
                              pos=[12, -10],
                              height=0.4,
                              color='black',
                              colorSpace=self.ColorSpace)

        trial_time_start = time.time()
        # first present references
        fix.draw()
        num.draw()
        leftRef.draw()
        rightRef.draw()
        self.win.flip()
        core.wait(1.0)

        # then present the standard and the test stimuli as well
        fix.draw()
        num.draw()
        leftRef.draw()
        rightRef.draw()
        sPatch.draw()
        tPatch.draw()
        self.win.flip()
        core.wait(self.trial_dur)

        fix.draw()
        self.win.flip()
        core.wait(0.2)  # 0.2 sec gray background
        react_time_start = time.time()

        # refresh the window and show a colored checkerboard
        horiz_n = 30
        vertic_n = 20
        rect = visual.ElementArrayStim(self.win,
                                       units='norm',
                                       nElements=horiz_n * vertic_n,
                                       elementMask=None,
                                       elementTex=None,
                                       sizes=(2 / horiz_n, 2 / vertic_n),
                                       colorSpace=self.ColorSpace)
        rect.xys = [
            (x, y)
            for x in np.linspace(-1, 1, horiz_n, endpoint=False) + 1 / horiz_n
            for y in np.linspace(-1, 1, vertic_n, endpoint=False) +
            1 / vertic_n
        ]

        rect.colors = [
            self.ColorPicker.newcolor(theta=x)[1]
            for x in np.random.randint(0, high=360, size=horiz_n * vertic_n)
        ]
        rect.draw()
        self.win.flip()
        core.wait(0.5)  # 0.5 sec checkerboard

        judge = None
        react_time_stop = -1
        kb = keyboard.Keyboard()
        get_keys = kb.getKeys(['right', 'left', 'escape'
                               ])  # if response during the checkerboard
        if ('left' in get_keys
                and rot * rndpos[0][0] > 0) or ('right' in get_keys
                                                and rot * rndpos[0][0] < 0):
            judge = 1  # correct
            react_time_stop = time.time()
        elif ('left' in get_keys
              and rot * rndpos[0][0] < 0) or ('right' in get_keys
                                              and rot * rndpos[0][0] > 0):
            judge = 0  # incorrect
            react_time_stop = time.time()
        if 'escape' in get_keys:
            config_tools.write_xrl(self.subject, break_info='userbreak')
            core.quit()

        self.win.flip()
        fix.draw()
        self.win.flip()

        if judge is None:  # if response after the checkerboard
            for wait_keys in event.waitKeys():
                if (wait_keys == 'left' and rot * rndpos[0][0] > 0) or (
                        wait_keys == 'right' and rot * rndpos[0][0] < 0):
                    judge = 1  # correct
                    react_time_stop = time.time()
                elif (wait_keys == 'left' and rot * rndpos[0][0] < 0) or (
                        wait_keys == 'right' and rot * rndpos[0][0] > 0):
                    judge = 0  # incorrect
                    react_time_stop = time.time()
                elif wait_keys == 'escape':
                    config_tools.write_xrl(self.subject,
                                           break_info='userbreak')
                    core.quit()

        react_time = react_time_stop - react_time_start

        return judge, react_time, trial_time_start
    def run_trial(self, rot, cond, count):
        # set two reference
        left = cond['leftRef']
        right = cond['rightRef']

        leftRef = self.patch_ref(left)
        leftRef.pos = self.cfg['leftRef.pos']
        rightRef = self.patch_ref(right)
        rightRef.pos = self.cfg['rightRef.pos']

        # set colors of two stimuli
        standard = cond['standard']  # standard should be fixed
        test = standard + rot

        sPatch = self.patch_stim()
        tPatch = self.patch_stim()
        sPatch.colors, tPatch.colors = self.choose_con(standard, test)

        # randomly assign patch positions: upper (+) or lower (-)
        patchpos = [self.cfg['standard.ylim'], self.cfg['test.ylim']]
        rndpos = patchpos.copy()
        np.random.shuffle(rndpos)

        sPatch.xys = self.patch_pos(self.cfg['standard.xlim'], rndpos[0])
        tPatch.xys = self.patch_pos(self.cfg['test.xlim'], rndpos[1])

        # fixation cross
        fix = visual.TextStim(self.win,
                              text="+",
                              units='deg',
                              pos=[0, 0],
                              height=0.4,
                              color='black',
                              colorSpace=self.ColorSpace)
        # number of trial
        num = visual.TextStim(self.win,
                              text="trial " + str(count),
                              units='deg',
                              pos=[12, -10],
                              height=0.4,
                              color='black',
                              colorSpace=self.ColorSpace)

        trial_time_start = time.time()
        # first present references for 0.5 sec
        fix.draw()
        num.draw()
        leftRef.draw()
        rightRef.draw()
        self.win.flip()
        core.wait(1.0)

        # then present the standard and the test stimuli as well for
        fix.draw()
        num.draw()
        leftRef.draw()
        rightRef.draw()
        sPatch.draw()
        tPatch.draw()
        self.win.flip()

        if self.trial_dur:
            # show stimuli for some time
            core.wait(self.trial_dur)

            # refresh the window, clear references and stimuli
            num.draw()
            self.win.flip()

        # get response
        judge = None

        while judge is None:
            allkeys = event.waitKeys()
            for key in allkeys:
                if (key == 'left' and rot * rndpos[0][0] > 0) or (
                        key == 'right' and rot * rndpos[0][0] < 0):
                    judge = 1  # correct
                    thiskey = key
                elif (key == 'left' and rot * rndpos[0][0] < 0) or (
                        key == 'right' and rot * rndpos[0][0] > 0):
                    judge = 0  # incorrect
                    thiskey = key
                elif key == 'escape':
                    breakinfo = 'userbreak'
                    # xrl.add_break(breakinfo)
                    config_tools.write_xrl(self.subject,
                                           break_info='userbreak')
                    core.quit()

        trial_time = time.time() - trial_time_start

        return judge, thiskey, trial_time
    def run_session(self):

        path = os.path.join(self.res_dir, self.subject)
        if not os.path.exists(path):
            os.makedirs(path)

        # welcome
        msg = visual.TextStim(self.win,
                              'Welcome!' + '\n' +
                              ' Press any key to start this session :)',
                              color='black',
                              units='deg',
                              pos=(0, 0),
                              height=0.8)
        msg.draw()
        self.win.mouseVisible = False
        self.win.flip()
        event.waitKeys()

        # read staircase parameters
        conditions = [
            dict({'stimulus': key}, **value)
            for key, value in self.param.items() if key.startswith('stimulus')
        ]

        if conditions[0]['stairType'] == 'simple':
            stairs = data.MultiStairHandler(stairType='simple',
                                            conditions=conditions,
                                            nTrials=self.trial_nmb,
                                            method='sequential')
        elif conditions[0]['stairType'] == 'quest':
            stairs = []
            for cond in conditions:
                if self.priors_file_path:
                    prior_file = self.priors_file_path + cond[
                        'label'] + '.psydat'
                    print(prior_file)
                    prior_handler = misc.fromFile(prior_file)
                else:
                    prior_handler = None
                cur_handler = data.QuestHandler(cond['startVal'],
                                                cond['startValSd'],
                                                pThreshold=cond['pThreshold'],
                                                nTrials=self.trial_nmb,
                                                minVal=cond['min_val'],
                                                maxVal=cond['max_val'],
                                                staircase=prior_handler,
                                                extraInfo=cond)
                stairs.append(cur_handler)
        elif conditions[0]['stairType'] == 'psi':
            stairs = []
            for cond in conditions:
                if self.priors_file_path:
                    prior_file = self.priors_file_path + cond['label'] + '.npy'
                else:
                    prior_file = None
                print(prior_file)
                cur_handler = data.PsiHandler(nTrials=self.trial_nmb,
                                              intensRange=[1, 10],
                                              alphaRange=[1, 10],
                                              betaRange=[0.01, 10],
                                              intensPrecision=0.1,
                                              alphaPrecision=0.1,
                                              betaPrecision=0.01,
                                              delta=0.01,
                                              extraInfo=cond,
                                              prior=prior_file,
                                              fromFile=(prior_file
                                                        is not None))
                stairs.append(cur_handler)

        # write configuration files
        xpp = config_tools.WriteXpp(self.subject, self.idx)
        xpp_file = xpp.head(self.cfg_file, self.par_file)
        config_tools.write_xrl(self.subject,
                               cfg_file=self.cfg_file,
                               par_file=self.par_file,
                               xpp_file=xpp_file)

        xlsname = path + '/' + self.idx + self.param[
            'noise_condition'] + '.xlsx'
        """ running staircase """

        if isinstance(stairs, data.MultiStairHandler):
            # start running the staircase using the MultiStairHandler for the up-down method
            count = 0

            for rot, cond in stairs:
                count += 1
                judge, thiskey, trial_time = self.run_trial(rot, cond, count)

                # check whether the theta is valid - if not, the rotation given by staircase should be corrected by
                # realizable values
                valid_theta = np.round(np.load(self.hue_list), decimals=1)

                disp_standard = self.take_closest(
                    valid_theta, cond['standard'])  # theta actually displayed
                stair_test = cond['standard'] + stairs._nextIntensity * (-1)**(
                    cond['label'].endswith('m'))  # theta for staircase
                if stair_test < 0:
                    stair_test += 360
                disp_test = self.take_closest(valid_theta, stair_test)
                disp_intensity = abs(disp_test - disp_standard)
                if disp_intensity > 300:
                    disp_intensity = 360 - (disp_test + disp_standard)
                stairs.addResponse(judge, disp_intensity)

                xpp.task(count, cond, rot, float(disp_intensity), judge,
                         trial_time)

                event.waitKeys(keyList=[
                    thiskey
                ])  # press the response key again to start the next trial

            config_tools.write_xrl(self.subject, xls_file=xlsname)
            stairs.saveAsExcel(xlsname)  # save results
            psydat_file_path = os.path.join(
                path, "psydat", self.idx + self.param['condition'] +
                '.psydat')  # save the handler into a psydat-file
            misc.toFile(psydat_file_path, stairs)

        elif isinstance(stairs, list):
            # start running the staircase using custom interleaving stairs for the quest and psi methods
            count = 0
            rot_all = []
            rot_all_disp = []
            judge_all = []
            estimates = {s.extraInfo['label']: [] for s in stairs}

            for trial_n in range(self.trial_nmb):
                for handler_idx, cur_handler in enumerate(stairs):
                    count += 1
                    rot = next(cur_handler)

                    if len(rot_all) <= handler_idx:
                        rot_all.append([])
                    rot_all[handler_idx].append(rot)
                    cond = cur_handler.extraInfo
                    judge, thiskey, trial_time = self.run_trial(
                        rot, cond, count)

                    if len(judge_all) <= handler_idx:
                        judge_all.append([])
                    judge_all[handler_idx].append(judge)
                    # cur_handler.addResponse(judge)  # to the next trial

                    valid_theta = np.round(np.load(self.hue_list), decimals=1)
                    disp_standard = self.take_closest(
                        valid_theta,
                        cond['standard'])  # theta actually displayed
                    stair_test = cond[
                        'standard'] + cur_handler._nextIntensity * (-1)**(
                            cond['label'].endswith('m'))  # theta for staircase
                    if stair_test < 0:
                        stair_test += 360
                    disp_test = self.take_closest(valid_theta, stair_test)
                    disp_intensity = abs(disp_test - disp_standard)
                    if disp_intensity > 300:
                        disp_intensity = 360 - (disp_test + disp_standard)
                    cur_handler.addResponse(judge, disp_intensity)

                    if len(rot_all_disp
                           ) <= handler_idx:  # add displayed intensities
                        rot_all_disp.append([])
                    rot_all_disp[handler_idx].append(disp_intensity)

                    print('stair test: ' + str(stair_test) + ', ' +
                          'disp_test:' + str(disp_test))

                    if isinstance(cur_handler, data.PsiHandler):
                        estimates[cur_handler.extraInfo['label']].append([
                            cur_handler.estimateLambda()[0],  # location
                            cur_handler.estimateLambda768()[1],  # slope
                            cur_handler.estimateThreshold(0.75)
                        ])
                    elif isinstance(cur_handler, data.QuestHandler):
                        estimates[cur_handler.extraInfo['label']].append([
                            cur_handler.mean(),
                            cur_handler.mode(),
                            cur_handler.quantile(0.5)
                        ])

                    xpp.task(count, cond, rot, disp_intensity, judge,
                             trial_time)
                    event.waitKeys(keyList=[
                        thiskey
                    ])  # press the response key again to start the next trial

            config_tools.write_xrl(self.subject, xls_file=xlsname)

            # save results in xls-file
            workbook = xlsxwriter.Workbook(xlsname)
            for handler_idx, cur_handler in enumerate(stairs):
                worksheet = workbook.add_worksheet(
                    cur_handler.extraInfo['label'])
                worksheet.write('A1', 'Reversal Intensities')
                worksheet.write('B1', 'Reversal Indices')
                worksheet.write('C1', 'All Intensities')
                worksheet.write('D1', 'All Responses')
                for i in range(len(rot_all[handler_idx])):
                    # worksheet.write('C' + str(i + 2), rot_all[handler_idx][i])
                    worksheet.write('C' + str(i + 2),
                                    rot_all_disp[handler_idx][i])
                    worksheet.write('D' + str(i + 2),
                                    judge_all[handler_idx][i])
            workbook.close()

            # print resulting parameters and estimates for each step
            res_file_path = os.path.join(path, self.idx + '_estimates.csv')
            res_writer = csv.writer(open(res_file_path, 'w'))
            for res_stim, res_vals in estimates.items():
                for res_val_id, res_val in enumerate(res_vals):
                    res_writer.writerow([
                        res_stim, res_val_id, res_val[0], res_val[1],
                        res_val[2]
                    ])

            # save each handler into a psydat-file and save posterior into a numpy-file
            for cur_handler in stairs:
                file_name = os.path.join(
                    path, self.idx + self.param['noise_condition'] +
                    cur_handler.extraInfo['label'])
                misc.toFile(file_name + '.psydat', cur_handler)
                if isinstance(cur_handler, data.PsiHandler):
                    cur_handler.savePosterior(file_name + '.npy')
    def run_session(self):

        path = os.path.join(self.res_dir, self.subject)
        if not os.path.exists(path):
            os.makedirs(path)
        psydat_path = os.path.join(path, 'psydat')
        if not os.path.exists(psydat_path):
            os.makedirs(psydat_path)

        # welcome
        msg = visual.TextStim(self.win,
                              'Welcome!' + '\n' +
                              ' Press any key to start this session :)',
                              color='black',
                              units='deg',
                              pos=(0, 0),
                              height=0.8)
        msg.draw()
        self.win.mouseVisible = False
        self.win.flip()
        event.waitKeys()

        # read staircase parameters
        conditions = [
            dict({'stimulus': key}, **value)
            for key, value in self.param.items() if key.startswith('stimulus')
        ]

        if conditions[0]['stairType'] == 'constant':
            stimuli = []
            rot_all_disp = []
            judge_all = []
            for cond in conditions:
                for diff in np.linspace(cond['minVal'], 0, 3, endpoint=False):
                    stimuli.append({'cond': cond, 'diff': diff})
                for diff in np.linspace(0, cond['maxVal'], 3, endpoint=False):
                    stimuli.append({'cond': cond, 'diff': diff})
                stimuli.append({'cond': cond, 'diff': 0})
            repeats_nmb = 20
            stairs = data.TrialHandler(stimuli, repeats_nmb, method='random')
        else:
            sys.exit("The stimuli are not constant!")

        # write configuration files
        xpp = config_tools.WriteXpp(self.subject, self.idx)
        xpp_file = xpp.head(self.cfg_file, self.par_file)
        config_tools.write_xrl(self.subject,
                               cfg_file=self.cfg_file,
                               par_file=self.par_file,
                               xpp_file=xpp_file)

        xlsname = path + '/' + self.idx + self.param[
            'noise_condition'] + '.xlsx'
        """ running staircase """

        if isinstance(stairs, data.TrialHandler):
            count = 0
            results = {cond['label']: [] for cond in conditions}
            for trial in stairs:
                count += 1
                judge, react_time, trial_time_start = self.run_trial(
                    trial['diff'], trial['cond'], count)
                valid_theta = np.round(np.load(self.hue_list), decimals=1)
                disp_standard = self.take_closest(valid_theta,
                                                  cond['standard'])
                stair_test = cond['standard'] + trial['diff']
                if stair_test < 0:
                    stair_test += 360
                disp_test = self.take_closest(valid_theta, stair_test)
                disp_intensity = disp_test - disp_standard
                if disp_intensity > 300:
                    disp_intensity = (disp_test + disp_standard) - 360
                xpp.task(count, cond, cond['diff'], float(disp_intensity),
                         judge, react_time, trial_time_start)
                results[trial['label']].append((trial['diff'], judge))

                if 'escape' in event.waitKeys():
                    config_tools.write_xrl(self.subject,
                                           break_info='userbreak')
                    core.quit()

            config_tools.write_xrl(self.subject, xls_file=xlsname)

            # save results in xls-file
            workbook = xlsxwriter.Workbook(xlsname)
            for res_label, res_data in results.items():
                worksheet = workbook.add_worksheet(res_label)
                worksheet.write('A1', 'Reversal Intensities')
                worksheet.write('B1', 'Reversal Indices')
                worksheet.write('C1', 'All Intensities')
                worksheet.write('D1', 'All Responses')
                for i, (res_diff, res_resp) in enumerate(res_data):
                    worksheet.write('C' + str(i + 2), res_diff)
                    worksheet.write('D' + str(i + 2), res_resp)
            workbook.close()
        else:
            sys.exit("The stimuli are not constant!")
Exemple #6
0
    def run_trial(self, rot, cond, count):

        ref = self.patch_ref(theta=cond['ref'], pos=self.cfg['ref.pos'])

        # Randomly assign patch positions: upper (+) or lower (-)
        patchpos = [self.cfg['standard.ylim'], self.cfg['test.ylim']]
        rndpos = patchpos.copy()
        np.random.shuffle(rndpos)

        sPatch = self.patch_stim(self.cfg['standard.xlim'], rndpos[0])
        tPatch = self.patch_stim(self.cfg['test.xlim'], rndpos[1])

        # Set colors of two stimuli
        standard = cond['standard']  # standard should be fixed
        test = standard + rot
        sPatch.colors, tPatch.colors = self.choose_con(standard, test,
                                                       cond['std'])

        # Fixation cross & Number of trial
        fix = visual.TextStim(self.win,
                              text="+",
                              units='deg',
                              pos=[0, 0],
                              height=0.5,
                              color='black',
                              colorSpace=self.ColorSpace)
        num = visual.TextStim(self.win,
                              text="trial " + str(count),
                              units='deg',
                              pos=[12, -10],
                              height=0.4,
                              color='black',
                              colorSpace=self.ColorSpace)

        trial_time_start = time.time()

        # Present the standard and the test stimuli together with the reference
        fix.draw()
        num.draw()
        ref.draw()
        sPatch.draw()
        tPatch.draw()
        self.win.flip()

        judge = None
        react_time_stop = -1
        react_time_start = time.time()

        # Allow entering a pause mode by pressing 'p', either response or exit is possible in pause mode
        pre_start = time.time()
        mode_keys = event.waitKeys(maxWait=self.trial_dur)
        if mode_keys is not None:
            press_start = time.time()
            if 'p' in mode_keys:
                react_time_start = time.time()
                enter_text = visual.TextStim(
                    self.win,
                    text="Enter pause mode. Exit by pressing 'p'",
                    units='deg',
                    pos=[12, -8],
                    height=0.4,
                    color='black',
                    colorSpace=self.ColorSpace)
                enter_text.draw()
                fix.draw()
                num.draw()
                ref.draw()
                sPatch.draw()
                tPatch.draw()
                self.win.flip()
                for wait_keys in event.waitKeys():
                    if (wait_keys == 'up'
                            and rndpos[1][0] > 0) or (wait_keys == 'down'
                                                      and rndpos[1][0] < 0):
                        judge = 1  # correct
                        react_time_stop = time.time()
                    elif (wait_keys == 'up'
                          and rndpos[1][0] < 0) or (wait_keys == 'down'
                                                    and rndpos[1][0] > 0):
                        judge = 0  # incorrect
                        react_time_stop = time.time()
                    elif wait_keys == 'escape':
                        config_tools.write_xrl(self.subject,
                                               break_info='userbreak')
                        core.quit()
                    elif wait_keys == 'p':
                        exit_text = visual.TextStim(self.win,
                                                    text="Exit pause mode.",
                                                    units='deg',
                                                    pos=[12, -8],
                                                    height=0.4,
                                                    color='black',
                                                    colorSpace=self.ColorSpace)
                        exit_text.draw()
                        fix.draw()
                        num.draw()
                        self.win.flip()
            else:  # keep presenting if other keys are pressed by accident
                core.wait(self.trial_dur - (press_start - pre_start))

        # Refresh and show a colored checkerboard mask for 0.5 sec
        mask_dur = 0.5
        horiz_n = 30
        vertic_n = 20
        rect = visual.ElementArrayStim(self.win,
                                       units='norm',
                                       nElements=horiz_n * vertic_n,
                                       elementMask=None,
                                       elementTex=None,
                                       sizes=(2 / horiz_n, 2 / vertic_n),
                                       colorSpace=self.ColorSpace)
        rect.xys = [
            (x, y)
            for x in np.linspace(-1, 1, horiz_n, endpoint=False) + 1 / horiz_n
            for y in np.linspace(-1, 1, vertic_n, endpoint=False) +
            1 / vertic_n
        ]

        rect.colors = [
            self.ColorPicker.newcolor(theta=x)[1]
            for x in np.random.randint(0, high=360, size=horiz_n * vertic_n)
        ]
        rect.draw()
        self.win.flip()
        core.wait(mask_dur)

        # If response is given during the mask
        if judge is None:
            kb = keyboard.Keyboard()
            get_keys = kb.getKeys(['up', 'down', 'escape'
                                   ])  # if response during the checkerboard
            if ('up' in get_keys
                    and rndpos[1][0] > 0) or ('down' in get_keys
                                              and rndpos[1][0] < 0):
                judge = 1  # correct
                react_time_stop = time.time()
            elif ('up' in get_keys
                  and rndpos[1][0] < 0) or ('down' in get_keys
                                            and rndpos[1][0] > 0):
                judge = 0  # incorrect
                react_time_stop = time.time()
            if 'escape' in get_keys:
                config_tools.write_xrl(self.subject, break_info='userbreak')
                core.quit()

        # Refresh and wait for response (if no response was given in the pause mode or during mask)
        self.win.flip()
        fix.draw()
        self.win.flip()

        if judge is None:  # if no response in the pause mode
            for wait_keys in event.waitKeys(keyList=['up', 'down', 'escape']):
                if (wait_keys == 'up'
                        and rndpos[1][0] > 0) or (wait_keys == 'down'
                                                  and rndpos[1][0] < 0):
                    judge = 1  # correct
                    react_time_stop = time.time()
                elif (wait_keys == 'up'
                      and rndpos[1][0] < 0) or (wait_keys == 'down'
                                                and rndpos[1][0] > 0):
                    judge = 0  # incorrect
                    react_time_stop = time.time()
                elif wait_keys == 'escape':
                    config_tools.write_xrl(self.subject,
                                           break_info='userbreak')
                    core.quit()

        react_time = react_time_stop - react_time_start - self.trial_dur

        return judge, react_time, trial_time_start