Example #1
0
def test_StaticPeriod():
    static = StaticPeriod()
    static.start(0.1)
    wait(0.05)
    assert static.complete()==1
    static.start(0.1)
    wait(0.11)
    assert static.complete()==0

    win = Window(autoLog=False)
    static = StaticPeriod(screenHz=60, win=win)
    static.start(.002)
    assert win.recordFrameIntervals is False
    static.complete()
    assert static._winWasRecordingIntervals == win.recordFrameIntervals
    win.close()

    # Test if screenHz parameter is respected, i.e., if after completion of the
    # StaticPeriod, 1/screenHz seconds are still remaining, so the period will
    # complete after the next flip.
    refresh_rate = 100.0
    period_duration = 0.1
    timer = CountdownTimer()
    win = Window(autoLog=False)

    static = StaticPeriod(screenHz=refresh_rate, win=win)
    static.start(period_duration)
    timer.reset(period_duration )
    static.complete()

    assert np.allclose(timer.getTime(),
                       1.0/refresh_rate,
                       atol=0.001)
    win.close()
Example #2
0
class StaticPeriod(object):
    """A class to help insert a timing period that includes code to be run.

    Typical usage::

        fixation.draw()
        win.flip()
        ISI = StaticPeriod(screenHz=60)
        ISI.start(0.5) #start a period of 0.5s
        stim.image = 'largeFile.bmp' #could take some time
        ISI.complete() #finish the 0.5s, taking into account one 60Hz frame

        stim.draw()
        win.flip() #the period takes into account the next frame flip
        #time should now be at exactly 0.5s later than when ISI.start() was called

    """

    #NB - this might seem to be more sensible in the clock.py module, but that creates a circular reference
    # with the logging module.

    def __init__(self, screenHz=None, win=None, name='StaticPeriod'):
        """
        :param screenHz: the frame rate of the monitor (leave as None if you don't want this accounted for)
        :param win: if a visual.Window is given then StaticPeriod will also pause/restart frame interval recording
        :param name: give this StaticPeriod a name for more informative logging messages
        """
        self.status=NOT_STARTED
        self.countdown = CountdownTimer()
        self.name = name
        self.win = win
        if screenHz is None:
            self.frameTime = 0
        else:
            self.frameTime = 1.0/screenHz
    def start(self, duration):
        """Start the period. If this is called a second time, the timer will be reset and starts again
        """
        self.status = STARTED
        self.countdown.reset(duration)
        #turn off recording of frame intervals throughout static period
        if self.win:
            self.win.recordFrameIntervals = False
            self._winWasRecordingIntervals = self.win.recordFrameIntervals
    def complete(self):
        """Completes the period, using up whatever time is remaining with a call to wait()

        :return: 1 for success, 0 for fail (the period overran)
        """
        self.status=FINISHED
        timeRemaining = self.countdown.getTime()
        if self.win:
            self.win.recordFrameIntervals = self._winWasRecordingIntervals
        if timeRemaining<0:
            logging.warn('We overshot the intended duration of %s by %.4fs. The intervening code took too long to execute.' %(self.name, abs(timeRemaining)))
            return 0
        else:
            wait(timeRemaining)
            return 1
Example #3
0
 def __init__(self, screenHz=None, win=None, name='StaticPeriod'):
     """
     :param screenHz: the frame rate of the monitor (leave as None if you don't want this accounted for)
     :param win: if a visual.Window is given then StaticPeriod will also pause/restart frame interval recording
     :param name: give this StaticPeriod a name for more informative logging messages
     """
     self.status = NOT_STARTED
     self.countdown = CountdownTimer()
     self.name = name
     self.win = win
     if screenHz is None:
         self.frameTime = 0
     else:
         self.frameTime = 1.0 / screenHz
Example #4
0
def test_StaticPeriod_screenHz():
    """Test if screenHz parameter is respected, i.e., if after completion of the
    StaticPeriod, 1/screenHz seconds are still remaining, so the period will
    complete after the next flip.
    """
    refresh_rate = 100.0
    period_duration = 0.1
    timer = CountdownTimer()
    win = Window(autoLog=False)

    static = StaticPeriod(screenHz=refresh_rate, win=win)
    static.start(period_duration)
    timer.reset(period_duration)
    static.complete()

    assert np.allclose(timer.getTime(), 1.0 / refresh_rate, atol=0.001)
    win.close()
Example #5
0
 def __init__(self, screenHz=None, win=None, name='StaticPeriod'):
     """
     :param screenHz: the frame rate of the monitor (leave as None if you don't want this accounted for)
     :param win: if a visual.Window is given then StaticPeriod will also pause/restart frame interval recording
     :param name: give this StaticPeriod a name for more informative logging messages
     """
     self.status=NOT_STARTED
     self.countdown = CountdownTimer()
     self.name = name
     self.win = win
     if screenHz is None:
         self.frameTime = 0
     else:
         self.frameTime = 1.0/screenHz
Example #6
0
class StaticPeriod(object):
    """A class to help insert a timing period that includes code to be run.

    Typical usage::

        fixation.draw()
        win.flip()
        ISI = StaticPeriod(screenHz=60)
        ISI.start(0.5) #start a period of 0.5s
        stim.image = 'largeFile.bmp' #could take some time
        ISI.complete() #finish the 0.5s, taking into account one 60Hz frame

        stim.draw()
        win.flip() #the period takes into account the next frame flip
        #time should now be at exactly 0.5s later than when ISI.start() was called

    """

    #NB - this might seem to be more sensible in the clock.py module, but that creates a circular reference
    # with the logging module.

    def __init__(self, screenHz=None, win=None, name='StaticPeriod'):
        """
        :param screenHz: the frame rate of the monitor (leave as None if you don't want this accounted for)
        :param win: if a visual.Window is given then StaticPeriod will also pause/restart frame interval recording
        :param name: give this StaticPeriod a name for more informative logging messages
        """
        self.status = NOT_STARTED
        self.countdown = CountdownTimer()
        self.name = name
        self.win = win
        if screenHz is None:
            self.frameTime = 0
        else:
            self.frameTime = 1.0 / screenHz

    def start(self, duration):
        """Start the period. If this is called a second time, the timer will be reset and starts again
        """
        self.status = STARTED
        self.countdown.reset(duration)
        #turn off recording of frame intervals throughout static period
        if self.win:
            self.win.recordFrameIntervals = False
            self._winWasRecordingIntervals = self.win.recordFrameIntervals

    def complete(self):
        """Completes the period, using up whatever time is remaining with a call to wait()

        :return: 1 for success, 0 for fail (the period overran)
        """
        self.status = FINISHED
        timeRemaining = self.countdown.getTime()
        if self.win:
            self.win.recordFrameIntervals = self._winWasRecordingIntervals
        if timeRemaining < 0:
            logging.warn(
                'We overshot the intended duration of %s by %.4fs. The intervening code took too long to execute.'
                % (self.name, abs(timeRemaining)))
            return 0
        else:
            wait(timeRemaining)
            return 1
def main():
    # I set up my siplay size and background colour
    DISPSIZE = (1400, 800)
    BGC = (-1, -1, -1)

    # for my game I need to create some variables:
    score = 0
    lives = 3
    level = 1
    mouse_x = 0
    mouse_y = 0

    # I create some objects:
    win = Window(size=DISPSIZE, units='pix', fullscr=False, color=BGC)

    mouse = Mouse(win)

    target = ImageStim(win, 'target.png', size=(420, 420))

    # I will display three text stimuli to the player while playing the game:
    lives_count = TextStim(
        win,
        text=f'Lives = {lives}',
        height=35,
        color=(1, 0.2, 0.6),
        pos=(100, 330),
    )

    score_count = TextStim(win,
                           text=f'Score = {score}',
                           height=35,
                           color=(0.2, 0.2, 0.8),
                           pos=(450, 330))

    level_count = TextStim(win,
                           text=f'Level = {level}',
                           height=35,
                           color=(1, -0.5, 1),
                           pos=(850, 330))

    # I define the messages to show the player the outcome of the game:
    you_have_lost = TextStim(
        win,
        text='Boo! Not a great game, pal... Get it together!',
        height=35,
        color=(0.2, 0.2, 0.8),
        pos=(250, 230))

    you_have_won = TextStim(win,
                            text='Yey! Well done, champ! Time to celebrate!',
                            height=35,
                            color=(0.2, 0.2, 0.8),
                            pos=(250, 230))

    # These are the images I use for the winning and loosing scenarios:
    looser = ImageStim(win, 'failed.jpg', pos=(0, -100), size=(420, 420))
    winner = ImageStim(win, 'tiny_trash.jpg', pos=(0, -100), size=(420, 420))

    # I introduce this dialog to save the user's ID:
    user_id_dialog = gui.Dlg(title="Target Game")
    user_id_dialog.addText('Please write your subject ID: a 4-digit code')
    user_id_dialog.addField('Subject ID:')
    ok_data = user_id_dialog.show()  # show dialog and wait for OK or Cancel

    if not user_id_dialog.OK:
        print('user cancelled')

    # NOW THE GAME WILL START:

    # If enabled, intro will play:
    enable_intro = True

    if enable_intro:
        show_intro(win)

    # We create this list to save our results into
    target_hits_per_level = [
        [],
        [],
        [],
        [],
    ]

    move_target_at_random_pos(
        target)  # first the target is shown on the screen

    lives_timer = CountdownTimer(
        5)  # Level 1 starts with 5 sec to hit the target
    mouse_click_clock = Clock()
    reaction_time_clock = Clock()
    change_target = False

    while level < 4 and lives > 0:
        target.draw()
        target_x, target_y = target.pos
        lives_count.draw()
        score_count.draw()
        level_count.draw()

        win.flip()

        keys_pressed = getKeys()
        if 'q' in keys_pressed:
            break
        mouse_is_pressed = mouse.getPressed()[0] == True

        mouse_x, mouse_y = mouse.getPos()
        level_count.setText(f'Level = {level}')

        #if the player does not click, the target moves and the player looses a life
        if lives_timer.getTime() <= 0:
            lives -= 1
            lives_count.setText(f'Lives = {lives}')
            mouse_in_target = None
            mouse_in_target_x = None
            mouse_in_target_y = None
            change_target = True

        # Check for a mouse click every 0.2s, so that we don't accept more than 1
        # press on mouse hold
        if mouse_is_pressed and mouse_click_clock.getTime() > 0.2:
            mouse_click_clock.reset()
            change_target = True

            if mouse_clicked_in_target(mouse, target):
                mouse_in_target = True
                mouse_in_target_x = mouse_x - target_x
                mouse_in_target_y = mouse_y - target_y
                score += 1
                score_count.setText(f'Score = {score}')

            else:
                lives -= 1
                lives_count.setText(f'Lives = {lives}')
                mouse_in_target = False
                mouse_in_target_x = None
                mouse_in_target_y = None

        if change_target:

            mouse_click = {
                'mouse_x': mouse_in_target_x,
                'mouse_y': mouse_in_target_y,
                'reaction_time': reaction_time_clock.getTime(),
                'mouse_in_target': mouse_in_target,
            }

            target_hits_per_level[level - 1].append(
                mouse_click)  # inddexes start from 0 --> level - 1

            if score == 5:
                lives_timer.reset(3)
                level = 2
            elif score == 10:
                lives_timer.reset(1)
                level = 3
            elif score == 15:
                level = 4

            move_target_at_random_pos(target)
            lives_timer.reset()
            reaction_time_clock.reset()
            change_target = False

    # Here we display the outcome of the game:
    if level == 4:
        you_have_won.draw()
        winner.draw()
    else:
        you_have_lost.draw()
        looser.draw()

    win.flip()
    wait(3)

    # Finally, we draw the overwivew for thr player

    draw_overview_target(
        win=win,
        level=1,
        target_pos=(-450, 0),
        text_pos=(50, 300),
        mouse_clicks_all_levels=target_hits_per_level,
    )

    draw_overview_target(
        win=win,
        level=2,
        target_pos=(0, 0),
        text_pos=(450, 300),
        mouse_clicks_all_levels=target_hits_per_level,
    )

    draw_overview_target(
        win=win,
        level=3,
        target_pos=(450, 0),
        text_pos=(850, 300),
        mouse_clicks_all_levels=target_hits_per_level,
    )

    win.flip()
    wait(4)

    # The user has not clicked Cancel on the subject ID window
    if ok_data is not None:
        write_results(target_hits_per_level, 'results-' + ok_data[0] + '.csv')

    win.close()