def trial( self, btype: PhaseType, show_boxes: List[int], onset: TaskTime = 0, deval_idx: int = 1, dur: Optional[TaskDur] = 1 ) -> Tuple[TaskTime, List[Keypress], Optional[TaskDur]]: """run a trial, flipping at onset @param btype - block type: what to show, how to score @param show_boxes - what box(es) to show @param deval_idx - for DD 0 to deval top, 1 to devalue bottom @return (fliptime, resp, rt) - key of response, how long til push could both be None """ # check things make sense if (btype == PhaseType.OD and len(show_boxes) != 2) or \ (btype != PhaseType.OD and len(show_boxes) != 1): raise Exception( 'trail got wrong length (%d) of boxes for block (%s)' % (len(show_boxes), btype.name)) # for most this is just drawing the box centered # but if we have two boxes to draw, align vert. (block = OD) for i, bn in enumerate(show_boxes): pos = i - (2 if len(show_boxes) > 1 else 0) # 0 or -2, -1 self.draw_box(box_states[btype], bn, pos, deval_idx == i) # show response arrows only if we have one centered box. # NB. feedback keeps arrows only on PhaseType.ID # SOA and DD both see arrows, but they are only indicators. never change color if len(show_boxes) == 1: self.show_arrows(Direction.No) if self.cheat: self.show_cheat() # START # NB. neg wait time treated like no wait wait_until(onset, verbose=True) fliptime = self.win.flip() # wait for response resp: List[Keypress] = [] rt: Optional[TaskDur] = None if dur is None or dur > 0: print(f' wait-for-response for {dur}sec') # NB. if two keys are held down, will report both! # make this None if dur: resp = event.waitKeys(maxWait=dur - .01, keyList=self.keys.keys()) else: resp = event.waitKeys(keyList=self.keys.keys()) rt = core.getTime() - fliptime return (fliptime, resp, rt)
def message(self, message: str, onset: TaskTime = 0) -> TaskTime: """show a message centered on the screen @param message @return fliptime """ self.textBox.text = message self.textBox.pos = (0, 0) self.textBox.color = 'white' self.textBox.height = 0.1 self.textBox.draw() wait_until(onset) return self.win.flip()
def ID_blk(task, DURS, seed, fout=None): # mprage takes 6.5 minutes. # allow 6 seconds to end and display score show_boxes = shuffle_box_idx(task.info.boxes, seed) block_score = 0 have_time = True bnum = 0 starttime = wait_for_scanner(task.win) nextflip = starttime while have_time: for trl_num, box_idx in enumerate(show_boxes): box = task.info.boxes[box_idx] event = EventOut(PhaseType.ID, box) trl_info = task.trial(PhaseType.ID, [box_idx], onset=nextflip, dur=None) event.read_resp(trl_info) block_score += event.resp.score event.write(trl_num, block_score, bnum, starttime, fout, 'ID_mprage') # give feedback fliptime = task.fbk(box_idx, event.resp.score, side=event.resp.side) event = EventOut(PhaseType.ID, box, fliptime, TrialType.FBK) event.write(trl_num, block_score, bnum, starttime, fout, 'ID_mprage') nextflip = fliptime + DURS['fbk'] if core.getTime() - starttime > DURS['mprage']: have_time = False break fliptime = task.message( f"In this block you scored {block_score} pnts!", nextflip) event = EventOut(PhaseType.ID, None, fliptime, TrialType.SCORE) event.write(0, block_score, bnum, starttime, fout, 'ID_mprage') # reset block for next go show_boxes = shuffle_box_idx(task.info.boxes, seed) block_score = 0 bnum += 1 nextflip = fliptime + DURS['score'] # when mprage is just about over. we wont start a new block # so need to wait here so participant has time to read score if not have_time: wait_until(nextflip) fliptime = task.message(f"Finished ID!", nextflip) wait_until(fliptime + DURS['score'])
def iti(self, onset: TaskTime = 0) -> TaskTime: """ show iti screen. @param onset - when to flip. default now (0) @return fliptime - when screen was flipped """ wait_until(onset) self.textBox.color = 'white' self.textBox.text = '+' # same hight as box that will replace it # but text hieght is a little different. still not centered self.textBox.height = self.box.size[1] self.textBox.pos = (0, 1 / 16) # try to offset cross self.textBox.draw() fliptime = self.win.flip() return fliptime
def grid(self, phase: PhaseType, deval_idxs: List[int], onset: TaskTime = 0) -> TaskTime: """ show grid of boxes (always same order) @param ceval_idxs - which to cross out @param onset - when to flip. default now (0) @return fliptime - when screen was flipped """ for bi in range(len(self.boxes)): # offset by one for showing teh grid boxis = 'open' if phase == PhaseType.SOA else 'closed' self.draw_box(boxis, bi, bi + 1, bi in deval_idxs) wait_until(onset, verbose=True) fliptime = self.win.flip() return fliptime
def fbk(self, show_box: Optional[int], score: int, onset: TaskTime = 0, side: Direction = Direction.No) -> TaskTime: """ give feedback - only for ID @param show_boxes - which box idx to show @param score - score to display (see Box.score()) @param onset - when to flip (default to now (0)) """ if score <= 0: show_box = None self.draw_box("open", show_box, 0) self.textBox.pos = self.scoreBox.pos # yellow if correct # red if not if score >= 1: self.scoreBox.fillColor = 'yellow' self.textBox.color = 'black' else: self.scoreBox.fillColor = 'red' self.textBox.color = 'white' self.textBox.text = "%d" % score self.textBox.height = .1 self.scoreBox.draw() self.textBox.draw() # show correct side self.show_arrows(side, score > 0) if self.cheat: self.show_cheat() wait_until(onset) return self.win.flip()
def slips_blk(task, DURS, seed, phase=PhaseType.SOA, fout=None): """ @param task - FabFruitTask object with boxes @param DURS - dict with durations. keys: grid, score, iti, timeout, OFF @param phase - DD or SOA [default: SOA] @param fout - where to save file [default: None] @side-effect: execute ~10min run for given phase type blks: dv0 OFF 2dv2 OFF 2dv4 OFF 2dv2 OFF 2dv4 OFF time: 40 40 80 40 80 40 80 40 80 40 | 560 (9.33 min) trls: 12 0 24 0 24 0 24 0 24 0 | 108 5 second grid 2 second score 12 x 1.3 seconds avg rt in 3 pilot MR is 1 second iti + (extra .7 ontop of rt for correct no resonse for the 2 or 4 devalued (each box seen twice) blk total = 12 * (1+1.3) + 5 +2 # 35 seconds (+ .7*2*2 or .7*4*2) 2 per 80 seconds => 2 ON for each 40s OFF 2 reps of: 1x dv0 , 2x dv2, 2x dv4. 5*80 + 3*40 == 520 == 8.67 min have 9 L/R matched pairs for each dv2 and dv4. using 8 len(deval_2(task.info.boxes,seed)) == 9 len(deval_4(task.info.boxes,seed)) == 9 """ all_deval_idxs = { 0: [[]] * 9, 2: deval_2(task.info.boxes, seed), 4: deval_4(task.info.boxes, seed) } for k in all_deval_idxs: seed.shuffle(all_deval_idxs[k]) draws = [0, 2, 2, 4, 4] seed.shuffle(draws) switch_blocks = [] # len == len(draws) == 5 deval_idxs = [] # len == 9 (len(draws)*2 - 1) i = 0 # draw twice from all but 0 devalued (only one dv0) for d in draws: deval_idxs.append(all_deval_idxs[d].pop()) if d != 0: deval_idxs.append(all_deval_idxs[d].pop()) i += 1 switch_blocks.append(i) i += 1 starttime = wait_for_scanner(task.win) next_flip = starttime for bnum, block_devaled_idxs in enumerate(deval_idxs): extra_col = f"{phase.name}_{len(block_devaled_idxs)}" # grid show_boxes = shuffle_box_idx(task.info.boxes, seed) block_score = 0 fliptime = task.grid(phase, block_devaled_idxs, next_flip) event = EventOut(phase, None, fliptime, TrialType.GRID) event.write(0, block_score, bnum, starttime, fout, extra_col) next_flip = fliptime + DURS['grid'] # trial for trl_num, box_idx in enumerate(show_boxes): box = task.boxes[box_idx] # 2 second timeout matches javascript event = EventOut(phase, box) trl_info = task.trial(phase, [box_idx], onset=next_flip, dur=DURS['timeout']) event.read_resp(trl_info, box_idx in block_devaled_idxs) block_score += event.resp.score event.write(trl_num, block_score, bnum, starttime, fout, extra_col) rt_fliptime = trl_info[0] + trl_info[2] # 1second iti matches javascript fliptime = task.iti(rt_fliptime) event = EventOut(phase, box, fliptime, TrialType.ITI) event.write(trl_num, block_score, bnum, starttime, fout, extra_col) next_flip = fliptime + DURS['iti'] fliptime = task.message( f"You scored {block_score} pnts\n\n" + f"Block {bnum+1}/{len(deval_idxs)}!", next_flip) event = EventOut(phase, None, fliptime, TrialType.SCORE) event.write(trl_num, block_score, bnum, starttime, fout, extra_col) next_flip = fliptime + DURS['score'] # moving to different number of devalued items. need OFF period # N.B. currently have wait block at very end if bnum in switch_blocks: fliptime = task.iti(next_flip) event = EventOut(phase, box, fliptime, TrialType.ITI) event.write(trl_num, block_score, bnum, starttime, fout) next_flip = fliptime + DURS['OFF'] print(f"# OFF waiting {DURS['OFF']} seconds until {next_flip:.2f}") # default trial() intentionally errors if waiting more than 30seconds # get around that by waiting here for a bit wait_until(fliptime + DURS['OFF'] - .1, maxwait=DURS['OFF']) fliptime = task.message(f"Finished {phase.name}!", next_flip) wait_until(fliptime + DURS['score'])