Beispiel #1
0
def run_block(filename, expInfo, task, reps, coherence_levels):
    ##################################################################################################################
    #########          Training
    ##################################################################################################################
    #make a text file to save data
    data_file = open(filename + '.csv', 'w')#a simple text file with 'comma-separated-values'
    data_file.write('motionDirection,coherence,correct,rt\n')
    # trial list - each coherence level in both directions
    trial_list = []
    for coherence in coherence_levels:
        trial_list.append({'coherence': coherence, 'direction': 0})
        trial_list.append({'coherence': coherence, 'direction': 180})

    # Create trial handler to randomize conditions
    trial_handler = TrialHandler(trial_list, reps, extraInfo=expInfo, method='fullRandom')
    # Show instructions
    task.display_instructions()
    # Keep track of total correct
    total_correct=0
    # Keep track of mean RT on most difficult trial
    total_hard_rt=0
    # Keep track of total task time
    task_clock=core.Clock()

    # Run training trials
    for trial_idx,trial in enumerate(trial_handler):
        if (trial_idx+1) % int(len(trial_list)*reps/5)==0:
            task.display_break()

        # Run trial
        correct, rt = task.runTrial(trial['coherence'], trial['direction'])

        #add the data to the trial handler
        trial_handler.addData('response', correct)
        trial_handler.addData('rt', rt)
        data_file.write('%i,%.4f,%i,%.4f\n' % (trial['direction'], trial['coherence'], correct, rt))

        # Update block statistics
        total_correct+=correct
        if trial['coherence']==min(coherence_levels):
            total_hard_rt+=rt

    print('block took %0.2f minutes' % (task_clock.getTime()/60.0))
    # compute feedback stats and provide feedback
    correct_per_min=float(total_correct)/(task_clock.getTime()/60.0)
    perc_correct=float(total_correct)/float(len(trial_list)*reps)*100.0
    mean_hard_rt=(float(total_hard_rt)/(2.0*float(reps)))*1000.0

    task.display_feedback(perc_correct, mean_hard_rt, correct_per_min)

    # training has ended
    data_file.close()
    # special python binary file to save all the info
    trial_handler.saveAsPickle(fileName)
Beispiel #2
0
class FabFruitTask:
    save_path: Optional[Filepath]

    def __init__(self,
                 win,
                 info: FabFruitInfo,
                 keys: KeypressDict = KEYS,
                 timing_method: TimeTypes = TimeTypes.onset):
        self.win = win

        # settings
        self.info = info
        self.fruits = info.fruits
        self.boxes = info.boxes
        self.keys = keys
        self.save_path = None
        self.cheat = False

        if timing_method != TimeTypes.block:
            self.set_times(timing_method)

        # display objects
        self.box = visual.ImageStim(self.win, image_path('box_open.png'))
        self.fruit = visual.ImageStim(self.win, image_path('apple.png'))
        self.X = visual.ImageStim(self.win, image_path('devalue.png'))
        self.confidence = visual.ImageStim(self.win,
                                           image_path("confidence.png"))
        self.hand = visual.ImageStim(self.win, image_path("hand_only.png"))
        # downsize hands
        # previously only for half screen. but why not all screens
        # if(self.win.size[1] <= 600):
        self.confidence.size *= .5
        self.hand.size *= .5
        # bottom align
        self.confidence.pos[1] = -1 + self.confidence.size[1] / 2
        self.hand.pos[1] = -1 + self.hand.size[1] / 2

        # score box for ID feeback
        (w, h) = self.box.size
        self.scoreBox = visual.Rect(self.win,
                                    lineWidth=2,
                                    fillColor='yellow',
                                    lineColor='black',
                                    pos=(0, h / 2),
                                    size=(w / 2, h / 5))
        self.textBox = visual.TextStim(self.win)

        self.arrowBox = visual.TextStim(self.win,
                                        color='darkgray',
                                        pos=(0, -.7 * h),
                                        height=.2)  # ← or →

    def set_times(self, timing_method: TimeTypes):
        # timing
        self.timing_method = timing_method  # alternative "dur"
        self.events = TrialHandler(self.info.timing.to_dict('records'),
                                   1,
                                   method='sequential',
                                   dataTypes=[
                                       'resp', 'resp_side', 'rt', 'score',
                                       'fliptime', 'block_score', 'skip'
                                   ])

    def show_arrows(self, picked_dir: Direction, cor: Optional[bool] = None):
        """draw arrows for left and right.
        default to two gray arrows.
        if side is not none picked_dir side black
        if cor make side blue if matches
        @param picked_dir what arrow to color
        @param fbk        feedback gets actual color
        @sideeffect       draws to screen"""
        Xit = False
        # not yet feedback. just color what we picked before saying right or wrong
        if cor is None:
            color = "black"
        # good feedback, show blue
        elif cor:
            color = "blue"
        # feedback show wrong. or we have nothing to show
        else:
            Xit = picked_dir != Direction.No
            color = "darkgray"

        w_offset = 0.08
        a_h = self.arrowBox.pos[1]
        for sinfo in [{
                'side': Direction.Left,
                'text': "<",
                'sign': -1
        }, {
                'side': Direction.Right,
                'text': ">",
                'sign': 1
        }]:
            self.arrowBox.pos = (sinfo['sign'] * w_offset, a_h)
            self.arrowBox.text = sinfo['text']
            if picked_dir == sinfo['side']:
                self.arrowBox.color = color
            else:
                self.arrowBox.color = "darkgray"
            self.arrowBox.draw()

            if Xit and picked_dir == sinfo['side']:
                self.X.pos = self.arrowBox.pos
                # hacky - move X over and reset size after. inefficent
                s = self.X.size
                self.X.size = (s[0] * self.arrowBox.height / s[1],
                               self.arrowBox.height)
                self.X.draw()
                self.X.size = s

    def draw_box(self,
                 boxtype,
                 box_number: Optional[int],
                 offset=0,
                 devalue=False) -> Tuple[float, float]:
        """draw a box and fruit
        @param boxtype - open or closed
        @param box_number - which box to draw
        @param offset - 0 is center (default).
                       -2 is above -1 is below
                        1 to 6 is grid (1-3 top left to right, 4-6 bottom L->R)
        @param devalue - should we draw an X over it?
        @param position of drawn box
        """
        self.box.setImage(image_path(f'box_{boxtype}.png'))
        # closed box see stim, open to see outcome
        sotype = SO.Stim if boxtype == "closed" else SO.Outcome
        if box_number is not None:
            fruit_img = self.boxes[box_number].__getattribute__(
                sotype.name).image
            self.fruit.setImage(fruit_img)
        # set postion of box
        (w, h) = self.box.size
        positions = [
            (0, h / 2),  # -2 - top
            (0, -h / 2),  # -1 - bottom
            (0, 0),  # 0 - center
            # top row
            (-w, h / 2),  # left
            (0, h / 2),  # center
            (w, h / 2),  # right
            # bottom row
            (-w, -h / 2),  # left
            (0, -h / 2),  # center
            (w, -h / 2)  # right
        ]
        self.box.pos = positions[offset + 2]
        self.fruit.pos = positions[offset + 2]
        self.X.pos = positions[offset + 2]

        self.box.draw()
        if box_number is not None:
            self.fruit.draw()
        if devalue:
            self.X.draw()

        return positions[offset + 2]

    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 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 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 fruit_only(self, fruit: Fruit) -> Tuple[str, TaskDur, bool]:
        """ show only a fruit, and ask what side it opens from
        @param fruit - fruit to question
        @return (resp, rt, iscorrect)"""
        self.textBox.text = "Push the left or right key for this fruit"
        self.textBox.pos = (0, .8)
        self.fruit.pos = (0, 0)
        self.fruit.setImage(fruit.image)

        self.textBox.draw()
        self.fruit.draw()
        if self.cheat:
            self.show_cheat()

        onset = self.win.flip()
        resp = event.waitKeys(keyList=self.keys.keys())
        resp = first_key(resp)
        rt: TaskDur = core.getTime() - onset
        correct: bool = self.keys.get(resp) == fruit.box.Dir
        return (resp, rt, correct)

    def get_confidence(self,
                       mesg: str = "How confident are you?"
                       ) -> Tuple[int, TaskDur]:
        """ put up confidence image and wait for 1-5 key push
        N.B. NUM_KEYS are reversed so using thumb ("1" returns "4", and pinky "5" returns "0")
        @param msg text shown, default "how confident are you"
        @return (resp, rt)
        """
        self.textBox.text = mesg
        self.textBox.pos = (0, .9)
        self.textBox.draw()
        self.confidence.draw()
        onset = self.win.flip()
        return wait_numkey(onset)

    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 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 fruit_and_four(self, this_outcome: Fruit):
        """ used in fruit_finger for survey pair question
        make a list of given fruit (correct answer) and 4 other (incorrect)
        @param stim - fruit used as stim
        @return show_fruits - fruits to present in survey/pair association test

        >>> from soapy import quick_task
        >>> task = quick_task() #doctest:+ELLIPSIS
        # ...
        >>> outcome = task.boxes[0].Outcome
        >>> fruits = task.fruit_and_four(outcome) #doctest:+ELLIPSIS
        # what pairs ...
        >>> outcome in fruits  # the answer should be in there
        True
        >>> len(set([f.name for f in fruits]))  # fruit for ea finger
        5
        """
        show_fruits = [
            f for f in self.fruits
            if f.name != this_outcome.name and f.SO == SO.Outcome
        ]
        self.info.seed.shuffle(show_fruits)
        # take out one at random (only have 5 fingers)
        show_fruits = show_fruits[0:(len(self.boxes) - 2)]
        # and put the correct answer in
        show_fruits.append(this_outcome)
        self.info.seed.shuffle(show_fruits)
        print(
            f'# what pairs to {this_outcome.name}; showing {[f.name for f in show_fruits]}'
        )
        return show_fruits

    def fruits_to_fingertips(self, show_fruits: List[Fruit]):
        """ Position each fruit at the end of a finger in hand.svg
        @param show_fruits - list of 5 fruits to display
        @sideeffect - draw to screen
        For fruit_finger()
        """
        # put a fruit on each finger
        fingure_ends = [
            [0.60, 0.55],  # thumb
            [0.25, 1.05],  # index
            [-.10, 1.10],  # middle
            [-.35, 1.03],  # ring
            [-.60, 0.75],  # pinky
        ]
        for i, pos in enumerate(fingure_ends):
            x = self.hand.pos[0] - (self.hand.size[0] * pos[0])
            y = -1 + (self.hand.size[1] * pos[1])
            self.fruit.setImage(show_fruits[i].image)
            self.fruit.pos = [x, y]
            self.fruit.draw()

    def fruit_fingers(
        self,
        stim: Fruit,
        show_fruits: Optional[List[Fruit]] = None
    ) -> Tuple[int, TaskDur, str, bool]:
        """ overlay outcome fruits ontop of hand image for a given stim
        @param stim - stim fruit. outcome pair will be among shown
        @return index of response (thumb to pinky), RT, picked, correct
        """
        self.textBox.text = "What is this label's pair"
        self.textBox.pos = (0, .9)
        self.fruit.pos = (0, .65)
        self.fruit.setImage(stim.image)
        self.textBox.draw()
        self.fruit.draw()
        self.hand.draw()

        print(f'testing {stim.box}')
        # fruits to show
        #  shuffle all outcomes but one we want to include

        this_outcome = stim.box.Outcome
        if not show_fruits:
            show_fruits = self.fruit_and_four(this_outcome)

        self.fruits_to_fingertips(show_fruits)

        if self.cheat:
            self.show_cheat()

        onset = self.win.flip()
        (resp, rt) = wait_numkey(onset)
        number_num_keys = len(NUM_KEYS)  # 5
        if resp > -1:
            rev_idx = number_num_keys - resp - 1
            picked = show_fruits[rev_idx].name  # N.B keys are reversed!
        else:
            picked = "Error"
            rev_idx = -1
        corr = picked == this_outcome.name
        print(f"picked {picked} (key={resp}=>{rev_idx}), is cor? {corr}")
        resp = rev_idx
        return (resp, rt, picked, corr)

    def survey(self):
        """ run through fruit and box survey"""
        outf = open(os.path.join(self.save_path), "w")
        outf.write(
            "type disp f_resp f_rt pick iscorrect correct c_resp c_rt\n")

        # N.B. side (L|R) order of self.fruits is already random
        for f in self.fruits:
            print(f"showing {f}")
            (f_resp, f_rt, f_correct) = self.fruit_only(f)
            f_side = self.keys[f_resp].name if self.keys.get(
                f_resp) else "TWO_KEYS"
            print(f"*  side: {f_resp}=>{f_side} vs {f.box.Dir}: {f_correct}")

            (c_resp, c_rt) = self.get_confidence()
            print(f"*  confidence: {c_resp}")
            outf.write(
                f"side {f.name} {f_resp} {f_rt} {f_side} {f_correct} {f.box.Dir.name} {c_resp} {c_rt}\n"
            )

        for b in self.boxes:
            (f_resp, f_rt, f_pick, f_correct) = self.fruit_fingers(b.Stim)
            print(f"*  pair: {f_resp}=>{f_pick} vs {f.box}: {f_correct}")

            (c_resp, c_rt) = self.get_confidence()
            print(f"*  confidence: {c_resp}")
            outf.write(
                f"pair {b.Stim.name} {f_resp} {f_rt} {f_pick} {f_correct} {b.Outcome.name} {c_resp} {c_rt}\n"
            )

        outf.close()

    def run(self, init_time: Optional[TaskTime] = None):
        """ run the task through all events """

        block_score: float = 0
        for e in self.events:
            # set clock if no prev trial, prev trial happens after next.
            # or current trial starts at 0
            prev = self.events.getEarlierTrial()
            prev_dur = 0 if not prev else prev.get('dur', 0)
            if prev is None or prev.onset > e.onset or e.onset == 0:
                if prev_dur > 0:
                    print(f"waiting {prev_dur} for prev dur")
                    core.wait(prev_dur)
                # second round of MR. wait for scanner
                # this is maybe a condtion we will never see
                if prev is not None and self.timing_method == TimeTypes.onset:
                    wait_for_scanner(self.win)

                starttime = core.getTime()
                block_score = 0

                print(f"* new starttime {starttime:.2f} and score reset")
                # if we don't start for a little bit of time, show fix cross
                if e.onset > 0:
                    self.iti()

            now = core.getTime()

            # re-adjust starttime if this is frist pass and we have an actual time
            # this will put us back on track if tehre is a little delay
            # between scanner trigger and actual setup
            if prev is None and init_time:
                start_offset = init_time - starttime
                print(
                    f"# setting starttime to {init_time:.2f}, lost {start_offset:.2f}s"
                )
                starttime = init_time

            # if we are using onset timing method, use preprogramed onsset times
            # if duration, wait until the previous duration
            #    -- duration will be changed when e.g. rt < max wait
            if self.timing_method == TimeTypes.onset:
                fliptime = starttime + e.onset

            # if timing "dur", should have set prev dur to 0
            elif self.timing_method == TimeTypes.onset:
                if prev and self.events.data['skip'][self.events.thisTrialN -
                                                     1] == True:
                    prev_dur = 0
                fliptime = now + prev_dur
            else:
                raise Exception(f"Unknown timing_method {self.timing_method}")

            eta = fliptime - now
            print(f"\n@{now:.2f}s {self.events.thisN}/{self.events.nTotal} " +
                  f"\ton{e.onset}|{prev_dur:.2f}pdur\n" +
                  f"\tETA {eta:.3f}s trl {e.trial} blk {e.blocknum}" +
                  f"\n\t{e.phase} {e.ttype} {e.LR1} {e.top} {e.deval}")

            # how should we handle this event (trialtype)
            if e.ttype == TrialType.SHOW:
                # get top box
                bi = e.bxidx[0][0]
                bx = self.boxes[bi]

                if e.deval & (e.phase == PhaseType.OD):
                    deval_idx = 0
                    # if top is devalued, pick bottom box to score
                    bx = self.boxes[e.bxidx[0][1]]
                else:
                    deval_idx = 1  # 1 means nothing for ID DD and SOA

                print(f"  # score using {bx}")
                # e.g.
                #  self.trial(PhaseType.SOA, 1, [3])
                #  self.trial(PhaseType.OD, 1, [2, 3], deval_idx=0)  # top deval
                show_boxes = e.bxidx[0]  # nested array
                (fliptime, e.resp, e.rt) = self.trial(e.phase, show_boxes,
                                                      fliptime, deval_idx,
                                                      e.dur)

                resp = first_key(e.resp)
                e.side = self.keys.get(resp) if resp else None
                print(f"  resp: {e.resp} => {e.side}")

                # indicate we pushed a button by changing the screen
                # to highlight arrow that was pushed
                if e.side and self.timing_method == TimeTypes.onset:
                    # if ID, show choice while waiting for feedback
                    # otherwise show early iti
                    if e.phase == PhaseType.ID:
                        self.draw_box(box_states[e.phase], show_boxes[0])
                        self.show_arrows(e.side)
                        self.win.flip()
                    else:
                        self.iti()

                # if we are running outside of scanner, don't wait for next
                if self.timing_method == TimeTypes.dur:
                    self.events.addData('skip', True)

                this_score = bx.score(e.phase, e.blocknum, e.side)
                block_score += this_score
                e.score = this_score
                print(
                    f"  #resp {resp} @ {e.rt:.2f}s is {e.side} =>  {this_score} pts; total: {block_score}"
                )
                self.events.addData('score', e.score)
                self.events.addData('rt', e.rt)
                if e.side:
                    self.events.addData('resp_side', e.side.name)
                self.events.addData('resp', ",".join(e.resp) if resp else None)

            elif e.ttype == TrialType.ITI:
                self.save_progress()
                fliptime = self.iti(fliptime)

            elif e.ttype == TrialType.FBK:
                bi = e.bxidx[0][0]
                # bx = self.boxes[bi]
                this_score = self.events.getEarlierTrial().score
                this_side = self.events.getEarlierTrial().side
                fliptime = self.fbk(bi, this_score, fliptime, this_side)

            elif e.ttype == TrialType.GRID:
                fliptime = self.grid(e.phase, e.bxidx[0], fliptime)

            elif e.ttype == TrialType.SCORE:
                #self.events.data
                #d = self.info.timing.loc[0:self.events.thisN]
                #d[(d.blocknum == d.blocknum[-1]) &

                # print("score: %d" % self.events.getEarlierTrial().score)
                self.message(f'In this block you scored {block_score} pnts',
                             fliptime)
                self.events.addData('block_score', block_score)
                block_score = 0  # reset block points after showing

                # if score is the last in this block. wait for a key
                nexttrial = self.events.getFutureTrial()
                if self.timing_method == TimeTypes.dur and (
                        not nexttrial or nexttrial.blocknum != e.blocknum):
                    # TODO: change accpet keys to not be glove box?
                    # TODO: consider making this fixed duration e.dur
                    event.waitKeys()

                # TODO: show fixation for some period of time?
                if not nexttrial and self.timing_method == TimeTypes.onset:
                    core.wait(e.dur)
                    #self.iti()

            else:
                print(f"#  doing nothing with {e.ttype}")

            # update fliptime
            e.fliptime = fliptime
            self.events.addData('fliptime', e.fliptime)

        print("done")

    def show_cheat(self):
        """ put SRO: fruit stim, outcome, and direction at bottom of the screen"""
        bottom = -.9
        a_h = self.arrowBox.pos[1]
        w = -.9
        for b in self.boxes:

            self.fruit.setImage(b.Outcome.image)
            self.fruit.pos = (w + .1, bottom)
            self.fruit.draw()
            w += .2

            self.fruit.setImage(b.Stim.image)
            self.fruit.pos = (w - .2, bottom)
            self.fruit.draw()
            w += .1

            self.arrowBox.pos = (w - .25, bottom)
            # better to compare to Direction.Left
            # but interactive modules loaded in funny order
            self.arrowBox.text = "<" if b.Dir.name == "Left" else ">"
            self.arrowBox.color = "black"
            self.arrowBox.draw()

        # reset arrows for primary use
        self.arrowBox.pos[1] = a_h

    def save_progress(self):
        """save progress. probably at an iti or at end"""
        if not self.save_path:
            print(f"WARNING: trying to save buth no 'save_path' given")
            return
        self.events.saveAsText(self.save_path,
                               appendFile=False,
                               fileCollisionMethod='overwrite')

    def instruction(self,
                    top: str,
                    func,
                    bottom="(spacebar to cont.)",
                    flip=True) -> Keypress:
        """print some text and run an arbitraty function"""

        # instructions are not timing sensitive.
        # and we need enough character columns to show the instructions
        if top:
            visual.TextStim(self.win,
                            text=top,
                            pos=(0, .7),
                            color='white',
                            wrapWidth=2).\
               draw()

        if bottom:
            self.textBox.height = .08
            self.textBox.color = 'white'
            self.textBox.pos = (0, -.8)
            self.textBox.text = bottom
            self.textBox.draw()
        if func:
            func(self)
        if flip:
            self.win.flip()
        key = dly_waitKeys(.5,
                           keyList=['space', '0', 'return', 'left', 'right'])
        return key
Beispiel #3
0
    def run(self, *args):
        """
        The run method contains your experiment logic. It is equal to what would be in your main psychopy experiment
        script.py file in a standard psychopy experiment setup. That is all there is too it really.
        """

        exp_conditions = importConditions('trial_conditions.xlsx')
        trials = TrialHandler(exp_conditions, 1)

        # Inform the ioDataStore that the experiment is using ac
        # TrialHandler. The ioDataStore will create a table
        # which can be used to record the actual trial variable values (DV or IV)
        # in the order run / collected.
        #
        self.hub.createTrialHandlerRecordTable(trials)

        selected_eyetracker_name = args[0]
        # Let's make some short-cuts to the devices we will be using in this 'experiment'.
        tracker = self.hub.devices.tracker
        display = self.hub.devices.display
        kb = self.hub.devices.keyboard
        mouse = self.hub.devices.mouse

        # Start by running the eye tracker default setup procedure.
        tracker.runSetupProcedure()

        # Create a psychopy window, full screen resolution, full screen mode...
        #
        res = display.getPixelResolution()
        window = visual.Window(res,
                               monitor=display.getPsychopyMonitorName(),
                               units=display.getCoordinateType(),
                               fullscr=True,
                               allowGUI=False,
                               screen=display.getIndex())

        # Create a dict of image stim for trials and a gaze blob to show gaze position.
        #
        display_coord_type = display.getCoordinateType()
        image_cache = dict()
        image_names = [
            'canal.jpg', 'fall.jpg', 'party.jpg', 'swimming.jpg', 'lake.jpg'
        ]

        for iname in image_names:
            image_cache[iname] = visual.ImageStim(window,
                                                  image=os.path.join(
                                                      './images/', iname),
                                                  name=iname,
                                                  units=display_coord_type)

        gaze_dot = visual.GratingStim(window,
                                      tex=None,
                                      mask="gauss",
                                      pos=(0, 0),
                                      size=(66, 66),
                                      color='green',
                                      units=display_coord_type)
        instructions_text_stim = visual.TextStim(window,
                                                 text='',
                                                 pos=[0, 0],
                                                 height=24,
                                                 color=[-1, -1, -1],
                                                 colorSpace='rgb',
                                                 alignHoriz='center',
                                                 alignVert='center',
                                                 wrapWidth=window.size[0] * .9)

        # Update Instruction Text and display on screen.
        # Send Message to ioHub DataStore with Exp. Start Screen display time.
        #
        instuction_text = "Press Any Key to Start Experiment."
        instructions_text_stim.setText(instuction_text)
        instructions_text_stim.draw()
        flip_time = window.flip()
        self.hub.sendMessageEvent(text="EXPERIMENT_START", sec_time=flip_time)

        # wait until a key event occurs after the instructions are displayed
        self.hub.clearEvents('all')
        kb.waitForPresses()

        # Send some information to the ioHub DataStore as experiment messages
        # including the eye tracker being used for this session.
        #
        self.hub.sendMessageEvent(text="IO_HUB EXPERIMENT_INFO START")
        self.hub.sendMessageEvent(text="ioHub Experiment started {0}".format(
            getCurrentDateTimeString()))
        self.hub.sendMessageEvent(
            text="Experiment ID: {0}, Session ID: {1}".format(
                self.hub.experimentID, self.hub.experimentSessionID))
        self.hub.sendMessageEvent(
            text="Stimulus Screen ID: {0}, Size (pixels): {1}, CoordType: {2}".
            format(display.getIndex(), display.getPixelResolution(),
                   display.getCoordinateType()))
        self.hub.sendMessageEvent(
            text="Calculated Pixels Per Degree: {0} x, {1} y".format(
                *display.getPixelsPerDegree()))
        self.hub.sendMessageEvent(text="Eye Tracker being Used: {0}".format(
            selected_eyetracker_name))
        self.hub.sendMessageEvent(text="IO_HUB EXPERIMENT_INFO END")

        self.hub.clearEvents('all')
        t = 0
        for trial in trials:
            # Update the instuction screen text...
            #
            instuction_text = "Press Space Key To Start Trial %d" % t
            instructions_text_stim.setText(instuction_text)
            instructions_text_stim.draw()
            flip_time = window.flip()
            self.hub.sendMessageEvent(text="EXPERIMENT_START",
                                      sec_time=flip_time)

            start_trial = False

            # wait until a space key event occurs after the instructions are displayed
            kb.waitForPresses(keys=' ')

            # So request to start trial has occurred...
            # Clear the screen, start recording eye data, and clear all events
            # received to far.
            #
            flip_time = window.flip()
            trial['session_id'] = self.hub.getSessionID()
            trial['trial_id'] = t + 1
            trial['TRIAL_START'] = flip_time
            self.hub.sendMessageEvent(text="TRIAL_START", sec_time=flip_time)
            self.hub.clearEvents('all')
            tracker.setRecordingState(True)

            # Get the image name for this trial
            #
            imageStim = image_cache[trial['IMAGE_NAME']]

            # Loop until we get a keyboard event
            #
            run_trial = True
            while run_trial is True:
                # Get the latest gaze position in dispolay coord space..
                #
                gpos = tracker.getLastGazePosition()
                if isinstance(gpos, (tuple, list)):
                    # If we have a gaze position from the tracker, draw the
                    # background image and then the gaze_cursor.
                    #
                    gaze_dot.setPos(gpos)
                    imageStim.draw()
                    gaze_dot.draw()
                else:
                    # Otherwise just draw the background image.
                    #
                    imageStim.draw()

                # flip video buffers, updating the display with the stim we just
                # updated.
                #
                flip_time = window.flip()

                # Send a message to the ioHub Process / DataStore indicating
                # the time the image was drawn and current position of gaze spot.
                #
                if isinstance(gpos, (tuple, list)):
                    self.hub.sendMessageEvent("IMAGE_UPDATE %s %.3f %.3f" %
                                              (iname, gpos[0], gpos[1]),
                                              sec_time=flip_time)
                else:
                    self.hub.sendMessageEvent("IMAGE_UPDATE %s [NO GAZE]" %
                                              (iname),
                                              sec_time=flip_time)

                # Check any new keyboard char events for a space key.
                # If one is found, set the trial end variable.
                #
                if ' ' in kb.getPresses():
                    run_trial = False

            # So the trial has ended, send a message to the DataStore
            # with the trial end time and stop recording eye data.
            # In this example, we have no use for any eye data between trials, so why save it.
            #
            flip_time = window.flip()
            trial['TRIAL_END'] = flip_time
            self.hub.sendMessageEvent(text="TRIAL_END %d" % t,
                                      sec_time=flip_time)
            tracker.setRecordingState(False)
            # Save the Experiment Condition Variable Data for this trial to the
            # ioDataStore.
            #
            self.hub.addRowToConditionVariableTable(trial.values())
            self.hub.clearEvents('all')
            t += 1

        # Disconnect the eye tracking device.
        #
        tracker.setConnectionState(False)

        # Update the instuction screen text...
        #
        instuction_text = "Press Any Key to Exit Demo"
        instructions_text_stim.setText(instuction_text)
        instructions_text_stim.draw()
        flip_time = window.flip()
        self.hub.sendMessageEvent(text="SHOW_DONE_TEXT", sec_time=flip_time)

        # wait until any key is pressed
        kb.waitForPresses()

        # So the experiment is done, all trials have been run.
        # Clear the screen and show an 'experiment  done' message using the
        # instructionScreen state. What for the trigger to exit that state.
        # (i.e. the space key was pressed)
        #
        self.hub.sendMessageEvent(text='EXPERIMENT_COMPLETE')
Beispiel #4
0
    def run(self, *args):
        """
        The run method contains your experiment logic. In this example we:

        1) Load an xlsx file containing the trial conditions for use
           during the experiment. All DV's and IV's to be used or updated
           for each trial must be specified as columns in the xlsx file.
        2) Inform the ioDataStore of the trial conditions to be used, resulting in the
           creation of an experiment specific results table, with a field for each
           DV and IV defined in the xls file.
        3) Run the eye tracking device's runSetupProcedure(), which allows
           the calibration, validation, etc. of the eye tracking system being used.
        4) Create the experiment runtime graphics, including creating a cache of
           images to be displayed for each trial of the experiment.
        5) Run the experimental block of trials of the demo. Each trial sequence
           consists of:
               a) The participant pressing the SPACE key to start the trial.
               b) Randomly displaying one of the background images for a trial.
               c) Starting recording of data from the eye tracker.
               d) Displaying a gaze contingent dot located at the gaze position reported by the eye tracker.
               e) Ending each trial by pressing the SPACE key.
               f) Sending any condition variable value changes for that trial
                  to the ioDataStore for easy future selection of device events
                  recorded during the trial or for specific condition variable values.
               g) Stopping of event recording on the eye tracker device.
        """

        exp_conditions = importConditions('trial_conditions.xlsx')
        trials = TrialHandler(exp_conditions, 1)

        # Inform the ioDataStore that the experiment is using a
        # TrialHandler. The ioDataStore will create a table
        # which can be used to record the actual trial variable values (DV or IV)
        # in the order run / collected.
        #
        self.createDataStoreConditionsTable(trials)

        # Let's make some short-cuts to the devices we will be using
        # in this 'example'.
        tracker = self.hub.devices.tracker
        display = self.hub.devices.display
        kb = self.hub.devices.keyboard
        mouse = self.hub.devices.mouse

        KEYBOARD_PRESS = EventConstants.KEYBOARD_PRESS

        # Start by running the eye tracker default setup procedure.
        # The details of the setup procedure (calibration, validation, etc)
        # are unique to each implementation of the Common Eye Tracker Interface.
        # All have the common end goal of calibrating the eye tracking system
        # prior to data collection.
        # Please see the eye tracker interface implementation details for the
        # hardware being used at:
        # http://www.isolver-solutions.com/iohubdocs/iohub/api_and_manual/device_details/eyetracker.html#eye-tracking-hardware-implementations
        #

        # Create a psychopy window for the experiment graphics,
        # ioHub supports the use of one full screen window during
        # the experiment runtime. (If you are using a window at all).
        #
        res = display.getPixelResolution(
        )  # Current pixel resolution of the Display to be used
        coord_type = display.getCoordinateType()
        window = visual.Window(
            res,
            monitor=display.getPsychopyMonitorName(
            ),  # name of the PsychoPy Monitor Config file if used.
            units=coord_type,  # coordinate space to use.
            fullscr=True,  # We need full screen mode.
            allowGUI=False,  # We want it to be borderless
            screen=display.getIndex(
            )  # The display index to use, assuming a multi display setup.
        )

        #self.window.winHandle.minimize()
        #tracker.runSetupProcedure()
        #self.window.winHandle.maximize()
        #self.window.winHandle.activate()

        # Hide the 'system mouse cursor' during the experiment.
        #
        mouse.setSystemCursorVisibility(False)

        # Create a dict of image stim for trials and a gaze blob to show the
        # reported gaze position with.
        #
        image_cache = dict()
        image_names = [
            'canal.jpg', 'fall.jpg', 'party.jpg', 'swimming.jpg', 'lake.jpg'
        ]
        for iname in image_names:
            image_cache[iname] = visual.ImageStim(window,
                                                  image=os.path.join(
                                                      './images/', iname),
                                                  name=iname,
                                                  units=coord_type)

        # Create a circle to use for the Gaze Cursor. Current units assume pix.
        #
        gaze_dot = visual.GratingStim(window,
                                      tex=None,
                                      mask="gauss",
                                      pos=(0, 0),
                                      size=(66, 66),
                                      color='green',
                                      units=coord_type)

        # Create a Text Stim for use on /instuction/ type screens.
        # Current units assume pix.
        instructions_text_stim = visual.TextStim(window,
                                                 text='',
                                                 pos=[0, 0],
                                                 height=24,
                                                 color=[-1, -1, -1],
                                                 colorSpace='rgb',
                                                 alignHoriz='center',
                                                 alignVert='center',
                                                 wrapWidth=window.size[0] * .9)

        # Update Instruction Text and display on screen.
        # Send Message to ioHub DataStore with Exp. Start Screen display time.
        #
        instuction_text = "Press Any Key to Start Experiment."
        instructions_text_stim.setText(instuction_text)
        instructions_text_stim.draw()
        flip_time = window.flip()
        self.hub.sendMessageEvent(text="EXPERIMENT_START", sec_time=flip_time)

        # Wait until a key event occurs after the instructions are displayed
        self.hub.clearEvents('all')
        while not kb.getEvents():
            self.hub.wait(0.2)

        # Send some information to the ioDataStore as experiment messages,
        # including the experiment and session id's, the calculated pixels per
        # degree, display resolution, etc.
        #
        self.hub.sendMessageEvent(text="IO_HUB EXPERIMENT_INFO START")
        self.hub.sendMessageEvent(text="ioHub Experiment started {0}".format(
            getCurrentDateTimeString()))
        self.hub.sendMessageEvent(
            text="Experiment ID: {0}, Session ID: {1}".format(
                self.hub.experimentID, self.hub.experimentSessionID))
        self.hub.sendMessageEvent(
            text="Stimulus Screen ID: {0}, Size (pixels): {1}, CoordType: {2}".
            format(display.getIndex(), display.getPixelResolution(),
                   display.getCoordinateType()))
        self.hub.sendMessageEvent(
            text="Calculated Pixels Per Degree: {0} x, {1} y".format(
                *display.getPixelsPerDegree()))
        self.hub.sendMessageEvent(text="IO_HUB EXPERIMENT_INFO END")

        self.hub.clearEvents('all')

        # For each trial in the set of trials within the current block.
        #
        t = 0
        for trial in trials:
            # Update the instruction screen text to indicate
            # a trial is about to start.
            #
            instuction_text = "Press Space Key To Start Trial %d" % t
            instructions_text_stim.setText(instuction_text)
            instructions_text_stim.draw()
            flip_time = window.flip()
            self.hub.sendMessageEvent(text="EXPERIMENT_START",
                                      sec_time=flip_time)

            # Wait until a space key press event occurs after the
            # start trial instuctions have been displayed.
            #
            self.hub.clearEvents('all')
            while not [
                    event
                    for event in kb.getEvents(event_type_id=KEYBOARD_PRESS)
                    if event.key == ' '
            ]:
                self.hub.wait(0.2)

            # Space Key has been pressed, start the trial.
            # Set the current session and trial id values to be saved
            # in the ioDataStore for the upcoming trial.
            #

            trial['session_id'] = self.hub.getSessionID()
            trial['trial_id'] = t + 1

            # Send a msg to the ioHub indicating that the trial started, and the time of
            # the first retrace displaying the trial stm.
            #
            self.hub.sendMessageEvent(text="TRIAL_START", sec_time=flip_time)

            # Start Recording Eye Data
            #
            tracker.setRecordingState(True)

            # Get the image stim for this trial.
            #
            imageStim = image_cache[trial['IMAGE_NAME']]
            imageStim.draw()
            flip_time = window.flip()
            # Clear all the events received prior to the trial start.
            #
            self.hub.clearEvents('all')
            # Send a msg to the ioHub indicating that the trial started,
            # and the time of the first retrace displaying the trial stim.
            #
            self.hub.sendMessageEvent(text="TRIAL_START", sec_time=flip_time)
            # Set the value of the trial start variable for this trial
            #
            trial['TRIAL_START'] = flip_time

            # Loop until we get a keyboard event
            #
            run_trial = True
            while run_trial is True:
                # Get the latest gaze position in display coord space..
                #
                gpos = tracker.getPosition()
                if type(gpos) in [tuple, list]:
                    # If we have a gaze position from the tracker,
                    # redraw the background image and then the
                    # gaze_cursor at the current eye position.
                    #
                    gaze_dot.setPos([gpos[0] * 2, gpos[1] * 2])
                    imageStim.draw()
                    gaze_dot.draw()
                else:
                    # Otherwise just draw the background image.
                    # This will remove the gaze cursor from the screen
                    # when the eye tracker is not successfully
                    # tracking eye position.
                    #
                    imageStim.draw()

                # Flip video buffers, displaying the stim we just
                # updated.
                #
                flip_time = window.flip()

                # Send an experiment message to the ioDataStore
                # indicating the time the image was drawn and
                # current position of gaze spot.
                #
                if type(gpos) in [tuple, list]:
                    self.hub.sendMessageEvent(
                        "IMAGE_UPDATE %s %.3f %.3f" %
                        (trial['IMAGE_NAME'], gpos[0], gpos[1]),
                        sec_time=flip_time)
                else:
                    self.hub.sendMessageEvent("IMAGE_UPDATE %s [NO GAZE]" %
                                              (trial['IMAGE_NAME']),
                                              sec_time=flip_time)

                # Check any new keyboard press events by a space key.
                # If one is found, set the trial end variable and break.
                # from the loop
                for event in kb.getEvents(event_type_id=KEYBOARD_PRESS):
                    if event.key == ' ':
                        run_trial = False
                        break

            # The trial has ended, so update the trial end time condition value,
            # and send a message to the ioDataStore with the trial end time.
            #
            flip_time = window.flip()
            trial['TRIAL_END'] = flip_time
            self.hub.sendMessageEvent(text="TRIAL_END %d" % t,
                                      sec_time=flip_time)

            # Stop recording eye data.
            # In this example, we have no use for any eye data
            # between trials, so why save it.
            #
            tracker.setRecordingState(False)

            # Save the experiment condition variable values for this
            # trial to the ioDataStore.
            #
            self.hub.addRowToConditionVariableTable(trial.values())

            # Clear all event buffers
            #
            self.hub.clearEvents('all')
            t += 1

        # All trials have been run, so end the experiment.
        #

        flip_time = window.flip()
        self.hub.sendMessageEvent(text='EXPERIMENT_COMPLETE',
                                  sec_time=flip_time)

        # Disconnect the eye tracking device.
        #
        tracker.setConnectionState(False)

        # The experiment is done, all trials have been run.
        # Clear the screen and show an 'experiment  done' message using the
        # instructionScreen text.
        #
        instuction_text = "Press Any Key to Exit Demo"
        instructions_text_stim.setText(instuction_text)
        instructions_text_stim.draw()
        flip_time = window.flip()
        self.hub.sendMessageEvent(text="SHOW_DONE_TEXT", sec_time=flip_time)

        # wait until any key is pressed
        while not kb.getEvents(event_type_id=KEYBOARD_PRESS):
            self.hub.wait(0.2)
Beispiel #5
0
def init(init_file=None):
    """ this is the init function that is called with an appropriate json init-file

    :Parameters:

    init_file : str
        a json formatted init file containing parameters about the experimental set-up

    :return:

    expInfo : dict
        dictionary gathering information about the experiment

    win : psychopy.visual.win
        window in which the experiment will be run

    kb : psychopy.hardware.keyboard
        the keyboard configuration that will be used with the appropriate listeners
    """
    # dictionary with the parameters from the init file
    init = utils.load_init(init_file)  

    # set-up the Display and monitor
    # make a monitor with monitor constants from the init file
    mon = makeMonitor(init['monitor_constants'])  
    # and make a window using that monitor
    win = visual.Window(
        monitor=mon, 
        color=init['experiment']['bkgcolor'],
        size=mon.getSizePix(), 
        units='deg', 
        screen=1,  # 0 is the monitor of the experiment control
        fullscr=True, 
        allowGUI=False,
        waitBlanking=False, 
        winType='pyglet')  # for iohub to function well

    # start to fill the ioHub_config
    ioHub_config = dict(
        experiment_code = init['experiment']['name'],
        experiment_info = dict(
            total_sessions_to_run = 2
        ),
        session_info = dict(
            user_variables = dict(
                modalities = init['experiment']['modalities'],
                observer = init['experiment']['observer'],
                dummy_mode = init['experiment']['dummy_mode'],
                hw = dict(
                    parallel_port = init['devices']['parallel_port']
                ),
                parameters = init['experiment']['stim']
            )
        ),
        psychopy_monitor_name = mon.name,
        data_store = dict(
            enable = False
        ),
        mouse = dict(
            enable = False
        )
    )

    # transform the velocity of the gratings to the plaid (vertical) velocity
    v = init['experiment']['stim']['vel']
    theta = init['experiment']['stim']['ori'] * math.pi / 180
    sf = init['experiment']['stim']['sf']
    ioHub_config['session_info']['user_variables']['parameters']['velocity'] = [0, v / math.sin(theta) * sf]

    # make sure the eyetracker is available as eyelink
    try:
        et_init_file = init['devices']['eyetracker']
        et_init = eyetracker_setup(win, et_init_file)
    except KeyError:
        print('no eyetracker configuration file given, using default Eyelink')
        et_init = eyetracker_setup(win)
    
    if 'GAZE' in [m.upper() for m in init['experiment']['modalities']]:
        ioHub_config.update(et_init)

    # Are we running in dummy_mode ?
    try:
        dummy_mode = init['experiment']['dummy_mode']
    except KeyError:
        dummy_mode = False

    # for a given subject the data directory is
    # <session_type><subject_id>
    # and all files are named as
    # <session_type><subject_id>_<session_number>_<modality>.<ext>
    if not dummy_mode:
        session_id, subject_path = utils.register_subject(datapath=init['data']['path'], modalities=init['modalities'])  
    else:
        session_id = 'tmp'
        subject_path = Path(Path.cwd(), 'subject_data')
        subject_path.mkdir()

    shutil.copy(init_file, Path(subject_path).joinpath(session_id + '_init.yaml'))
    ioHub_config['session_code'] = session_id
    ioHub_config['data_store'].update(dict(
                enable = True,
                filename = session_id,
                parent_dir = str(subject_path)  # serialisation of json strings requires casting to string
            ))
    logfile = Path(subject_path).joinpath(session_id + '_log.tsv')
    triggerfile = Path(subject_path).joinpath(session_id + '_triggers.tsv')
    ioHub_config['session_info']['user_variables']['logfile'] = str(logfile)
    ioHub_config['session_info']['user_variables']['triggerfile'] = str(triggerfile)

    # setting up the eye-tracker
    if 'GAZE' in [m.upper() for m in init['experiment']['modalities']]:
        ioHub_config['session_info']['user_variables']['guiding_eye'] = get_guiding_eye()
    
    # Start the ioHub process. 'io' can now be used during the
    # experiment to access iohub devices and read iohub device events
    # important devices for us are
    #   io.getDevice('keyboard')
    #   io.getDevice('eyelink')
    io = client.launchHubServer(**ioHub_config)

    # the four different conditions, key to our experiment
    nBlocks = init['experiment']['structure']['nBlocks']
    conditions = [
        dict(
            cond='nAmb_nKp',
            img='img/NoPRESS.jpg',
         ),
         dict(
             cond='nAmb_Kp',
             img='img/PRESS.jpg',
         ),
         dict(
             cond='Amb_nKp',
             img='img/NoPRESS.jpg',
         ),
         dict(
             cond='Amb_Kp',
             img='img/PRESS.jpg',
         )]  

    # the subject learns the different conditions (only first three of them)
    learning_phase = TrialHandler(conditions[:-1], nReps=nBlocks['learn'], method='sequential') 
    # the subject goes through 4 blocks of a single trial 'Amb_Kp', will be used to estimate the parameters for the perceptual transitions
    estimation_phase = TrialHandler([conditions[3]], nReps=nBlocks['estim'], method='sequential') 
    # this is the final phase of the experiments with blocks of randomly shuffled conditions
    testing_phase = TrialHandler(conditions, nReps=nBlocks['test'], method='random')

    exp_structure = TrialHandler([
        dict(name="learn", trials=learning_phase, img='img/Consigne.jpg', text="Phase d'habituation I"),
        dict(name="estim", trials=estimation_phase, img='', text="Phase d'habituation II"),
        dict(name="test", trials=testing_phase, img='', text="Phase d'expérimentation")
        ], 
        nReps=1, method='sequential')

    # generate the different stimuli
    plaid_stims = stims.plaids(win, **init['experiment']['stim'])
    plaid_stims.update(dict(fix=stims.fixation(win)))
    return io, win, exp_structure, plaid_stims
Beispiel #6
0
# Load a trial handler and
# create an associated table in the iohub data file
#
from psychopy.data import TrialHandler,importConditions

from psychopy.iohub import launchHubServer

# Start the ioHub process. The return variable is what is used
# during the experiment to control the iohub process itself,
# as well as any running iohub devices.
io=launchHubServer()

exp_conditions=importConditions('trialTypes.xlsx')
trials = TrialHandler(exp_conditions,1)

# Inform the ioHub server about the TrialHandler
#
io.createTrialHandlerRecordTable(trials)

# Read a row of the trial handler for
# each trial of your experiment
#
for trial in trials:
    print trial
    # do whatever...


# During the trial, trial variable values can be updated
#
#trial['TRIAL_START']=flip_time