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!")
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