-
Notifications
You must be signed in to change notification settings - Fork 5
Sample Experiment 1
This sample program can be used to setup a simple eyetracking experiment to do the following task:
- Present sentence based stimuli to record eyetracking patterns of subjects.
- Present a response task after each stimulus. Subjects are presented with a question and response to that question is recorded.
The experiment script can be found here: clementine.py
The program can handle multiple trials for individual subjects. All the information for the above task (eyetracking information, response to the question) for each subject is logged in a file. In addition, one can also log information about individual subject at the start of a session.
Here is an example of a single trial in a session.
- Prompt user to give personal information at the start of a session ~ - e.g. ID, Age, Gender, Mother tongue, etc.
- Show a welcome screen, provide some instructions about the trial
- Display a sentence for which eyetracking information needs to be recorded
- Display a question related to the previous sentence. Record the response.
- Go to the next trial
- ...
Before you run the script, make sure to tweek the code so that it behaves correctly for your experiment. Here are some of the things that you might want to change:
In the sample code, look for the following line in def session():
practStimList = es.StimList("clementine.practice.txt", order='sequential')You must specify your practice file name instead of
clementine.practice.txt above. Make sure that the file is in the
correct format (Data Format). If your experiment does not have a
practice trial, then all the statements related to practice trials ([2],
[4], [13]) should be commented out in def session():
def session():
#practStimList = es.StimList("clementine.practice.txt", order='sequential')
stimList = es.LatinSquareList("clementine.items.txt")
#practTrialList = [PracticeTrial(stim) for stim in practStimList]
trialList = [ReadingTrial(stim) for stim in stimList]
es.calibrateTracker()
es.ContinueDisplay(u"Willkommen zum Experiment.", response_device=es.Keyboard).run()
es.ContinueDisplay(u"Lies die Sätze und drücke die Daumentaste, wenn du fertig bist. "
u"\nDann beantworte die Frage mit den Zeigefingertasten."
u"\nZuerst wirst du ein paar Übungssätze lesen.",
vertical_spacing=10, response_device=es.Keyboard).run()
#for tr in practTrialList: tr.record()
es.ContinueDisplay(u"Nun beginnen die Experimentalsätze.", response_device=es.Keyboard).run()
for tr in trialList: tr.record()
es.TextDisplay(u"Vielen Dank für die Teilnahme. "
u"\nLeertaste zum beenden.", response_device=es.Keyboard).run()Note that you can easily change the welcome message at the start of
session by changing the text in es.ContinueDisplay at [7] and [9].
In the sample code, look for the following line in def session():
stimList = es.LatinSquareList("clementine.items.txt")You must speciify your experiment trial file name instead of
clementine.items.txt above. Make sure that the file has the
correct format (Data Format).
At various steps through the experiment, a subject is required to respond. This is of course required when answering the question, but also to move from one trial to the next. As an experiment designer, you can choose the device that will be used by the user to interact with the program. And based on what device is chosen, you will have to specify the response type. The sample code specifies keyboard as the response device. In the experiment a user is required to provide three kinds of reponse
- To move from Welcome screen/instruction screen to the next screen, a user can press any key on the keyboard.
- To move from a sentence stimulus screen to the question screen, a user can only press a 'spacebar'.
- To provide the answer to the question, a user can press 'f' (for 'no' as a response to the question) or 'j' (for 'yes' as a response). This will also initiate the next trial of the session.
To change the response device for the above tasks (to say, a
button-pad), you will have to change the value for all the instances of
response_device to es.EyeLinkButtons. Note that once you choose
es.EyeLinkButtons as your response_device, you have to subsequently
provide the appropriate response options for this input type. In
particular, you will have to specify the responses for (2) and (3)
above.
Notice the changes in possible_resp, cresp, continue_text and of
course response_device. Also note the all the es.ContinueDisplay
functions in def session() have no response_device, this is because
when absent, response_device defaults to es.EyeLinkButtons.
When you run your experiment you can change certain aspect of the sample code that specify things like font type [6], font size [5], screen-resolution [4], etc.:
es.Experiment(session_info = ['session','time','age','gender','study','glasses','nativelang','origin','hsleep','alc24'],
align=('left','center'),
margins=[10,0,10,0],
screen_size=(1600,900),
font_size=18,
font_name='arial.ttf'
)If you don't provide the values to these variable, they will default to values mentioned in defaults.py
The sample code prompts a user at the start of a session to provide
certain information such as gender, age, study, etc. This can be seen at
[1] above. If you intend to modify these, you will have to change
def __init__() of the Experiment class:
while 1:
subjectString=self['subject']=raw_input("Enter subject ID (0 for no data logging):").decode('437')
if self['subject'].isdigit():
self['subject'] = int(self['subject'])
subjectString = "%03d"%self['subject']
for attribute in self['session_info']:
longattr = attribute
if attribute == "study": longattr = "field of study"
if attribute == "gender": longattr = "gender (m/w)"
if attribute == "glasses": longattr = "glasses/lenses?"
if attribute == "alc24": longattr = "alcohol in last 24h? (y/n)"
if attribute == "hsleep": longattr = "hours of sleep last night"
if attribute == "nativelang": longattr = "mother tongue"
if attribute == "origin": longattr = "origin (Bundesland)"
if attribute == "time": longattr = "time (morning/noon/afternoon/evening)"
self[attribute] = raw_input("Enter %s: "%longattr).decode('437')
if self[attribute].isdigit(): self[attribute] = int(self[attribute])
if self[attribute] == "": self[attribute] = "."The sample code expects a tab-delimited stimuli text file. Each row (except for the 1st) corresponds to a stimulus in a trial. The first row provides the column header.
| experiment | itemnumber | condition | sentence | question | cresp |
|---|---|---|---|---|---|
| clementine | 01 | a | Es ist Maria, die das Klavier spielen kann und außerdem noch die Geige, sagte der Lehrer. | Kann Maria Klavier spielen? | Y |
| clementine | 01 | b | Es ist Maria, die das Klavier spielen kann und außerdem noch Luise und Jana, sagte der Lehrer. | Kann Maria Gitarre spielen? | N |
| clementine | 01 | c | Nur Maria kann das Klavier spielen und außerdem noch die Geige, sagte der Lehrer. | Kann Maria Klavier und Geige spielen? | Y |
| clementine | 01 | d | Nur Maria kann das Klavier spielen und außerdem noch Luise und Jana, sagte der Lehrer. | Können die Mädchen Flöte spielen? | N |
| clementine | 02 | a | ... | ... | ... |
| clementine | 02 | b | ... | ... | ... |
| clementine | 02 | c | ... | ... | ... |
| clementine | 02 | d | ... | ... | ... |
To run the program open your terminal (or command line interface) and type:
python clementine.py
Once the experiment is over, separate log info. for the subject can be
obtained from the data\ directory in your experiment folder. You will
also find some more directories in the data directory that contain
images shown during each trial (screenimages) and area of interest for
each trial (interestareas).
The sample code starts with instantiating an Experiment
(Experiment) object.:
es.Experiment(session_info = ['session','time','age','gender','study','glasses','nativelang','origin','hsleep','alc24'],
align=('left','center'),
margins=[10,0,10,0],
screen_size=(1600,900),
font_size=18,
font_name='arial.ttf'
)As mentioned earlier, this is to handle certain global experimental
settings such as font type [6], font size [5], screen-resolution [4],
etc. One could provide additional (or fewer) parameters while
instantiating an object. The complete list (with the default values) can
be found in (defaults.py)[../blob/master/EyeScript/defaults.py]. These parameters are related to
Screen defaults, Display object defaults, Text data file defaults,
Eye tracker data file defaults, Tracker parameters,
Drift correction, Cedrus button box. Except for the subject id
that a user has to mandatorily enter, the session_info attribute (if
provided) enables the experimenter to ask additional question at the
start of a session.
ReadingTrial and PracticeTrial definitions describe the trials in a
session. They inherit the Trial class (Trial).
The __init__ method in both these definitions prepares the trial
stimulus (that are stored as images in data/screenimages). The image
file name comprises of experiment name, item number, and
condition. These variables are conjoined by underscores and finally
suffixed with '_s1' followed by either a '.jpg' or '.ias'
(depending on the type of file) [3]-[6]. s1 stands for 'slide 1'. In
case a sequence of sentences is shown in each trial, the corresponding
images can be distinguished by s2, s3, etc. The yes/no comprehension
question on the other hand is not stored as an image.:
def __init__(self,stim):
self.slides = []
imageName = "%s_%s_%s_s"%(stim['experiment'],
stim['itemnumber'],
stim['condition'],
)
nr_lines = len(stim['sentence'].split('\n'))
self.slides.append(es.TextDisplay(unicode(stim['sentence']),
name='slide_1',
font_size=14,
response_device=es.Keyboard,
logging=['rt','resp'],
possible_resp = ["space"],
background_for=self,
screen_image_file=os.path.join('screenimages', imageName+"1.jpg"),
interest_area_file=os.path.join('interestareas', imageName+"1.ias"),
nr_lines = nr_lines
))
self.question = es.ContinueDisplay(stim['question'],
name='question',
font_size=16,
align=('center','center'),
vertical_spacing=0,
response_device=es.Keyboard,
possible_resp = ['f','j'],
cresp=stim['cresp'] == "Y" and 'f' or 'j',
continue_text = u'f: NEIN j: JA'
)
self.setMetadata(stim)The __init__ method takes a StimList object
(StimList) (which is basically a list of dictionaries,
each dictionary representing a row in the stimuli file) as one of its
parameters. The stimulus and the question is instantiated as a
TextDisplay [9] and ContinueDisplay [20] object (Display).
Note that the parameters used while initializing a TextDisplay and
ContinueDisplay object, if not provided, will be obtained from
defaults.py. For both the stimulus and the following yes/no
comprehension question, the response_device is keyboard (lines [11]
and [24] respectively). Notice how the possible_resp differs for them.
To move from the stimulus screen one can only use a 'space' [13],
whereas one has to use either a 'f' key or a 'j' key to move from a
question screen [25]. Additionally, one has to specify the correct
response (cresp) for the question slide [26]. This is important so
that one can compare subject's response to the correct response. The
background_for is initialized to 'self' (instead of 'NONE') so that
the screen_image_file and interest_area_file are stored in the
appropriate directories.
Finally, we also keep some 'metadata' [31] that will be used for logging purposes later.
The run method of ReadingTrial/PracticeTrial defines the events that
comprise a trial:
def run(self):
#es.driftCorrect()
es.startRecording()
for slide in self.slides:
x, y = defaultParams['gcTargetCoords']
if slide.params['nr_lines'] > 1:
y = y - 0.5*(slide.params['nr_lines']-1)*(slide.params['vertical_spacing']+slide.params['singleline_height'])
es.gcFixation(target = (x,y))
slide.run()
es.stopRecording()
self.question.run()
if self.question['acc']:
correctFeedback.run()
else:
incorrectFeedback.run()The method initiates the eye-tracker (or a stub if tracker is not
attached) [2] - [3] (Trials Utility). Each trial begins with
displaying a fixation point and waiting until the subject has fixated on
it for a minimum duration [8]. We then display the actual slide (a
TextDisplay object; Display) [9]. slide.run() not only
displays the stimulus slide but also collects the subject response and
logs it if necessary. Once all the slides in a trial have been displayed
we stop the eyetracker [10] and display the question slide [11] (a
ContinueDisplay object; Display). Depending on the response
to the question we display the appropriate slide [12]-[15].
correctFeedback and incorrectFeedback are ContinueDisplay objects
and have been instantiated as:
correctFeedback = es.ContinueDisplay("Richtig!",align=('center','center'),duration=800,continue_text="")
incorrectFeedback = es.ContinueDisplay("Falsch!",align=('center','center'),duration=800,continue_text="")session() defines what a session in your experiment comprises of:
def session():
practStimList = es.StimList("clementine.practice.txt", order='sequential')
stimList = es.LatinSquareList("clementine.items.txt")
practTrialList = [PracticeTrial(stim) for stim in practStimList]
trialList = [ReadingTrial(stim) for stim in stimList]
es.calibrateTracker()
es.ContinueDisplay(u"Willkommen zum Experiment.", response_device=es.Keyboard).run()
es.ContinueDisplay(u"Lies die Sätze und drücke die Daumentaste, wenn du fertig bist. "
u"\nDann beantworte die Frage mit den Zeigefingertasten."
u"\nZuerst wirst du ein paar Übungssätze lesen.",
vertical_spacing=10, response_device=es.Keyboard).run()
for tr in practTrialList: tr.record()
es.ContinueDisplay(u"Nun beginnen die Experimentalsätze.", response_device=es.Keyboard).run()
for tr in trialList: tr.record()
es.TextDisplay(u"Vielen Dank für die Teilnahme. "
u"\nLeertaste zum beenden.", response_device=es.Keyboard).run()A session begins by reading the practice file by instantiating a
StimList object (see StimList; a StimList object
is basically a list of dictionaries, each dictionary representing a row
in the stimulis file). The experiment stimuli file is read as a
LatinSquareList object that inherits the StimList class [3]; it
ensures that the conditions in the trial file are counterbalanced across
subjects. If the experiment stimuli should not be counterbalanced
automatically, they can also be read as a StimList with order being
either 'sequential' or 'randomized'.
In [4] and [5] these StimList objects are used to instantiate lists of
ReadingTrial and PracticeTrial objects (Trial). We
then go on to display the welcome screen [7]-[11] followed by presenting
the practice [12] and the trial slides [14]. tr.record() is a method
in the Trial class (Trial) that initiates each trial (by
calling the ReadingTrial/PracticeTrial run() method that we saw
earlier), does some logging, etc. We finally end the session by
displaying the 'thank you' screen [15].
The session() function just dicussed is called by the callback
function runSession() (Experiment). runSession() also
closes the experiment and writes the log data onto the logfile
(textdatafile in Experiment's __init__())