Showing posts with label ButtonChooser. Show all posts
Showing posts with label ButtonChooser. Show all posts

Thursday, October 22, 2009

Presenting a stimulus for a fixed duration (despite button presses)

Especially with fMRI, I want my trials to last a particular duration, whether or not the subject responded by pressing a button.

The standard PRESENT function terminates as soon as a button has been pressed. You could immediately re-present the stimulus for the remaining duration, but this creates an annoying flicker.

I sub-classed Text and Image to add a PRESENT_FIXED_DUR function that deals with this problem. This issue was also raised on the PyEPL mailing list.

For images:

#!/usr/bin/python

# from pyepl.hardware.graphics import Image
from pyepl.locals import Image
from pyepl.display import VideoTrack
from pyepl.locals import PresentationClock

class Image2(Image):

    def present_fixed_dur(self, clk=None, duration=None, bc=None, minDuration=None):
        """
        This is just like the standard Image.present, except
        that if you press a button, the image stays on the
        screen until the full duration has passed.

        I got rid of the jitter argument, because it makes
        things complicated.
        """

        v = VideoTrack.lastInstance()
        
        # get the clock if needed
        if clk is None: clk = PresentationClock()

        # show the image
        t = v.showCentered(self)
        timestamp = v.updateScreen(clk)

        if bc:
            # wait for button press
            button,bc_time = bc.waitWithTime(minDuration,duration,clk)
            # figure out how much time is remaining, now
            # that they've pressed the button, and delay for
            # just that
            rt = bc_time[0] - timestamp[0]
            clk.delay(duration-rt)
        else:
            clk.delay(duration)

        # unshow that image
        v.unshow(t)
        upd_ts = v.updateScreen(clk)
        # print 'presented for %ims' % (upd_ts[0] - timestamp[0])

        if bc: return timestamp,button,bc_time
        else: return timestamp

For text:

#!/usr/bin/python

# from pyepl.hardware.graphics import Image
from pyepl.locals import Text
from pyepl.display import VideoTrack

class Text2(Text):

    def present_fixed_dur(self, clk=None, duration=None, bc=None, minDuration=None, xProp=.5, yProp=.5):
        """
        Just as Text2.present, but (like
        Image2.present_fixed_dur): the standard
        Text.present, except that if you press a button, the
        stimulus stays on the screen until the full duration
        has passed.
        """

        # print 'starting present_fixed_dur'
        v = VideoTrack.lastInstance()
        
        # get the clock if needed
        if clk is None: clk = exputils.PresentationClock()

        # show the image
        t = v.showProportional(self,xProp,yProp)
        timestamp = v.updateScreen(clk)

        if bc:
            # wait for button press
            button,bc_time = bc.waitWithTime(minDuration,duration,clk)
            rt = bc_time[0] - timestamp[0]
            # print 'delaying by %i' % (duration-rt)
            # only delay if duration is True
            if duration and (duration-rt): clk.delay(duration-rt)
        else:
            # print 'delaying by %i' % duration
            if duration: clk.delay(duration)

        v.unshow(t)
        v.updateScreen(clk)

        # print 'ending present_fixed_dur'

        if bc: return timestamp,button,bc_time
        else: return timestamp

Sunday, August 3, 2008

Don't initialize the ButtonChooser with lowercase letters

Otherwise, you could get an inscrutable ValueError, 'Key already bound' error message. Always use uppercase letters, and all should be well. I think this has something to do with the error-checking in the KeyboardTrack that won't allow multiple ButtonChoosers to simultaneously be checking the same key.

How do I check whether a particular key has been pressed?

# initialize your ButtonChooser
pressed, rt = waitWithChoice(blah,blah)

if pressed==None:
  print 'You did not press a key'
else:
  if pressed==Key('a'):
    print 'You pressed the A key'
  else:
    print 'You pressed something other than the A key'

Creating a ButtonChooser with a list of keys

Because the ButtonChooser takes in a separate argument for each key, it's not obvious how to feed it a list. The trick is to make use of a handy bit of python syntax (see 7/8 of the way down for info on the *arguments kind of optional arguments).

For instance:

keys = ['A', 'B', 'C']
bc = ButtonChooser(*[Key(key) for key in keys])