def on_enter(self): if isinstance(director.scene, TransitionScene): return super(Task, self).on_enter() header = [] if director.settings['mode'] == 'Experiment': header += ["datestamp", "encrypted_rin"] header += ["system_time", "mode", "trial", "state", "event_source", "event_type", "event_id", "screen_width", "screen_height", "mouse_x", "mouse_y", "study_time", "search_time"] if director.settings['eyetracker'] and self.client: self.smi_spl_header = ["smi_time", "smi_type", "smi_sxl", "smi_sxr", "smi_syl", "smi_syr", "smi_dxl", "smi_dxr", "smi_dyl", "smi_dyr", "smi_exl", "smi_exr", "smi_eyl", "smi_eyr", "smi_ezl", "smi_ezr"] header += self.smi_spl_header + ["smi_fx", "smi_fy"] self.logger = Logger(header) self.tarfile = tarfile.open('data/%s.tar.gz' % director.settings['filebase'], mode='w:gz') self.position = ((self.screen[0] - self.screen[1]) / 2, 0) self.cm = CollisionManagerBruteForce() self.mono = font.load("Mono", 32) self.shapes = {"oval":"F", # "diamond":"T", "crescent":"Q", "cross":"Y", "star":"C"} # star 83x79 1.05 2581/6557 .39 # oval 55x83 .66 3427/4565 .75 # crescent 51x77 .66 1580/3927 .40 # cross 61x62 .98 1783/3782 .47 # rect 46x73. .63 3358/3358 1 # cross2 57x84 .67 1532/4788 .31 self.shape_mod = {"oval":[1.0, 1.0, 1.0], # "diamond":"T", "crescent":[1.07, 1.07, 1.07], "cross":[1.15, 1.15, 1.15], "star":[1.0, 1.0, 1.0]} self.font = font.load('Cut Outs for 3D FX', 128) for shape in self.shapes: self.shapes[shape] = self.font.get_glyphs(self.shapes[shape])[0].get_texture(True) s = 50 v = 100 self.colors = {"red": hsv_to_rgb(0, s, v), "yellow": hsv_to_rgb(72, s, v), "green": hsv_to_rgb(144, s, v), # "purple": hsv_to_rgb(288, s, v), "blue": hsv_to_rgb(216, s, v)} self.side = self.screen[1] / 11 self.ratio = self.side / 128 self.scales = [self.ratio * 2, self.ratio * 1.25, self.ratio * .5] self.sizes = ["large", "medium", "small"] self.ready_label = Label("Click mouse when ready!", position=(self.width / 2, self.height / 2), font_name='Pipe Dream', font_size=24, color=(0, 0, 0, 255), anchor_x='center', anchor_y='center') self.gaze = Label('G',font_name='Cut Outs for 3D FX', font_size=48, position=(self.width / 2, self.height / 2), color=(255, 0, 0, 192), anchor_x='center', anchor_y='center') self.attention = Label('G',font_name='Cut Outs for 3D FX', font_size=48, position=(self.width / 2, self.height / 2), color=(0, 0, 255, 192), anchor_x='center', anchor_y='center') self.reset_state() if director.settings['player'] == "ACT-R": self.add(self.gaze, z=99) self.add(self.attention, z=99) self.client_actr.addDispatcher(self.actr_d) self.state = self.STATE_WAIT_ACTR_CONNECTION self.dispatch_event("actr_wait_connection") elif director.settings['eyetracker']: self.state = self.STATE_CALIBRATE self.dispatch_event("start_calibration", self.calibration_ok, self.calibration_bad) else: self.next_trial()
class Task(ColorLayer, pyglet.event.EventDispatcher): d = Dispatcher() actr_d = JNI_Dispatcher() states = ["INIT", "WAIT_ACTR_CONNECTION", "WAIT_ACTR_MODEL", "CALIBRATE", "IGNORE_INPUT", "WAIT", "STUDY", "SEARCH", "RESULTS"] STATE_INIT = 0 STATE_CALIBRATE = 1 STATE_IGNORE_INPUT = 2 STATE_WAIT_ACTR_CONNECTION = 3 STATE_WAIT_ACTR_MODEL = 4 STATE_WAIT = 5 STATE_STUDY = 6 STATE_SEARCH = 7 STATE_RESULTS = 8 is_event_handler = True def __init__(self, client, actr): self.screen = director.get_window_size() super(Task, self).__init__(168, 168, 168, 255, self.screen[1], self.screen[1]) self.state = self.STATE_INIT self.client = client self.client_actr = actr self.circles = [] self.trial_complete = False def on_enter(self): if isinstance(director.scene, TransitionScene): return super(Task, self).on_enter() header = [] if director.settings['mode'] == 'Experiment': header += ["datestamp", "encrypted_rin"] header += ["system_time", "mode", "trial", "state", "event_source", "event_type", "event_id", "screen_width", "screen_height", "mouse_x", "mouse_y", "study_time", "search_time"] if director.settings['eyetracker'] and self.client: self.smi_spl_header = ["smi_time", "smi_type", "smi_sxl", "smi_sxr", "smi_syl", "smi_syr", "smi_dxl", "smi_dxr", "smi_dyl", "smi_dyr", "smi_exl", "smi_exr", "smi_eyl", "smi_eyr", "smi_ezl", "smi_ezr"] header += self.smi_spl_header + ["smi_fx", "smi_fy"] self.logger = Logger(header) self.tarfile = tarfile.open('data/%s.tar.gz' % director.settings['filebase'], mode='w:gz') self.position = ((self.screen[0] - self.screen[1]) / 2, 0) self.cm = CollisionManagerBruteForce() self.mono = font.load("Mono", 32) self.shapes = {"oval":"F", # "diamond":"T", "crescent":"Q", "cross":"Y", "star":"C"} # star 83x79 1.05 2581/6557 .39 # oval 55x83 .66 3427/4565 .75 # crescent 51x77 .66 1580/3927 .40 # cross 61x62 .98 1783/3782 .47 # rect 46x73. .63 3358/3358 1 # cross2 57x84 .67 1532/4788 .31 self.shape_mod = {"oval":[1.0, 1.0, 1.0], # "diamond":"T", "crescent":[1.07, 1.07, 1.07], "cross":[1.15, 1.15, 1.15], "star":[1.0, 1.0, 1.0]} self.font = font.load('Cut Outs for 3D FX', 128) for shape in self.shapes: self.shapes[shape] = self.font.get_glyphs(self.shapes[shape])[0].get_texture(True) s = 50 v = 100 self.colors = {"red": hsv_to_rgb(0, s, v), "yellow": hsv_to_rgb(72, s, v), "green": hsv_to_rgb(144, s, v), # "purple": hsv_to_rgb(288, s, v), "blue": hsv_to_rgb(216, s, v)} self.side = self.screen[1] / 11 self.ratio = self.side / 128 self.scales = [self.ratio * 2, self.ratio * 1.25, self.ratio * .5] self.sizes = ["large", "medium", "small"] self.ready_label = Label("Click mouse when ready!", position=(self.width / 2, self.height / 2), font_name='Pipe Dream', font_size=24, color=(0, 0, 0, 255), anchor_x='center', anchor_y='center') self.gaze = Label('G',font_name='Cut Outs for 3D FX', font_size=48, position=(self.width / 2, self.height / 2), color=(255, 0, 0, 192), anchor_x='center', anchor_y='center') self.attention = Label('G',font_name='Cut Outs for 3D FX', font_size=48, position=(self.width / 2, self.height / 2), color=(0, 0, 255, 192), anchor_x='center', anchor_y='center') self.reset_state() if director.settings['player'] == "ACT-R": self.add(self.gaze, z=99) self.add(self.attention, z=99) self.client_actr.addDispatcher(self.actr_d) self.state = self.STATE_WAIT_ACTR_CONNECTION self.dispatch_event("actr_wait_connection") elif director.settings['eyetracker']: self.state = self.STATE_CALIBRATE self.dispatch_event("start_calibration", self.calibration_ok, self.calibration_bad) else: self.next_trial() def reset_state(self): s = int(director.settings['seed']) if s > 0: seed(s) self.current_trial = 0 self.total_trials = None if director.settings['mode'] == 'Experiment': self.gen_trials() self.fake_cursor = (self.screen[0] / 2, self.screen[1] / 2) def calibration_ok(self): self.dispatch_event("stop_calibration") self.next_trial() def calibration_bad(self): self.dispatch_event("stop_calibration") self.logger.close(True) self.tarfile.close() director.scene.dispatch_event("show_intro_scene") def on_exit(self): if isinstance(director.scene, TransitionScene): return if self.client_actr: self.client_actr.removeDispatcher(self.actr_d) self.client_actr.disconnect() super(Task, self).on_exit() for c in self.get_children(): if c != self.gaze and c != self.attention: self.remove(c) def next_trial(self): self.trial_complete = False if self.current_trial == self.total_trials: self.logger.close(True) self.tarfile.close() director.scene.dispatch_event("show_intro_scene") else: self.search_time = -1 self.study_time = -1 director.window.set_mouse_visible(False) self.clear_shapes() self.log_extra = {'screen_width':self.screen[0], 'screen_height': self.screen[1]} if director.settings['player'] == 'Human' and director.settings['mode'] == 'Experiment': self.log_extra['datestamp'] = director.settings['si']['timestamp'] self.log_extra['encrypted_rin'] = director.settings['si']['encrypted_rin'] self.state = self.STATE_WAIT self.current_trial += 1 self.gen_combos() self.add(self.ready_label) self.logger.open(StringIO()) self.logger.write(system_time=get_time(), mode=director.settings['mode'], trial=self.current_trial, event_source="TASK", event_type=self.states[self.state], state=self.states[self.state], event_id="START", **self.log_extra) self.dispatch_event("new_trial", self.current_trial, self.total_trials) if self.client: self.dispatch_event("show_headposition") if director.settings['player'] == 'ACT-R': X = VisualChunk(None, "text", self.screen[0] / 2, self.screen[1] / 2, value="Click mouse when ready!", width=self.ready_label.element.content_width, height=self.ready_label.element.content_height) self.client_actr.update_display([X], clear=True) def trial_done(self): self.trial_complete = True t = get_time() self.search_time = t - self.start_time self.logger.write(system_time=t, mode=director.settings['mode'], trial=self.current_trial, event_source="TASK", event_type=self.states[self.state], state=self.states[self.state], event_id="END", **self.log_extra) self.state = self.STATE_RESULTS self.logger.write(system_time=t, mode=director.settings['mode'], trial=self.current_trial, event_source="TASK", event_type=self.states[self.state], state=self.states[self.state], study_time=self.study_time, search_time=self.search_time, **self.log_extra) tmp = self.logger.file.getvalue() data = tarfile.TarInfo("%s/trial-%02d.txt" % (director.settings['filebase'], self.current_trial)) data.size = len(tmp) self.tarfile.addfile(data, StringIO(tmp)) tmp = StringIO() screenshot().save(tmp, "png") data = tarfile.TarInfo("%s/trial-%02d.png" % (director.settings['filebase'], self.current_trial)) data.size = len(tmp.getvalue()) tmp.seek(0) self.tarfile.addfile(data, tmp) self.logger.close() if director.settings['eyetracker'] and self.client: self.client.removeDispatcher(self.d) self.client.stopFixationProcessing() if director.settings['player'] == 'ACT-R' and director.settings['mode'] != 'Experiment': self.client_actr.trigger_event(":trial-complete") else: self.next_trial() def gen_trials(self): self.trials = [] for scale in self.sizes: for color in self.colors: for shape in self.shapes: for c in range(0, 8): self.trials.append([shape, color, scale, c]) self.total_trials = len(self.trials) shuffle(self.trials) def gen_combos(self): ids = range(1, len(self.sizes) * len(self.colors) * len(self.shapes) + 1) shuffle(ids) self.combos = [] for scale in self.sizes: for color in self.colors: for shape in self.shapes: self.combos.append([shape, color, scale, ids.pop()]) def gen_probe(self): for c in self.get_children(): if c != self.gaze and c != self.attention: self.remove(c) s = 0 if director.settings['mode'] == 'Experiment': trial = self.trials.pop() for c in self.combos: if c[0] == trial[0] and c[1] == trial[1] and c[2] == trial[2]: chunk = c s = trial[3] break else: if director.settings['mode'] == 'Moderate': s = choice(range(0, 4)) elif director.settings['mode'] == 'Hard': s = choice(range(0, 7)) elif director.settings['mode'] == 'Insane': s = choice(range(0, 8)) chunk = choice(self.combos) self.probe = Probe(chunk, s, self.side, (self.screen[1] / 2, self.screen[1] / 2), 14 * self.ratio) self.add(self.probe) def clear_shapes(self): self.circles = [] self.cm.clear() for c in self.get_children(): if c != self.gaze and c != self.attention: self.remove(c) self.batch = BatchNode() self.id_batch = BatchNode() def show_shapes(self): self.cm.add(self.probe) ratio = self.side / 128 self.circles = [] shapeinfo = {} shapeinfo['probe'] = {"id": self.probe.chunk[3], "color": self.probe.color_visible, "shape": self.probe.shape_visible, "size": self.probe.size_visible} actr_chunks = self.probe.actr_chunks for c in self.combos: img = self.shapes[c[0]] img.anchor_x = 'center' img.anchor_y = 'center' sprite = Shape(img, chunk=c, rotation=randrange(0, 365), color=self.colors[c[1]], scale=self.shape_mod[c[0]][self.sizes.index(c[2])] * self.scales[self.sizes.index(c[2])]) pad = sprite.radius sprite.set_position(uniform(pad, self.screen[1] - pad), uniform(pad, self.screen[1] - pad)) while self.cm.objs_colliding(sprite): sprite.set_position(uniform(pad, self.screen[1] - pad), uniform(pad, self.screen[1] - pad)) fs = 14 * ratio text.Label("%02d" % c[3], font_size=fs, x=sprite.position[0], y=sprite.position[1], font_name="Monospace", color=(32, 32, 32, 255), anchor_x='center', anchor_y='center', batch=self.id_batch.batch) shapeinfo[c[3]] = {'shape':c[0], 'color':c[1], 'size':c[2], 'id': c[3], 'radius':sprite.cshape.r, 'x':sprite.position[0], 'y':sprite.position[1]} self.circles.append(Circle(sprite.position[0] + (self.screen[0] - self.screen[1]) / 2, sprite.position[1], width=2 * sprite.cshape.r)) self.cm.add(sprite) self.batch.add(sprite) actr_chunks.append(PAAVChunk(None, "visual-object", sprite.position[0] + (self.screen[0] - self.screen[1]) / 2, sprite.position[1], width = 2 * sprite.cshape.r, height = 2 * sprite.cshape.r, fshape = ":w67-%s" % c[0], fcolor = ":w67-%s" % c[1], fsize = ":w67-%s" % c[2])) actr_chunks.append(VisualChunk(None, "text", sprite.position[0] + (self.screen[0] - self.screen[1]) / 2, sprite.position[1], width = 2 * fs, height = fs, value = "%02d" % c[3])) if director.settings['player'] == 'ACT-R' and actr_chunks: self.client_actr.update_display(actr_chunks, clear=False) self.circles.append(Circle(self.probe.position[0] + (self.screen[0] - self.screen[1]) / 2, self.probe.position[1], width=2 * self.probe.cshape.r)) self.add(self.batch, z=1) self.add(self.id_batch, z=2) s = StringIO(json.dumps(shapeinfo, sort_keys=True, indent=4)) data = tarfile.TarInfo("%s/trial-%02d.json" % (director.settings['filebase'], self.current_trial)) data.size = len(s.getvalue()) s.seek(0) self.tarfile.addfile(data, s) if ACTR6: @actr_d.listen('connectionMade') def ACTR6_JNI_Event(self, model, params): print "ACT-R Connection Made" self.state = self.STATE_WAIT_ACTR_MODEL self.dispatch_event("actr_wait_model") self.client_actr.setup(self.screen[1], self.screen[1]) @actr_d.listen('connectionLost') def ACTR6_JNI_Event(self, model, params): print "ACT-R Connection Lost" self.state = self.STATE_WAIT_ACTR_CONNECTION self.dispatch_event("actr_wait_connection") @actr_d.listen('reset') def ACTR6_JNI_Event(self, model, params): print "ACT-R Reset" self.state = self.STATE_WAIT_ACTR_MODEL self.dispatch_event("actr_wait_model") self.reset_state() @actr_d.listen('model-run') def ACTR6_JNI_Event(self, model, params): print "ACT-R Model Run" self.dispatch_event("actr_running") if params['resume']: if self.trial_complete: self.next_trial() else: self.reset_state() self.next_trial() @actr_d.listen('model-stop') def ACTR6_JNI_Event(self, model, params): print "ACT-R Model Stop" @actr_d.listen('gaze-loc') def ACTR6_JNI_Event(self, model, params): if params['loc']: params['loc'][0] -= (self.screen[0] - self.screen[1]) / 2 print "ACT-R Gaze: ", print params['loc'] self.gaze.position = params['loc'] self.gaze.visible = True else: self.gaze.visible = False @actr_d.listen('attention-loc') def ACTR6_JNI_Event(self, model, params): if params['loc']: params['loc'][0] -= (self.screen[0] - self.screen[1]) / 2 print "ACT-R Attention: ", print params['loc'] self.attention.position = params['loc'] self.attention.visible = True else: self.attention.visible = False @actr_d.listen('keypress') def ACTR6_JNI_Event(self, model, params): print "ACT-R Keypress: %s" % chr(params['keycode']) self.on_key_press(params['keycode'], None) @actr_d.listen('mousemotion') def ACTR6_JNI_Event(self, model, params): # Store "ACT-R" cursor in variable since we are # not going to move the real mouse print "ACT-R Mousemotion: ", print params self.fake_cursor = params['loc'] self.on_mouse_motion(self.fake_cursor[0], self.fake_cursor[1], None, None) @actr_d.listen('mouseclick') def ACTR6_JNI_Event(self, model, params): # Simulate a button press using the "ACT-R" cursor loc print "ACT-R Mouseclick" self.on_mouse_press(self.fake_cursor[0], self.fake_cursor[1], 1, None) if eyetracking: @d.listen('ET_FIX') def iViewXEvent(self, inResponse): eyedata = {} eyedata.update(self.log_extra) eyedata["smi_type"] = inResponse[0] eyedata["smi_time"] = inResponse[1] eyedata["smi_fx"] = inResponse[2] eyedata["smi_fy"] = inResponse[3] self.logger.write(system_time=get_time(), mode=director.settings['mode'], state=self.states[self.state], trial=self.current_trial, event_source="SMI", event_type="ET_FIX", **eyedata) @d.listen('ET_SPL') def iViewXEvent(self, inResponse): eyedata = {} eyedata.update(self.log_extra) for i, _ in enumerate(self.smi_spl_header): eyedata[self.smi_spl_header[i]] = inResponse[i] self.logger.write(system_time=get_time(), mode=director.settings['mode'], state=self.states[self.state], trial=self.current_trial, event_source="SMI", event_type="ET_SPL", **eyedata) # def draw(self): # super(Task, self).draw() # for c in self.circles: c.render() def on_mouse_press(self, x, y, buttons, modifiers): if self.state < self.STATE_IGNORE_INPUT: return if self.state == self.STATE_WAIT: self.logger.write(system_time=get_time(), mode=director.settings['mode'], trial=self.current_trial, event_source="TASK", event_type=self.states[self.state], state=self.states[self.state], event_id="END", **self.log_extra) self.gen_probe() self.state = self.STATE_STUDY if director.settings['player'] == 'ACT-R': self.client_actr.update_display(self.probe.actr_chunks, clear=True) t = get_time() self.start_time = t self.logger.write(system_time=t, mode=director.settings['mode'], trial=self.current_trial, event_source="TASK", event_type=self.states[self.state], state=self.states[self.state], event_id="START", **self.log_extra) if director.settings['eyetracker'] and self.client: self.dispatch_event("hide_headposition") self.client.addDispatcher(self.d) self.client.startFixationProcessing() elif self.state == self.STATE_SEARCH: self.logger.write(system_time=get_time(), mode=director.settings['mode'], trial=self.current_trial, event_source="USER", event_type=self.states[self.state], state=self.states[self.state], event_id="MOUSE_PRESS", mouse_x=x, mouse_y=y, **self.log_extra) if director.settings['player'] != "ACT-R": x, y = director.get_virtual_coordinates(x, y) for obj in self.cm.objs_touching_point(x - (self.screen[0] - self.screen[1]) / 2, y): if obj != self.probe and obj.chunk == self.probe.chunk: self.trial_done() else: t = get_time() self.study_time = t - self.start_time self.logger.write(system_time=t, mode=director.settings['mode'], trial=self.current_trial, event_source="TASK", event_type=self.states[self.state], state=self.states[self.state], event_id="END", **self.log_extra) self.show_shapes() window = director.window.get_size() nx = int(window[0] / 2) ny = int(window[1] / 2 - self.probe.cshape.r * .75 * (window[1] / self.screen[1])) t = get_time() self.start_time = t self.state = self.STATE_SEARCH self.logger.write(system_time=t, mode=director.settings['mode'], trial=self.current_trial, event_source="TASK", event_type=self.states[self.state], state=self.states[self.state], event_id="START", **self.log_extra) self.logger.write(system_time=t, mode=director.settings['mode'], trial=self.current_trial, event_source="TASK", event_type=self.states[self.state], state=self.states[self.state], event_id="MOUSE_RESET", mouse_x=nx, mouse_y=ny, **self.log_extra) if director.settings['player'] == "ACT-R": self.client_actr.set_cursor_location([nx, ny]) else: director.window.set_mouse_position(nx, ny) director.window.set_mouse_visible(True) def on_mouse_motion(self, x, y, dx, dy): if self.state < self.STATE_IGNORE_INPUT: return if self.state == self.STATE_SEARCH: self.logger.write(system_time=get_time(), mode=director.settings['mode'], trial=self.current_trial, event_source="USER", event_type=self.states[self.state], state=self.states[self.state], event_id="MOUSE_MOTION", mouse_x=x, mouse_y=y, **self.log_extra) def on_key_press(self, symbol, modifiers): if self.state <= self.STATE_IGNORE_INPUT: return if symbol == key.W and (modifiers & key.MOD_ACCEL): self.logger.close(True) self.tarfile.close() director.scene.dispatch_event("show_intro_scene") True elif symbol == key.ESCAPE and director.settings['player'] == "ACT-R": if self.state == self.STATE_WAIT_ACTR_CONNECTION: director.scene.dispatch_event("show_intro_scene") True elif self.state > self.STATE_WAIT_ACTR_MODEL: self.client_actr.trigger_event(":break") True