def define_stimuli(self):
     """
     Creates standard and deviant stimuli.          
     """
     # The stimului. This can be anything compatible with VisionEgg
     dev = FilledCircle(
         color=(0, 1.0, 0),
         position=[self.geometry[2] / 2, self.geometry[3] / 2],
         radius=100)
     std1 = Target2D(color=(0, 0, 1.0),
                     position=[self.geometry[2] / 2, self.geometry[3] / 2],
                     size=(200, 200))
     std2 = Target2D(color=(1.0, 0, 0),
                     position=[self.geometry[2] / 2, self.geometry[3] / 2],
                     size=(200, 200))
     #dev = Text(text='Deviant', font_size=72, position=(300, 200), anchor='center')
     #std1 = Text(text='Standard I', font_size=72, position=(300, 200), anchor='center')
     #std2 = Text(text='Standard II', font_size=72, position=(300, 200), anchor='center')
     return [std1, std2], [dev]
Exemple #2
0
class VisualSpellerVE(MainloopFeedback):
    '''
    Visual Speller with six circles like the classical HexOSpell.
    '''

    # Triggers: look in Marker
    END_LEVEL1, END_LEVEL2 = 244, 245  # end of hex levels
    COPYSPELLING_FINISHED = 246
    STIMULUS = [[11, 12, 13, 14, 15, 16], [21, 22, 23, 24, 25, 26]]
    RESPONSE = [[51, 52, 53, 54, 55, 56], [61, 62, 63, 64, 65, 66]]
    TARGET_ADD = 20
    ERROR_ADD = 100
    COUNTDOWN_STIMULI = 239
    ERROR_POTENTIAL = 96  # send if error potential is classified

    def init(self):
        '''
        initialize parameters
        '''
        self.log_filename = 'VisualSpellerVE.log'

        self.geometry = [0, 0, 1280, 800]  ## size

        self.letterbox_size = (60, 60)
        self.osc_size = 40
        self.font_size_phrase = 60  # the spelled phrase at the top
        self.font_size_current_letter = 80  # the spelled phrase at the top
        self.font_size_countdown = 150  # number during countdown
        self.desired_phrase = ""

        ## colors:
        self.bg_color = (0., 0., 0.)
        self.phrase_color = (0.2, 0.0, 1.0)
        self.current_letter_color = (1.0, 0.0, 0.0)
        self.countdown_color = (0.2, 0.0, 1.0)
        self.osc_color = (1, 1, 1)

        self.letter_set = [['A','B','C','D','E'], \
                           ['F','G','H','I','J'], \
                           ['K','L','M','N','O'], \
                           ['P','Q','R','S','T'], \
                           ['U','V','W','X','Y'], \
                           ['Z','_','.',',','<']]
        self.fullscreen = False
        self.use_oscillator = True
        self.offline = True
        self.copy_spelling = True  # in copy-spelling mode, selection of the target symbol is forced
        self.debug = False
        self.nCountdown = 5
        self.nr_sequences = 6
        self.randomize_sequence = True  # set to False to present a fixed stimulus sequence
        self.min_dist = 2  # Min number of intermediate flashes bef. a flash is repeated twice

        self.stimulus_duration = 0.083  # 5 frames @60 Hz = 83ms flash
        self.interstimulus_duration = 0.1
        self.animation_time = 1
        self.wait_before_classify = 1.
        self.feedback_duration = 1.
        self.feedback_ErrP_duration = 1.0
        self.wait_after_start = 0.

        # Countdown options
        self.do_animation = True
        self.synchronized_countdown = True
        if (self.synchronized_countdown):
            self.do_animation = False

        self.countdown_level1 = True
        self.countdown_level2 = True

        self.countdown_shapes = {
            'circle': FilledCircle,
            'hexagon': FilledHexagon
        }
        self.countdown_shape_select = 'hexagon'
        self.countdown_shape_color = (0.7, 0.7, 0.7)
        self.countdown_shape_on = True
        self.countdown_blinking_nr = 5  #number of pre-sequence stimuli(1 sec is approx. 5 frames at 60 Hz)

        self.wait_after_early_stopping = 3  #sec
        self.abort_trial = False
        self.output_per_stimulus = True
        self.use_ErrP_detection = False

        self.serialtrigger = False

        # FIXME: this should be fixed properly
        try:
            self.serialport = serialport.SerialPort(13)
        except:
            self.serialport = None
        self.send_parallel_bak = self.send_parallel

        if self.debug:
            msg = "!!! YOU\'RE IN DEBUG MODE! CLASSIFICATION WILL BE RANDOM OR KEYBOARD CONTROLLED !!!"
            self.logger.warning(msg)

    def pre_mainloop(self):

        ## logging
        assert (len(self.log_filename) != 0
                )  # 'log_filename' must not be empty string!
        logger.setLevel(logging.ERROR)
        handler = logging.FileHandler(self.log_filename, 'w')
        handler.setLevel(logging.INFO)
        formatter = logging.Formatter("%(asctime)s: %(message)s")
        handler.setFormatter(formatter)
        self.logger.setLevel(logging.INFO)
        self.logger.addHandler(handler)

        self._nr_elements = 6
        self._idx_backdoor = 5
        self._init_classifier_output()
        self._classified_element = -1
        self._classified_letter = -1
        for s in self.desired_phrase:
            assert s in [l for ls in self.letter_set
                         for l in ls]  # invalid letters in desired phrase!
        self._spelled_phrase = ""
        self._spelled_letters = ""
        self._desired_letters = self.desired_phrase
        self._copyspelling_finished = False
        #        if self.offline:
        #            self.__idle()                # In offline mode: set the first to-be-spelled letter

        self._spellerHeight = self.geometry[3] - self.letterbox_size[1]
        self._centerPos = (self.geometry[2] / 2., self._spellerHeight / 2.)

        self._nr_letters = 0
        for i in xrange(len(self.letter_set)):
            self._nr_letters += len(self.letter_set[i])

        self._current_level = 1  # Index of current level
        self._current_sequence = 0  # Index of current sequence
        self._current_stimulus = 0  # Index of current stimlus
        self._current_countdown = self.nCountdown
        self.random = random.Random(clock())
        self._debug_classified = None

        ## init states:
        self._state_countdown = True
        if not self.countdown_level1:
            self._state_countdown = False
            self._state_trial = True
        else:
            #self._state_countdown = not self.offline
            self._state_trial = False

        self._state_classify = False
        self._state_feedback = False
        self._state_abort = False

        ## init containers for VE elements:
        self._ve_elements = []

        ## oscillator state:
        if not self.use_oscillator:
            self.osc_color = self.bg_color
            self.osc_size = 0

        ## call subclass-specific pre_mainloop:
        self.prepare_mainloop()

        ## build screen elements:
        self.__init_screen()
        if self.offline:
            self.__idle()
        if self.abort_trial:
            '''
            Start listener for abort_trial event eg.
            '''

        ## delay after play (might be useful for filters...)
        pygame.time.wait(int(self.wait_after_start * 1000))
        self.logger.info("waiting %d seconds after play." %
                         self.wait_after_start)

        ## send start trigger:
        self.send_parallel(marker.RUN_START)
        self.logger.info("[TRIGGER] %d" % marker.RUN_START)

        ## error potential classifier:
        self._ErrP_classifier = None

    def on_interaction_event(self, data):
        self.logger.debug('interaction event')
        serial = data.get('serialtrigger', None)
        if serial is None:
            return
        if serial:
            self.logger.debug('using serial port')
            self.send_parallel = self.serialport.send
        else:
            self.logger.debug('using parallel port')
            self.send_parallel = self.send_parallel_bak

    def post_mainloop(self):
        """
        Sends end marker to parallel port.
        """

        if self.abort_trial:
            '''
            Stop listener for abort_trial event
           '''
            pass

        pygame.time.wait(500)
        self.send_parallel(marker.RUN_END)
        self.logger.info("[TRIGGER] %d" % marker.RUN_END)
        pygame.time.wait(500)
        self._presentation.set(quit=True)
        self._screen.close()

    def __init_screen(self):
        ## create screen:
        if not self.fullscreen:
            os.environ['SDL_VIDEO_WINDOW_POS'] = '%d, %d' % (self.geometry[0],
                                                             self.geometry[1])
        self._screen = Screen(size=(self.geometry[2], self.geometry[3]),
                              fullscreen=self.fullscreen,
                              bgcolor=self.bg_color,
                              sync_swap=True)

        ## create letter box on top:
        self._ve_letterbox = Target2D(position=(self._centerPos[0],
                                                self.geometry[3] * (1 - 0.01) -
                                                self.letterbox_size[1] / 2.),
                                      size=(self.letterbox_size[0],
                                            self.letterbox_size[1]),
                                      color=self.phrase_color)
        self._ve_innerbox = Target2D(position=(self._centerPos[0],
                                               self.geometry[3] * (1 - 0.01) -
                                               self.letterbox_size[1] / 2.),
                                     size=(self.letterbox_size[0] - 6,
                                           self.letterbox_size[1] - 6),
                                     color=self.bg_color)

        self._current_letter_position = (self._centerPos[0],
                                         self.geometry[3] * (1 - 0.015) -
                                         self.letterbox_size[1] / 2.)
        self._ve_current_letter = Text(
            position=self._current_letter_position,
            text=(len(self._desired_letters[:1]) == 0 and " "
                  or self._desired_letters[:1]),
            font_size=self.font_size_current_letter,
            color=self.current_letter_color,
            anchor='center')

        self._ve_desired_letters = Text(
            position=(self._centerPos[0] + 5 + self.letterbox_size[0] / 2.,
                      self._current_letter_position[1]),
            text=(len(self._desired_letters[1:]) == 0 and " "
                  or self._desired_letters[1:]),
            font_size=self.font_size_phrase,
            color=self.phrase_color,
            anchor='left')

        self._ve_spelled_phrase = Text(
            position=(self._centerPos[0] - 5 - self.letterbox_size[0] / 2.,
                      self._current_letter_position[1]),
            text=(len(self._spelled_phrase) == 0 and " "
                  or self._spelled_phrase),
            font_size=self.font_size_phrase,
            color=self.phrase_color,
            anchor='right')

        # if we're in free spelling mode, we hide all text fields but
        # the _ve_spelled_phrase. we also need a multiline
        # _ve_spelled_phrase instead of the single lined one
        if self.offline == self.copy_spelling == False:
            self._spelled_phrase = "   "
            self._ve_spelled_phrase = WrappedText(
                position=(0, self._current_letter_position[1]),
                text=(len(self._spelled_phrase) == 0 and " "
                      or self._spelled_phrase),
                font_size=self.font_size_phrase,
                color=self.phrase_color,
                size=(float(self.geometry[2]), float(self.geometry[3])))
            for i in self._ve_letterbox, self._ve_innerbox, self._ve_current_letter, self._ve_desired_letters:
                i.set(on=False)

        ## add word box to elementlist:
        self._ve_elements.extend([
            self._ve_letterbox, self._ve_innerbox, self._ve_current_letter,
            self._ve_desired_letters, self._ve_spelled_phrase
        ])

        ## create countdown:
        self._ve_countdown = Text(position=self._centerPos,
                                  text=" ",
                                  font_size=self.font_size_countdown,
                                  color=self.countdown_color,
                                  anchor='center',
                                  on=False)

        ## create countdown shapes
        self._ve_countdown_shape = self.countdown_shapes[
            self.countdown_shape_select](radius=90,
                                         position=self._centerPos,
                                         color=self.countdown_shape_color,
                                         on=False)

        ## create oscillator circle:
        self._ve_oscillator = FilledCircle(position=(self.osc_size / 2 + 10,
                                                     self.osc_size / 2 + 10),
                                           radius=self.osc_size / 2,
                                           color=self.osc_color,
                                           on=False)

        ## create shapes and letters:
        self.init_screen_elements()

        ## add remaining elements to element list:
        self._ve_elements.extend([
            self._ve_countdown_shape, self._ve_countdown, self._ve_oscillator
        ])

        ## add elements to viewport:
        self._viewport = Viewport(screen=self._screen,
                                  stimuli=self._ve_elements)
        self._presentation = Presentation(viewports=[self._viewport],
                                          handle_event_callbacks=[
                                              (pygame.KEYDOWN,
                                               self.keyboard_input),
                                              (pygame.QUIT, self.__stop)
                                          ])

    def play_tick(self):
        """
        called every loop, if in play mode.
        """

        self.pre_play_tick()

        if self._state_countdown:
            self.pre__countdown()
            self.__countdown()
            self.post__countdown()
        elif self._state_trial:
            self.pre__trial()
            self.__trial()
            self.post__trial()
        elif self._state_classify:
            self.pre__classify()
            self.__classify()
            self.post__classify()
        elif self._state_feedback:
            self.pre__feedback()
            self.__feedback()
            self.post__feedback()
        elif self._state_abort:
            self.__abort()
            self.post__abort()
        else:
            self.pre__idle()
            self.__idle()
            self.post__idle()
        self.post_play_tick()

    def __stop(self, *args):
        self.on_stop()

    def __idle(self):
        if self.offline and len(self._desired_letters) > 0:
            # add new letter:
            for e in xrange(len(self.letter_set)):
                for l in xrange(len(self.letter_set[e])):
                    if self._desired_letters[0] == self.letter_set[e][l]:
                        self._classified_element = e
                        self._classified_letter = l
            if self.countdown_level1:
                self._state_countdown = True
            else:
                self._state_trial = True
        else:
            ## otherwise just wait until a new letter is sent:
            self._presentation.set(go_duration=(0.1, 'seconds'))
            self._presentation.go()

    def __countdown(self):
        def blink():
            i = 0
            while (i < self.countdown_blinking_nr):
                self._ve_countdown_shape.set(on=True)
                self._ve_countdown.set(text="%d" % self._current_countdown,
                                       on=True)
                self.send_parallel(self.COUNTDOWN_STIMULI)
                self.logger.info("[TRIGGER] %d" % self.COUNTDOWN_STIMULI)
                self._presentation.set(go_duration=(self.stimulus_duration,
                                                    'seconds'))
                self._presentation.go()
                self._ve_countdown_shape.set(on=False)
                self._ve_countdown.set(on=False)
                self._presentation.set(
                    go_duration=(self.interstimulus_duration, 'seconds'))
                self._presentation.go()
                i = i + 1

        if self._current_countdown == self.nCountdown:
            self.send_parallel(marker.COUNTDOWN_START)
            self.logger.info("[TRIGGER] %d" % marker.COUNTDOWN_START)
            self.set_countdown_screen()
            self._ve_countdown.set(on=True)
            self._ve_countdown_shape.set(on=self.countdown_shape_on)
            self._presentation.set(go_duration=(1, 'seconds'))

        self._ve_countdown.set(text="%d" % self._current_countdown)
        self._presentation.go()
        self._current_countdown = (self._current_countdown -
                                   1) % self.nCountdown

        if self.synchronized_countdown and self._current_countdown == 1:
            self.set_synchronized_countdown_screen()
            blink()
            self._current_countdown = self.nCountdown
            self.set_standard_screen()
            self._state_countdown = False
            self._state_trial = True
            self._ve_countdown.set(on=False)
            self._ve_countdown_shape.set(on=False)
            self._ve_countdown.set(color=self.countdown_color)

        if self._current_countdown == 0:
            # Executed only if self.synchronized_countdown = False
            self._current_countdown = self.nCountdown
            self.set_standard_screen()
            pygame.time.wait(10)
            self._state_countdown = False
            self._state_trial = True
            self._ve_countdown_shape.set(on=False)
            self._ve_countdown.set(on=False)
            self._ve_countdown.set(color=self.countdown_color)

    def __trial(self):

        if self._current_sequence == 0 and self._current_stimulus == 0:

            #level 1 animation when there is no countdown
            if self._current_level == 1 and not self.countdown_level1:
                if self.do_animation:
                    self.set_countdown_screen()
                self.set_standard_screen()
            # generate random sequences:
            if self.randomize_sequence:
                self.flash_sequence = []
                for _ in range(self.nr_sequences):
                    random_flash_sequence(self,
                                          set=range(self._nr_elements),
                                          min_dist=self.min_dist,
                                          seq_len=self._nr_elements)
            # or else use fixed sequence:
            else:
                self.flash_sequence = range(self._nr_elements)

        if self.randomize_sequence:
            currentStimulus = self.flash_sequence[self._current_sequence *
                                                  self._nr_elements +
                                                  self._current_stimulus]
        else:
            currentStimulus = self.flash_sequence[self._current_stimulus]
        # set stimulus:
        self.stimulus(currentStimulus, True)
        #self._ve_oscillator.set(on=True)

        if self.abort_trial and self.abort_trial_check():
            # restart trial on abort_trial event:
            self._state_trial = False
            self._state_abort = True
            return

        # check if current stimulus is target and then send trigger:
        target_add = 0
        if len(self._desired_letters) > 0:
            if self._current_level == 1:
                if self._desired_letters[:1] in self.letter_set[
                        currentStimulus]:
                    # current stimulus is target group:
                    target_add = self.TARGET_ADD
            else:
                if currentStimulus == self._idx_backdoor:
                    # current stimulus is backdoor:
                    if not self._desired_letters[:1] in self.letter_set[
                            self._classified_element]:
                        # we are in the wrong group. backdoor is target:
                        target_add = self.TARGET_ADD
                else:
                    # current stimulus is no backdoor:
                    if self._desired_letters[:1] == self.letter_set[
                            self._classified_element][currentStimulus]:
                        # current stimulus is target symbol:
                        target_add = self.TARGET_ADD

        self.send_parallel(self.STIMULUS[self._current_level -
                                         1][currentStimulus] + target_add)
        self.logger.info(
            "[TRIGGER] %d" %
            (self.STIMULUS[self._current_level - 1][currentStimulus] +
             target_add))

        # present stimulus:
        self._presentation.set(go_duration=(self.stimulus_duration, 'seconds'))
        self._presentation.go()

        # reset to normal:
        self._ve_oscillator.set(on=False)
        self.stimulus(currentStimulus, False)

        # present interstimulus:
        self._presentation.set(go_duration=(self.interstimulus_duration,
                                            'seconds'))
        self._presentation.go()

        if self.debug:
            self.on_control_event(
                {'cl_output': (self.random.random(), currentStimulus + 1)})

        ## TODO: check here for classification !!!!
        if self.output_per_stimulus:
            # increase
            self._current_stimulus = (self._current_stimulus +
                                      1) % self._nr_elements
            if self._current_stimulus == 0:
                self._current_sequence = (self._current_sequence +
                                          1) % self.nr_sequences

            # check for end of trial:
            if self._current_sequence == 0 and self._current_stimulus == 0:

                # send trigger:
                if self._current_level == 1:
                    self.send_parallel(self.END_LEVEL1)
                    self.logger.info("[TRIGGER] %d" % self.END_LEVEL1)
                else:
                    self.send_parallel(self.END_LEVEL2)
                    self.logger.info("[TRIGGER] %d" % self.END_LEVEL2)

                # decide how to continue:
                self._state_trial = False
                self._state_classify = True
        else:
            # increase
            self._current_stimulus = (self._current_stimulus +
                                      1) % self._nr_elements
            if self._current_stimulus == 0:
                self._current_sequence = (self._current_sequence +
                                          1) % self.nr_sequences
                if self.check_classification(self._current_sequence + 1):
                    self._state_trial = False
                    self._state_classify = True
                    pygame.time.wait(self.wait_after_early_stopping * 1000)

            if self._current_sequence == 0 and self._current_stimulus == 0:

                # send trigger:
                if self._current_level == 1:
                    self.send_parallel(self.END_LEVEL1)
                    self.logger.info("[TRIGGER] %d" % self.END_LEVEL1)
                else:
                    self.send_parallel(self.END_LEVEL2)
                    self.logger.info("[TRIGGER] %d" % self.END_LEVEL2)

                # decide how to continue:
                self._state_trial = False
                self._state_classify = True

    def check_classification(self, nr):
        #print self._classifier_output
        means = [None] * self._nr_elements
        minimum = maxint
        classified = None
        for ii in range(self._nr_elements):
            means[ii] = sum(self._classifier_output[ii]) / nr
            if means[ii] < minimum:
                minimum = means[ii]
                classified = ii + 1

        print "\n**** Class: %d (mean=%f)\n" % (classified,
                                                means[classified - 1])
        return classified

    def __classify(self):

        ## wait until all classifier outputs are received:
        self._presentation.set(go_duration=(self.wait_before_classify,
                                            'seconds'))
        self._presentation.go()
        if self.offline:
            if self._current_level == 1:
                classified = self._classified_element
            else:
                classified = self._classified_letter
        elif not self._debug_classified == None:
            classified = self._debug_classified
            self._debug_classified = None
        else:
            if self.output_per_stimulus:
                nClassified = sum([
                    len(self._classifier_output[i])
                    for i in xrange(self._nr_elements)
                ])
                if nClassified < self._nr_elements * self.nr_sequences:
                    pygame.time.wait(20)
                    print 'not enough classifier-outputs received! (something may be wrong)'
                    return

                ## classify and set output:
                means = [None] * self._nr_elements
                minimum = maxint
                classified = None
                for ii in range(self._nr_elements):
                    means[ii] = sum(
                        self._classifier_output[ii]) / self.nr_sequences
                    if means[ii] < minimum:
                        minimum = means[ii]
                        classified = ii
                print "\n**** Class: %d (mean=%f)\n" % (classified + 1,
                                                        means[classified])
            else:
                means = [None] * self._nr_elements
                minimum = maxint
                classified = None
                for ii in range(self._nr_elements):
                    means[ii] = sum(
                        self._classifier_output[ii]) / self.nr_sequences
                    if means[ii] < minimum:
                        minimum = means[ii]
                        classified = ii
                print "\n**** Class: %d (mean=%f)\n" % (classified + 1,
                                                        means[classified])

            ## Reset classifier output to empty lists
            self._init_classifier_output()

        error_add = 0
        ## evaluate classification:
        if self._current_level == 1:
            self._classified_element = classified
            if len(self._desired_letters
                   ) > 0 and not self._desired_letters[:1] in self.letter_set[
                       classified]:
                # wrong group selected:
                error_add = self.ERROR_ADD
        else:
            self._classified_letter = classified
            if self._classified_letter == self._idx_backdoor:
                ## backdoor classified:
                if len(self._desired_letters
                       ) > 0 and self._desired_letters[:1] in self.letter_set[
                           self._classified_element]:
                    # backdoor selection wrong:
                    error_add = self.ERROR_ADD
            else:
                ## no backdoor classified:
                spelled_letter = self.letter_set[self._classified_element][
                    self._classified_letter]
                if len(self._desired_letters
                       ) > 0 and spelled_letter != self._desired_letters[:1]:
                    # wrong letter spelled:
                    error_add = self.ERROR_ADD

        ## send response trigger:
        self.send_parallel(self.RESPONSE[self._current_level - 1][classified] +
                           error_add)
        self.logger.info(
            "[TRIGGER] %d" %
            (self.RESPONSE[self._current_level - 1][classified] + error_add))

        self._state_classify = False
        self._state_feedback = True

    def __feedback(self):
        self._state_feedback = False

        ## call subclass method:
        self.feedback()

        ## check ErrP classification:
        if self.use_ErrP_detection:
            t = 0
            while self._ErrP_classifier is None and t < 1000:
                t += 50
                pygame.time.wait(50)
            if self._ErrP_classifier is None:
                print "no ErrP classifier received! "
            if self._ErrP_classifier:
                self.send_parallel(self.ERROR_POTENTIAL)
                self.logger.info("[TRIGGER] %d" % (self.ERROR_POTENTIAL))

        ## call subclass method:
        if not self.countdown_level2:
            self.switch_level()

        ## update phrases:
        if ((self._current_level == 2)
                and  # only update, if we are at the end of level 2,
            (self._classified_letter != self._idx_backdoor
             or self.copy_spelling)
                and  # if copyspelling off, we have no backdoor selected
            (self._ErrP_classifier is None or not self._ErrP_classifier)
            ):  # no ErrP was detected (or ErrP detection is off)
            spelled_letter = ""
            if self.copy_spelling:
                ## in copy spelling we force the desired letter to be spelled
                if len(self._desired_letters) > 0:
                    spelled_letter = self._desired_letters[:1]
                else:
                    print "??? moved beyond desired phrase in copy spelling ???"
            else:
                spelled_letter = self.letter_set[self._classified_element][
                    self._classified_letter]

            ## update desired phrase:
            if len(self._desired_letters) > 0:
                if spelled_letter == self._desired_letters[:1]:
                    # correct letter spelled:
                    self._desired_letters = self._desired_letters[
                        1:]  # remove first letter
                else:
                    # wrong letter spelled:
                    if spelled_letter == "<":
                        self._desired_letters = self._spelled_phrase[
                            -1:] + self._desired_letters
                    else:
                        self._desired_letters = "<" + self._desired_letters
                if len(self._desired_letters) == 0:
                    self._copyspelling_finished = True

            ## update spelled phrase:
            self._spelled_letters += spelled_letter
            if spelled_letter == "<":
                self._spelled_phrase = self._spelled_phrase[:-1]
            else:
                self._spelled_phrase += spelled_letter

            ## update screen phrases:
            self.logger.info('Current Phrase:')
            self.logger.info(self._spelled_phrase)
            self._ve_spelled_phrase.set(
                text=(len(self._spelled_phrase) == 0 and " "
                      or self._spelled_phrase))
            self._ve_current_letter.set(
                text=(len(self._desired_letters[:1]) == 0 and " "
                      or self._desired_letters[:1]))
            self._ve_desired_letters.set(
                text=(len(self._desired_letters[1:]) == 0 and " "
                      or self._desired_letters[1:]))

        if self.use_ErrP_detection and self._ErrP_classifier:
            self._state_trial = True

        else:
            if self._current_level == 1:
                # continue with level2 trial:
                if self.countdown_level2:
                    self._state_countdown = True
                else:
                    self._state_trial = True

            elif not self.offline:
                # start countdown
                if self.countdown_level1:
                    self._state_countdown = True
                else:
                    self._state_trial = True

            # set new level:
            self._current_level = 3 - self._current_level

        ## reset ErrP_classifier:
        self._ErrP_classifier = None

        # check copyspelling:
        if self._copyspelling_finished:
            self._copyspelling_finished = False
            self.on_control_event({'print': 0})  # print desired phrase
            self.on_control_event({'print': 1})  # print spelled phrase
            self.on_control_event({'print': 2})  # print all spelled letters
            self.send_parallel(self.COPYSPELLING_FINISHED)
            self.logger.info("[TRIGGER] %d" % (self.COPYSPELLING_FINISHED))
            pygame.time.wait(50)

    def __abort(self):

        # play warning sound

        def sine_array_onecycle(hz, peak, sample_rate):
            length = sample_rate / float(hz)
            omega = NP.pi * 2 / length
            xvalues = NP.arange(int(length)) * omega
            return (peak * NP.sin(xvalues))

        def sine_array(hz, peak, samples_rate):
            return NP.resize(sine_array_onecycle(hz, peak, sample_rate),
                             (sample_rate, ))

        sample_rate = 44100
        pygame.mixer.init(sample_rate, -16,
                          2)  # 44.1kHz, 16-bit signed, stereo
        f = sine_array(8000, 1, sample_rate)
        f = NP.array(zip(f, f))
        sound = pygame.sndarray.make_sound(f)
        channel = sound.play(-1)
        channel.set_volume(0.2, 0.2)
        pygame.time.delay(1000)
        sound.stop()

        if self._current_level == 1 and self.countdown_level1:
            self._state_countdown = True
        elif self._current_level == 2 and self.countdown_level2:
            self._state_countdown = True
        else:
            self._state_trial = True

        self._init_classifier_output()

    def _init_classifier_output(self):
        ## Empty lists
        self._classifier_output = [list() for _ in xrange(self._nr_elements)]

    def abort_trial_check(self):
        '''
        Check if event is an abort trial event
        '''
        return False

    def keyboard_input(self, event):
        if event.key == pygame.K_ESCAPE:
            self.on_stop()
        elif event.key == pygame.K_KP_ENTER:
            self.on_control_event({'print': 0})  # print desired phrase
            self.on_control_event({'print': 1})  # print spelled phrase
            self.on_control_event({'print': 2})  # print all spelled letters
        elif event.key == pygame.K_DELETE:
            # The DELETE key empties the spelled text shown this works
            # only in free spelling mode (i.e. offline and copy_spelling
            # are set to False)
            if self.offline == self.copy_spelling == False:
                self.logger.info('Clearing Text.')
                self._spelled_phrase = "   "
        elif self.debug:
            if ((event.key >= pygame.K_a and event.key <= pygame.K_z)
                    or (event.key == pygame.K_LESS)
                    or (event.key == pygame.K_PERIOD)
                    or (event.key == pygame.K_COMMA)):
                self.on_control_event({'new_letter': chr(event.key).upper()})
            elif event.key == pygame.K_MINUS:
                self.on_control_event({'new_letter': chr(pygame.K_UNDERSCORE)})
            elif event.key == pygame.K_BACKSPACE:
                self.on_control_event({'new_letter': chr(pygame.K_LESS)})
            elif event.key == pygame.K_SPACE:
                self.on_control_event({'new_letter': chr(pygame.K_UNDERSCORE)})
            elif event.key == pygame.K_UP and self.use_ErrP_detection:
                self.on_control_event({'cl_output': (1, 7)})
            elif event.key == pygame.K_DOWN and self.use_ErrP_detection:
                self.on_control_event({'cl_output': (0, 7)})
            if not self.offline:
                if (event.key >= pygame.K_0 and event.key <= pygame.K_5):
                    self._debug_classified = int(chr(event.key))
                elif (event.key >= pygame.K_KP0 and event.key <= pygame.K_KP5):
                    self._debug_classified = int(chr(event.key - 208))

    def on_control_event(self, data):
        self.logger.info("[CONTROL_EVENT] %s" % str(data))
        if data.has_key(u'cl_output'):
            # classification output was sent:
            score_data = data[u'cl_output']
            cl_out = score_data[0]
            iSubstim = int(score_data[1])  # evt auch "Subtrial"
            if iSubstim in range(1, 7):
                self._classifier_output[iSubstim - 1].append(cl_out)
            elif self.use_ErrP_detection:
                self._ErrP_classifier = cl_out
        elif data.has_key('new_letter'):
            # get new letter to spell:
            self._desired_letters += data['new_letter']
            self._ve_current_letter.set(
                text=(len(self._desired_letters[:1]) == 0 and " "
                      or self._desired_letters[:1]))
            self._ve_desired_letters.set(
                text=(len(self._desired_letters[1:]) == 0 and " "
                      or self._desired_letters[1:]))

        elif data.has_key(u'print'):
            if data[u'print'] == 0:
                self.logger.info("[DESIRED_PHRASE] %s" % self.desired_phrase)
            elif data[u'print'] == 1:
                self.logger.info("[SPELLED_PHRASE] %s" % self._spelled_phrase)
            elif data[u'print'] == 2:
                self.logger.info("[SPELLED_LETTERS] %s" %
                                 self._spelled_letters)

    '''
    ==========================
    == METHODS TO OVERLOAD: ==
    ==========================
    '''

    def init_screen_elements(self):
        '''
        overwrite this function in subclass.
        '''
        pass

    def prepare_mainloop(self):
        '''
        overwrite this function in subclass.
        '''
        pass

    def set_countdown_screen(self):
        '''
        set screen how it should look during countdown.
        overwrite this function in subclass.
        '''
        pass

    def set_standard_screen(self):
        '''
        set screen elements to standard state.
        overwrite this function in subclass.
        '''
        pass

    def set_synchronized_countdown_screen(self):
        '''
        set screen elements to for the synchronized countdown.
        overwrite this function in subclass.
        '''
        pass

    def stimulus(self, i_element, on=True):
        '''
        turn on/off the stimulus elements and turn off/on the normal elements.
        overwrite this function in subclass.
        '''
        pass

    def feedback(self):
        '''
        set screen how it should look during feedback presentation.
        overwrite this function in subclass.
        '''
        pass

    def switch_level(self):
        '''
        overwrite this function in subclass.
        '''
        pass

    def pre_play_tick(self):
        pass

    def post_play_tick(self):
        pass

    def pre__countdown(self):
        pass

    def post__countdown(self):
        pass

    def pre__trial(self):
        pass

    def post__trial(self):
        pass

    def pre__classify(self):
        pass

    def post__classify(self):
        pass

    def pre__feedback(self):
        pass

    def post__feedback(self):
        pass

    def pre__idle(self):
        pass

    def post__idle(self):
        pass

    def post__abort(self):
        pass
Exemple #3
0
    def __init_screen(self):
        ## create screen:
        if not self.fullscreen:
            os.environ['SDL_VIDEO_WINDOW_POS'] = '%d, %d' % (self.geometry[0],
                                                             self.geometry[1])
        self._screen = Screen(size=(self.geometry[2], self.geometry[3]),
                              fullscreen=self.fullscreen,
                              bgcolor=self.bg_color,
                              sync_swap=True)

        ## create letter box on top:
        self._ve_letterbox = Target2D(position=(self._centerPos[0],
                                                self.geometry[3] * (1 - 0.01) -
                                                self.letterbox_size[1] / 2.),
                                      size=(self.letterbox_size[0],
                                            self.letterbox_size[1]),
                                      color=self.phrase_color)
        self._ve_innerbox = Target2D(position=(self._centerPos[0],
                                               self.geometry[3] * (1 - 0.01) -
                                               self.letterbox_size[1] / 2.),
                                     size=(self.letterbox_size[0] - 6,
                                           self.letterbox_size[1] - 6),
                                     color=self.bg_color)

        self._current_letter_position = (self._centerPos[0],
                                         self.geometry[3] * (1 - 0.015) -
                                         self.letterbox_size[1] / 2.)
        self._ve_current_letter = Text(
            position=self._current_letter_position,
            text=(len(self._desired_letters[:1]) == 0 and " "
                  or self._desired_letters[:1]),
            font_size=self.font_size_current_letter,
            color=self.current_letter_color,
            anchor='center')

        self._ve_desired_letters = Text(
            position=(self._centerPos[0] + 5 + self.letterbox_size[0] / 2.,
                      self._current_letter_position[1]),
            text=(len(self._desired_letters[1:]) == 0 and " "
                  or self._desired_letters[1:]),
            font_size=self.font_size_phrase,
            color=self.phrase_color,
            anchor='left')

        self._ve_spelled_phrase = Text(
            position=(self._centerPos[0] - 5 - self.letterbox_size[0] / 2.,
                      self._current_letter_position[1]),
            text=(len(self._spelled_phrase) == 0 and " "
                  or self._spelled_phrase),
            font_size=self.font_size_phrase,
            color=self.phrase_color,
            anchor='right')

        # if we're in free spelling mode, we hide all text fields but
        # the _ve_spelled_phrase. we also need a multiline
        # _ve_spelled_phrase instead of the single lined one
        if self.offline == self.copy_spelling == False:
            self._spelled_phrase = "   "
            self._ve_spelled_phrase = WrappedText(
                position=(0, self._current_letter_position[1]),
                text=(len(self._spelled_phrase) == 0 and " "
                      or self._spelled_phrase),
                font_size=self.font_size_phrase,
                color=self.phrase_color,
                size=(float(self.geometry[2]), float(self.geometry[3])))
            for i in self._ve_letterbox, self._ve_innerbox, self._ve_current_letter, self._ve_desired_letters:
                i.set(on=False)

        ## add word box to elementlist:
        self._ve_elements.extend([
            self._ve_letterbox, self._ve_innerbox, self._ve_current_letter,
            self._ve_desired_letters, self._ve_spelled_phrase
        ])

        ## create countdown:
        self._ve_countdown = Text(position=self._centerPos,
                                  text=" ",
                                  font_size=self.font_size_countdown,
                                  color=self.countdown_color,
                                  anchor='center',
                                  on=False)

        ## create countdown shapes
        self._ve_countdown_shape = self.countdown_shapes[
            self.countdown_shape_select](radius=90,
                                         position=self._centerPos,
                                         color=self.countdown_shape_color,
                                         on=False)

        ## create oscillator circle:
        self._ve_oscillator = FilledCircle(position=(self.osc_size / 2 + 10,
                                                     self.osc_size / 2 + 10),
                                           radius=self.osc_size / 2,
                                           color=self.osc_color,
                                           on=False)

        ## create shapes and letters:
        self.init_screen_elements()

        ## add remaining elements to element list:
        self._ve_elements.extend([
            self._ve_countdown_shape, self._ve_countdown, self._ve_oscillator
        ])

        ## add elements to viewport:
        self._viewport = Viewport(screen=self._screen,
                                  stimuli=self._ve_elements)
        self._presentation = Presentation(viewports=[self._viewport],
                                          handle_event_callbacks=[
                                              (pygame.KEYDOWN,
                                               self.keyboard_input),
                                              (pygame.QUIT, self.__stop)
                                          ])
Exemple #4
0
class CenterSpellerVE(VisualSpellerVE):
    '''
    classdocs
    '''

    def init(self):
        ''' initialize parameters '''

        VisualSpellerVE.init(self)


        ## sizes:
        self.letter_radius = 40
        self.speller_radius = 250
        self.font_size_level1 = 45         # letters in level 1
        self.font_size_level2 = 130         # letters in level 2
        self.feedbackbox_size = 200.0
        self.fixationpoint_size = 4.0
        self.font_size_feedback_ErrP = 300

        ## stimulus types:
        self.stimulustype_color = True
        self.shape_on=True

        self.stimulus_duration = 0.083   # 5 frames @60 Hz = 83ms flash
        self.interstimulus_duration = 0.1


        ## feedback type:
        self.feedback_show_shape = True
        self.feedback_show_shape_at_center =True

        ## level 2 appearance:
        self.level_2_shapes = True
        self.level_2_letter_colors = False
        self.level_2_animation = True
        self.backdoor_symbol = "^"


        ## colors:
        self.shape_color = (1.0, 1.0, 1.0)

        self.stimuli_colors = [[0.0,0.0,1.0],
                                [0.0,0.53,0.006],
                                [1.0,0.0,0.0],
                                [1.0,1.0,0.0],
                                [0.86,0.0,0.86],
                                [0.95,0.95,0.95]]
        self.letter_color = (.5, .5, .5)
        self.feedback_color = (0.9, 0.9, 0.9)
        self.fixationpoint_color = (1.0, 1.0, 1.0)
        self.feedback_ErrP_color = (0.7, 0.1, 0.1)

        ## register possible shapes:
        self.registered_shapes = {'circle':FilledCircle,
                                    'cross' : FilledCross,
                                    'hexagon':FilledHexagon,
                                    'hourglass':FilledHourglass,
                                    'triangle':FilledTriangle,
                                    'rectangle':Target2D}

        ## define shapes in the form ['name', {parameters}]:
        # where parameters can be VisionEgg parameters eg. 'radius', 'size', 'orientation' etc.

        self.shapes = [
            [ "triangle", {"innerColor":self.bg_color, "innerSize": 60, "size": 200}],
            [ "hourglass", {"size": 100 }],
            [ "cross", {"orientation": 45, "size": [30,180], "innerColor":self.bg_color}],
            [ "triangle", {"innerColor":self.bg_color, "innerSize": 60, "orientation": 180, "size": 200}],
            [ "hourglass", {"orientation": 90, "size": 100}],
            [ "cross", {"size": [30,180], "innerColor":self.bg_color }]
        ]
    # If False only shapes are shown in that level but not group symbols(Letters)
        self.level_1_symbols=True
        self.level_2_symbols=True

    def prepare_mainloop(self):
        '''
        called in pre_mainloop of superclass.
        '''

        if not self.do_animation:
            self.level_2_animation = False

        ## init containers for VE elements:
        self._ve_shapes = []
        self._ve_letters = []

        self._letter_positions = []
        self._countdown_shape_positions = []
        self._countdown_letter_positions = []
        self._countdown_screen = False

        if self.stimulustype_color:
            assert len(self.stimuli_colors)==self._nr_elements
            self.shape_color = self.stimuli_colors
        else:
            self.shape_color = [self.shape_color]*self._nr_elements

        if not self.feedback_show_shape:
            self.feedback_show_shape_at_center = False

        if not self.shape_on:
            self.shapes=[['triangle',  {'size':200}],
                        ['triangle',  {'size':200}],
                        ['triangle',  {'size':200}],
                        ['triangle',  {'size':200}],
                        ['triangle',  {'size':200}],
                        ['triangle',  {'size':200}]]



    def init_screen_elements(self):
        '''
        Initializing screen elements
        '''

        ## create shapes:
        if self.do_animation:
            for i in xrange(self._nr_elements):
                self._ve_shapes.append(self.registered_shapes[self.shapes[i][0]](
                    position=self._centerPos,
                    color=self.shape_color[i],
                    on=False,
                    **self.shapes[i][1]))


            ## add letters of level 1:
            circle_layout = CircularLayout(nr_elements=self._nr_elements,
                    radius=self.speller_radius,
                    start=NP.pi/6.*5)
            circle_layout.positions.reverse()
            self._letter_layout = CircularLayout(nr_elements=self._nr_elements,
                    radius=self.letter_radius,
                    start=NP.pi/6.*5)
            self._letter_layout.positions.reverse()
            for i in xrange(self._nr_elements):
                # store countdown position:
                self._countdown_shape_positions.append((self._centerPos[0] + circle_layout.positions[i][0],
                    self._centerPos[1] + circle_layout.positions[i][1]))

                # put shape in container:
                self._ve_elements.append(self._ve_shapes[i])

                for j in xrange(len(self.letter_set[i])): # warning: self.letter_set must be at least of length self._nr_elements!!!
                    # store position:
                    self._letter_positions.append((self._letter_layout.positions[j][0] + self._centerPos[0],
                        self._letter_layout.positions[j][1] + self._centerPos[1]))

                    # store countdown position:
                    self._countdown_letter_positions.append((self._letter_layout.positions[j][0] + self._countdown_shape_positions[-1][0],
                        self._letter_layout.positions[j][1] + self._countdown_shape_positions[-1][1]))

                    # add letter:
                    self._ve_letters.append(Text(position=self._letter_positions[-1],
                        text=self.letter_set[i][j],
                        font_size=self.font_size_level1,
                        color=self.letter_color,
                        anchor='center',
                        on=False))

            # add letters of level 2:
            for i in xrange(self._nr_elements):
                self._letter_positions.append(self._centerPos)
                self._countdown_letter_positions.append(self._countdown_shape_positions[i])
                self._ve_letters.append(Text(position=self._centerPos,
                    text=" ",
                    font_size=self.font_size_level2,
                    color=(self.level_2_letter_colors and self.stimuli_colors[i] or self.letter_color),
                    anchor='center',
                    on=False))

                # put letters in container:
            self._ve_elements.extend(self._ve_letters)


            ## create feedback box:
            self._ve_feedback_box = Target2D(position=self._centerPos,
                    size=(self.feedbackbox_size, self.feedbackbox_size),
                    color=self.feedback_color,
                    on=False)

            ## add feedback letters:
            self._ve_feedback_letters = []
            for i in xrange(self._nr_elements):
                self._ve_feedback_letters.append(Text(position=(self._letter_layout.positions[i][0]+self._centerPos[0],
                    self._letter_layout.positions[i][1]+self._centerPos[1]),
                    color=self.letter_color,
                    font_size=self.font_size_level1,
                    text=" ",
                    on=False,
                    anchor="center"))
                self._ve_feedback_letters.append(Text(position=self._centerPos,
                    color=self.letter_color,
                    font_size=self.font_size_level2,
                    text=" ",
                    anchor='center',
                    on=False))

            ## add feedback note (whether or not there was an ErrP detected):
            self._ve_feedback_ErrP = Text(position=self._centerPos,
                    color=self.feedback_ErrP_color,
                    text="X",
                    font_size=self.font_size_feedback_ErrP,
                    anchor='center',
                    on=False)

            ## add fixation point:
            self._ve_fixationpoint = FilledCircle(radius=self.fixationpoint_size,
                    position=self._centerPos,
                    color=self.fixationpoint_color,
                    on=False)

            ##################### IF NOT DO ANIMATION #########################
        else:
            ## add letters of level 1:
            circle_layout = CircularLayout(nr_elements=self._nr_elements,
                    radius=self.speller_radius,
                    start=NP.pi/6.*5)
            self._letter_layout = CircularLayout(nr_elements=self._nr_elements,
                    radius=self.letter_radius,
                    start=NP.pi/6.*5)
            circle_layout.positions.reverse()
            self._letter_layout.positions.reverse()

            for i in xrange(self._nr_elements):
                self._ve_shapes.append(self.registered_shapes[self.shapes[i][0]](
                    position=(self._centerPos[0] + circle_layout.positions[i][0],
                        self._centerPos[1] + circle_layout.positions[i][1]),
                    color=self.shape_color[i],
                    on=False,
                    **self.shapes[i][1]))



            for i in xrange(self._nr_elements):
                # store countdown position:
                self._countdown_shape_positions.append((self._centerPos[0] + circle_layout.positions[i][0],
                    self._centerPos[1] + circle_layout.positions[i][1]))
                # put shape in container:
                self._ve_elements.append(self._ve_shapes[i])

                for j in xrange(len(self.letter_set[i])): # warning: self.letter_set must be at least of length self._nr_elements!!!
                    # store position:
                    self._letter_positions.append((self._letter_layout.positions[j][0] + self._centerPos[0],
                        self._letter_layout.positions[j][1] + self._centerPos[1]))

                    # store countdown position:
                    self._countdown_letter_positions.append((self._letter_layout.positions[j][0] + self._countdown_shape_positions[-1][0],
                        self._letter_layout.positions[j][1] + self._countdown_shape_positions[-1][1]))

                    # add letter:
                    self._ve_letters.append(Text(position=(self._letter_layout.positions[j][0] + self._centerPos[0]+ circle_layout.positions[i][0],
                        self._letter_layout.positions[j][1] + self._centerPos[1]+circle_layout.positions[i][1]),
                        text=self.letter_set[i][j],
                        font_size=self.font_size_level1,
                        color=self.letter_color,
                        anchor='center',
                        on=False))

            # add letters of level 2:
            for i in xrange(self._nr_elements):
                self._letter_positions.append((self._letter_layout.positions[i][0] + self._centerPos[0],
                    self._letter_layout.positions[i][1] + self._centerPos[1]))
                self._countdown_letter_positions.append((self._letter_layout.positions[i][0] + self._countdown_shape_positions[-1][0],
                    self._letter_layout.positions[i][1] + self._countdown_shape_positions[-1][1]))
                self._ve_letters.append(Text(position=(self._letter_layout.positions[i][0] + self._centerPos[0]+ circle_layout.positions[i][0],
                    self._letter_layout.positions[i][1] + self._centerPos[1]+circle_layout.positions[i][1]),
                    text=" ",
                    font_size=self.font_size_level2,
                    color=(self.level_2_letter_colors and self.stimuli_colors[i] or self.letter_color),
                    anchor='center',
                    on=False))

            # put letters in container:
            self._ve_elements.extend(self._ve_letters)


            ## create feedback box:
            self._ve_feedback_box = Target2D(position=self._centerPos,
                    size=(self.feedbackbox_size, self.feedbackbox_size),
                    color=self.feedback_color,
                    on=False)

            ## add feedback letters:
            self._ve_feedback_letters = []
            for i in xrange(self._nr_elements):
                self._ve_feedback_letters.append(Text(position=(self._letter_layout.positions[i][0]+self._centerPos[0],
                    self._letter_layout.positions[i][1]+self._centerPos[1]),
                    color=self.letter_color,
                    font_size=self.font_size_level1,
                    text=" ",
                    on=False,
                    anchor="center"))
                self._ve_feedback_letters.append(Text(position=self._centerPos,
                    color=self.letter_color,
                    font_size=self.font_size_level2,
                    text=" ",
                    anchor='center',
                    on=False))

            ## add feedback note (whether or not there was an ErrP detected):
            self._ve_feedback_ErrP = Text(position=self._centerPos,
                    color=self.feedback_ErrP_color,
                    text="X",
                    font_size=self.font_size_feedback_ErrP,
                    anchor='center',
                    on=False)

            ## add fixation point:
            self._ve_fixationpoint = FilledCircle(radius=self.fixationpoint_size,
                    position=self._centerPos,
                    color=self.fixationpoint_color,
                    on=False)

        # put letters in container:
        self._ve_elements.append(self._ve_feedback_box)
        self._ve_elements.extend(self._ve_feedback_letters)
        self._ve_elements.append(self._ve_feedback_ErrP)
        self._ve_elements.append(self._ve_fixationpoint)


    def set_countdown_screen(self):
        '''
        set screen how it should look during countdown.
        '''
        if self._countdown_screen:
            return


        self._countdown_screen = True
        is_level1 = self._current_level==1

        ## turn on visible elements:
        for i in xrange(self._nr_elements): # shapes
            self._ve_shapes[i].set(on=is_level1 or self.level_2_shapes)

        for i in xrange(self._nr_letters): # level 1 letters
            self._ve_letters[i].set(on=is_level1 )

        for i in xrange(len(self.letter_set[self._classified_element])): # level 2 letters
            self._ve_letters[self._nr_letters + i].set(on=not is_level1,
                    text=(is_level1 and " " or self.letter_set[self._classified_element][i]))

        self._ve_letters[self._nr_letters + self._nr_elements-1].set(on=not is_level1,
                text=(is_level1 and " " or self.backdoor_symbol))

        ## move all elements with their letters to countdown position:
        def update(t):
            dt = t/self.animation_time
            for i in xrange(self._nr_elements):
                pos = animate_sigmoid(self._centerPos, self._countdown_shape_positions[i], dt)
                self._ve_shapes[i].set(position=pos) # shapes
                self._ve_letters[self._nr_letters + i].set(position=pos) # level 2 letters
            for i in xrange(self._nr_letters):
                pos = animate_sigmoid(self._letter_positions[i], self._countdown_letter_positions[i], dt)
                self._ve_letters[i].set(position=pos) # level 1 letters

        def update2(t): # if not do animation
            for i in xrange(self._nr_elements):
                self._ve_shapes[i].set(position=self._countdown_shape_positions[i])# shapes
                self._ve_letters[self._nr_letters + i].set(position=self._countdown_shape_positions[i])# level 2 letters
            for i in xrange(self._nr_letters):
                self._ve_letters[i].set(position=self._countdown_letter_positions[i])# level 1 letters

        if self.do_animation:
            self._presentation.set(go_duration=(self.animation_time, 'seconds'))
            self._presentation.add_controller(None,None,FunctionController(during_go_func=update))
        else:
            self._presentation.set(go_duration=(0,'seconds'))
            self._presentation.add_controller(None,None,FunctionController(during_go_func=update2))


        # send to screen:
        self._presentation.go()
        self._presentation.remove_controller(None,None,None)

        for i in xrange(self._nr_elements):
            self._ve_shapes[i].set(position=self._countdown_shape_positions[i])
            self._ve_letters[self._nr_letters + i].set(position=self._countdown_shape_positions[i])
        for i in xrange(self._nr_letters):
            self._ve_letters[i].set(position=self._countdown_letter_positions[i])

    def set_synchronized_countdown_screen(self):
        for i in xrange(self._nr_elements):
            self._ve_shapes[i].set(on=False)
        if self._current_level==1:
           for j in xrange(self._nr_letters):
              self._ve_letters[j].set(on=False)
        else:
           for j in xrange(self._nr_elements+1):
              self._ve_letters[-j].set(on=False)

    def set_standard_screen(self):
        '''
        set screen elements to standard state.
        '''

        self._countdown_screen = False

        # move all elements with their letters to standard position:

        def update(t):
            dt = t/self.animation_time
            for i in xrange(self._nr_elements):
                pos = animate_sigmoid(self._countdown_shape_positions[i], self._centerPos, dt)
                self._ve_shapes[i].set(position=pos) # shapes
                self._ve_letters[self._nr_letters + i].set(position=pos) # level 2 letters
            for i in xrange(self._nr_letters):
                pos = animate_sigmoid(self._countdown_letter_positions[i], self._letter_positions[i], dt)
                self._ve_letters[i].set(position=pos) # level 1 letters

        if self.do_animation:
            self._presentation.set(go_duration=(self.animation_time, 'seconds'))
            self._presentation.add_controller(None,None,FunctionController(during_go_func=update))
        else:
            self._presentation.set(go_duration=(0,'seconds'))

        # send to screen:
        self._presentation.go()
        self._presentation.remove_controller(None,None,None)

        for i in xrange(self._nr_elements):
            self._ve_shapes[i].set(position=self._centerPos, on=False)
            self._ve_letters[self._nr_letters + i].set(position=self._centerPos, on=False)
        for i in xrange(self._nr_letters):
            self._ve_letters[i].set(position=self._letter_positions[i], on=False)

        self._ve_countdown.set(on=False, text=" ")

    def stimulus(self, i_element, on=True):
        '''
        turn on/off the stimulus elements and turn off/on the normal elements.
        '''
        if self._current_level==1:
            self._ve_shapes[i_element].set(on=on)
            for i in xrange(len(self.letter_set[i_element])):
                self._ve_letters[(self._nr_elements-1) * i_element + i].set(on=on and self.level_1_symbols)
        else:
            self._ve_shapes[i_element].set(on=(on and self.level_2_shapes))
            if i_element < len(self.letter_set[self._classified_element]):
                self._ve_letters[self._nr_letters + i_element].set(on=on and self.level_2_symbols, text=(on and self.letter_set[self._classified_element][i_element] or " "))
            else:
                self._ve_letters[self._nr_letters + self._nr_elements-1].set(on=on and self.level_2_symbols, text=(on and self.backdoor_symbol or " "))


    def feedback(self):
        '''
        Show classified element / letter(s).
        '''
        self._show_feedback(True)

        ## present:
        self._presentation.set(go_duration=(self.feedback_duration, 'seconds'))
        self._presentation.go()

        self._show_feedback(False)


    def _show_feedback(self, on=True):
        ## turn on/off feedback box:
        if not self.feedback_show_shape_at_center:
            self._ve_feedback_box.set(on=on)

        if self._current_level == 1:
            ## turn on/off center letter group:
            if not self.feedback_show_shape_at_center:
                for i in xrange(self._nr_elements):
                    self._ve_feedback_letters[i].set(on=on,
                            text=(i<self._nr_elements-1 and
                                self.letter_set[self._classified_element][i] or
                                self.backdoor_symbol))
            if self.feedback_show_shape:
                if self.feedback_show_shape_at_center or not on:
                    pos = self._centerPos
                else:
                    pos = self._countdown_shape_positions[self._classified_element]

                ## turn on/off selected element:
                self._ve_shapes[self._classified_element].set(on=on, position=pos)

                ## turn on/off letters of selected element:
                idx_start = self._classified_element*(self._nr_elements-1)
                idx_end = idx_start + self._nr_elements-1
                for i in xrange(idx_start, idx_end):
                    self._ve_letters[i].set(on=on, position=(on and
                        list(NP.add(pos, self._letter_layout.positions[i % (self._nr_elements-1)])) or
                        self._letter_positions[i]))
        else: ### level 2:
            ## check if backdoor classified:
            if self._classified_letter >= len(self.letter_set[self._classified_element]):
                text = self.backdoor_symbol
            else:
                text = self.letter_set[self._classified_element][self._classified_letter]

            ## turn on/off letter:
            if self.offline or not self.feedback_show_shape_at_center:
                self._ve_feedback_letters[-1].set(on=on,
                        text=text,
                        color=(self.level_2_letter_colors and
                            self.stimuli_colors[self._classified_letter] or
                            self.letter_color))
            if self.feedback_show_shape:
                if self.feedback_show_shape_at_center:
                    pos = self._centerPos
                else:
                    pos = self._countdown_shape_positions[self._classified_element]

                ## turn on/off current element:
                self._ve_shapes[self._classified_letter].set(on=(on and self.level_2_shapes),
                        position=(on and pos or self._centerPos))

                ## turn on/off letter of current element:
                idx = self._nr_letters + self._classified_letter
                self._ve_letters[idx].set(on=on, text=text, position=(on and pos or self._letter_positions[idx]))


    def switch_level(self):

        if self.use_ErrP_detection and self._ErrP_classifier:
            self._ve_feedback_ErrP.set(on=True)
            self._show_feedback(True)
            self._presentation.set(go_duration=(self.feedback_ErrP_duration, 'seconds'))
            self._presentation.go()
            self._ve_feedback_ErrP.set(on=False)
            self._show_feedback(False)
            return

        if self._current_level==1:
            '''level 1: move classified letters to circles '''

            if self.level_2_animation:
                ## turn on all elements:
                for i in xrange(self._nr_elements):
                    self._ve_shapes[i].set(on=self.level_2_shapes, position=self._countdown_shape_positions[i])

                ## animate letters:
                def update(t):
                    dt = t/self.animation_time
                    self._viewport.parameters.stimuli = self._viewport.parameters.stimuli[:-(self._nr_elements)]
                    feedback_letters = []
                    for i in xrange(self._nr_elements):
                        pos = (animate_sigmoid(NP.add(self._letter_layout.positions[i], self._centerPos), self._countdown_shape_positions[i], dt))
                        font_size = int(round(animate(self.font_size_level1, self.font_size_level2, dt)))
                        color = (self.level_2_letter_colors and
                                list(animate(self.letter_color, self.stimuli_colors[i], dt)) or
                                self.letter_color)
                        text = (i==self._nr_elements-1 and
                                self.backdoor_symbol or
                                self.letter_set[self._classified_element][i])
                        feedback_letters.append(Text(position=pos,
                            color=color,
                            font_size=font_size,
                            text=text,
                            anchor="center"))
                        self._viewport.parameters.stimuli.extend(feedback_letters)
                    if self.feedback_show_shape_at_center:
                        pos = animate_sigmoid(self._centerPos, self._countdown_shape_positions[self._classified_element], dt)
                        self._ve_shapes[self._classified_element].set(position=pos)

                # send to screen:
                self._viewport.parameters.stimuli.extend([None]*(self._nr_elements))
                self._presentation.set(go_duration=(self.animation_time, 'seconds'))
                self._presentation.add_controller(None,None,FunctionController(during_go_func=update))
                self._presentation.go()
                self._presentation.remove_controller(None,None,None)
                self._viewport.parameters.stimuli = self._viewport.parameters.stimuli[:-(self._nr_elements)]

                ## turn on level 2 letters:
                for i in xrange(self._nr_elements):
                    text = (i==self._nr_elements-1 and
                            self.backdoor_symbol or
                            self.letter_set[self._classified_element][i])
                    self._ve_letters[self._nr_letters + i].set(on=True,
                            text=text,
                            position=self._countdown_letter_positions[self._nr_letters + i])
            else:
                ## turn on all elements:
                self.set_standard_screen()


        else:
            ''' level 2: move classified letter to wordbox '''

            ## check if backdoor classified:
            if self._classified_letter >= len(self.letter_set[self._classified_element]):
                text = self.backdoor_symbol
            else:
                text = self.letter_set[self._classified_element][self._classified_letter]

            ## animate letter, but not if backdoor classified:
            if self._classified_letter < len(self.letter_set[self._classified_element]):
                def update(t):
                    dt = t/self.animation_time
                    self._viewport.parameters.stimuli = self._viewport.parameters.stimuli[:-1]
                    pos = animate_sigmoid(self._centerPos, self._current_letter_position, dt)
                    color = (self.level_2_letter_colors and
                            list(animate_sigmoid(self.stimuli_colors[self._classified_letter], self.current_letter_color, dt)) or
                            list(animate_sigmoid(self.letter_color, self.current_letter_color, dt)))
                    font_size = int(round(animate(self.font_size_level2, self.font_size_current_letter, dt)))
                    self._viewport.parameters.stimuli.append(Text(position=pos,
                        color=color,
                        font_size=font_size,
                        text=text,
                        anchor='center'))
                # send to screen:
                self._viewport.parameters.stimuli.append(None)
                self._presentation.set(go_duration=(self.animation_time, 'seconds'))
                self._presentation.add_controller(None,None,FunctionController(during_go_func=update))
                self._presentation.go()
                self._presentation.remove_controller(None,None,None)
                self._viewport.parameters.stimuli = self._viewport.parameters.stimuli[:-1]
            else:
                self._presentation.set(go_duration=(self.animation_time, 'seconds'))
                self._presentation.go()

        ## turn off feedback box:
        self._ve_feedback_box.set(on=False)


    def pre__classify(self):
        self._ve_fixationpoint.set(on=True)

    def post__classify(self):
        self._ve_fixationpoint.set(on=False)
Exemple #5
0
    def init_screen_elements(self):
        '''
        Initializing screen elements
        '''

        ## create shapes:
        if self.do_animation:
            for i in xrange(self._nr_elements):
                self._ve_shapes.append(self.registered_shapes[self.shapes[i][0]](
                    position=self._centerPos,
                    color=self.shape_color[i],
                    on=False,
                    **self.shapes[i][1]))


            ## add letters of level 1:
            circle_layout = CircularLayout(nr_elements=self._nr_elements,
                    radius=self.speller_radius,
                    start=NP.pi/6.*5)
            circle_layout.positions.reverse()
            self._letter_layout = CircularLayout(nr_elements=self._nr_elements,
                    radius=self.letter_radius,
                    start=NP.pi/6.*5)
            self._letter_layout.positions.reverse()
            for i in xrange(self._nr_elements):
                # store countdown position:
                self._countdown_shape_positions.append((self._centerPos[0] + circle_layout.positions[i][0],
                    self._centerPos[1] + circle_layout.positions[i][1]))

                # put shape in container:
                self._ve_elements.append(self._ve_shapes[i])

                for j in xrange(len(self.letter_set[i])): # warning: self.letter_set must be at least of length self._nr_elements!!!
                    # store position:
                    self._letter_positions.append((self._letter_layout.positions[j][0] + self._centerPos[0],
                        self._letter_layout.positions[j][1] + self._centerPos[1]))

                    # store countdown position:
                    self._countdown_letter_positions.append((self._letter_layout.positions[j][0] + self._countdown_shape_positions[-1][0],
                        self._letter_layout.positions[j][1] + self._countdown_shape_positions[-1][1]))

                    # add letter:
                    self._ve_letters.append(Text(position=self._letter_positions[-1],
                        text=self.letter_set[i][j],
                        font_size=self.font_size_level1,
                        color=self.letter_color,
                        anchor='center',
                        on=False))

            # add letters of level 2:
            for i in xrange(self._nr_elements):
                self._letter_positions.append(self._centerPos)
                self._countdown_letter_positions.append(self._countdown_shape_positions[i])
                self._ve_letters.append(Text(position=self._centerPos,
                    text=" ",
                    font_size=self.font_size_level2,
                    color=(self.level_2_letter_colors and self.stimuli_colors[i] or self.letter_color),
                    anchor='center',
                    on=False))

                # put letters in container:
            self._ve_elements.extend(self._ve_letters)


            ## create feedback box:
            self._ve_feedback_box = Target2D(position=self._centerPos,
                    size=(self.feedbackbox_size, self.feedbackbox_size),
                    color=self.feedback_color,
                    on=False)

            ## add feedback letters:
            self._ve_feedback_letters = []
            for i in xrange(self._nr_elements):
                self._ve_feedback_letters.append(Text(position=(self._letter_layout.positions[i][0]+self._centerPos[0],
                    self._letter_layout.positions[i][1]+self._centerPos[1]),
                    color=self.letter_color,
                    font_size=self.font_size_level1,
                    text=" ",
                    on=False,
                    anchor="center"))
                self._ve_feedback_letters.append(Text(position=self._centerPos,
                    color=self.letter_color,
                    font_size=self.font_size_level2,
                    text=" ",
                    anchor='center',
                    on=False))

            ## add feedback note (whether or not there was an ErrP detected):
            self._ve_feedback_ErrP = Text(position=self._centerPos,
                    color=self.feedback_ErrP_color,
                    text="X",
                    font_size=self.font_size_feedback_ErrP,
                    anchor='center',
                    on=False)

            ## add fixation point:
            self._ve_fixationpoint = FilledCircle(radius=self.fixationpoint_size,
                    position=self._centerPos,
                    color=self.fixationpoint_color,
                    on=False)

            ##################### IF NOT DO ANIMATION #########################
        else:
            ## add letters of level 1:
            circle_layout = CircularLayout(nr_elements=self._nr_elements,
                    radius=self.speller_radius,
                    start=NP.pi/6.*5)
            self._letter_layout = CircularLayout(nr_elements=self._nr_elements,
                    radius=self.letter_radius,
                    start=NP.pi/6.*5)
            circle_layout.positions.reverse()
            self._letter_layout.positions.reverse()

            for i in xrange(self._nr_elements):
                self._ve_shapes.append(self.registered_shapes[self.shapes[i][0]](
                    position=(self._centerPos[0] + circle_layout.positions[i][0],
                        self._centerPos[1] + circle_layout.positions[i][1]),
                    color=self.shape_color[i],
                    on=False,
                    **self.shapes[i][1]))



            for i in xrange(self._nr_elements):
                # store countdown position:
                self._countdown_shape_positions.append((self._centerPos[0] + circle_layout.positions[i][0],
                    self._centerPos[1] + circle_layout.positions[i][1]))
                # put shape in container:
                self._ve_elements.append(self._ve_shapes[i])

                for j in xrange(len(self.letter_set[i])): # warning: self.letter_set must be at least of length self._nr_elements!!!
                    # store position:
                    self._letter_positions.append((self._letter_layout.positions[j][0] + self._centerPos[0],
                        self._letter_layout.positions[j][1] + self._centerPos[1]))

                    # store countdown position:
                    self._countdown_letter_positions.append((self._letter_layout.positions[j][0] + self._countdown_shape_positions[-1][0],
                        self._letter_layout.positions[j][1] + self._countdown_shape_positions[-1][1]))

                    # add letter:
                    self._ve_letters.append(Text(position=(self._letter_layout.positions[j][0] + self._centerPos[0]+ circle_layout.positions[i][0],
                        self._letter_layout.positions[j][1] + self._centerPos[1]+circle_layout.positions[i][1]),
                        text=self.letter_set[i][j],
                        font_size=self.font_size_level1,
                        color=self.letter_color,
                        anchor='center',
                        on=False))

            # add letters of level 2:
            for i in xrange(self._nr_elements):
                self._letter_positions.append((self._letter_layout.positions[i][0] + self._centerPos[0],
                    self._letter_layout.positions[i][1] + self._centerPos[1]))
                self._countdown_letter_positions.append((self._letter_layout.positions[i][0] + self._countdown_shape_positions[-1][0],
                    self._letter_layout.positions[i][1] + self._countdown_shape_positions[-1][1]))
                self._ve_letters.append(Text(position=(self._letter_layout.positions[i][0] + self._centerPos[0]+ circle_layout.positions[i][0],
                    self._letter_layout.positions[i][1] + self._centerPos[1]+circle_layout.positions[i][1]),
                    text=" ",
                    font_size=self.font_size_level2,
                    color=(self.level_2_letter_colors and self.stimuli_colors[i] or self.letter_color),
                    anchor='center',
                    on=False))

            # put letters in container:
            self._ve_elements.extend(self._ve_letters)


            ## create feedback box:
            self._ve_feedback_box = Target2D(position=self._centerPos,
                    size=(self.feedbackbox_size, self.feedbackbox_size),
                    color=self.feedback_color,
                    on=False)

            ## add feedback letters:
            self._ve_feedback_letters = []
            for i in xrange(self._nr_elements):
                self._ve_feedback_letters.append(Text(position=(self._letter_layout.positions[i][0]+self._centerPos[0],
                    self._letter_layout.positions[i][1]+self._centerPos[1]),
                    color=self.letter_color,
                    font_size=self.font_size_level1,
                    text=" ",
                    on=False,
                    anchor="center"))
                self._ve_feedback_letters.append(Text(position=self._centerPos,
                    color=self.letter_color,
                    font_size=self.font_size_level2,
                    text=" ",
                    anchor='center',
                    on=False))

            ## add feedback note (whether or not there was an ErrP detected):
            self._ve_feedback_ErrP = Text(position=self._centerPos,
                    color=self.feedback_ErrP_color,
                    text="X",
                    font_size=self.font_size_feedback_ErrP,
                    anchor='center',
                    on=False)

            ## add fixation point:
            self._ve_fixationpoint = FilledCircle(radius=self.fixationpoint_size,
                    position=self._centerPos,
                    color=self.fixationpoint_color,
                    on=False)

        # put letters in container:
        self._ve_elements.append(self._ve_feedback_box)
        self._ve_elements.extend(self._ve_feedback_letters)
        self._ve_elements.append(self._ve_feedback_ErrP)
        self._ve_elements.append(self._ve_fixationpoint)
Exemple #6
0
    def __init_screen(self):
        ## create screen:
        if not self.fullscreen:
            os.environ['SDL_VIDEO_WINDOW_POS'] = '%d, %d' % (self.geometry[0], self.geometry[1])
        self._screen = Screen(size=(self.geometry[2], self.geometry[3]),
                              fullscreen=self.fullscreen,
                              bgcolor=self.bg_color,
                              sync_swap=True)
        
        ## create letter box on top:
        self._ve_letterbox = Target2D(position=(self._centerPos[0], self.geometry[3] * (1 - 0.01) - self.letterbox_size[1]/2.),
                                     size=(self.letterbox_size[0], self.letterbox_size[1]),
                                     color=self.phrase_color)
        self._ve_innerbox = Target2D(position=(self._centerPos[0], self.geometry[3] * (1 - 0.01) - self.letterbox_size[1]/2.),
                               size=(self.letterbox_size[0]-6, self.letterbox_size[1]-6),
                               color=self.bg_color)
        
        self._current_letter_position = (self._centerPos[0], self.geometry[3] * (1 - 0.015) - self.letterbox_size[1]/2.)
        self._ve_current_letter = Text(position=self._current_letter_position,
                             text=(len(self._desired_letters[:1])==0 and " " or self._desired_letters[:1]),
                             font_size=self.font_size_current_letter,
                             color=self.current_letter_color,
                             anchor='center')
             
        self._ve_desired_letters = Text(position=(self._centerPos[0] + 5 + self.letterbox_size[0]/2., self._current_letter_position[1]),
                                        text=(len(self._desired_letters[1:])==0 and " " or self._desired_letters[1:]),
                                        font_size=self.font_size_phrase,
                                        color=self.phrase_color,
                                        anchor='left')
        
        self._ve_spelled_phrase = Text(position=(self._centerPos[0] - 5 - self.letterbox_size[0]/2., self._current_letter_position[1]),
                                       text=(len(self._spelled_phrase)==0 and " " or self._spelled_phrase),
                                       font_size=self.font_size_phrase,
                                       color=self.phrase_color,
                                       anchor='right')
        
        ## add word box to elementlist:
        self._ve_elements.extend([self._ve_letterbox, self._ve_innerbox, self._ve_current_letter, self._ve_desired_letters, self._ve_spelled_phrase])


        ## create countdown: 
        self._ve_countdown = Text(position=self._centerPos,
                                  text=" ",
                                  font_size=self.font_size_countdown,
                                  color=self.countdown_color,
                                  anchor='center',
                                  on=False)


        ## create countdown shapes 
        self._ve_countdown_shape=self.countdown_shapes[self.countdown_shape_select](radius=90, 
                                       position=self._centerPos, 
                                       color=self.countdown_shape_color,
                                       on=False)
        
        ## create oscillator circle:
        self._ve_oscillator = FilledCircle(position=(self.osc_size/2+10,self.osc_size/2+10),
                                       radius=self.osc_size/2,
                                       color=self.osc_color,
                                       on=False)
        

        
        ## create shapes and letters:
        self.init_screen_elements()


        
        ## add remaining elements to element list:
        self._ve_elements.extend([self._ve_countdown_shape, self._ve_countdown, self._ve_oscillator])
        
        ## add elements to viewport:
        self._viewport = Viewport(screen=self._screen, stimuli=self._ve_elements)
        self._presentation = Presentation(viewports=[self._viewport],
                                          handle_event_callbacks=[(pygame.KEYDOWN, self.keyboard_input),
                                                                  (pygame.QUIT, self.__stop)])
Exemple #7
0
 def init_screen_elements(self):
     '''
     Initialize screen elements
     '''        
     self._letter_positions = []
     ## create triangles:
     self._letter_layout = CircularLayout(nr_elements=self._nr_elements,
                                    radius=self.letter_radius,
                                    start=NP.pi/6.*5)
     self._letter_layout.positions.reverse()
     a = self.speller_radius / 2.
     b = a * NP.sqrt(3) / 3.
     self._shape_positions = [(self._centerPos[0],     self._centerPos[1] + 2*b),
                              (self._centerPos[0] + a, self._centerPos[1] + b),
                              (self._centerPos[0] + a, self._centerPos[1] - b),
                              (self._centerPos[0],     self._centerPos[1] - 2*b),
                              (self._centerPos[0] - a, self._centerPos[1] - b),
                              (self._centerPos[0] - a, self._centerPos[1] + b)]
     orientaion = [180., 0.,
                   180., 0.,
                   180., 0.]
     for i in xrange(self._nr_elements):
         self._ve_edges.append(FilledTriangle(size=self.speller_radius,
                                              position=self._shape_positions[i],
                                              orientation=orientaion[i],
                                              color=self.edge_color))
         self._ve_shapes.append(FilledTriangle(size=self.speller_radius - self.edge_size,
                                               position=self._shape_positions[i],
                                               orientation=orientaion[i],
                                               color=self.shape_color))
         
         ## add the letters of level 1:
         for j in xrange(len(self.letter_set[i])): # warning: self.letter_set must be at least of length self._nr_elements!!!
             self._letter_positions.append((self._letter_layout.positions[j][0]+self._shape_positions[i][0],
                                            self._letter_layout.positions[j][1]+self._shape_positions[i][1]))
             self._ve_letters.append(Text(position=self._letter_positions[-1],
                                          text=self.letter_set[i][j],
                                          font_size=self.font_size_level1,
                                          color=self.letter_color,
                                          anchor='center'))
     
     ## add letters of level 2:
     for i in xrange(self._nr_elements):
         self._ve_letters.append(Text(position=self._shape_positions[i],
                                      text=" ",
                                      font_size=self.font_size_level2,
                                      color=self.letter_color,
                                      anchor='center',
                                      on=False))
     
     
     ## add fixation point:
     self._ve_fixationpoint = FilledCircle(radius=self.fixationpoint_size,
                                           position=self._centerPos,
                                           color=self.fixationpoint_color)
             
     
     ## create feedback box:
     self._ve_feedback_box = Target2D(position=self._centerPos,
                                      size=(self.feedbackbox_size, self.feedbackbox_size),
                                      color=self.feedback_color,
                                      on=False)
     
     ## add feedback letters:
     self._ve_feedback_letters = []
     for i in xrange(self._nr_elements-1):
         self._ve_feedback_letters.append(Text(position=(self._letter_layout.positions[i][0]+self._centerPos[0],
                                                         self._letter_layout.positions[i][1]+self._centerPos[1]),
                                               color=self.letter_color,
                                               font_size=self.font_size_level1,
                                               text=" ",
                                               on=False,
                                               anchor="center"))
     self._ve_feedback_letters.append(Text(position=self._centerPos,
                                           color=self.letter_color,
                                           font_size=self.font_size_level2,
                                           text=" ",
                                           anchor='center',
                                           on=False))
     
     ## put all in elements container:
     self._ve_elements.extend(self._ve_edges)
     self._ve_elements.extend(self._ve_shapes)
     self._ve_elements.extend(self._ve_letters)
     self._ve_elements.append(self._ve_feedback_box)
     self._ve_elements.extend(self._ve_feedback_letters)
     self._ve_elements.append(self._ve_fixationpoint)
Exemple #8
0
class VisualSpellerVE(MainloopFeedback):
    """
    Visual Speller with six circles like the classical HexOSpell.
    """

    # Triggers: look in Marker
    END_LEVEL1, END_LEVEL2 = 244, 245  # end of hex levels
    COPYSPELLING_FINISHED = 246
    STIMULUS = [[11, 12, 13, 14, 15, 16], [21, 22, 23, 24, 25, 26]]
    RESPONSE = [[51, 52, 53, 54, 55, 56], [61, 62, 63, 64, 65, 66]]
    TARGET_ADD = 20
    ERROR_ADD = 100
    COUNTDOWN_STIMULI = 239
    ERROR_POTENTIAL = 96  # send if error potential is classified

    def init(self):
        """
        initialize parameters
        """
        self.log_filename = "VisualSpellerVE.log"

        self.geometry = [0, 0, 1280, 800]  ## size

        self.letterbox_size = (60, 60)
        self.osc_size = 40
        self.font_size_phrase = 60  # the spelled phrase at the top
        self.font_size_current_letter = 80  # the spelled phrase at the top
        self.font_size_countdown = 150  # number during countdown
        self.desired_phrase = ""

        ## colors:
        self.bg_color = (0.0, 0.0, 0.0)
        self.phrase_color = (0.2, 0.0, 1.0)
        self.current_letter_color = (1.0, 0.0, 0.0)
        self.countdown_color = (0.2, 0.0, 1.0)
        self.osc_color = (1, 1, 1)

        self.letter_set = [
            ["A", "B", "C", "D", "E"],
            ["F", "G", "H", "I", "J"],
            ["K", "L", "M", "N", "O"],
            ["P", "Q", "R", "S", "T"],
            ["U", "V", "W", "X", "Y"],
            ["Z", "_", ".", ",", "<"],
        ]
        self.fullscreen = False
        self.use_oscillator = True
        self.offline = True
        self.copy_spelling = True  # in copy-spelling mode, selection of the target symbol is forced
        self.debug = False
        self.nCountdown = 5
        self.nr_sequences = 6
        self.randomize_sequence = True  # set to False to present a fixed stimulus sequence
        self.min_dist = 2  # Min number of intermediate flashes bef. a flash is repeated twice

        self.stimulus_duration = 0.083  # 5 frames @60 Hz = 83ms flash
        self.interstimulus_duration = 0.1
        self.animation_time = 1
        self.wait_before_classify = 1.0
        self.feedback_duration = 1.0
        self.feedback_ErrP_duration = 1.0
        self.wait_after_start = 0.0

        # Countdown options
        self.do_animation = True
        self.synchronized_countdown = True
        if self.synchronized_countdown:
            self.do_animation = False

        self.countdown_level1 = True
        self.countdown_level2 = True

        self.countdown_shapes = {"circle": FilledCircle, "hexagon": FilledHexagon}
        self.countdown_shape_select = "hexagon"
        self.countdown_shape_color = (0.7, 0.7, 0.7)
        self.countdown_shape_on = True
        self.countdown_blinking_nr = 5  # number of pre-sequence stimuli(1 sec is approx. 5 frames at 60 Hz)

        self.wait_after_early_stopping = 3  # sec
        self.abort_trial = False
        self.output_per_stimulus = True
        self.use_ErrP_detection = False

        self.serialtrigger = False
        self.serialport = serialport.SerialPort(13)
        self.send_parallel_bak = self.send_parallel

        if self.debug:
            msg = "!!! YOU'RE IN DEBUG MODE! CLASSIFICATION WILL BE RANDOM OR KEYBOARD CONTROLLED !!!"
            self.logger.warning(msg)

    def pre_mainloop(self):

        ## logging
        assert len(self.log_filename) != 0  # 'log_filename' must not be empty string!
        logger.setLevel(logging.ERROR)
        handler = logging.FileHandler(self.log_filename, "w")
        handler.setLevel(logging.INFO)
        formatter = logging.Formatter("%(asctime)s: %(message)s")
        handler.setFormatter(formatter)
        self.logger.setLevel(logging.INFO)
        self.logger.addHandler(handler)

        self._nr_elements = 6
        self._idx_backdoor = 5
        self._init_classifier_output()
        self._classified_element = -1
        self._classified_letter = -1
        for s in self.desired_phrase:
            assert s in [l for ls in self.letter_set for l in ls]  # invalid letters in desired phrase!
        self._spelled_phrase = ""
        self._spelled_letters = ""
        self._desired_letters = self.desired_phrase
        self._copyspelling_finished = False
        #        if self.offline:
        #            self.__idle()                # In offline mode: set the first to-be-spelled letter

        self._spellerHeight = self.geometry[3] - self.letterbox_size[1]
        self._centerPos = (self.geometry[2] / 2.0, self._spellerHeight / 2.0)

        self._nr_letters = 0
        for i in xrange(len(self.letter_set)):
            self._nr_letters += len(self.letter_set[i])

        self._current_level = 1  # Index of current level
        self._current_sequence = 0  # Index of current sequence
        self._current_stimulus = 0  # Index of current stimlus
        self._current_countdown = self.nCountdown
        self.random = random.Random(clock())
        self._debug_classified = None

        ## init states:
        self._state_countdown = True
        if not self.countdown_level1:
            self._state_countdown = False
            self._state_trial = True
        else:
            # self._state_countdown = not self.offline
            self._state_trial = False

        self._state_classify = False
        self._state_feedback = False
        self._state_abort = False

        ## init containers for VE elements:
        self._ve_elements = []

        ## oscillator state:
        if not self.use_oscillator:
            self.osc_color = self.bg_color
            self.osc_size = 0

        ## call subclass-specific pre_mainloop:
        self.prepare_mainloop()

        ## build screen elements:
        self.__init_screen()
        if self.offline:
            self.__idle()
        if self.abort_trial:
            """
            Start listener for abort_trial event eg.
            """

        ## delay after play (might be useful for filters...)
        pygame.time.wait(int(self.wait_after_start * 1000))
        self.logger.info("waiting %d seconds after play." % self.wait_after_start)

        ## send start trigger:
        self.send_parallel(marker.RUN_START)
        self.logger.info("[TRIGGER] %d" % marker.RUN_START)

        ## error potential classifier:
        self._ErrP_classifier = None

    def on_interaction_event(self, data):
        self.logger.debug("interaction event")
        serial = data.get("serialtrigger", None)
        if serial is None:
            return
        if serial:
            self.logger.debug("using serial port")
            self.send_parallel = self.serialport.send
        else:
            self.logger.debug("using parallel port")
            self.send_parallel = self.send_parallel_bak

    def post_mainloop(self):
        """
        Sends end marker to parallel port.
        """

        if self.abort_trial:
            """
            Stop listener for abort_trial event
           """
            pass

        pygame.time.wait(500)
        self.send_parallel(marker.RUN_END)
        self.logger.info("[TRIGGER] %d" % marker.RUN_END)
        pygame.time.wait(500)
        self._presentation.set(quit=True)
        self._screen.close()

    def __init_screen(self):
        ## create screen:
        if not self.fullscreen:
            os.environ["SDL_VIDEO_WINDOW_POS"] = "%d, %d" % (self.geometry[0], self.geometry[1])
        self._screen = Screen(
            size=(self.geometry[2], self.geometry[3]), fullscreen=self.fullscreen, bgcolor=self.bg_color, sync_swap=True
        )

        ## create letter box on top:
        self._ve_letterbox = Target2D(
            position=(self._centerPos[0], self.geometry[3] * (1 - 0.01) - self.letterbox_size[1] / 2.0),
            size=(self.letterbox_size[0], self.letterbox_size[1]),
            color=self.phrase_color,
        )
        self._ve_innerbox = Target2D(
            position=(self._centerPos[0], self.geometry[3] * (1 - 0.01) - self.letterbox_size[1] / 2.0),
            size=(self.letterbox_size[0] - 6, self.letterbox_size[1] - 6),
            color=self.bg_color,
        )

        self._current_letter_position = (
            self._centerPos[0],
            self.geometry[3] * (1 - 0.015) - self.letterbox_size[1] / 2.0,
        )
        self._ve_current_letter = Text(
            position=self._current_letter_position,
            text=(len(self._desired_letters[:1]) == 0 and " " or self._desired_letters[:1]),
            font_size=self.font_size_current_letter,
            color=self.current_letter_color,
            anchor="center",
        )

        self._ve_desired_letters = Text(
            position=(self._centerPos[0] + 5 + self.letterbox_size[0] / 2.0, self._current_letter_position[1]),
            text=(len(self._desired_letters[1:]) == 0 and " " or self._desired_letters[1:]),
            font_size=self.font_size_phrase,
            color=self.phrase_color,
            anchor="left",
        )

        self._ve_spelled_phrase = Text(
            position=(self._centerPos[0] - 5 - self.letterbox_size[0] / 2.0, self._current_letter_position[1]),
            text=(len(self._spelled_phrase) == 0 and " " or self._spelled_phrase),
            font_size=self.font_size_phrase,
            color=self.phrase_color,
            anchor="right",
        )

        # if we're in free spelling mode, we hide all text fields but
        # the _ve_spelled_phrase. we also need a multiline
        # _ve_spelled_phrase instead of the single lined one
        if self.offline == self.copy_spelling == False:
            self._spelled_phrase = "   "
            self._ve_spelled_phrase = WrappedText(
                position=(0, self._current_letter_position[1]),
                text=(len(self._spelled_phrase) == 0 and " " or self._spelled_phrase),
                font_size=self.font_size_phrase,
                color=self.phrase_color,
                size=(float(self.geometry[2]), float(self.geometry[3])),
            )
            for i in self._ve_letterbox, self._ve_innerbox, self._ve_current_letter, self._ve_desired_letters:
                i.set(on=False)

        ## add word box to elementlist:
        self._ve_elements.extend(
            [
                self._ve_letterbox,
                self._ve_innerbox,
                self._ve_current_letter,
                self._ve_desired_letters,
                self._ve_spelled_phrase,
            ]
        )

        ## create countdown:
        self._ve_countdown = Text(
            position=self._centerPos,
            text=" ",
            font_size=self.font_size_countdown,
            color=self.countdown_color,
            anchor="center",
            on=False,
        )

        ## create countdown shapes
        self._ve_countdown_shape = self.countdown_shapes[self.countdown_shape_select](
            radius=90, position=self._centerPos, color=self.countdown_shape_color, on=False
        )

        ## create oscillator circle:
        self._ve_oscillator = FilledCircle(
            position=(self.osc_size / 2 + 10, self.osc_size / 2 + 10),
            radius=self.osc_size / 2,
            color=self.osc_color,
            on=False,
        )

        ## create shapes and letters:
        self.init_screen_elements()

        ## add remaining elements to element list:
        self._ve_elements.extend([self._ve_countdown_shape, self._ve_countdown, self._ve_oscillator])

        ## add elements to viewport:
        self._viewport = Viewport(screen=self._screen, stimuli=self._ve_elements)
        self._presentation = Presentation(
            viewports=[self._viewport],
            handle_event_callbacks=[(pygame.KEYDOWN, self.keyboard_input), (pygame.QUIT, self.__stop)],
        )

    def play_tick(self):
        """
        called every loop, if in play mode.
        """

        self.pre_play_tick()

        if self._state_countdown:
            self.pre__countdown()
            self.__countdown()
            self.post__countdown()
        elif self._state_trial:
            self.pre__trial()
            self.__trial()
            self.post__trial()
        elif self._state_classify:
            self.pre__classify()
            self.__classify()
            self.post__classify()
        elif self._state_feedback:
            self.pre__feedback()
            self.__feedback()
            self.post__feedback()
        elif self._state_abort:
            self.__abort()
            self.post__abort()
        else:
            self.pre__idle()
            self.__idle()
            self.post__idle()
        self.post_play_tick()

    def __stop(self, *args):
        self.on_stop()

    def __idle(self):
        if self.offline and len(self._desired_letters) > 0:
            # add new letter:
            for e in xrange(len(self.letter_set)):
                for l in xrange(len(self.letter_set[e])):
                    if self._desired_letters[0] == self.letter_set[e][l]:
                        self._classified_element = e
                        self._classified_letter = l
            if self.countdown_level1:
                self._state_countdown = True
            else:
                self._state_trial = True
        else:
            ## otherwise just wait until a new letter is sent:
            self._presentation.set(go_duration=(0.1, "seconds"))
            self._presentation.go()

    def __countdown(self):
        def blink():
            i = 0
            while i < self.countdown_blinking_nr:
                self._ve_countdown_shape.set(on=True)
                self._ve_countdown.set(text="%d" % self._current_countdown, on=True)
                self.send_parallel(self.COUNTDOWN_STIMULI)
                self.logger.info("[TRIGGER] %d" % self.COUNTDOWN_STIMULI)
                self._presentation.set(go_duration=(self.stimulus_duration, "seconds"))
                self._presentation.go()
                self._ve_countdown_shape.set(on=False)
                self._ve_countdown.set(on=False)
                self._presentation.set(go_duration=(self.interstimulus_duration, "seconds"))
                self._presentation.go()
                i = i + 1

        if self._current_countdown == self.nCountdown:
            self.send_parallel(marker.COUNTDOWN_START)
            self.logger.info("[TRIGGER] %d" % marker.COUNTDOWN_START)
            self.set_countdown_screen()
            self._ve_countdown.set(on=True)
            self._ve_countdown_shape.set(on=self.countdown_shape_on)
            self._presentation.set(go_duration=(1, "seconds"))

        self._ve_countdown.set(text="%d" % self._current_countdown)
        self._presentation.go()
        self._current_countdown = (self._current_countdown - 1) % self.nCountdown

        if self.synchronized_countdown and self._current_countdown == 1:
            self.set_synchronized_countdown_screen()
            blink()
            self._current_countdown = self.nCountdown
            self.set_standard_screen()
            self._state_countdown = False
            self._state_trial = True
            self._ve_countdown.set(on=False)
            self._ve_countdown_shape.set(on=False)
            self._ve_countdown.set(color=self.countdown_color)

        if self._current_countdown == 0:
            # Executed only if self.synchronized_countdown = False
            self._current_countdown = self.nCountdown
            self.set_standard_screen()
            pygame.time.wait(10)
            self._state_countdown = False
            self._state_trial = True
            self._ve_countdown_shape.set(on=False)
            self._ve_countdown.set(on=False)
            self._ve_countdown.set(color=self.countdown_color)

    def __trial(self):

        if self._current_sequence == 0 and self._current_stimulus == 0:

            # level 1 animation when there is no countdown
            if self._current_level == 1 and not self.countdown_level1:
                if self.do_animation:
                    self.set_countdown_screen()
                self.set_standard_screen()
            # generate random sequences:
            if self.randomize_sequence:
                self.flash_sequence = []
                for _ in range(self.nr_sequences):
                    random_flash_sequence(
                        self, set=range(self._nr_elements), min_dist=self.min_dist, seq_len=self._nr_elements
                    )
            # or else use fixed sequence:
            else:
                self.flash_sequence = range(self._nr_elements)

        if self.randomize_sequence:
            currentStimulus = self.flash_sequence[self._current_sequence * self._nr_elements + self._current_stimulus]
        else:
            currentStimulus = self.flash_sequence[self._current_stimulus]
        # set stimulus:
        self.stimulus(currentStimulus, True)
        # self._ve_oscillator.set(on=True)

        if self.abort_trial and self.abort_trial_check():
            # restart trial on abort_trial event:
            self._state_trial = False
            self._state_abort = True
            return

        # check if current stimulus is target and then send trigger:
        target_add = 0
        if len(self._desired_letters) > 0:
            if self._current_level == 1:
                if self._desired_letters[:1] in self.letter_set[currentStimulus]:
                    # current stimulus is target group:
                    target_add = self.TARGET_ADD
            else:
                if currentStimulus == self._idx_backdoor:
                    # current stimulus is backdoor:
                    if not self._desired_letters[:1] in self.letter_set[self._classified_element]:
                        # we are in the wrong group. backdoor is target:
                        target_add = self.TARGET_ADD
                else:
                    # current stimulus is no backdoor:
                    if self._desired_letters[:1] == self.letter_set[self._classified_element][currentStimulus]:
                        # current stimulus is target symbol:
                        target_add = self.TARGET_ADD

        self.send_parallel(self.STIMULUS[self._current_level - 1][currentStimulus] + target_add)
        self.logger.info("[TRIGGER] %d" % (self.STIMULUS[self._current_level - 1][currentStimulus] + target_add))

        # present stimulus:
        self._presentation.set(go_duration=(self.stimulus_duration, "seconds"))
        self._presentation.go()

        # reset to normal:
        self._ve_oscillator.set(on=False)
        self.stimulus(currentStimulus, False)

        # present interstimulus:
        self._presentation.set(go_duration=(self.interstimulus_duration, "seconds"))
        self._presentation.go()

        if self.debug:
            self.on_control_event({"cl_output": (self.random.random(), currentStimulus + 1)})

        ## TODO: check here for classification !!!!
        if self.output_per_stimulus:
            # increase
            self._current_stimulus = (self._current_stimulus + 1) % self._nr_elements
            if self._current_stimulus == 0:
                self._current_sequence = (self._current_sequence + 1) % self.nr_sequences

            # check for end of trial:
            if self._current_sequence == 0 and self._current_stimulus == 0:

                # send trigger:
                if self._current_level == 1:
                    self.send_parallel(self.END_LEVEL1)
                    self.logger.info("[TRIGGER] %d" % self.END_LEVEL1)
                else:
                    self.send_parallel(self.END_LEVEL2)
                    self.logger.info("[TRIGGER] %d" % self.END_LEVEL2)

                # decide how to continue:
                self._state_trial = False
                self._state_classify = True
        else:
            # increase
            self._current_stimulus = (self._current_stimulus + 1) % self._nr_elements
            if self._current_stimulus == 0:
                self._current_sequence = (self._current_sequence + 1) % self.nr_sequences
                if self.check_classification(self._current_sequence + 1):
                    self._state_trial = False
                    self._state_classify = True
                    pygame.time.wait(self.wait_after_early_stopping * 1000)

            if self._current_sequence == 0 and self._current_stimulus == 0:

                # send trigger:
                if self._current_level == 1:
                    self.send_parallel(self.END_LEVEL1)
                    self.logger.info("[TRIGGER] %d" % self.END_LEVEL1)
                else:
                    self.send_parallel(self.END_LEVEL2)
                    self.logger.info("[TRIGGER] %d" % self.END_LEVEL2)

                # decide how to continue:
                self._state_trial = False
                self._state_classify = True

    def check_classification(self, nr):
        # print self._classifier_output
        means = [None] * self._nr_elements
        minimum = maxint
        classified = None
        for ii in range(self._nr_elements):
            means[ii] = sum(self._classifier_output[ii]) / nr
            if means[ii] < minimum:
                minimum = means[ii]
                classified = ii + 1

        print "\n**** Class: %d (mean=%f)\n" % (classified, means[classified - 1])
        return classified

    def __classify(self):

        ## wait until all classifier outputs are received:
        self._presentation.set(go_duration=(self.wait_before_classify, "seconds"))
        self._presentation.go()
        if self.offline:
            if self._current_level == 1:
                classified = self._classified_element
            else:
                classified = self._classified_letter
        elif not self._debug_classified == None:
            classified = self._debug_classified
            self._debug_classified = None
        else:
            if self.output_per_stimulus:
                nClassified = sum([len(self._classifier_output[i]) for i in xrange(self._nr_elements)])
                if nClassified < self._nr_elements * self.nr_sequences:
                    pygame.time.wait(20)
                    print "not enough classifier-outputs received! (something may be wrong)"
                    return

                ## classify and set output:
                means = [None] * self._nr_elements
                minimum = maxint
                classified = None
                for ii in range(self._nr_elements):
                    means[ii] = sum(self._classifier_output[ii]) / self.nr_sequences
                    if means[ii] < minimum:
                        minimum = means[ii]
                        classified = ii
                print "\n**** Class: %d (mean=%f)\n" % (classified + 1, means[classified])
            else:
                means = [None] * self._nr_elements
                minimum = maxint
                classified = None
                for ii in range(self._nr_elements):
                    means[ii] = sum(self._classifier_output[ii]) / self.nr_sequences
                    if means[ii] < minimum:
                        minimum = means[ii]
                        classified = ii
                print "\n**** Class: %d (mean=%f)\n" % (classified + 1, means[classified])

            ## Reset classifier output to empty lists
            self._init_classifier_output()

        error_add = 0
        ## evaluate classification:
        if self._current_level == 1:
            self._classified_element = classified
            if len(self._desired_letters) > 0 and not self._desired_letters[:1] in self.letter_set[classified]:
                # wrong group selected:
                error_add = self.ERROR_ADD
        else:
            self._classified_letter = classified
            if self._classified_letter == self._idx_backdoor:
                ## backdoor classified:
                if (
                    len(self._desired_letters) > 0
                    and self._desired_letters[:1] in self.letter_set[self._classified_element]
                ):
                    # backdoor selection wrong:
                    error_add = self.ERROR_ADD
            else:
                ## no backdoor classified:
                spelled_letter = self.letter_set[self._classified_element][self._classified_letter]
                if len(self._desired_letters) > 0 and spelled_letter != self._desired_letters[:1]:
                    # wrong letter spelled:
                    error_add = self.ERROR_ADD

        ## send response trigger:
        self.send_parallel(self.RESPONSE[self._current_level - 1][classified] + error_add)
        self.logger.info("[TRIGGER] %d" % (self.RESPONSE[self._current_level - 1][classified] + error_add))

        self._state_classify = False
        self._state_feedback = True

    def __feedback(self):
        self._state_feedback = False

        ## call subclass method:
        self.feedback()

        ## check ErrP classification:
        if self.use_ErrP_detection:
            t = 0
            while self._ErrP_classifier is None and t < 1000:
                t += 50
                pygame.time.wait(50)
            if self._ErrP_classifier is None:
                print "no ErrP classifier received! "
            if self._ErrP_classifier:
                self.send_parallel(self.ERROR_POTENTIAL)
                self.logger.info("[TRIGGER] %d" % (self.ERROR_POTENTIAL))

        ## call subclass method:
        if not self.countdown_level2:
            self.switch_level()

        ## update phrases:
        if (
            (self._current_level == 2)
            and (  # only update, if we are at the end of level 2,
                self._classified_letter != self._idx_backdoor or self.copy_spelling
            )
            and (  # if copyspelling off, we have no backdoor selected
                self._ErrP_classifier is None or not self._ErrP_classifier
            )
        ):  # no ErrP was detected (or ErrP detection is off)
            spelled_letter = ""
            if self.copy_spelling:
                ## in copy spelling we force the desired letter to be spelled
                if len(self._desired_letters) > 0:
                    spelled_letter = self._desired_letters[:1]
                else:
                    print "??? moved beyond desired phrase in copy spelling ???"
            else:
                spelled_letter = self.letter_set[self._classified_element][self._classified_letter]

            ## update desired phrase:
            if len(self._desired_letters) > 0:
                if spelled_letter == self._desired_letters[:1]:
                    # correct letter spelled:
                    self._desired_letters = self._desired_letters[1:]  # remove first letter
                else:
                    # wrong letter spelled:
                    if spelled_letter == "<":
                        self._desired_letters = self._spelled_phrase[-1:] + self._desired_letters
                    else:
                        self._desired_letters = "<" + self._desired_letters
                if len(self._desired_letters) == 0:
                    self._copyspelling_finished = True

            ## update spelled phrase:
            self._spelled_letters += spelled_letter
            if spelled_letter == "<":
                self._spelled_phrase = self._spelled_phrase[:-1]
            else:
                self._spelled_phrase += spelled_letter

            ## update screen phrases:
            self.logger.info("Current Phrase:")
            self.logger.info(self._spelled_phrase)
            self._ve_spelled_phrase.set(text=(len(self._spelled_phrase) == 0 and " " or self._spelled_phrase))
            self._ve_current_letter.set(text=(len(self._desired_letters[:1]) == 0 and " " or self._desired_letters[:1]))
            self._ve_desired_letters.set(
                text=(len(self._desired_letters[1:]) == 0 and " " or self._desired_letters[1:])
            )

        if self.use_ErrP_detection and self._ErrP_classifier:
            self._state_trial = True

        else:
            if self._current_level == 1:
                # continue with level2 trial:
                if self.countdown_level2:
                    self._state_countdown = True
                else:
                    self._state_trial = True

            elif not self.offline:
                # start countdown
                if self.countdown_level1:
                    self._state_countdown = True
                else:
                    self._state_trial = True

            # set new level:
            self._current_level = 3 - self._current_level

        ## reset ErrP_classifier:
        self._ErrP_classifier = None

        # check copyspelling:
        if self._copyspelling_finished:
            self._copyspelling_finished = False
            self.on_control_event({"print": 0})  # print desired phrase
            self.on_control_event({"print": 1})  # print spelled phrase
            self.on_control_event({"print": 2})  # print all spelled letters
            self.send_parallel(self.COPYSPELLING_FINISHED)
            self.logger.info("[TRIGGER] %d" % (self.COPYSPELLING_FINISHED))
            pygame.time.wait(50)

    def __abort(self):

        # play warning sound

        def sine_array_onecycle(hz, peak, sample_rate):
            length = sample_rate / float(hz)
            omega = NP.pi * 2 / length
            xvalues = NP.arange(int(length)) * omega
            return peak * NP.sin(xvalues)

        def sine_array(hz, peak, samples_rate):
            return NP.resize(sine_array_onecycle(hz, peak, sample_rate), (sample_rate,))

        sample_rate = 44100
        pygame.mixer.init(sample_rate, -16, 2)  # 44.1kHz, 16-bit signed, stereo
        f = sine_array(8000, 1, sample_rate)
        f = NP.array(zip(f, f))
        sound = pygame.sndarray.make_sound(f)
        channel = sound.play(-1)
        channel.set_volume(0.2, 0.2)
        pygame.time.delay(1000)
        sound.stop()

        if self._current_level == 1 and self.countdown_level1:
            self._state_countdown = True
        elif self._current_level == 2 and self.countdown_level2:
            self._state_countdown = True
        else:
            self._state_trial = True

        self._init_classifier_output()

    def _init_classifier_output(self):
        ## Empty lists
        self._classifier_output = [list() for _ in xrange(self._nr_elements)]

    def abort_trial_check(self):
        """
        Check if event is an abort trial event
        """
        return False

    def keyboard_input(self, event):
        if event.key == pygame.K_ESCAPE:
            self.on_stop()
        elif event.key == pygame.K_KP_ENTER:
            self.on_control_event({"print": 0})  # print desired phrase
            self.on_control_event({"print": 1})  # print spelled phrase
            self.on_control_event({"print": 2})  # print all spelled letters
        elif event.key == pygame.K_DELETE:
            # The DELETE key empties the spelled text shown this works
            # only in free spelling mode (i.e. offline and copy_spelling
            # are set to False)
            if self.offline == self.copy_spelling == False:
                self.logger.info("Clearing Text.")
                self._spelled_phrase = "   "
        elif self.debug:
            if (
                (event.key >= pygame.K_a and event.key <= pygame.K_z)
                or (event.key == pygame.K_LESS)
                or (event.key == pygame.K_PERIOD)
                or (event.key == pygame.K_COMMA)
            ):
                self.on_control_event({"new_letter": chr(event.key).upper()})
            elif event.key == pygame.K_MINUS:
                self.on_control_event({"new_letter": chr(pygame.K_UNDERSCORE)})
            elif event.key == pygame.K_BACKSPACE:
                self.on_control_event({"new_letter": chr(pygame.K_LESS)})
            elif event.key == pygame.K_SPACE:
                self.on_control_event({"new_letter": chr(pygame.K_UNDERSCORE)})
            elif event.key == pygame.K_UP and self.use_ErrP_detection:
                self.on_control_event({"cl_output": (1, 7)})
            elif event.key == pygame.K_DOWN and self.use_ErrP_detection:
                self.on_control_event({"cl_output": (0, 7)})
            if not self.offline:
                if event.key >= pygame.K_0 and event.key <= pygame.K_5:
                    self._debug_classified = int(chr(event.key))
                elif event.key >= pygame.K_KP0 and event.key <= pygame.K_KP5:
                    self._debug_classified = int(chr(event.key - 208))

    def on_control_event(self, data):
        self.logger.info("[CONTROL_EVENT] %s" % str(data))
        if data.has_key(u"cl_output"):
            # classification output was sent:
            score_data = data[u"cl_output"]
            cl_out = score_data[0]
            iSubstim = int(score_data[1])  # evt auch "Subtrial"
            if iSubstim in range(1, 7):
                self._classifier_output[iSubstim - 1].append(cl_out)
            elif self.use_ErrP_detection:
                self._ErrP_classifier = cl_out
        elif data.has_key("new_letter"):
            # get new letter to spell:
            self._desired_letters += data["new_letter"]
            self._ve_current_letter.set(text=(len(self._desired_letters[:1]) == 0 and " " or self._desired_letters[:1]))
            self._ve_desired_letters.set(
                text=(len(self._desired_letters[1:]) == 0 and " " or self._desired_letters[1:])
            )

        elif data.has_key(u"print"):
            if data[u"print"] == 0:
                self.logger.info("[DESIRED_PHRASE] %s" % self.desired_phrase)
            elif data[u"print"] == 1:
                self.logger.info("[SPELLED_PHRASE] %s" % self._spelled_phrase)
            elif data[u"print"] == 2:
                self.logger.info("[SPELLED_LETTERS] %s" % self._spelled_letters)

    """
    ==========================
    == METHODS TO OVERLOAD: ==
    ==========================
    """

    def init_screen_elements(self):
        """
        overwrite this function in subclass.
        """
        pass

    def prepare_mainloop(self):
        """
        overwrite this function in subclass.
        """
        pass

    def set_countdown_screen(self):
        """
        set screen how it should look during countdown.
        overwrite this function in subclass.
        """
        pass

    def set_standard_screen(self):
        """
        set screen elements to standard state.
        overwrite this function in subclass.
        """
        pass

    def set_synchronized_countdown_screen(self):
        """
        set screen elements to for the synchronized countdown.
        overwrite this function in subclass.
        """
        pass

    def stimulus(self, i_element, on=True):
        """
        turn on/off the stimulus elements and turn off/on the normal elements.
        overwrite this function in subclass.
        """
        pass

    def feedback(self):
        """
        set screen how it should look during feedback presentation.
        overwrite this function in subclass.
        """
        pass

    def switch_level(self):
        """
        overwrite this function in subclass.
        """
        pass

    def pre_play_tick(self):
        pass

    def post_play_tick(self):
        pass

    def pre__countdown(self):
        pass

    def post__countdown(self):
        pass

    def pre__trial(self):
        pass

    def post__trial(self):
        pass

    def pre__classify(self):
        pass

    def post__classify(self):
        pass

    def pre__feedback(self):
        pass

    def post__feedback(self):
        pass

    def pre__idle(self):
        pass

    def post__idle(self):
        pass

    def post__abort(self):
        pass
class HexoSpellerVE(VisualSpellerVE):
    '''
    Visual Speller with six circles like the classical HexOSpell.
    '''

    def init(self):
        '''
        initialize parameters
        '''
        VisualSpellerVE.init(self)
        
        ## sizes:
        self.circle_radius = 100
        self.edge_size = 2
        self.speller_radius = 260
        self.fixationpoint_size = 4.0
        self.font_size_level1 = 70         # letters in level 1
        self.font_size_level2 = 130         # letters in level 2
        self.feedbackbox_size = 200.0
        
        ## colors:
        self.shape_color = (0.0, 0.0, 0.0)
        self.edge_color = (1.0, 1.0, 1.0)
        self.stimuli_colors = [(1.0, 0.0, 0.0),
                               (0.0, 1.0, 0.0),
                               (0.0, 0.0, 1.0),
                               (1.0, 1.0, 0.0),
                               (1.0, 0.0, 1.0),
                               (0.0, 1.0, 1.0)]
        self.letter_color = (1.0, 1.0, 1.0)
        self.letter_stimulus_color = (0.0, 0.0, 0.0)
        self.fixationpoint_color = (1.0, 1.0, 1.0)
        self.feedback_color = (0.7, 0.7, 0.7)
        
        ## stimulus types:
        self.stimulustype_color = True
        self.stimulustype_flash = True
        self.flash_size_factor = 1.2
        

        self.countdown_shape_select='circle'

    def prepare_mainloop(self):
        '''
        called in pre_mainloop of superclass.
        '''        
        if not self.stimulustype_flash:
            self.flash_size_factor = 1.0
            
        if self.stimulustype_color:
            assert len(self.stimuli_colors)==self._nr_elements
        else:
            self.stimuli_colors = [self.shape_color]*self._nr_elements
            
        ## init containers for VE elements:
        self._ve_shapes = []
        self._ve_edges = []
        self._ve_letters = []
        
        self._edge_radius = self.circle_radius
        self._circle_radius = self.circle_radius - self.edge_size


    
    def init_screen_elements(self):
        '''
        Initializing screen elements
        '''
        self._letter_positions = []        
        ## create and place the circles and letters:
        circle_layout = CircularLayout(nr_elements=self._nr_elements,
                                       radius=self.speller_radius,
                                       start=NP.pi/6.*5)
        circle_layout.positions.reverse()
        self._letter_layout = CircularLayout(nr_elements=self._nr_elements,
                                       radius=self.circle_radius*0.65,
                                       start=NP.pi/6.*5)
        self._letter_layout.positions.reverse()
        self._shape_positions = [(x+self.geometry[2]/2, y+self._spellerHeight/2) for (x,y) in circle_layout.positions]
        # add the standard elements:
        for i in xrange(self._nr_elements):
            self._ve_edges.append(FilledCircle(radius=self._edge_radius,
                                                position=self._shape_positions[i],
                                                color=self.edge_color))
            self._ve_shapes.append(FilledCircle(radius=self._circle_radius,
                                                position=self._shape_positions[i],
                                                color=self.shape_color))
            # add the letters of level 1:
            for j in xrange(len(self.letter_set[i])): # warning: self.letter_set must be at least of length self._nr_elements!!!
                self._letter_positions.append((self._letter_layout.positions[j][0]+self._shape_positions[i][0],
                                               self._letter_layout.positions[j][1]+self._shape_positions[i][1]))
                self._ve_letters.append(Text(position=self._letter_positions[-1],
                                             text=self.letter_set[i][j],
                                             font_size=self.font_size_level1,
                                             color=self.letter_color,
                                             anchor='center'))
        
        # add the stimuli letters of level 1:
        letter_layout2 = CircularLayout(nr_elements=self._nr_elements,
                                       radius=self.circle_radius*0.65*self.flash_size_factor,
                                       start=NP.pi/6.*5)
        letter_layout2.positions.reverse()
        for i in xrange(self._nr_elements):
            for j in xrange(len(self.letter_set[i])): # warning: self.letter_set must be at least of length self._nr_elements!!!
                self._letter_positions.append((letter_layout2.positions[j][0]+self._shape_positions[i][0],
                                               letter_layout2.positions[j][1]+self._shape_positions[i][1]))
                self._ve_letters.append(Text(position=self._letter_positions[-1],
                                             text=self.letter_set[i][j],
                                             font_size=int(NP.floor(self.font_size_level1*self.flash_size_factor)),
                                             color=self.letter_stimulus_color,
                                             anchor='center',
                                             on=False))
        
        # add letters of level 2:
        for i in xrange(self._nr_elements):
            self._ve_letters.append(Text(position=self._shape_positions[i],
                                         text=" ",
                                         font_size=self.font_size_level2,
                                         color=self.letter_color,
                                         anchor='center',
                                         on=False))
            
        # add stimuli letters of level 2:
        for i in xrange(self._nr_elements):
            self._ve_letters.append(Text(position=self._shape_positions[i],
                                         text=" ",
                                         font_size=int(NP.floor(self.font_size_level2*self.flash_size_factor)),
                                         color=self.letter_stimulus_color,
                                         anchor='center',
                                         on=False))
            
        ## add fixation point:
        self._ve_fixationpoint = FilledCircle(radius=self.fixationpoint_size,
                                              position=self._centerPos,
                                              color=self.fixationpoint_color)
                
        
        ## create feedback box:
        self._ve_feedback_box = Target2D(position=self._centerPos,
                                         size=(self.feedbackbox_size, self.feedbackbox_size),
                                         color=self.feedback_color,
                                         on=False)
        
        ## add feedback letters:
        self._ve_feedback_letters = []
        for i in xrange(self._nr_elements-1):
            self._ve_feedback_letters.append(Text(position=(self._letter_layout.positions[i][0]+self._centerPos[0],
                                                            self._letter_layout.positions[i][1]+self._centerPos[1]),
                                                  color=self.letter_color,
                                                  font_size=self.font_size_level1,
                                                  text=" ",
                                                  on=False,
                                                  anchor="center"))
        self._ve_feedback_letters.append(Text(position=self._centerPos,
                                              color=self.letter_color,
                                              font_size=self.font_size_level2,
                                              text=" ",
                                              anchor='center',
                                              on=False))
        
        ## put all in elements container:
        self._ve_elements.extend(self._ve_edges)
        self._ve_elements.extend(self._ve_shapes)
        self._ve_elements.extend(self._ve_letters)
        self._ve_elements.extend([self._ve_feedback_box])
        self._ve_elements.extend(self._ve_feedback_letters)
        self._ve_elements.append(self._ve_fixationpoint)
        
    
    def set_countdown_screen(self):
        '''
        set screen how it should look during countdown.
        '''
        self.set_standard_screen(False)
        
    
    def set_standard_screen(self, std=True):
        '''
        set screen elements to standard state.
        '''
        is_level1 = self._current_level==1        
        for i in xrange(self._nr_elements):
            self._ve_edges[i].set(radius=(std and self._edge_radius or self._edge_radius * self.flash_size_factor))
            self._ve_shapes[i].set(color=(std and  self.shape_color or self.stimuli_colors[i]),
                                   radius=(std and self._circle_radius or self._circle_radius * self.flash_size_factor))
            self._ve_letters[self._nr_letters*2 + i].set(on=(std and (not is_level1)))
            self._ve_letters[self._nr_letters*2 + self._nr_elements + i].set(on=((not std) and (not is_level1)))
        for i in xrange(self._nr_letters):
            self._ve_letters[i].set(on=(std and is_level1))
            self._ve_letters[self._nr_letters + i].set(on=((not std) and is_level1))
        self._ve_fixationpoint.set(on=std)

    def set_synchronized_countdown_screen(self):
        self.set_standard_screen(True)

    
    def stimulus(self, i_element, on=True):
        '''
        turn on/off the stimulus elements and turn off/on the normal elements.
        '''
        self._ve_shapes[i_element].set(color=(on and self.stimuli_colors[i_element] or self.shape_color),
                                       radius=self._circle_radius*(on and self.flash_size_factor or 1))
        self._ve_edges[i_element].set(radius=self._edge_radius*(on and self.flash_size_factor or 1))
        if self._current_level==1:
            for i in xrange(len(self.letter_set[i_element])):
                i_normal = (self._nr_elements-1) * i_element + i
                i_stimulus = self._nr_letters + i_normal
                self._ve_letters[i_normal].set(on=not on)
                self._ve_letters[i_stimulus].set(on=on)
        else:
            i_normal = 2*self._nr_letters + i_element
            i_stimulus = self._nr_elements + i_normal 
            self._ve_letters[i_normal].set(on=not on)
            self._ve_letters[i_stimulus].set(on=on)
    
    
    def feedback(self):
        # turn on feedback box and turn off fixationpoint:
        self._ve_feedback_box.set(on=True)
        self._ve_fixationpoint.set(on=False)
        
        if self._current_level == 1:
            
            '''level 1: present classified letter group in center and move letters to circles '''
            
            ## display letter group:
            for i in xrange(self._nr_elements-1):
                self._ve_feedback_letters[i].set(on=True,
                                                 text=self.letter_set[self._classified_element][i])
            ## turn on current element:
            self.stimulus(self._classified_element, True)
            
            ## turn off other letters:
            idx_start = self._classified_element*(self._nr_elements-1)
            idx_end = idx_start + self._nr_elements-1
            for i in xrange(idx_start):
                self._ve_letters[i].set(on=False)
            for i in xrange(idx_end, self._nr_letters):
                self._ve_letters[i].set(on=False)
            
            ## present:
            self._presentation.set(go_duration=(1, 'seconds'))
            self._presentation.go()
            
            ## turn off current element:
            self.stimulus(self._classified_element, False)
            for i in xrange(idx_start, idx_end):
                self._ve_letters[i].set(on=False)
            for i in xrange(self._nr_elements-1):
                self._ve_feedback_letters[i].set(on=False)
            
            ## animate letters:
            self._presentation.set(go_duration=(self.animation_time, 'seconds'))
            self._viewport.parameters.stimuli.extend([None]*(self._nr_elements-1))
            def update(t):
                dt = t/self.animation_time
                self._viewport.parameters.stimuli = self._viewport.parameters.stimuli[:-(self._nr_elements-1)]
                feedback_letters = []
                for i in xrange(self._nr_elements-1):
                    pos = animate_sigmoid(NP.add(self._letter_layout.positions[i], self._centerPos), self._shape_positions[i], dt)
                    font_size = int(round(animate(self.font_size_level1, self.font_size_level2, dt)))
                    feedback_letters.append(Text(position=pos,
                                                 color=self.letter_color,
                                                 font_size=font_size,
                                                 text=self.letter_set[self._classified_element][i],
                                                 anchor="center"))
                self._viewport.parameters.stimuli.extend(feedback_letters)
            self._presentation.add_controller(None,None,FunctionController(during_go_func=update))
            
            # send to screen:
            self._presentation.go()
            self._presentation.remove_controller(None,None,None)
            self._viewport.parameters.stimuli = self._viewport.parameters.stimuli[:-(self._nr_elements-1)]
                
            ## turn on level 2 letters:
            for i in xrange(len(self.letter_set[self._classified_element])):
                self._ve_letters[self._nr_letters*2 + i].set(on=True, text=self.letter_set[self._classified_element][i])
                self._ve_letters[self._nr_letters*2 + self._nr_elements + i].set(text=self.letter_set[self._classified_element][i])
                    
        else: 
            ''' level 2: present classified letter and move it to wordbox '''
            
            ## check if backdoor classified:
            if self._classified_letter >= len(self.letter_set[self._classified_element]):
                text = ' '
            else:
                text = self.letter_set[self._classified_element][self._classified_letter]
                
            ## display letter:
            self._ve_feedback_letters[-1].set(on=True, text=text)
            
            ## turn on current element stimulusw:
            if not self.offline:
                self.stimulus(self._classified_letter, True)
            
            ## turn off other letters:
            for i in xrange(self._classified_letter):
                self._ve_letters[self._nr_letters*2 + i].set(on=False)
            for i in xrange(self._classified_letter+1, self._nr_elements):
                self._ve_letters[self._nr_letters*2 + i].set(on=False)
            
            ## present:
            self._presentation.set(go_duration=(self.feedback_duration, 'seconds'))
            self._presentation.go()
            
            ## turn off current element stimulus:
            if not self.offline:
                self.stimulus(self._classified_letter, False)
                self._ve_letters[self._nr_letters*2 + self._classified_letter].set(on=False)
            self._ve_feedback_letters[-1].set(on=False)
            
            ## animate letter, but not if backdoor classified:
            if self._classified_letter < len(self.letter_set[self._classified_element]):
                self._presentation.set(go_duration=(self.animation_time, 'seconds'))
                self._viewport.parameters.stimuli.append(None)
                def update(t):
                    # Add one screen refresh (otherwise animation lags by 1 frame)
                    dt = t/self.animation_time
                    self._viewport.parameters.stimuli = self._viewport.parameters.stimuli[:-1]
                    pos = animate_sigmoid(self._centerPos, self._current_letter_position, dt)
                    color = animate_sigmoid(self.letter_color, self.current_letter_color, dt)
                    font_size = int(round(animate(self.font_size_level2, self.font_size_current_letter, dt)))
                    self._viewport.parameters.stimuli.append(Text(position=pos,
                                                                  color=color,
                                                                  font_size=font_size,
                                                                  text=text,
                                                                  anchor='center'))
                self._presentation.add_controller(None,None,FunctionController(during_go_func=update))
                
                # send to screen:
                self._presentation.go()
                self._presentation.remove_controller(None,None,None)

                self._viewport.parameters.stimuli = self._viewport.parameters.stimuli[:-1]
            else:
                self._presentation.set(go_duration=(self.animation_time, 'seconds'))
                self._presentation.go()
                
                    
            ## turn on level 1 letters:
            for i in xrange(self._nr_letters):
                self._ve_letters[i].set(on=True)
        
        ## turn off feedback box and turn on fixationpoint:
        self._ve_feedback_box.set(on=False)
        self._ve_fixationpoint.set(on=True)
class CenterSpellerVE(VisualSpellerVE):
    '''
    classdocs
    '''
    def init(self):
        ''' initialize parameters '''

        VisualSpellerVE.init(self)

        ## sizes:
        self.letter_radius = 40
        self.speller_radius = 250
        self.font_size_level1 = 45  # letters in level 1
        self.font_size_level2 = 130  # letters in level 2
        self.feedbackbox_size = 200.0
        self.fixationpoint_size = 4.0
        self.font_size_feedback_ErrP = 300

        ## stimulus types:
        self.stimulustype_color = True
        self.shape_on = True

        self.stimulus_duration = 0.083  # 5 frames @60 Hz = 83ms flash
        self.interstimulus_duration = 0.1

        ## feedback type:
        self.feedback_show_shape = True
        self.feedback_show_shape_at_center = True

        ## level 2 appearance:
        self.level_2_shapes = True
        self.level_2_letter_colors = False
        self.level_2_animation = True
        self.backdoor_symbol = "^"

        ## colors:
        self.shape_color = (1.0, 1.0, 1.0)

        self.stimuli_colors = [[0.0, 0.0, 1.0], [0.0, 0.53, 0.006],
                               [1.0, 0.0, 0.0], [1.0, 1.0, 0.0],
                               [0.86, 0.0, 0.86], [0.95, 0.95, 0.95]]
        self.letter_color = (.5, .5, .5)
        self.feedback_color = (0.9, 0.9, 0.9)
        self.fixationpoint_color = (1.0, 1.0, 1.0)
        self.feedback_ErrP_color = (0.7, 0.1, 0.1)

        ## register possible shapes:
        self.registered_shapes = {
            'circle': FilledCircle,
            'cross': FilledCross,
            'hexagon': FilledHexagon,
            'hourglass': FilledHourglass,
            'triangle': FilledTriangle,
            'rectangle': Target2D
        }

        ## define shapes in the form ['name', {parameters}]:
        # where parameters can be VisionEgg parameters eg. 'radius', 'size', 'orientation' etc.

        self.shapes = [[
            "triangle", {
                "innerColor": self.bg_color,
                "innerSize": 60,
                "size": 200
            }
        ], ["hourglass", {
            "size": 100
        }],
                       [
                           "cross", {
                               "orientation": 45,
                               "size": [30, 180],
                               "innerColor": self.bg_color
                           }
                       ],
                       [
                           "triangle", {
                               "innerColor": self.bg_color,
                               "innerSize": 60,
                               "orientation": 180,
                               "size": 200
                           }
                       ], ["hourglass", {
                           "orientation": 90,
                           "size": 100
                       }],
                       [
                           "cross", {
                               "size": [30, 180],
                               "innerColor": self.bg_color
                           }
                       ]]
        # If False only shapes are shown in that level but not group symbols(Letters)
        self.level_1_symbols = True
        self.level_2_symbols = True

    def prepare_mainloop(self):
        '''
        called in pre_mainloop of superclass.
        '''

        if not self.do_animation:
            self.level_2_animation = False

        ## init containers for VE elements:
        self._ve_shapes = []
        self._ve_letters = []

        self._letter_positions = []
        self._countdown_shape_positions = []
        self._countdown_letter_positions = []
        self._countdown_screen = False

        if self.stimulustype_color:
            assert len(self.stimuli_colors) == self._nr_elements
            self.shape_color = self.stimuli_colors
        else:
            self.shape_color = [self.shape_color] * self._nr_elements

        if not self.feedback_show_shape:
            self.feedback_show_shape_at_center = False

        if not self.shape_on:
            self.shapes = [['triangle', {
                'size': 200
            }], ['triangle', {
                'size': 200
            }], ['triangle', {
                'size': 200
            }], ['triangle', {
                'size': 200
            }], ['triangle', {
                'size': 200
            }], ['triangle', {
                'size': 200
            }]]

    def init_screen_elements(self):
        '''
        Initializing screen elements
        '''

        ## create shapes:
        if self.do_animation:
            for i in xrange(self._nr_elements):
                self._ve_shapes.append(
                    self.registered_shapes[self.shapes[i][0]](
                        position=self._centerPos,
                        color=self.shape_color[i],
                        on=False,
                        **self.shapes[i][1]))

            ## add letters of level 1:
            circle_layout = CircularLayout(nr_elements=self._nr_elements,
                                           radius=self.speller_radius,
                                           start=NP.pi / 6. * 5)
            circle_layout.positions.reverse()
            self._letter_layout = CircularLayout(nr_elements=self._nr_elements,
                                                 radius=self.letter_radius,
                                                 start=NP.pi / 6. * 5)
            self._letter_layout.positions.reverse()
            for i in xrange(self._nr_elements):
                # store countdown position:
                self._countdown_shape_positions.append(
                    (self._centerPos[0] + circle_layout.positions[i][0],
                     self._centerPos[1] + circle_layout.positions[i][1]))

                # put shape in container:
                self._ve_elements.append(self._ve_shapes[i])

                for j in xrange(
                        len(self.letter_set[i])
                ):  # warning: self.letter_set must be at least of length self._nr_elements!!!
                    # store position:
                    self._letter_positions.append(
                        (self._letter_layout.positions[j][0] +
                         self._centerPos[0],
                         self._letter_layout.positions[j][1] +
                         self._centerPos[1]))

                    # store countdown position:
                    self._countdown_letter_positions.append(
                        (self._letter_layout.positions[j][0] +
                         self._countdown_shape_positions[-1][0],
                         self._letter_layout.positions[j][1] +
                         self._countdown_shape_positions[-1][1]))

                    # add letter:
                    self._ve_letters.append(
                        Text(position=self._letter_positions[-1],
                             text=self.letter_set[i][j],
                             font_size=self.font_size_level1,
                             color=self.letter_color,
                             anchor='center',
                             on=False))

            # add letters of level 2:
            for i in xrange(self._nr_elements):
                self._letter_positions.append(self._centerPos)
                self._countdown_letter_positions.append(
                    self._countdown_shape_positions[i])
                self._ve_letters.append(
                    Text(position=self._centerPos,
                         text=" ",
                         font_size=self.font_size_level2,
                         color=(self.level_2_letter_colors
                                and self.stimuli_colors[i]
                                or self.letter_color),
                         anchor='center',
                         on=False))

                # put letters in container:
            self._ve_elements.extend(self._ve_letters)

            ## create feedback box:
            self._ve_feedback_box = Target2D(position=self._centerPos,
                                             size=(self.feedbackbox_size,
                                                   self.feedbackbox_size),
                                             color=self.feedback_color,
                                             on=False)

            ## add feedback letters:
            self._ve_feedback_letters = []
            for i in xrange(self._nr_elements):
                self._ve_feedback_letters.append(
                    Text(position=(self._letter_layout.positions[i][0] +
                                   self._centerPos[0],
                                   self._letter_layout.positions[i][1] +
                                   self._centerPos[1]),
                         color=self.letter_color,
                         font_size=self.font_size_level1,
                         text=" ",
                         on=False,
                         anchor="center"))
                self._ve_feedback_letters.append(
                    Text(position=self._centerPos,
                         color=self.letter_color,
                         font_size=self.font_size_level2,
                         text=" ",
                         anchor='center',
                         on=False))

            ## add feedback note (whether or not there was an ErrP detected):
            self._ve_feedback_ErrP = Text(
                position=self._centerPos,
                color=self.feedback_ErrP_color,
                text="X",
                font_size=self.font_size_feedback_ErrP,
                anchor='center',
                on=False)

            ## add fixation point:
            self._ve_fixationpoint = FilledCircle(
                radius=self.fixationpoint_size,
                position=self._centerPos,
                color=self.fixationpoint_color,
                on=False)

            ##################### IF NOT DO ANIMATION #########################
        else:
            ## add letters of level 1:
            circle_layout = CircularLayout(nr_elements=self._nr_elements,
                                           radius=self.speller_radius,
                                           start=NP.pi / 6. * 5)
            self._letter_layout = CircularLayout(nr_elements=self._nr_elements,
                                                 radius=self.letter_radius,
                                                 start=NP.pi / 6. * 5)
            circle_layout.positions.reverse()
            self._letter_layout.positions.reverse()

            for i in xrange(self._nr_elements):
                self._ve_shapes.append(
                    self.registered_shapes[self.shapes[i][0]](
                        position=(self._centerPos[0] +
                                  circle_layout.positions[i][0],
                                  self._centerPos[1] +
                                  circle_layout.positions[i][1]),
                        color=self.shape_color[i],
                        on=False,
                        **self.shapes[i][1]))

            for i in xrange(self._nr_elements):
                # store countdown position:
                self._countdown_shape_positions.append(
                    (self._centerPos[0] + circle_layout.positions[i][0],
                     self._centerPos[1] + circle_layout.positions[i][1]))
                # put shape in container:
                self._ve_elements.append(self._ve_shapes[i])

                for j in xrange(
                        len(self.letter_set[i])
                ):  # warning: self.letter_set must be at least of length self._nr_elements!!!
                    # store position:
                    self._letter_positions.append(
                        (self._letter_layout.positions[j][0] +
                         self._centerPos[0],
                         self._letter_layout.positions[j][1] +
                         self._centerPos[1]))

                    # store countdown position:
                    self._countdown_letter_positions.append(
                        (self._letter_layout.positions[j][0] +
                         self._countdown_shape_positions[-1][0],
                         self._letter_layout.positions[j][1] +
                         self._countdown_shape_positions[-1][1]))

                    # add letter:
                    self._ve_letters.append(
                        Text(position=(self._letter_layout.positions[j][0] +
                                       self._centerPos[0] +
                                       circle_layout.positions[i][0],
                                       self._letter_layout.positions[j][1] +
                                       self._centerPos[1] +
                                       circle_layout.positions[i][1]),
                             text=self.letter_set[i][j],
                             font_size=self.font_size_level1,
                             color=self.letter_color,
                             anchor='center',
                             on=False))

            # add letters of level 2:
            for i in xrange(self._nr_elements):
                self._letter_positions.append(
                    (self._letter_layout.positions[i][0] + self._centerPos[0],
                     self._letter_layout.positions[i][1] + self._centerPos[1]))
                self._countdown_letter_positions.append(
                    (self._letter_layout.positions[i][0] +
                     self._countdown_shape_positions[-1][0],
                     self._letter_layout.positions[i][1] +
                     self._countdown_shape_positions[-1][1]))
                self._ve_letters.append(
                    Text(position=(self._letter_layout.positions[i][0] +
                                   self._centerPos[0] +
                                   circle_layout.positions[i][0],
                                   self._letter_layout.positions[i][1] +
                                   self._centerPos[1] +
                                   circle_layout.positions[i][1]),
                         text=" ",
                         font_size=self.font_size_level2,
                         color=(self.level_2_letter_colors
                                and self.stimuli_colors[i]
                                or self.letter_color),
                         anchor='center',
                         on=False))

            # put letters in container:
            self._ve_elements.extend(self._ve_letters)

            ## create feedback box:
            self._ve_feedback_box = Target2D(position=self._centerPos,
                                             size=(self.feedbackbox_size,
                                                   self.feedbackbox_size),
                                             color=self.feedback_color,
                                             on=False)

            ## add feedback letters:
            self._ve_feedback_letters = []
            for i in xrange(self._nr_elements):
                self._ve_feedback_letters.append(
                    Text(position=(self._letter_layout.positions[i][0] +
                                   self._centerPos[0],
                                   self._letter_layout.positions[i][1] +
                                   self._centerPos[1]),
                         color=self.letter_color,
                         font_size=self.font_size_level1,
                         text=" ",
                         on=False,
                         anchor="center"))
                self._ve_feedback_letters.append(
                    Text(position=self._centerPos,
                         color=self.letter_color,
                         font_size=self.font_size_level2,
                         text=" ",
                         anchor='center',
                         on=False))

            ## add feedback note (whether or not there was an ErrP detected):
            self._ve_feedback_ErrP = Text(
                position=self._centerPos,
                color=self.feedback_ErrP_color,
                text="X",
                font_size=self.font_size_feedback_ErrP,
                anchor='center',
                on=False)

            ## add fixation point:
            self._ve_fixationpoint = FilledCircle(
                radius=self.fixationpoint_size,
                position=self._centerPos,
                color=self.fixationpoint_color,
                on=False)

        # put letters in container:
        self._ve_elements.append(self._ve_feedback_box)
        self._ve_elements.extend(self._ve_feedback_letters)
        self._ve_elements.append(self._ve_feedback_ErrP)
        self._ve_elements.append(self._ve_fixationpoint)

    def set_countdown_screen(self):
        '''
        set screen how it should look during countdown.
        '''
        if self._countdown_screen:
            return

        self._countdown_screen = True
        is_level1 = self._current_level == 1

        ## turn on visible elements:
        for i in xrange(self._nr_elements):  # shapes
            self._ve_shapes[i].set(on=is_level1 or self.level_2_shapes)

        for i in xrange(self._nr_letters):  # level 1 letters
            self._ve_letters[i].set(on=is_level1)

        for i in xrange(len(
                self.letter_set[self._classified_element])):  # level 2 letters
            self._ve_letters[self._nr_letters + i].set(
                on=not is_level1,
                text=(is_level1 and " "
                      or self.letter_set[self._classified_element][i]))

        self._ve_letters[self._nr_letters + self._nr_elements - 1].set(
            on=not is_level1, text=(is_level1 and " " or self.backdoor_symbol))

        ## move all elements with their letters to countdown position:
        def update(t):
            dt = t / self.animation_time
            for i in xrange(self._nr_elements):
                pos = animate_sigmoid(self._centerPos,
                                      self._countdown_shape_positions[i], dt)
                self._ve_shapes[i].set(position=pos)  # shapes
                self._ve_letters[self._nr_letters + i].set(
                    position=pos)  # level 2 letters
            for i in xrange(self._nr_letters):
                pos = animate_sigmoid(self._letter_positions[i],
                                      self._countdown_letter_positions[i], dt)
                self._ve_letters[i].set(position=pos)  # level 1 letters

        def update2(t):  # if not do animation
            for i in xrange(self._nr_elements):
                self._ve_shapes[i].set(
                    position=self._countdown_shape_positions[i])  # shapes
                self._ve_letters[self._nr_letters + i].set(
                    position=self._countdown_shape_positions[i]
                )  # level 2 letters
            for i in xrange(self._nr_letters):
                self._ve_letters[i].set(
                    position=self._countdown_letter_positions[i]
                )  # level 1 letters

        if self.do_animation:
            self._presentation.set(go_duration=(self.animation_time,
                                                'seconds'))
            self._presentation.add_controller(
                None, None, FunctionController(during_go_func=update))
        else:
            self._presentation.set(go_duration=(0, 'seconds'))
            self._presentation.add_controller(
                None, None, FunctionController(during_go_func=update2))

        # send to screen:
        self._presentation.go()
        self._presentation.remove_controller(None, None, None)

        for i in xrange(self._nr_elements):
            self._ve_shapes[i].set(position=self._countdown_shape_positions[i])
            self._ve_letters[self._nr_letters + i].set(
                position=self._countdown_shape_positions[i])
        for i in xrange(self._nr_letters):
            self._ve_letters[i].set(
                position=self._countdown_letter_positions[i])

    def set_synchronized_countdown_screen(self):
        for i in xrange(self._nr_elements):
            self._ve_shapes[i].set(on=False)
        if self._current_level == 1:
            for j in xrange(self._nr_letters):
                self._ve_letters[j].set(on=False)
        else:
            for j in xrange(self._nr_elements + 1):
                self._ve_letters[-j].set(on=False)

    def set_standard_screen(self):
        '''
        set screen elements to standard state.
        '''

        self._countdown_screen = False

        # move all elements with their letters to standard position:

        def update(t):
            dt = t / self.animation_time
            for i in xrange(self._nr_elements):
                pos = animate_sigmoid(self._countdown_shape_positions[i],
                                      self._centerPos, dt)
                self._ve_shapes[i].set(position=pos)  # shapes
                self._ve_letters[self._nr_letters + i].set(
                    position=pos)  # level 2 letters
            for i in xrange(self._nr_letters):
                pos = animate_sigmoid(self._countdown_letter_positions[i],
                                      self._letter_positions[i], dt)
                self._ve_letters[i].set(position=pos)  # level 1 letters

        if self.do_animation:
            self._presentation.set(go_duration=(self.animation_time,
                                                'seconds'))
            self._presentation.add_controller(
                None, None, FunctionController(during_go_func=update))
        else:
            self._presentation.set(go_duration=(0, 'seconds'))

        # send to screen:
        self._presentation.go()
        self._presentation.remove_controller(None, None, None)

        for i in xrange(self._nr_elements):
            self._ve_shapes[i].set(position=self._centerPos, on=False)
            self._ve_letters[self._nr_letters + i].set(
                position=self._centerPos, on=False)
        for i in xrange(self._nr_letters):
            self._ve_letters[i].set(position=self._letter_positions[i],
                                    on=False)

        self._ve_countdown.set(on=False, text=" ")

    def stimulus(self, i_element, on=True):
        '''
        turn on/off the stimulus elements and turn off/on the normal elements.
        '''
        if self._current_level == 1:
            self._ve_shapes[i_element].set(on=on)
            for i in xrange(len(self.letter_set[i_element])):
                self._ve_letters[(self._nr_elements - 1) * i_element +
                                 i].set(on=on and self.level_1_symbols)
        else:
            self._ve_shapes[i_element].set(on=(on and self.level_2_shapes))
            if i_element < len(self.letter_set[self._classified_element]):
                self._ve_letters[self._nr_letters + i_element].set(
                    on=on and self.level_2_symbols,
                    text=(on and
                          self.letter_set[self._classified_element][i_element]
                          or " "))
            else:
                self._ve_letters[self._nr_letters + self._nr_elements - 1].set(
                    on=on and self.level_2_symbols,
                    text=(on and self.backdoor_symbol or " "))

    def feedback(self):
        '''
        Show classified element / letter(s).
        '''
        self._show_feedback(True)

        ## present:
        self._presentation.set(go_duration=(self.feedback_duration, 'seconds'))
        self._presentation.go()

        self._show_feedback(False)

    def _show_feedback(self, on=True):
        ## turn on/off feedback box:
        if not self.feedback_show_shape_at_center:
            self._ve_feedback_box.set(on=on)

        if self._current_level == 1:
            ## turn on/off center letter group:
            if not self.feedback_show_shape_at_center:
                for i in xrange(self._nr_elements):
                    self._ve_feedback_letters[i].set(
                        on=on,
                        text=(i < self._nr_elements - 1
                              and self.letter_set[self._classified_element][i]
                              or self.backdoor_symbol))
            if self.feedback_show_shape:
                if self.feedback_show_shape_at_center or not on:
                    pos = self._centerPos
                else:
                    pos = self._countdown_shape_positions[
                        self._classified_element]

                ## turn on/off selected element:
                self._ve_shapes[self._classified_element].set(on=on,
                                                              position=pos)

                ## turn on/off letters of selected element:
                idx_start = self._classified_element * (self._nr_elements - 1)
                idx_end = idx_start + self._nr_elements - 1
                for i in xrange(idx_start, idx_end):
                    self._ve_letters[i].set(
                        on=on,
                        position=(on and list(
                            NP.add(
                                pos, self._letter_layout.positions[
                                    i % (self._nr_elements - 1)]))
                                  or self._letter_positions[i]))
        else:  ### level 2:
            ## check if backdoor classified:
            if self._classified_letter >= len(
                    self.letter_set[self._classified_element]):
                text = self.backdoor_symbol
            else:
                text = self.letter_set[self._classified_element][
                    self._classified_letter]

            ## turn on/off letter:
            if self.offline or not self.feedback_show_shape_at_center:
                self._ve_feedback_letters[-1].set(
                    on=on,
                    text=text,
                    color=(self.level_2_letter_colors
                           and self.stimuli_colors[self._classified_letter]
                           or self.letter_color))
            if self.feedback_show_shape:
                if self.feedback_show_shape_at_center:
                    pos = self._centerPos
                else:
                    pos = self._countdown_shape_positions[
                        self._classified_element]

                ## turn on/off current element:
                self._ve_shapes[self._classified_letter].set(
                    on=(on and self.level_2_shapes),
                    position=(on and pos or self._centerPos))

                ## turn on/off letter of current element:
                idx = self._nr_letters + self._classified_letter
                self._ve_letters[idx].set(
                    on=on,
                    text=text,
                    position=(on and pos or self._letter_positions[idx]))

    def switch_level(self):

        if self.use_ErrP_detection and self._ErrP_classifier:
            self._ve_feedback_ErrP.set(on=True)
            self._show_feedback(True)
            self._presentation.set(go_duration=(self.feedback_ErrP_duration,
                                                'seconds'))
            self._presentation.go()
            self._ve_feedback_ErrP.set(on=False)
            self._show_feedback(False)
            return

        if self._current_level == 1:
            '''level 1: move classified letters to circles '''

            if self.level_2_animation:
                ## turn on all elements:
                for i in xrange(self._nr_elements):
                    self._ve_shapes[i].set(
                        on=self.level_2_shapes,
                        position=self._countdown_shape_positions[i])

                ## animate letters:
                def update(t):
                    dt = t / self.animation_time
                    self._viewport.parameters.stimuli = self._viewport.parameters.stimuli[:-(
                        self._nr_elements)]
                    feedback_letters = []
                    for i in xrange(self._nr_elements):
                        pos = (animate_sigmoid(
                            NP.add(self._letter_layout.positions[i],
                                   self._centerPos),
                            self._countdown_shape_positions[i], dt))
                        font_size = int(
                            round(
                                animate(self.font_size_level1,
                                        self.font_size_level2, dt)))
                        color = (self.level_2_letter_colors and list(
                            animate(self.letter_color, self.stimuli_colors[i],
                                    dt)) or self.letter_color)
                        text = (
                            i == self._nr_elements - 1 and self.backdoor_symbol
                            or self.letter_set[self._classified_element][i])
                        feedback_letters.append(
                            Text(position=pos,
                                 color=color,
                                 font_size=font_size,
                                 text=text,
                                 anchor="center"))
                        self._viewport.parameters.stimuli.extend(
                            feedback_letters)
                    if self.feedback_show_shape_at_center:
                        pos = animate_sigmoid(
                            self._centerPos, self._countdown_shape_positions[
                                self._classified_element], dt)
                        self._ve_shapes[self._classified_element].set(
                            position=pos)

                # send to screen:
                self._viewport.parameters.stimuli.extend([None] *
                                                         (self._nr_elements))
                self._presentation.set(go_duration=(self.animation_time,
                                                    'seconds'))
                self._presentation.add_controller(
                    None, None, FunctionController(during_go_func=update))
                self._presentation.go()
                self._presentation.remove_controller(None, None, None)
                self._viewport.parameters.stimuli = self._viewport.parameters.stimuli[:-(
                    self._nr_elements)]

                ## turn on level 2 letters:
                for i in xrange(self._nr_elements):
                    text = (i == self._nr_elements - 1 and self.backdoor_symbol
                            or self.letter_set[self._classified_element][i])
                    self._ve_letters[self._nr_letters + i].set(
                        on=True,
                        text=text,
                        position=self._countdown_letter_positions[
                            self._nr_letters + i])
            else:
                ## turn on all elements:
                self.set_standard_screen()

        else:
            ''' level 2: move classified letter to wordbox '''

            ## check if backdoor classified:
            if self._classified_letter >= len(
                    self.letter_set[self._classified_element]):
                text = self.backdoor_symbol
            else:
                text = self.letter_set[self._classified_element][
                    self._classified_letter]

            ## animate letter, but not if backdoor classified:
            if self._classified_letter < len(
                    self.letter_set[self._classified_element]):

                def update(t):
                    dt = t / self.animation_time
                    self._viewport.parameters.stimuli = self._viewport.parameters.stimuli[:
                                                                                          -1]
                    pos = animate_sigmoid(self._centerPos,
                                          self._current_letter_position, dt)
                    color = (self.level_2_letter_colors and list(
                        animate_sigmoid(
                            self.stimuli_colors[self._classified_letter],
                            self.current_letter_color, dt)) or list(
                                animate_sigmoid(self.letter_color,
                                                self.current_letter_color,
                                                dt)))
                    font_size = int(
                        round(
                            animate(self.font_size_level2,
                                    self.font_size_current_letter, dt)))
                    self._viewport.parameters.stimuli.append(
                        Text(position=pos,
                             color=color,
                             font_size=font_size,
                             text=text,
                             anchor='center'))

                # send to screen:
                self._viewport.parameters.stimuli.append(None)
                self._presentation.set(go_duration=(self.animation_time,
                                                    'seconds'))
                self._presentation.add_controller(
                    None, None, FunctionController(during_go_func=update))
                self._presentation.go()
                self._presentation.remove_controller(None, None, None)
                self._viewport.parameters.stimuli = self._viewport.parameters.stimuli[:
                                                                                      -1]
            else:
                self._presentation.set(go_duration=(self.animation_time,
                                                    'seconds'))
                self._presentation.go()

        ## turn off feedback box:
        self._ve_feedback_box.set(on=False)

    def pre__classify(self):
        self._ve_fixationpoint.set(on=True)

    def post__classify(self):
        self._ve_fixationpoint.set(on=False)
    def init_screen_elements(self):
        '''
        Initializing screen elements
        '''

        ## create shapes:
        if self.do_animation:
            for i in xrange(self._nr_elements):
                self._ve_shapes.append(
                    self.registered_shapes[self.shapes[i][0]](
                        position=self._centerPos,
                        color=self.shape_color[i],
                        on=False,
                        **self.shapes[i][1]))

            ## add letters of level 1:
            circle_layout = CircularLayout(nr_elements=self._nr_elements,
                                           radius=self.speller_radius,
                                           start=NP.pi / 6. * 5)
            circle_layout.positions.reverse()
            self._letter_layout = CircularLayout(nr_elements=self._nr_elements,
                                                 radius=self.letter_radius,
                                                 start=NP.pi / 6. * 5)
            self._letter_layout.positions.reverse()
            for i in xrange(self._nr_elements):
                # store countdown position:
                self._countdown_shape_positions.append(
                    (self._centerPos[0] + circle_layout.positions[i][0],
                     self._centerPos[1] + circle_layout.positions[i][1]))

                # put shape in container:
                self._ve_elements.append(self._ve_shapes[i])

                for j in xrange(
                        len(self.letter_set[i])
                ):  # warning: self.letter_set must be at least of length self._nr_elements!!!
                    # store position:
                    self._letter_positions.append(
                        (self._letter_layout.positions[j][0] +
                         self._centerPos[0],
                         self._letter_layout.positions[j][1] +
                         self._centerPos[1]))

                    # store countdown position:
                    self._countdown_letter_positions.append(
                        (self._letter_layout.positions[j][0] +
                         self._countdown_shape_positions[-1][0],
                         self._letter_layout.positions[j][1] +
                         self._countdown_shape_positions[-1][1]))

                    # add letter:
                    self._ve_letters.append(
                        Text(position=self._letter_positions[-1],
                             text=self.letter_set[i][j],
                             font_size=self.font_size_level1,
                             color=self.letter_color,
                             anchor='center',
                             on=False))

            # add letters of level 2:
            for i in xrange(self._nr_elements):
                self._letter_positions.append(self._centerPos)
                self._countdown_letter_positions.append(
                    self._countdown_shape_positions[i])
                self._ve_letters.append(
                    Text(position=self._centerPos,
                         text=" ",
                         font_size=self.font_size_level2,
                         color=(self.level_2_letter_colors
                                and self.stimuli_colors[i]
                                or self.letter_color),
                         anchor='center',
                         on=False))

                # put letters in container:
            self._ve_elements.extend(self._ve_letters)

            ## create feedback box:
            self._ve_feedback_box = Target2D(position=self._centerPos,
                                             size=(self.feedbackbox_size,
                                                   self.feedbackbox_size),
                                             color=self.feedback_color,
                                             on=False)

            ## add feedback letters:
            self._ve_feedback_letters = []
            for i in xrange(self._nr_elements):
                self._ve_feedback_letters.append(
                    Text(position=(self._letter_layout.positions[i][0] +
                                   self._centerPos[0],
                                   self._letter_layout.positions[i][1] +
                                   self._centerPos[1]),
                         color=self.letter_color,
                         font_size=self.font_size_level1,
                         text=" ",
                         on=False,
                         anchor="center"))
                self._ve_feedback_letters.append(
                    Text(position=self._centerPos,
                         color=self.letter_color,
                         font_size=self.font_size_level2,
                         text=" ",
                         anchor='center',
                         on=False))

            ## add feedback note (whether or not there was an ErrP detected):
            self._ve_feedback_ErrP = Text(
                position=self._centerPos,
                color=self.feedback_ErrP_color,
                text="X",
                font_size=self.font_size_feedback_ErrP,
                anchor='center',
                on=False)

            ## add fixation point:
            self._ve_fixationpoint = FilledCircle(
                radius=self.fixationpoint_size,
                position=self._centerPos,
                color=self.fixationpoint_color,
                on=False)

            ##################### IF NOT DO ANIMATION #########################
        else:
            ## add letters of level 1:
            circle_layout = CircularLayout(nr_elements=self._nr_elements,
                                           radius=self.speller_radius,
                                           start=NP.pi / 6. * 5)
            self._letter_layout = CircularLayout(nr_elements=self._nr_elements,
                                                 radius=self.letter_radius,
                                                 start=NP.pi / 6. * 5)
            circle_layout.positions.reverse()
            self._letter_layout.positions.reverse()

            for i in xrange(self._nr_elements):
                self._ve_shapes.append(
                    self.registered_shapes[self.shapes[i][0]](
                        position=(self._centerPos[0] +
                                  circle_layout.positions[i][0],
                                  self._centerPos[1] +
                                  circle_layout.positions[i][1]),
                        color=self.shape_color[i],
                        on=False,
                        **self.shapes[i][1]))

            for i in xrange(self._nr_elements):
                # store countdown position:
                self._countdown_shape_positions.append(
                    (self._centerPos[0] + circle_layout.positions[i][0],
                     self._centerPos[1] + circle_layout.positions[i][1]))
                # put shape in container:
                self._ve_elements.append(self._ve_shapes[i])

                for j in xrange(
                        len(self.letter_set[i])
                ):  # warning: self.letter_set must be at least of length self._nr_elements!!!
                    # store position:
                    self._letter_positions.append(
                        (self._letter_layout.positions[j][0] +
                         self._centerPos[0],
                         self._letter_layout.positions[j][1] +
                         self._centerPos[1]))

                    # store countdown position:
                    self._countdown_letter_positions.append(
                        (self._letter_layout.positions[j][0] +
                         self._countdown_shape_positions[-1][0],
                         self._letter_layout.positions[j][1] +
                         self._countdown_shape_positions[-1][1]))

                    # add letter:
                    self._ve_letters.append(
                        Text(position=(self._letter_layout.positions[j][0] +
                                       self._centerPos[0] +
                                       circle_layout.positions[i][0],
                                       self._letter_layout.positions[j][1] +
                                       self._centerPos[1] +
                                       circle_layout.positions[i][1]),
                             text=self.letter_set[i][j],
                             font_size=self.font_size_level1,
                             color=self.letter_color,
                             anchor='center',
                             on=False))

            # add letters of level 2:
            for i in xrange(self._nr_elements):
                self._letter_positions.append(
                    (self._letter_layout.positions[i][0] + self._centerPos[0],
                     self._letter_layout.positions[i][1] + self._centerPos[1]))
                self._countdown_letter_positions.append(
                    (self._letter_layout.positions[i][0] +
                     self._countdown_shape_positions[-1][0],
                     self._letter_layout.positions[i][1] +
                     self._countdown_shape_positions[-1][1]))
                self._ve_letters.append(
                    Text(position=(self._letter_layout.positions[i][0] +
                                   self._centerPos[0] +
                                   circle_layout.positions[i][0],
                                   self._letter_layout.positions[i][1] +
                                   self._centerPos[1] +
                                   circle_layout.positions[i][1]),
                         text=" ",
                         font_size=self.font_size_level2,
                         color=(self.level_2_letter_colors
                                and self.stimuli_colors[i]
                                or self.letter_color),
                         anchor='center',
                         on=False))

            # put letters in container:
            self._ve_elements.extend(self._ve_letters)

            ## create feedback box:
            self._ve_feedback_box = Target2D(position=self._centerPos,
                                             size=(self.feedbackbox_size,
                                                   self.feedbackbox_size),
                                             color=self.feedback_color,
                                             on=False)

            ## add feedback letters:
            self._ve_feedback_letters = []
            for i in xrange(self._nr_elements):
                self._ve_feedback_letters.append(
                    Text(position=(self._letter_layout.positions[i][0] +
                                   self._centerPos[0],
                                   self._letter_layout.positions[i][1] +
                                   self._centerPos[1]),
                         color=self.letter_color,
                         font_size=self.font_size_level1,
                         text=" ",
                         on=False,
                         anchor="center"))
                self._ve_feedback_letters.append(
                    Text(position=self._centerPos,
                         color=self.letter_color,
                         font_size=self.font_size_level2,
                         text=" ",
                         anchor='center',
                         on=False))

            ## add feedback note (whether or not there was an ErrP detected):
            self._ve_feedback_ErrP = Text(
                position=self._centerPos,
                color=self.feedback_ErrP_color,
                text="X",
                font_size=self.font_size_feedback_ErrP,
                anchor='center',
                on=False)

            ## add fixation point:
            self._ve_fixationpoint = FilledCircle(
                radius=self.fixationpoint_size,
                position=self._centerPos,
                color=self.fixationpoint_color,
                on=False)

        # put letters in container:
        self._ve_elements.append(self._ve_feedback_box)
        self._ve_elements.extend(self._ve_feedback_letters)
        self._ve_elements.append(self._ve_feedback_ErrP)
        self._ve_elements.append(self._ve_fixationpoint)
Exemple #12
0
    def init_screen_elements(self):
        '''
        Initialize screen elements
        '''
        self._letter_positions = []
        ## create triangles:
        self._letter_layout = CircularLayout(nr_elements=self._nr_elements,
                                             radius=self.letter_radius,
                                             start=NP.pi / 6. * 5)
        self._letter_layout.positions.reverse()
        a = self.speller_radius / 2.
        b = a * NP.sqrt(3) / 3.
        self._shape_positions = [
            (self._centerPos[0], self._centerPos[1] + 2 * b),
            (self._centerPos[0] + a, self._centerPos[1] + b),
            (self._centerPos[0] + a, self._centerPos[1] - b),
            (self._centerPos[0], self._centerPos[1] - 2 * b),
            (self._centerPos[0] - a, self._centerPos[1] - b),
            (self._centerPos[0] - a, self._centerPos[1] + b)
        ]
        orientaion = [180., 0., 180., 0., 180., 0.]
        for i in xrange(self._nr_elements):
            self._ve_edges.append(
                FilledTriangle(size=self.speller_radius,
                               position=self._shape_positions[i],
                               orientation=orientaion[i],
                               color=self.edge_color))
            self._ve_shapes.append(
                FilledTriangle(size=self.speller_radius - self.edge_size,
                               position=self._shape_positions[i],
                               orientation=orientaion[i],
                               color=self.shape_color))

            ## add the letters of level 1:
            for j in xrange(
                    len(self.letter_set[i])
            ):  # warning: self.letter_set must be at least of length self._nr_elements!!!
                self._letter_positions.append(
                    (self._letter_layout.positions[j][0] +
                     self._shape_positions[i][0],
                     self._letter_layout.positions[j][1] +
                     self._shape_positions[i][1]))
                self._ve_letters.append(
                    Text(position=self._letter_positions[-1],
                         text=self.letter_set[i][j],
                         font_size=self.font_size_level1,
                         color=self.letter_color,
                         anchor='center'))

        ## add letters of level 2:
        for i in xrange(self._nr_elements):
            self._ve_letters.append(
                Text(position=self._shape_positions[i],
                     text=" ",
                     font_size=self.font_size_level2,
                     color=self.letter_color,
                     anchor='center',
                     on=False))

        ## add fixation point:
        self._ve_fixationpoint = FilledCircle(radius=self.fixationpoint_size,
                                              position=self._centerPos,
                                              color=self.fixationpoint_color)

        ## create feedback box:
        self._ve_feedback_box = Target2D(position=self._centerPos,
                                         size=(self.feedbackbox_size,
                                               self.feedbackbox_size),
                                         color=self.feedback_color,
                                         on=False)

        ## add feedback letters:
        self._ve_feedback_letters = []
        for i in xrange(self._nr_elements - 1):
            self._ve_feedback_letters.append(
                Text(position=(self._letter_layout.positions[i][0] +
                               self._centerPos[0],
                               self._letter_layout.positions[i][1] +
                               self._centerPos[1]),
                     color=self.letter_color,
                     font_size=self.font_size_level1,
                     text=" ",
                     on=False,
                     anchor="center"))
        self._ve_feedback_letters.append(
            Text(position=self._centerPos,
                 color=self.letter_color,
                 font_size=self.font_size_level2,
                 text=" ",
                 anchor='center',
                 on=False))

        ## put all in elements container:
        self._ve_elements.extend(self._ve_edges)
        self._ve_elements.extend(self._ve_shapes)
        self._ve_elements.extend(self._ve_letters)
        self._ve_elements.append(self._ve_feedback_box)
        self._ve_elements.extend(self._ve_feedback_letters)
        self._ve_elements.append(self._ve_fixationpoint)
Exemple #13
0
class CakeSpellerVE(VisualSpellerVE):
    '''
    Visual speller as a cake with six pieces.
    '''
    def init(self):
        '''
        initialize parameters
        '''
        VisualSpellerVE.init(self)

        ## sizes:
        self.letter_radius = 70
        self.speller_radius = 380
        self.fixationpoint_size = 5.0
        self.edge_size = 5
        self.font_size_level1 = 70  # letters in level 1
        self.font_size_level2 = 130  # letters in level 2
        self.feedbackbox_size = 200.0

        ## colors:
        self.shape_color = (0.0, 0.0, 0.0)
        self.edge_color = (1.0, 1.0, 1.0)
        self.stimuli_colors = [(1.0, 0.0, 0.0), (0.0, 1.0, 0.0),
                               (0.0, 0.0, 1.0), (1.0, 1.0, 0.0),
                               (1.0, 0.0, 1.0), (0.0, 1.0, 1.0)]
        self.letter_color = (1.0, 1.0, 1.0)
        self.letter_stimulus_color = (0.0, 0.0, 0.0)
        self.fixationpoint_color = (1.0, 1.0, 1.0)
        self.feedback_color = (0.7, 0.7, 0.7)
        self.countdown_color = self.bg_color

    def prepare_mainloop(self):
        '''
        called in pre_mainloop of superclass.
        '''
        assert len(self.stimuli_colors) == self._nr_elements

        ## init containers for VE elements:
        self._ve_shapes = []
        self._ve_edges = []
        self._ve_letters = []

    def init_screen_elements(self):
        '''
        Initialize screen elements
        '''
        self._letter_positions = []
        ## create triangles:
        self._letter_layout = CircularLayout(nr_elements=self._nr_elements,
                                             radius=self.letter_radius,
                                             start=NP.pi / 6. * 5)
        self._letter_layout.positions.reverse()
        a = self.speller_radius / 2.
        b = a * NP.sqrt(3) / 3.
        self._shape_positions = [
            (self._centerPos[0], self._centerPos[1] + 2 * b),
            (self._centerPos[0] + a, self._centerPos[1] + b),
            (self._centerPos[0] + a, self._centerPos[1] - b),
            (self._centerPos[0], self._centerPos[1] - 2 * b),
            (self._centerPos[0] - a, self._centerPos[1] - b),
            (self._centerPos[0] - a, self._centerPos[1] + b)
        ]
        orientaion = [180., 0., 180., 0., 180., 0.]
        for i in xrange(self._nr_elements):
            self._ve_edges.append(
                FilledTriangle(size=self.speller_radius,
                               position=self._shape_positions[i],
                               orientation=orientaion[i],
                               color=self.edge_color))
            self._ve_shapes.append(
                FilledTriangle(size=self.speller_radius - self.edge_size,
                               position=self._shape_positions[i],
                               orientation=orientaion[i],
                               color=self.shape_color))

            ## add the letters of level 1:
            for j in xrange(
                    len(self.letter_set[i])
            ):  # warning: self.letter_set must be at least of length self._nr_elements!!!
                self._letter_positions.append(
                    (self._letter_layout.positions[j][0] +
                     self._shape_positions[i][0],
                     self._letter_layout.positions[j][1] +
                     self._shape_positions[i][1]))
                self._ve_letters.append(
                    Text(position=self._letter_positions[-1],
                         text=self.letter_set[i][j],
                         font_size=self.font_size_level1,
                         color=self.letter_color,
                         anchor='center'))

        ## add letters of level 2:
        for i in xrange(self._nr_elements):
            self._ve_letters.append(
                Text(position=self._shape_positions[i],
                     text=" ",
                     font_size=self.font_size_level2,
                     color=self.letter_color,
                     anchor='center',
                     on=False))

        ## add fixation point:
        self._ve_fixationpoint = FilledCircle(radius=self.fixationpoint_size,
                                              position=self._centerPos,
                                              color=self.fixationpoint_color)

        ## create feedback box:
        self._ve_feedback_box = Target2D(position=self._centerPos,
                                         size=(self.feedbackbox_size,
                                               self.feedbackbox_size),
                                         color=self.feedback_color,
                                         on=False)

        ## add feedback letters:
        self._ve_feedback_letters = []
        for i in xrange(self._nr_elements - 1):
            self._ve_feedback_letters.append(
                Text(position=(self._letter_layout.positions[i][0] +
                               self._centerPos[0],
                               self._letter_layout.positions[i][1] +
                               self._centerPos[1]),
                     color=self.letter_color,
                     font_size=self.font_size_level1,
                     text=" ",
                     on=False,
                     anchor="center"))
        self._ve_feedback_letters.append(
            Text(position=self._centerPos,
                 color=self.letter_color,
                 font_size=self.font_size_level2,
                 text=" ",
                 anchor='center',
                 on=False))

        ## put all in elements container:
        self._ve_elements.extend(self._ve_edges)
        self._ve_elements.extend(self._ve_shapes)
        self._ve_elements.extend(self._ve_letters)
        self._ve_elements.append(self._ve_feedback_box)
        self._ve_elements.extend(self._ve_feedback_letters)
        self._ve_elements.append(self._ve_fixationpoint)

    def set_countdown_screen(self):
        '''
        set screen elements to countdown state
        '''
        self.set_standard_screen(False)

    def set_standard_screen(self, std=True):
        '''
        set screen elements to standard state.
        '''
        is_level1 = self._current_level == 1
        for i in xrange(self._nr_elements):
            self._ve_shapes[i].set(color=(std and self.shape_color
                                          or self.stimuli_colors[i]),
                                   on=True)
            self._ve_letters[self._nr_letters + i].set(
                on=not is_level1,
                color=(std and self.letter_color
                       or self.letter_stimulus_color))
        for i in xrange(self._nr_letters):
            self._ve_letters[i].set(on=is_level1,
                                    color=(std and self.letter_color
                                           or self.letter_stimulus_color))

    def stimulus(self, i_element, on=True):
        '''
        turn on/off the stimulus elements and turn off/on the normal elements.
        '''
        self._ve_shapes[i_element].set(
            color=(on and self.stimuli_colors[i_element] or self.shape_color))
        if self._current_level == 1:
            for i in xrange(len(self.letter_set[i_element])):
                self._ve_letters[(self._nr_elements - 1) * i_element + i].set(
                    color=(on and self.letter_stimulus_color
                           or self.letter_color))
        else:
            self._ve_letters[self._nr_letters + i_element].set(
                color=(on and self.letter_stimulus_color or self.letter_color))

    def fixation(self, state=True):
        """
        turn on/off the fixation elements.

        """
        self._ve_fixationpoint.set(on=state)

    def feedback(self):
        # turn on feedback box and turn off fixationpoint:
        self._ve_feedback_box.set(on=True)
        self.fixation(False)

        if self._current_level == 1:
            '''level 1: present classified letter group in center and move letters to circles '''

            ## display letter group:
            for i in xrange(self._nr_elements - 1):
                self._ve_feedback_letters[i].set(
                    on=True, text=self.letter_set[self._classified_element][i])
            ## turn on current element:
            self.stimulus(self._classified_element, True)

            ## turn off other letters:
            idx_start = self._classified_element * (self._nr_elements - 1)
            idx_end = idx_start + self._nr_elements - 1
            for i in xrange(idx_start):
                self._ve_letters[i].set(on=False)
            for i in xrange(idx_end, self._nr_letters):
                self._ve_letters[i].set(on=False)

            ## present:
            self.stimulus(self._classified_element, False)
            self._presentation.set(go_duration=(self.feedback_duration,
                                                'seconds'))
            self._presentation.go()

            ## turn off current element:
            for i in xrange(idx_start, idx_end):
                self._ve_letters[i].set(on=False)
            for i in xrange(self._nr_elements - 1):
                self._ve_feedback_letters[i].set(on=False)

            ## animate letters:
            self._presentation.set(go_duration=(self.animation_time,
                                                'seconds'))
            self._viewport.parameters.stimuli.extend([None] *
                                                     (self._nr_elements - 1))

            def update(t):
                dt = t / self.animation_time
                self._viewport.parameters.stimuli = self._viewport.parameters.stimuli[:-(
                    self._nr_elements - 1)]
                feedback_letters = []
                for i in xrange(self._nr_elements - 1):
                    pos = animate_sigmoid(
                        NP.add(self._letter_layout.positions[i],
                               self._centerPos), self._shape_positions[i], dt)
                    font_size = int(
                        round(
                            animate(self.font_size_level1,
                                    self.font_size_level2, dt)))
                    feedback_letters.append(
                        Text(position=pos,
                             color=self.letter_color,
                             font_size=font_size,
                             text=self.letter_set[self._classified_element][i],
                             anchor="center"))
                self._viewport.parameters.stimuli.extend(feedback_letters)

            self._presentation.add_controller(
                None, None, FunctionController(during_go_func=update))

            # send to screen:
            self._presentation.go()
            self._presentation.remove_controller(None, None, None)
            self._viewport.parameters.stimuli = self._viewport.parameters.stimuli[:-(
                self._nr_elements - 1)]

            ## turn on level 2 letters:
            for i in xrange(len(self.letter_set[self._classified_element])):
                self._ve_letters[self._nr_letters + i].set(
                    on=True, text=self.letter_set[self._classified_element][i])

        else:
            ''' level 2: present classified letter and move it to wordbox '''

            ## check if backdoor classified:
            if self._classified_letter >= len(
                    self.letter_set[self._classified_element]):
                text = ' '
            else:
                text = self.letter_set[self._classified_element][
                    self._classified_letter]

            ## display letter:
            self._ve_feedback_letters[-1].set(on=True, text=text)

            ## turn on current element stimulusw:
            if not self.offline:
                self.stimulus(self._classified_letter, True)

            ## turn off other letters:
            for i in xrange(self._classified_letter):
                self._ve_letters[self._nr_letters + i].set(on=False)
            for i in xrange(self._classified_letter + 1, self._nr_elements):
                self._ve_letters[self._nr_letters + i].set(on=False)

            ## present:
            self._presentation.set(go_duration=(1, 'seconds'))
            self._presentation.go()

            ## turn off current element stimulus:
            if not self.offline:
                self.stimulus(self._classified_letter, False)
                self._ve_letters[self._nr_letters +
                                 self._classified_letter].set(on=False)
            self._ve_feedback_letters[-1].set(on=False)

            ## animate letter, but not if backdoor classified:
            if self._classified_letter < len(
                    self.letter_set[self._classified_element]):
                self._presentation.set(go_duration=(self.animation_time,
                                                    'seconds'))
                self._viewport.parameters.stimuli.append(None)

                def update(t):
                    dt = t / self.animation_time
                    self._viewport.parameters.stimuli = self._viewport.parameters.stimuli[:
                                                                                          -1]
                    pos = animate_sigmoid(self._centerPos,
                                          self._current_letter_position, dt)
                    color = animate_sigmoid(self.letter_color,
                                            self.current_letter_color, dt)
                    font_size = int(
                        round(
                            animate(self.font_size_level2,
                                    self.font_size_current_letter, dt)))
                    self._viewport.parameters.stimuli.append(
                        Text(position=pos,
                             color=color,
                             font_size=font_size,
                             text=text,
                             anchor='center'))

                self._presentation.add_controller(
                    None, None, FunctionController(during_go_func=update))

                # send to screen:
                self._presentation.go()
                self._presentation.remove_controller(None, None, None)
                self._viewport.parameters.stimuli = self._viewport.parameters.stimuli[:
                                                                                      -1]
            else:
                self._presentation.set(go_duration=(self.animation_time,
                                                    'seconds'))
                self._presentation.go()

            ## turn on level 1 letters:
            for i in xrange(self._nr_letters):
                self._ve_letters[i].set(on=True)

        ## turn off feedback box and turn on fixationpoint:
        self._ve_feedback_box.set(on=False)
        self.fixation()
	def __init__(self, position, radius=2.0):
		FilledCircle.__init__(self, color=self.grey, position=position, 
					    radius=radius)
 def init_screen_elements(self):
     '''
     Initializing screen elements
     '''
     self._letter_positions = []        
     ## create and place the circles and letters:
     circle_layout = CircularLayout(nr_elements=self._nr_elements,
                                    radius=self.speller_radius,
                                    start=NP.pi/6.*5)
     circle_layout.positions.reverse()
     self._letter_layout = CircularLayout(nr_elements=self._nr_elements,
                                    radius=self.circle_radius*0.65,
                                    start=NP.pi/6.*5)
     self._letter_layout.positions.reverse()
     self._shape_positions = [(x+self.geometry[2]/2, y+self._spellerHeight/2) for (x,y) in circle_layout.positions]
     # add the standard elements:
     for i in xrange(self._nr_elements):
         self._ve_edges.append(FilledCircle(radius=self._edge_radius,
                                             position=self._shape_positions[i],
                                             color=self.edge_color))
         self._ve_shapes.append(FilledCircle(radius=self._circle_radius,
                                             position=self._shape_positions[i],
                                             color=self.shape_color))
         # add the letters of level 1:
         for j in xrange(len(self.letter_set[i])): # warning: self.letter_set must be at least of length self._nr_elements!!!
             self._letter_positions.append((self._letter_layout.positions[j][0]+self._shape_positions[i][0],
                                            self._letter_layout.positions[j][1]+self._shape_positions[i][1]))
             self._ve_letters.append(Text(position=self._letter_positions[-1],
                                          text=self.letter_set[i][j],
                                          font_size=self.font_size_level1,
                                          color=self.letter_color,
                                          anchor='center'))
     
     # add the stimuli letters of level 1:
     letter_layout2 = CircularLayout(nr_elements=self._nr_elements,
                                    radius=self.circle_radius*0.65*self.flash_size_factor,
                                    start=NP.pi/6.*5)
     letter_layout2.positions.reverse()
     for i in xrange(self._nr_elements):
         for j in xrange(len(self.letter_set[i])): # warning: self.letter_set must be at least of length self._nr_elements!!!
             self._letter_positions.append((letter_layout2.positions[j][0]+self._shape_positions[i][0],
                                            letter_layout2.positions[j][1]+self._shape_positions[i][1]))
             self._ve_letters.append(Text(position=self._letter_positions[-1],
                                          text=self.letter_set[i][j],
                                          font_size=int(NP.floor(self.font_size_level1*self.flash_size_factor)),
                                          color=self.letter_stimulus_color,
                                          anchor='center',
                                          on=False))
     
     # add letters of level 2:
     for i in xrange(self._nr_elements):
         self._ve_letters.append(Text(position=self._shape_positions[i],
                                      text=" ",
                                      font_size=self.font_size_level2,
                                      color=self.letter_color,
                                      anchor='center',
                                      on=False))
         
     # add stimuli letters of level 2:
     for i in xrange(self._nr_elements):
         self._ve_letters.append(Text(position=self._shape_positions[i],
                                      text=" ",
                                      font_size=int(NP.floor(self.font_size_level2*self.flash_size_factor)),
                                      color=self.letter_stimulus_color,
                                      anchor='center',
                                      on=False))
         
     ## add fixation point:
     self._ve_fixationpoint = FilledCircle(radius=self.fixationpoint_size,
                                           position=self._centerPos,
                                           color=self.fixationpoint_color)
             
     
     ## create feedback box:
     self._ve_feedback_box = Target2D(position=self._centerPos,
                                      size=(self.feedbackbox_size, self.feedbackbox_size),
                                      color=self.feedback_color,
                                      on=False)
     
     ## add feedback letters:
     self._ve_feedback_letters = []
     for i in xrange(self._nr_elements-1):
         self._ve_feedback_letters.append(Text(position=(self._letter_layout.positions[i][0]+self._centerPos[0],
                                                         self._letter_layout.positions[i][1]+self._centerPos[1]),
                                               color=self.letter_color,
                                               font_size=self.font_size_level1,
                                               text=" ",
                                               on=False,
                                               anchor="center"))
     self._ve_feedback_letters.append(Text(position=self._centerPos,
                                           color=self.letter_color,
                                           font_size=self.font_size_level2,
                                           text=" ",
                                           anchor='center',
                                           on=False))
     
     ## put all in elements container:
     self._ve_elements.extend(self._ve_edges)
     self._ve_elements.extend(self._ve_shapes)
     self._ve_elements.extend(self._ve_letters)
     self._ve_elements.extend([self._ve_feedback_box])
     self._ve_elements.extend(self._ve_feedback_letters)
     self._ve_elements.append(self._ve_fixationpoint)
Exemple #16
0
    def __init_screen(self):
        ## create screen:
        if not self.fullscreen:
            os.environ["SDL_VIDEO_WINDOW_POS"] = "%d, %d" % (self.geometry[0], self.geometry[1])
        self._screen = Screen(
            size=(self.geometry[2], self.geometry[3]), fullscreen=self.fullscreen, bgcolor=self.bg_color, sync_swap=True
        )

        ## create letter box on top:
        self._ve_letterbox = Target2D(
            position=(self._centerPos[0], self.geometry[3] * (1 - 0.01) - self.letterbox_size[1] / 2.0),
            size=(self.letterbox_size[0], self.letterbox_size[1]),
            color=self.phrase_color,
        )
        self._ve_innerbox = Target2D(
            position=(self._centerPos[0], self.geometry[3] * (1 - 0.01) - self.letterbox_size[1] / 2.0),
            size=(self.letterbox_size[0] - 6, self.letterbox_size[1] - 6),
            color=self.bg_color,
        )

        self._current_letter_position = (
            self._centerPos[0],
            self.geometry[3] * (1 - 0.015) - self.letterbox_size[1] / 2.0,
        )
        self._ve_current_letter = Text(
            position=self._current_letter_position,
            text=(len(self._desired_letters[:1]) == 0 and " " or self._desired_letters[:1]),
            font_size=self.font_size_current_letter,
            color=self.current_letter_color,
            anchor="center",
        )

        self._ve_desired_letters = Text(
            position=(self._centerPos[0] + 5 + self.letterbox_size[0] / 2.0, self._current_letter_position[1]),
            text=(len(self._desired_letters[1:]) == 0 and " " or self._desired_letters[1:]),
            font_size=self.font_size_phrase,
            color=self.phrase_color,
            anchor="left",
        )

        self._ve_spelled_phrase = Text(
            position=(self._centerPos[0] - 5 - self.letterbox_size[0] / 2.0, self._current_letter_position[1]),
            text=(len(self._spelled_phrase) == 0 and " " or self._spelled_phrase),
            font_size=self.font_size_phrase,
            color=self.phrase_color,
            anchor="right",
        )

        # if we're in free spelling mode, we hide all text fields but
        # the _ve_spelled_phrase. we also need a multiline
        # _ve_spelled_phrase instead of the single lined one
        if self.offline == self.copy_spelling == False:
            self._spelled_phrase = "   "
            self._ve_spelled_phrase = WrappedText(
                position=(0, self._current_letter_position[1]),
                text=(len(self._spelled_phrase) == 0 and " " or self._spelled_phrase),
                font_size=self.font_size_phrase,
                color=self.phrase_color,
                size=(float(self.geometry[2]), float(self.geometry[3])),
            )
            for i in self._ve_letterbox, self._ve_innerbox, self._ve_current_letter, self._ve_desired_letters:
                i.set(on=False)

        ## add word box to elementlist:
        self._ve_elements.extend(
            [
                self._ve_letterbox,
                self._ve_innerbox,
                self._ve_current_letter,
                self._ve_desired_letters,
                self._ve_spelled_phrase,
            ]
        )

        ## create countdown:
        self._ve_countdown = Text(
            position=self._centerPos,
            text=" ",
            font_size=self.font_size_countdown,
            color=self.countdown_color,
            anchor="center",
            on=False,
        )

        ## create countdown shapes
        self._ve_countdown_shape = self.countdown_shapes[self.countdown_shape_select](
            radius=90, position=self._centerPos, color=self.countdown_shape_color, on=False
        )

        ## create oscillator circle:
        self._ve_oscillator = FilledCircle(
            position=(self.osc_size / 2 + 10, self.osc_size / 2 + 10),
            radius=self.osc_size / 2,
            color=self.osc_color,
            on=False,
        )

        ## create shapes and letters:
        self.init_screen_elements()

        ## add remaining elements to element list:
        self._ve_elements.extend([self._ve_countdown_shape, self._ve_countdown, self._ve_oscillator])

        ## add elements to viewport:
        self._viewport = Viewport(screen=self._screen, stimuli=self._ve_elements)
        self._presentation = Presentation(
            viewports=[self._viewport],
            handle_event_callbacks=[(pygame.KEYDOWN, self.keyboard_input), (pygame.QUIT, self.__stop)],
        )
Exemple #17
0
 def __init__(self):
     self.color = [random.random() for i in (1,2,3)]
     self.type = 'circle'
     
     FilledCircle.__init__(self, color = self.color, radius = 50, **std_params)
 def __init__(self, position, radius=2.0):
     FilledCircle.__init__(self,
                           color=self.grey,
                           position=position,
                           radius=radius)
Exemple #19
0
class CakeSpellerVE(VisualSpellerVE):
    '''
    Visual speller as a cake with six pieces.
    '''


    def init(self):
        '''
        initialize parameters
        '''
        VisualSpellerVE.init(self)
        
        ## sizes:
        self.letter_radius = 70
        self.speller_radius = 380
        self.fixationpoint_size = 5.0
        self.edge_size = 5
        self.font_size_level1 = 70         # letters in level 1
        self.font_size_level2 = 130         # letters in level 2
        self.feedbackbox_size = 200.0
        
        ## colors:
        self.shape_color = (0.0, 0.0, 0.0)
        self.edge_color = (1.0, 1.0, 1.0)
        self.stimuli_colors = [(1.0, 0.0, 0.0),
                               (0.0, 1.0, 0.0),
                               (0.0, 0.0, 1.0),
                               (1.0, 1.0, 0.0),
                               (1.0, 0.0, 1.0),
                               (0.0, 1.0, 1.0)]
        self.letter_color = (1.0, 1.0, 1.0)
        self.letter_stimulus_color = (0.0, 0.0, 0.0)
        self.fixationpoint_color = (1.0, 1.0, 1.0)
        self.feedback_color = (0.7, 0.7, 0.7)
        self.countdown_color =self.bg_color
       
    def prepare_mainloop(self):
        '''
        called in pre_mainloop of superclass.
        '''
        assert len(self.stimuli_colors)==self._nr_elements
        
        ## init containers for VE elements:
        self._ve_shapes = []
        self._ve_edges = []
        self._ve_letters = []
        
    
    def init_screen_elements(self):
        '''
        Initialize screen elements
        '''        
        self._letter_positions = []
        ## create triangles:
        self._letter_layout = CircularLayout(nr_elements=self._nr_elements,
                                       radius=self.letter_radius,
                                       start=NP.pi/6.*5)
        self._letter_layout.positions.reverse()
        a = self.speller_radius / 2.
        b = a * NP.sqrt(3) / 3.
        self._shape_positions = [(self._centerPos[0],     self._centerPos[1] + 2*b),
                                 (self._centerPos[0] + a, self._centerPos[1] + b),
                                 (self._centerPos[0] + a, self._centerPos[1] - b),
                                 (self._centerPos[0],     self._centerPos[1] - 2*b),
                                 (self._centerPos[0] - a, self._centerPos[1] - b),
                                 (self._centerPos[0] - a, self._centerPos[1] + b)]
        orientaion = [180., 0.,
                      180., 0.,
                      180., 0.]
        for i in xrange(self._nr_elements):
            self._ve_edges.append(FilledTriangle(size=self.speller_radius,
                                                 position=self._shape_positions[i],
                                                 orientation=orientaion[i],
                                                 color=self.edge_color))
            self._ve_shapes.append(FilledTriangle(size=self.speller_radius - self.edge_size,
                                                  position=self._shape_positions[i],
                                                  orientation=orientaion[i],
                                                  color=self.shape_color))
            
            ## add the letters of level 1:
            for j in xrange(len(self.letter_set[i])): # warning: self.letter_set must be at least of length self._nr_elements!!!
                self._letter_positions.append((self._letter_layout.positions[j][0]+self._shape_positions[i][0],
                                               self._letter_layout.positions[j][1]+self._shape_positions[i][1]))
                self._ve_letters.append(Text(position=self._letter_positions[-1],
                                             text=self.letter_set[i][j],
                                             font_size=self.font_size_level1,
                                             color=self.letter_color,
                                             anchor='center'))
        
        ## add letters of level 2:
        for i in xrange(self._nr_elements):
            self._ve_letters.append(Text(position=self._shape_positions[i],
                                         text=" ",
                                         font_size=self.font_size_level2,
                                         color=self.letter_color,
                                         anchor='center',
                                         on=False))
        
        
        ## add fixation point:
        self._ve_fixationpoint = FilledCircle(radius=self.fixationpoint_size,
                                              position=self._centerPos,
                                              color=self.fixationpoint_color)
                
        
        ## create feedback box:
        self._ve_feedback_box = Target2D(position=self._centerPos,
                                         size=(self.feedbackbox_size, self.feedbackbox_size),
                                         color=self.feedback_color,
                                         on=False)
        
        ## add feedback letters:
        self._ve_feedback_letters = []
        for i in xrange(self._nr_elements-1):
            self._ve_feedback_letters.append(Text(position=(self._letter_layout.positions[i][0]+self._centerPos[0],
                                                            self._letter_layout.positions[i][1]+self._centerPos[1]),
                                                  color=self.letter_color,
                                                  font_size=self.font_size_level1,
                                                  text=" ",
                                                  on=False,
                                                  anchor="center"))
        self._ve_feedback_letters.append(Text(position=self._centerPos,
                                              color=self.letter_color,
                                              font_size=self.font_size_level2,
                                              text=" ",
                                              anchor='center',
                                              on=False))
        
        ## put all in elements container:
        self._ve_elements.extend(self._ve_edges)
        self._ve_elements.extend(self._ve_shapes)
        self._ve_elements.extend(self._ve_letters)
        self._ve_elements.append(self._ve_feedback_box)
        self._ve_elements.extend(self._ve_feedback_letters)
        self._ve_elements.append(self._ve_fixationpoint)
    
    
    def set_countdown_screen(self):
        '''
        set screen elements to countdown state
        '''
        self.set_standard_screen(False)
 
    
    
    def set_standard_screen(self, std=True):
        '''
        set screen elements to standard state.
        '''
        is_level1 = self._current_level==1
        for i in xrange(self._nr_elements):
            self._ve_shapes[i].set(color=(std and self.shape_color or self.stimuli_colors[i]), on=True)
            self._ve_letters[self._nr_letters + i].set(on=not is_level1, color=(std and self.letter_color or self.letter_stimulus_color))
        for i in xrange(self._nr_letters):
            self._ve_letters[i].set(on=is_level1, color=(std and self.letter_color or self.letter_stimulus_color))
    
    def stimulus(self, i_element, on=True):
        '''
        turn on/off the stimulus elements and turn off/on the normal elements.
        '''
        self._ve_shapes[i_element].set(color=(on and self.stimuli_colors[i_element] or self.shape_color))
        if self._current_level==1:
            for i in xrange(len(self.letter_set[i_element])):
                self._ve_letters[(self._nr_elements-1)*i_element + i].set(color=(on and self.letter_stimulus_color or self.letter_color))
        else:
            self._ve_letters[self._nr_letters + i_element].set(color=(on and self.letter_stimulus_color or self.letter_color))

    def fixation(self, state=True):
        """
        turn on/off the fixation elements.

        """
        self._ve_fixationpoint.set(on=state)
    
    def feedback(self):
        # turn on feedback box and turn off fixationpoint:
        self._ve_feedback_box.set(on=True)
        self.fixation(False)
        
        if self._current_level == 1:
            
            '''level 1: present classified letter group in center and move letters to circles '''
            
            ## display letter group:
            for i in xrange(self._nr_elements-1):
                self._ve_feedback_letters[i].set(on=True,
                                                 text=self.letter_set[self._classified_element][i])
            ## turn on current element:
            self.stimulus(self._classified_element, True)
            
            ## turn off other letters:
            idx_start = self._classified_element*(self._nr_elements-1)
            idx_end = idx_start + self._nr_elements-1
            for i in xrange(idx_start):
                self._ve_letters[i].set(on=False)
            for i in xrange(idx_end, self._nr_letters):
                self._ve_letters[i].set(on=False)
            
            ## present:
            self.stimulus(self._classified_element, False)
            self._presentation.set(go_duration=(self.feedback_duration, 'seconds'))
            self._presentation.go()
            
            ## turn off current element:
            for i in xrange(idx_start, idx_end):
                self._ve_letters[i].set(on=False)
            for i in xrange(self._nr_elements-1):
                self._ve_feedback_letters[i].set(on=False)
            
            ## animate letters:
            self._presentation.set(go_duration=(self.animation_time, 'seconds'))
            self._viewport.parameters.stimuli.extend([None]*(self._nr_elements-1))
            def update(t):
                dt = t/self.animation_time
                self._viewport.parameters.stimuli = self._viewport.parameters.stimuli[:-(self._nr_elements-1)]
                feedback_letters = []
                for i in xrange(self._nr_elements-1):
                    pos = animate_sigmoid(NP.add(self._letter_layout.positions[i], self._centerPos), self._shape_positions[i], dt)
                    font_size = int(round(animate(self.font_size_level1, self.font_size_level2, dt)))
                    feedback_letters.append(Text(position=pos,
                                                 color=self.letter_color,
                                                 font_size=font_size,
                                                 text=self.letter_set[self._classified_element][i],
                                                 anchor="center"))
                self._viewport.parameters.stimuli.extend(feedback_letters)
            self._presentation.add_controller(None,None,FunctionController(during_go_func=update))
            
            # send to screen:
            self._presentation.go()
            self._presentation.remove_controller(None,None,None)
            self._viewport.parameters.stimuli = self._viewport.parameters.stimuli[:-(self._nr_elements-1)]
                
            ## turn on level 2 letters:
            for i in xrange(len(self.letter_set[self._classified_element])):
                self._ve_letters[self._nr_letters + i].set(on=True, text=self.letter_set[self._classified_element][i])

        else: 
            ''' level 2: present classified letter and move it to wordbox '''
            
            ## check if backdoor classified:
            if self._classified_letter >= len(self.letter_set[self._classified_element]):
                text = ' '
            else:
                text = self.letter_set[self._classified_element][self._classified_letter]
                
            ## display letter:
            self._ve_feedback_letters[-1].set(on=True, text=text)
            
            ## turn on current element stimulusw:
            if not self.offline:
                self.stimulus(self._classified_letter, True)
            
            ## turn off other letters:
            for i in xrange(self._classified_letter):
                self._ve_letters[self._nr_letters + i].set(on=False)
            for i in xrange(self._classified_letter+1, self._nr_elements):
                self._ve_letters[self._nr_letters + i].set(on=False)
            
            ## present:
            self._presentation.set(go_duration=(1, 'seconds'))
            self._presentation.go()
            
            ## turn off current element stimulus:
            if not self.offline:
                self.stimulus(self._classified_letter, False)
                self._ve_letters[self._nr_letters + self._classified_letter].set(on=False)
            self._ve_feedback_letters[-1].set(on=False)
            
            ## animate letter, but not if backdoor classified:
            if self._classified_letter < len(self.letter_set[self._classified_element]):
                self._presentation.set(go_duration=(self.animation_time, 'seconds'))
                self._viewport.parameters.stimuli.append(None)
                def update(t):
                    dt = t/self.animation_time
                    self._viewport.parameters.stimuli = self._viewport.parameters.stimuli[:-1]
                    pos = animate_sigmoid(self._centerPos, self._current_letter_position, dt)
                    color = animate_sigmoid(self.letter_color, self.current_letter_color, dt) 
                    font_size = int(round(animate(self.font_size_level2, self.font_size_current_letter, dt)))
                    self._viewport.parameters.stimuli.append(Text(position=pos,
                                                                  color=color,
                                                                  font_size=font_size,
                                                                  text=text,
                                                                  anchor='center'))
                self._presentation.add_controller(None,None,FunctionController(during_go_func=update))
                
                # send to screen:
                self._presentation.go()
                self._presentation.remove_controller(None,None,None)
                self._viewport.parameters.stimuli = self._viewport.parameters.stimuli[:-1]
            else:
                self._presentation.set(go_duration=(self.animation_time, 'seconds'))
                self._presentation.go()
                
                    
            ## turn on level 1 letters:
            for i in xrange(self._nr_letters):
                self._ve_letters[i].set(on=True)
        
        ## turn off feedback box and turn on fixationpoint:
        self._ve_feedback_box.set(on=False)
        self.fixation()