def blur(self, level): """Blur the shape. This blurs the stimulus, by scaling it down and up by the factor of 'level'. Notes ----- Depending on the blur level and the size of your stimulus, this method may take some time! Parameters ---------- level : int level of bluring Returns ------- time : int the time it took to execute this method """ start = get_time() self.scale((1.0 / level, 1.0 / level)) self.scale((level, level)) return int((get_time() - start) * 1000)
def send(self, code=None, duration=None): """Send a marker. This sends a marker via the specified interface. If a duration is given, a 0 will be sent automatically after each code. Note for EEG/MEG systems: If the system is receiving the markers on a parallel port, the duration between sending a code an the subsequent 0 should be at least 1000/samplerate! Parameters ---------- code : int, optional a specific code duration : int, optional duration (in ms) for sending a 0 after a code """ if not code: code = self.default_code if not duration: duration = self.default_duration self._interface.send(code) if duration: start = get_time() while (get_time() - start) * 1000 < duration: pass self._interface.send(0) if self._logging: expyriment._active_exp._event_file_log( "MarkerOutput,sent,{0}".format(code))
def decompress(self): """Decompress the stimulus. This will decompress the stimulus. The surface will now be read from memory again. Depending on the size of the stimulus, this method may take some time to compute! Returns ------- time : int the time it took to execute this method """ start = get_time() if self.is_compressed: self._surface = pygame.image.load( self._compression_filename).convert_alpha() self._is_compressed = False if self._logging: expyriment._active_exp._event_file_log( "Stimulus,decompressed,{0}".format(self.id), 2) return int((get_time() - start) * 1000)
def request_data(self, request, *args): """Request data from Turbo-Satori. Parameters: ----------- request : str The request to be sent to Turbo-Satori. Returns: -------- data : str The byte string of the received data. rt : int The time it took to get the data. """ start = get_time() self._tcp.clear() request = unicode2byte(request) self._send(request, *args) data = self._wait() arg_length = sum([len(x) for x in args]) arg = b"".join(args) if data is None: raise TurbosatoriNetworkInterface.TimeoutError( "Waiting for requested data timed out!") elif byte2unicode(data).startswith("Wrong request!"): raise TurbosatoriNetworkInterface.RequestError( "Wrong request '{0}'!".format(data[19:-1])) elif data[0:len(request) + 1 + arg_length] != request + b"\x00" + arg: raise TurbosatoriNetworkInterface.DataError( "Received data does not match request!") else: return data[len(request) + 1:], int((get_time() - start) * 1000)
def move(self, offset): """Moves the stimulus in 2D space. When using OpenGL, this can take longer then 1ms! Parameters ---------- offset : list, optional translation along x and y axis Returns ------- time : int the time it took to execute this method """ start = get_time() moved = False x = offset[0] y = offset[1] if x > 0 or x < 0: self._position[0] = self._position[0] + x moved = True if y > 0 or y < 0: self._position[1] = self._position[1] + y moved = True if moved and self._ogl_screen is not None: self._ogl_screen.refresh_position() return int((get_time() - start) * 1000)
def readMouse(startTime, button, duration=None): iKeyboard = Keyboard() if duration is not None: while int((get_time() - startTime) * 1000) <= duration: alle = pygame.event.get() rt = int((get_time() - startTime)*1000) for e in alle: if e.type == pygame.MOUSEBUTTONDOWN and e.button == button: return rt, coordinates2position(e.pos) if iKeyboard.process_control_keys(): break return None, None else: while True: alle = pygame.event.get() rt = int((get_time() - startTime)*1000) for e in alle: if e.type == pygame.MOUSEBUTTONDOWN and e.button == button: return rt, coordinates2position(e.pos) if iKeyboard.process_control_keys(): break
def flip(self, booleans): """Flip the stimulus. This is a surface operation. After this, a surface will be present! Parameters ---------- booleans : (bool, bool) booleans to flip or not Returns ------- time : int the time it took to execute this method Notes ----- Depending on the size of the stimulus, this method may take some time to compute! """ start = get_time() if not self._set_surface(self._get_surface()): raise RuntimeError(Visual._compression_exception_message.format( "flip()")) self.unload(keep_surface=True) self._set_surface(pygame.transform.flip(self._get_surface(), booleans[0], booleans[1])) if self._logging: expyriment._active_exp._event_file_log( "Stimulus,flipped,{0}, booleans={1}".format(self.id, booleans), 2) return int((get_time() - start) * 1000)
def present(self, clear=False, update=False): """ Present the stimulus :param clear: Whether to clear the screen buffer prior to presenting. Default=False :type clear: bool :param update: Whether to flip buffer after plotting. Default=False :type update: bool :return: The time it took this function to run. (seconds) """ start_time = get_time() self.preload() #-- Plot all visual elements on the canvas if self._visible: i = 0 for k in self._visual_objects: do_clear = clear and i == 0 do_update = update and i == len(self._visual_objects) - 1 self._visual_objects[k].present(clear=do_clear, update=do_update) i += 1 return get_time() - start_time
def request_data(self, request, *args): """Request data from Turbo-Satori. Parameters: ----------- request : str The request to be sent to Turbo-Satori. Returns: -------- data : str The byte string of the received data. rt : int The time it took to get the data. """ start = get_time() self._tcp.clear() self._send(request, *args) data = self._wait() if data is None: return None, None elif data[0:len(request)] != request: return data, None else: return data[len(request) + 1:], int((get_time() - start) * 1000)
def blur(self, level): """Blur the stimulus. This blurs the stimulus, by scaling it down and up by the factor of 'level'. Parameters ---------- level : int level of bluring Returns ------- time : int the time it took to execute this method Notes ----- Depending on the size of the stimulus, this method may take some time to compute! """ start = get_time() self.scale((1.0 / level, 1.0 / level)) self.scale((level, level)) if self._logging: expyriment._active_exp._event_file_log( "Stimulus,blured,{0}, level={1}".format(self.id, level), 2) return int((get_time() - start) * 1000)
def readMouse(startTime, button, duration=None): iKeyboard = Keyboard() if duration is not None: while int((get_time() - startTime) * 1000) <= duration: alle = pygame.event.get() rt = int((get_time() - startTime) * 1000) for e in alle: if e.type == pygame.MOUSEBUTTONDOWN and e.button == button: return rt, coordinates2position(e.pos) if iKeyboard.process_control_keys(): break return None, None else: while True: alle = pygame.event.get() rt = int((get_time() - startTime) * 1000) for e in alle: if e.type == pygame.MOUSEBUTTONDOWN and e.button == button: return rt, coordinates2position(e.pos) if iKeyboard.process_control_keys(): break
def rotate(self, degree): """Rotate the stimulus. This is a surface operation. After this, a surface will be present! Rotating goes along with a quality loss. Thus, rotating an already rotated stimulus is not a good idea. Parameters ---------- degree : int degree to rotate counterclockwise Returns ------- time : int the time it took to execute this method Notes ----- Depending on the size of the stimulus, this method may take some time to compute! """ start = get_time() if not self._set_surface(self._get_surface()): raise RuntimeError(Visual._compression_exception_message.format( "rotate()")) self.unload(keep_surface=True) self._set_surface(pygame.transform.rotate(self._get_surface(), degree)) if self._logging: expyriment._active_exp._event_file_log( "Stimulus,rotated,{0}, degree={1}".format(self.id, degree)) return int((get_time() - start) * 1000)
def move(self, offset): """Moves the stimulus in 2D space. When using OpenGL, this can take longer then 1ms! Parameters ---------- offset : tuple (x,y) translation along x and y axis Returns ------- time : int the time it took to execute this method Notes -------- see also reposition """ start = get_time() moved = False if offset[0] != 0: self._position[0] = self._position[0] + offset[0] moved = True if offset[1] != 0: self._position[1] = self._position[1] + offset[1] moved = True if moved and self._ogl_screen is not None: self._ogl_screen.refresh_position() return int((get_time() - start) * 1000)
def clear_surface(self): """Clear the stimulus surface. Surfaces are automatically created after any surface operation (presenting, plotting, rotating, scaling, flipping etc.) and preloading. If the stimulus was preloaded, this method unloads the stimulus. This method is functionally equivalent with unload(keep_surface=False). Returns ------- time : int the time it took to execute this method Notes ----- Depending on the size of the stimulus, this method may take some time to compute! """ start = get_time() if self.is_preloaded: self.unload(keep_surface=False) self._is_compressed = False self._set_surface(None) if self._logging: expyriment._active_exp._event_file_log( "Stimulus,surface cleared,{0}".format(self.id), 2) return int((get_time() - start) * 1000)
def compress(self): """"Compress the stimulus. This will create a temporary file on the disk where the surface of the stimululs is written to. The surface will now be read from the disk to free memory. Compressed stimuli cannot do surface operations! Preloading comressed stimuli is possible and highly recommended. Depending on the size of the stimulus, this method may take some time to compute! Returns ------- time : int the time it took to execute this method """ start = get_time() if self.is_compressed is False: if self._compression_filename is None: fid, self._compression_filename = tempfile.mkstemp( dir=defaults.tempdir, suffix=".tga") os.close(fid) pygame.image.save(self._get_surface(), self._compression_filename) self._is_compressed = True self._surface = None if self._logging: expyriment._active_exp._event_file_log( "Stimulus,compressed,{0}".format(self.id), 2) return int((get_time() - start) * 1000)
def preload(self): """ Pre-load the number line - prepare for plotting :return: The time it took this function to run. (seconds) """ start_time = get_time() if self._preloaded: # Already pre-loaded return get_time() - start_time self.validate() self._preloaded = True if self._line_colour is not None: self._prepare_main_line() if self._end_tick_height != 0 and self._end_tick_height is not None: self._prepare_end_of_line_ticks() if self._labels_visible: self._prepare_labels() return get_time() - start_time
def save(self): """Save the new data to data-file. Returns ------- time : int the time it took to execute this method """ start = get_time() if len(self._subject_info) > 0 or len(self._experiment_info) > 0 \ or self._variable_names_changed: # Re-write header and varnames tmpfile_name = "{0}{1}{2}".format(self.directory, os.path.sep, uuid.uuid4()) os.rename(self._fullpath, tmpfile_name) fl = open(self._fullpath, 'w+') tmpfl = open(tmpfile_name, 'r') section = None while True: line = tmpfl.readline() if not line: break if line.startswith(self.comment_char + "e"): section = "e" elif line.startswith(self.comment_char + "s"): section = "s" else: if section == "e": # Previous line was last #e if len(self._experiment_info) > 0: fl.write("".join(self._experiment_info)) self._experiment_info = [] section = None elif section == "s": # Previous line was last #s if len(self._subject_info) > 0: fl.write("".join(self._subject_info)) self._subject_info = [] section = None # Re-write variable names after #s-section fl.write(unicode2str( self.variable_names + defaults.outputfile_eol)) self._variable_names_changed = False line = '' # Skip old varnames fl.write(line) tmpfl.close() fl.close() os.remove(tmpfile_name) self._subject_info = [] self._experiment_info = [] if self._buffer != []: OutputFile.save(self) if self._logging: expyriment._active_exp._event_file_log("Data,saved") return int((get_time() - start) * 1000)
def save(self): """Save file to disk.""" start = get_time() if self._buffer != []: with open(self._fullpath, 'a') as f: f.write("".join(self._buffer)) self._buffer = [] return int((get_time() - start) * 1000)
def preload(self, inhibit_ogl_compress=False): """Preload the stimulus to memory. This will prepare the stimulus for a fast presentation. In OpenGL mode this method creates an OpenGL texture based on the surface of the stimulus. When OpenGL is switched off, this method will create a surface if it doesn't exists yet. If stimuli are not preloaded manually, this will happen automatically during presentation. However, stimulus presentation will take some time then! Always preload your stimuli when a timing acurate presentation is needed! Parameters ---------- inhibit_ogl_compress : bool, optional inhibits OpenGL stimuli to be automatically compressed (default=False) Returns ------- time : int the time it took to execute this method Notes ----- Depending on the size of the stimulus, this method may take some time to compute! """ start = get_time() if not expyriment._active_exp.is_initialized: message = "Can't preload stimulus. Expyriment needs to be " + \ "initilized before preloading a stimulus." raise RuntimeError(message) self._was_compressed_before_preload = self.is_compressed if not self.is_preloaded: if expyriment._active_exp.screen.open_gl: self._ogl_screen = _LaminaPanelSurface( self._get_surface(), position=self.position) if not inhibit_ogl_compress: self.compress() else: self.decompress() self._set_surface(self._get_surface()) self._is_preloaded = True if self._logging: expyriment._active_exp._event_file_log( "Stimulus,preloaded,{0}".format(self.id), 2) return int((get_time() - start) * 1000)
def present(self, clear=True, update=True): """Present the stimulus on the screen. This clears and updates the screen automatically. When not preloaded, depending on the size of the stimulus, this method can take some time to compute! Parameters ---------- clear : bool, optional if True the screen will be cleared automatically (default = True) update : bool, optional if False the screen will be not be updated automatically (default = True) Returns ------- time : int the time it took to execute this method """ if not expyriment._active_exp.is_initialized or\ expyriment._active_exp.screen is None: raise RuntimeError("Cannot not find a screen!") start = get_time() preloading_required = not(self.is_preloaded) if clear: expyriment._active_exp.screen.clear() if preloading_required: # Check if stimulus has surface keep_surface = self.has_surface self.preload(inhibit_ogl_compress=True) if expyriment._active_exp.screen.open_gl: self._ogl_screen.display() else: screen = expyriment._active_exp.screen.surface rect = pygame.Rect((0, 0), self.surface_size) screen_size = screen.get_size() rect.center = [self.position[0] + screen_size[0] / 2, - self.position[1] + screen_size[1] / 2] screen.blit(self._get_surface(), rect) if self._logging: expyriment._active_exp._event_file_log("Stimulus,presented,{0}"\ .format(self.id), 1) if update: expyriment._active_exp.screen.update() if preloading_required: self.unload(keep_surface=keep_surface) return int((get_time() - start) * 1000)
def scale(self, factors): """Scale the stimulus. This is a surface operation. After this, a surface will be present! Negative scaling values will flip the stimulus. Scaling goes along with a quality loss. Thus, scaling an already scaled stimulus is not a good idea. Parameters ---------- factors : (int, int) or (float, float) tuple representing the x and y factors to scale or a single number. In the case of a single number x and y scaling will be the identical (i.e., proportional scaling) Returns ------- time : int the time it took to execute this method Notes ----- Depending on the size of the stimulus, this method may take some time to compute! """ start = get_time() if not self._set_surface(self._get_surface()): raise RuntimeError(Visual._compression_exception_message.format( "scale()")) self.unload(keep_surface=True) flip = [False, False] if type(factors) in [types.IntType, types.FloatType]: factors = [factors, factors] else: factors = list(factors) if factors[0] < 0: flip[0] = True factors[0] = abs(factors[0]) if factors[1] < 0: flip[1] = True factors[1] = abs(factors[1]) self._set_surface(pygame.transform.smoothscale( self._get_surface(), (int(round(self.surface_size[0] * factors[0])), int(round(self.surface_size[1] * factors[1]))))) if True in flip: self.flip(flip) if self._logging: expyriment._active_exp._event_file_log( "Stimulus,scaled,{0}, factors={1}".format(self.id, factors), 2) return int((get_time() - start) * 1000)
def create_mask(self): """Creates a new visual mask. Notes ----- CAUTION: Depending on the size of the stimulus, this method may take some time to execute. Returns ------- time : int the time it took to execute this method in ms """ start = get_time() was_preloaded = self.is_preloaded if was_preloaded: self.unload() s = (self._size[0] + 4 * self.smoothing, self._size[1] + 4 * self.smoothing) #somewhat larger mask im = Image.new("RGB", s) draw = ImageDraw.Draw(im) draw.rectangle([(0, 0), s], outline=self.background_colour, fill=self.background_colour) n_dots_x = int(s[0] / self.dot_size[0]) + 1 n_dots_y = int(s[1] / self.dot_size[1]) + 1 dots = range(n_dots_x * n_dots_y) shuffle(dots) for d in dots[:int(len(dots) * self.dot_percentage / 100)]: y = (d / n_dots_x) * self.dot_size[1] x = (d % n_dots_x) * self.dot_size[0] draw.rectangle([(x, y), (x + self.dot_size[0], y + self.dot_size[1])], outline=self.dot_colour, fill=self.dot_colour) for x in range(self.smoothing): im = im.filter(ImageFilter.BLUR).filter(ImageFilter.SMOOTH_MORE) #crop image and save c = (im.size[0] / 2, im.size[1] / 2) box = (c[0] - self._size[0] / 2, c[1] - self._size[1] / 2, c[0] + self._size[0] / 2, c[1] + self._size[1] / 2) im = im.crop(box) im.save(self._filename, format="png") if was_preloaded: self.preload() return int((get_time() - start) * 1000)
def unload(self, keep_surface=False): """Unload the stimulus from memory. This will unload preloaded stimuli. In OpenGL mode, this method will remove the reference to the OpenGL texture and the surface (when 'keep_surface' is False). When OpenGL is switched off, the reference to the surface will be removed (when 'keep_surface' is False). Parameters ---------- keep_surface : bool, optional keep the surface after unload (default=False) Returns ------- time : int the time it took to execute this method See Also -------- clear_surface. Notes ----- Depending on the size of the stimulus, this method may take some time to compute! """ start = get_time() if expyriment._active_exp.screen.open_gl: self._ogl_screen = None if self.is_preloaded and not self._was_compressed_before_preload \ and keep_surface: self.decompress() else: # Pygame surface if self.is_preloaded and self._was_compressed_before_preload \ and keep_surface: self.compress() if self.is_preloaded and self._logging: expyriment._active_exp._event_file_log("Stimulus,unloaded,{0}"\ .format(self.id), 2) if not keep_surface: self._is_compressed = False self._surface = None if self._logging: expyriment._active_exp._event_file_log("Stimulus,surface cleared,{0}"\ .format(self.id), 2) self._is_preloaded = False return int((get_time() - start) * 1000)
def wait_press(self, buttons=None, duration=None): """Wait for gamepad button press. Returns the found button and the reaction time. Parameters ---------- buttons : int or list, optional specific buttons to wait for duration : int, optional maximal time to wait in ms Returns ------- button : int button _id of the pressed button rt : int reaction time in ms """ start = get_time() rt = None _button = None self.clear() if buttons is None: buttons = range(self.get_numbuttons()) if type(buttons) is not list: buttons = [buttons] done = False while not done: expyriment._active_exp._execute_wait_callback() for button in buttons: if self.get_button(button): _button = button rt = int((get_time() - start) * 1000) done = True break if _button is not None or Keyboard.process_control_keys(): done = True break if duration: if int((get_time() - start) * 1000) >= duration: done = True break time.sleep(0.0005) if self._logging: expyriment._active_exp._event_file_log( "Gamepad,received,{0},wait_press".format(_button)) return _button, rt
def scramble(self, grain_size): """Scramble the stimulus. Attention: If the surface size is not a multiple of the grain size, you may loose some pixels on the edge. Parameters ---------- grain_size : int or (int, int) size of a grain (use tuple of integers for different width & height) Returns ------- time : int the time it took to execute this method Notes ----- Depending on the size of the stimulus, this method may take some time to compute! """ start = get_time() if type(grain_size) is int: grain_size = [grain_size, grain_size] # Make Rect list if not self._set_surface(self._get_surface()): raise RuntimeError(Visual._compression_exception_message.format( "scramble()")) s = self.surface_size source = [] for r in range(s[1] / int(grain_size[1])): for c in range(s[0] / int(grain_size[0])): xy = (c * int(grain_size[0]), r * int(grain_size[1])) source.append(pygame.Rect(xy, grain_size)) # Make copy and shuffle dest = copy.deepcopy(source) random.shuffle(dest) # Create a new surface tmp_surface = pygame.surface.Surface( s, pygame.SRCALPHA).convert_alpha() for n, s in enumerate(source): tmp_surface.blit(self._get_surface(), dest[n], s) self._set_surface(tmp_surface) if self._logging: expyriment._active_exp._event_file_log( "Stimulus,scrambled,{0}, grain_size={1}".format( self.id, grain_size), 2) return int((get_time() - start) * 1000)
def create_mask(self): """Creates a new visual mask. Notes ----- CAUTION: Depending on the size of the stimulus, this method may take some time to execute. Returns ------- time : int the time it took to execute this method in ms """ start = get_time() was_preloaded = self.is_preloaded if was_preloaded: self.unload() s = (self._size[0] + 4 * self.smoothing, self._size[1] + 4 * self.smoothing) #somewhat larger mask im = Image.new("RGB", s) draw = ImageDraw.Draw(im) draw.rectangle([(0, 0), s], outline=self.background_colour, fill=self.background_colour) n_dots_x = int(s[0] / self.dot_size[0]) + 1 n_dots_y = int(s[1] / self.dot_size[1]) + 1 dots = list(range(n_dots_x * n_dots_y)) shuffle(dots) for d in dots[:int(len(dots) * self.dot_percentage / 100)]: y = (d // n_dots_x) * self.dot_size[1] x = (d % n_dots_x) * self.dot_size[0] draw.rectangle([(x, y), (x + self.dot_size[0], y + self.dot_size[1])], outline=self.dot_colour, fill=self.dot_colour) for x in range(self.smoothing): im = im.filter(ImageFilter.BLUR).filter(ImageFilter.SMOOTH_MORE) #crop image and save c = (im.size[0] // 2, im.size[1] // 2) box = (c[0] - self._size[0] // 2, c[1] - self._size[1] // 2, c[0] + self._size[0] // 2, c[1] + self._size[1] // 2) im = im.crop(box) im.save(self._filename, format="png") if was_preloaded: self.preload() return int((get_time() - start) * 1000)
def wait_char(self, char, duration=None, check_for_control_keys=True): """Wait for character(s) (optionally for a certain amount of time). This function will wait for one or more characters and returns the found character as well as the reaction time. (This function clears the event queue!) Parameters ---------- char : int or list a specific character or list of characters to wait for duration : int, optional maximal time to wait in ms check_for_control_keys : bool, optional checks if control key has been pressed (default=True) Returns ------- found : char pressed charater rt : int reaction time in ms """ start = get_time() rt = None found_char = None self.clear() if type(char) is not list: char = [char] pygame.event.pump() done = False while not done: expyriment._active_exp._execute_wait_callback() for event in pygame.event.get(): if check_for_control_keys and Keyboard.process_control_keys(event): done = True elif event.type == pygame.KEYDOWN: if event.unicode in char: rt = int((get_time() - start) * 1000) found_char = event.unicode done = True if duration and not done: done = int((get_time() - start) * 1000) >= duration time.sleep(0.0005) if self._logging: expyriment._active_exp._event_file_log( "Keyboard,received,{0},wait_char".format(found_char)) return found_char, rt
def add_noise(self, grain_size, percentage, colour): """Add visual noise on top of the stimulus. This function might take very long for large stimuli. Parameters ---------- grain_size : int size of the grains for the noise percentage : int percentage of covered area colour : (int, int, int) colour (RGB) of the noise Returns ------- time : int the time it took to execute this method Notes ----- Depending on the size of the stimulus, this method may take some time to compute! """ import _rectangle start = get_time() if not self._set_surface(self._get_surface()): raise RuntimeError(Visual._compression_exception_message.format( "add_noise()")) self.unload(keep_surface=True) number_of_pixel_x = int(self.surface_size[0] / grain_size) + 1 number_of_pixel_y = int(self.surface_size[1] / grain_size) + 1 seq = range(number_of_pixel_x * number_of_pixel_y) random.seed() random.shuffle(seq) for idx in seq[:int(len(seq) * (percentage) / 100.0)]: x = (idx % number_of_pixel_x) * grain_size x = int(self.surface_size[0] / 2 - grain_size / 2 - x) y = (idx / number_of_pixel_x) * grain_size y = int(self.surface_size[1] / 2 - grain_size / 2 - y) dot = _rectangle.Rectangle(size=(grain_size, grain_size), position=(x, y), colour=colour) dot.plot(self) if self._logging: expyriment._active_exp._event_file_log( "Stimulus,noise added,{0}, grain_size={1}, percentage={2}"\ .format(self.id, grain_size, percentage)) return int((get_time() - start) * 1000)
def wait(self, duration=None, button_fields=None, check_for_control_keys=True): """Wait for a touchscreen button box click. Parameters ---------- button_fields : Expyriment stimulus or list of stimuli, optional The button fields that will be checked for. duration : int, optional maximal time to wait in ms Returns ------- pressed_button_field : Expyriment stimulus or None the button field defined by a stimulus that has been pressed rt : int reaction time Notes ----- Don't forget to show the TouchScreenButtonBox. See Also -------- design.experiment.register_wait_callback_function """ if expyriment.control.defaults._skip_wait_functions: return None, None start = get_time() self.clear_event_buffer() while True: rtn_callback = expyriment._active_exp._execute_wait_callback() if isinstance(rtn_callback, expyriment.control.CallbackQuitEvent): return rtn_callback, int((get_time() - start) * 1000) pressed_button_field, touch_time = self.check( button_fields, check_for_control_keys) if pressed_button_field is not None: rt = int((get_time() - start) * 1000) break elif (duration is not None and int( (get_time() - start) * 1000) >= duration): pressed_button_field, rt = None, None break return pressed_button_field, rt
def get_time(): """ Get the current time (in seconds). This is a wrapper to Expyriment's get_time function :return: float """ return xpy_timer.get_time()
def wait(self, code=None, bitwise_comparison=False): """Wait for a trigger. Returns the code received and the reaction time [code, rt]. If bitwise_comparison = True, the function performs a bitwise comparison (logical and) between code and received input and waits until a certain bit pattern is set. Parameters code -- a specific code to wait for (int) (optional) bitwise_comparison -- make a bitwise comparison (default=False) See Also -------- design.experiment.register_wait_callback_function """ if expyriment.control.defaults._skip_wait_functions: return None, None start = get_time() found = None rt = None if code is None: code = self._default_code self.interface.clear() while True: rtn_callback = expyriment._active_exp._execute_wait_callback() if isinstance(rtn_callback, expyriment.control.CallbackQuitEvent): return rtn_callback, int((get_time() - start) * 1000) read = self.interface.poll() if read is not None: if code is None: #return for every event rt = int((get_time() - start) * 1000) found = read break elif compare_codes(read, code, bitwise_comparison): rt = int((get_time() - start) * 1000) found = read break if Keyboard.process_control_keys(): break if self._logging: expyriment._active_exp._event_file_log( "TriggerInput,received,{0},wait".format(found)) return found, rt
def wait(self, code=None, bitwise_comparison=False): """Wait for a trigger. Returns the code received and the reaction time [code, rt]. If bitwise_comparison = True, the function performs a bitwise comparison (logical and) between code and received input and waits until a certain bit pattern is set. Parameters code -- a specific code to wait for (int) (optional) bitwise_comparison -- make a bitwise comparison (default=False) See Also -------- design.experiment.register_wait_callback_function """ if expyriment.control.defaults._skip_wait_functions: return None, None start = get_time() found = None rt = None if code is None: code = self._default_code self.interface.clear() while True: rtn_callback = expyriment._active_exp._execute_wait_callback() if isinstance(rtn_callback, expyriment.control.CallbackQuitEvent): return rtn_callback, int((get_time() - start) * 1000) read = self.interface.poll() if read is not None: if code is None: # return for every event rt = int((get_time() - start) * 1000) found = read break elif compare_codes(read, code, bitwise_comparison): rt = int((get_time() - start) * 1000) found = read break if Keyboard.process_control_keys(): break if self._logging: expyriment._active_exp._event_file_log("TriggerInput,received,{0},wait".format(found)) return found, rt
def wait(self, duration=None, button_fields=None, check_for_control_keys=True): """Wait for a touchscreen button box click. Parameters ---------- button_fields : Expyriment stimulus or list of stimuli, optional The button fields that will be checked for. duration : int, optional maximal time to wait in ms Returns ------- pressed_button_field : Expyriment stimulus or None the button field defined by a stimulus that has been pressed rt : int reaction time Notes ----- Don't forget to show the TouchScreenButtonBox. See Also -------- design.experiment.register_wait_callback_function """ if expyriment.control.defaults._skip_wait_functions: return None, None start = get_time() self.clear_event_buffer() while True: rtn_callback = expyriment._active_exp._execute_wait_callback() if isinstance(rtn_callback, expyriment.control.CallbackQuitEvent): return rtn_callback, int((get_time()-start)*1000) pressed_button_field, touch_time = self.check(button_fields, check_for_control_keys) if pressed_button_field is not None: rt = int((get_time()-start)*1000) break elif (duration is not None and int((get_time()-start)*1000)>=duration): pressed_button_field, rt = None, None break return pressed_button_field, rt
def plot(self, stim): """ Plot the number line on another stimulus :param stim: Any Expyriment visual object :return: The time it took this function to run (seconds) """ start_time = get_time() self.preload() #-- Plot all visual elements on the canvas if self._visible: for k in self._visual_objects: self._visual_objects[k].plot(stim) return get_time() - start_time
def _wait(self): start = get_time() receive, rt = self._tcp.wait(package_size=8, duration=self.timeout, process_control_events=False) if receive is None: return None length = struct.unpack('!q', receive)[0] data = None timeout = self.timeout - int((get_time() - start) * 1000) if timeout > 0: data, rt = self._tcp.wait(package_size=length, duration=timeout, process_control_events=False) if data is None: return None return data[4:]
def _test2(): info = """This will test if stimulus presentation can be synchronized to the refreshrate of the screen. A good result is a fast, constant and smooth flickering without any distortions (e.g. horizontal stripes, tearing). The estimated refreshrate should resemble your actual screen refreshrate (common refreshrates are between 40 and 240 Hz). [Press RETURN to continue]""" text = stimuli.TextScreen("Stimulus presentation test (2)", info) text.present() exp.keyboard.wait([constants.K_RETURN]) black = stimuli.BlankScreen(colour=constants.C_BLACK) black.preload() white = stimuli.BlankScreen(colour=constants.C_WHITE) white.preload() times = [] black.present() for _x in range(100): start = get_time() black.present() times.append(get_time() - start) start = get_time() white.present() times.append(get_time() - start) refresh_rate = 1000 / (statistics.mean(times) * 1000) info = """Your estimated refresh rate is {0} Hz. [Press RETURN to continue] """.format(refresh_rate) text = stimuli.TextScreen("Results", info) text.present() exp.keyboard.wait([constants.K_RETURN]) text = stimuli.TextScreen( "Was the flickering fast, constant and smooth, without any distortions?", "[Press Y or N]") text.present() key, _rt = exp.keyboard.wait([constants.K_y, constants.K_n]) if key == constants.K_y: response2 = "Yes" elif key == constants.K_n: response2 = "No" return refresh_rate, response2
def plot(self, stimulus): """Plot the stimulus on the surface of another stimulus. Use this to plot more than one stimulus and to present them at the same time afterwards by presenting the stimulus on which they were plotted on. Parameters ---------- stimulus : expyriment stimulus stimulus to whose surface should be plotted Returns ------- time : int the time it took to execute this method Notes ----- Depending on the size of the stimulus, this method may take some time to compute! """ start = get_time() if not stimulus._set_surface(stimulus._get_surface()): raise RuntimeError(Visual._compression_exception_message.format( "plot()")) stimulus.unload(keep_surface=True) self._parent = stimulus rect = pygame.Rect((0, 0), self.surface_size) stimulus_surface_size = stimulus.surface_size rect.center = [self.position[0] + stimulus_surface_size[0] / 2, - self.position[1] + stimulus_surface_size[1] / 2] stimulus._get_surface().blit(self._get_surface(), rect) if self._logging: expyriment._active_exp._event_file_log( "Stimulus,plotted,{0},{1}".format(self.id, stimulus.id), 2) return int((get_time() - start) * 1000)
def scale_to_fullscreen(self, keep_aspect_ratio=True): """Scale the stimulus to fullscreen. This is a surface operation. After this, a surface will be present! Scaling goes along with a quality loss. Thus, scaling an already scaled stimulus is not a good idea. Parameters ---------- keep_aspect_ratio : boolean, optional if this boolean is False, stimulus will be stretched so that it fills out the whole screen (default = False) Returns ------- time : int the time it took to execute this method Notes ----- Depending on the size of the stimulus, this method may take some time to compute! """ start = get_time() if not self._set_surface(self._get_surface()): raise RuntimeError(Visual._compression_exception_message.format( "scale_to_fullscreen()")) surface_size = self.surface_size screen_size = expyriment._active_exp.screen.surface.get_size() scale = (screen_size[0]/float(surface_size[0]), screen_size[1]/float(surface_size[1])) if keep_aspect_ratio: scale = [min(scale)]*2 self.scale(factors=scale) return int((get_time() - start) * 1000)
def move(self, offset): """Moves the stimulus in 2D space. Parameters ---------- offset : list, optional translation along x and y axis Returns ------- time : int the time it took to execute this method Notes ----- When using OpenGL, this can take longer then 1ms! """ start = get_time() moved = False x = offset[0] y = offset[1] if x > 0 or x < 0: self._position[0] = self._position[0] + x moved = True if y > 0 or y < 0: self._position[1] = self._position[1] + y moved = True if moved and self._ogl_screen is not None: self._ogl_screen.refresh_position() self._start_point[0] = self._start_point[0] + x self._start_point[1] = self._start_point[1] + y self._end_point[0] = self._end_point[0] + x self._end_point[1] = self._end_point[1] + y return int((get_time() - start) * 1000)
def check(self, button_fields=None, check_for_control_keys=True): """Check if a button field is clicked. Parameters ---------- button_fields : Expyriment stimulus or list of stimuli, optional The button fields that will be checked for. check_for_control_keys : bool, optional checks if control key has been pressed (default=True) Returns ------- pressed_button_field : Expyriment stimulus or list of stimuli, optional The button fields that will be checked for. touch_time : integer The time when the button was touched. Function might return delayed, because button field comparison (after touch) takes time. The return time is most accurate. Notes ----- Don't forget to show the TouchScreenButtonBox. """ if button_fields is not None: try: button_fields = list(button_fields) except: button_fields = [button_fields] if check_for_control_keys: expyriment.io.Keyboard.process_control_keys() pressed_button_field = None touch_time = None if self._mouse.get_last_button_down_event() is not None: touch_time = get_time() self._last_touch_position = self._mouse.position pressed_button_field = self._get_button_field(self._last_touch_position, button_fields) if self._logging and pressed_button_field is not None: expyriment._active_exp._event_file_log( "{0},received, button press,check".format( self.__class__.__name__)) return pressed_button_field, touch_time
def check(self, button_fields=None, check_for_control_keys=True): """Check if a button field is clicked. Parameters ---------- button_fields : Expyriment stimulus or list of stimuli, optional The button fields that will be checked for. check_for_control_keys : bool, optional checks if control key has been pressed (default=True) Returns ------- pressed_button_field : Expyriment stimulus or list of stimuli, optional The button fields that will be checked for. touch_time : integer The time when the button was touched. Function might return delayed, because button field comparison (after touch) takes time. The return time is most accurate. Notes ----- Don't forget to show the TouchScreenButtonBox. """ if button_fields is not None: try: button_fields = list(button_fields) except: button_fields = [button_fields] if check_for_control_keys: expyriment.io.Keyboard.process_control_keys() pressed_button_field = None touch_time = None if self._mouse.get_last_button_down_event() is not None: touch_time = get_time() self._last_touch_position = self._mouse.position pressed_button_field = self._get_button_field( self._last_touch_position, button_fields) if self._logging and pressed_button_field is not None: expyriment._active_exp._event_file_log( "{0},received, button press,check".format( self.__class__.__name__)) return pressed_button_field, touch_time
def wait(self, duration=None, button_fields=None, check_for_control_keys=True): """Wait for a touchscreen button box click. Parameters ---------- button_fields : Expyriment stimulus or list of stimuli, optional The button fields that will be checked for. duration : int, optional maximal time to wait in ms Returns ------- pressed_button_field : Expyriment stimulus or None the button field defined by a stimulus that has been pressed rt : int reaction time Notes ----- Don't forget to show the TouchScreenButtonBox. """ start = get_time() self.clear_event_buffer() while True: expyriment._active_exp._execute_wait_callback() pressed_button_field, touch_time = self.check(button_fields, check_for_control_keys) if pressed_button_field is not None: rt = int((touch_time - start) * 1000) break elif (duration is not None and rt>= duration): pressed_button_field, rt = None, None break return pressed_button_field, rt
def wait(self, events, duration=None, callback_function=None, process_control_events=True): """Wait for (a) certain event(s). Events to wait for are in the form of a list with 4 elements and do not include a timestamp: [status, data1, data2, data3] Parameters ---------- events : int or list event(s) to wait for duration : int, optional maximal time to wait in ms callback_function : function, optional function to repeatedly execute during waiting loop process_control_events : bool, optional process ``io.Keyboard.process_control_keys()`` and ``io.Mouse.process_quit_event()`` (default = True) Returns ------- evt : int found event rt : int reaction time in ms Notes ----- This will also by default process control events (quit and pause). Thus, keyboard events will be cleared from the cue and cannot be received by a Keyboard().check() anymore! See Also -------- design.experiment.register_wait_callback_function """ if _internals.skip_wait_methods: return None, None start = get_time() rt = None _event = None self.clear() if isinstance(events, (list, tuple)) and \ len(events) == 4 and \ isinstance(events[0], int) and \ isinstance(events[1], int) and \ isinstance(events[2], int) and \ isinstance(events[3], int): events = [events] done = False while not done: if isinstance(callback_function, FunctionType): callback_function() if _internals.active_exp is not None and \ _internals.active_exp.is_initialized: rtn_callback = _internals.active_exp._execute_wait_callback() if isinstance(rtn_callback, CallbackQuitEvent): _event = rtn_callback rt = int((get_time() - start) * 1000) break if process_control_events: if _internals.active_exp.mouse.process_quit_event() or \ _internals.active_exp.keyboard.process_control_keys(): break else: _internals.pump_pygame_events() event = self.read(1) if event is not None and event[0][0] in events: rt = int((get_time() - start) * 1000) _event = event[0][0] done = True break if duration: if int((get_time() - start) * 1000) >= duration: done = True break time.sleep(0.0005) if self._logging: _internals.active_exp._event_file_log( "MIDI In ({0}),received,{1},wait".format(self.id, _event), 2) return _event, rt
def wait(self, length, package_size=None, duration=None, callback_function=None, process_control_events=True): """Wait for data. Parameters ---------- length : int The length of the data to be waited for in bytes. If not set, a single package will be waited for. package_size : int, optional The size of the package to be waited for, optional. If not set, the default package size will be used. If length < package_size, package_size = length. duration: int, optional The duration to wait in milliseconds. callback_function : function, optional function to repeatedly execute during waiting loop process_control_events : bool, optional process ``io.Keyboard.process_control_keys()`` and ``io.Mouse.process_quit_event()`` (default = True) Returns ------- data : str The received data. rt : int The time it took to receive the data in milliseconds. Notes ----- This will also by default process control events (quit and pause). Thus, keyboard events will be cleared from the cue and cannot be received by a Keyboard().check() anymore! """ if _internals.skip_wait_methods: return None, None start = get_time() data = None rt = None if package_size is None: package_size = self._default_package_size if length is None: length = package_size elif length < package_size: package_size = length while True: try: if data is None: data = self._client[0].recv(package_size) while len(data) < length: if length - len(data) >= package_size: data = data + self._client[0].recv(package_size) else: data = data + self._client[0].recv(length - len(data)) if duration: if int((get_time() - start) * 1000) >= duration: data = None rt = None break rt = int((get_time() - start) * 1000) break except socket.error as e: err = e.args[0] if err == errno.EAGAIN or err == errno.EWOULDBLOCK: if isinstance(callback_function, FunctionType): callback_function() if _internals.active_exp is not None and \ _internals.active_exp.is_initialized: rtn_callback = _internals.active_exp._execute_wait_callback( ) if isinstance(rtn_callback, CallbackQuitEvent): data = rtn_callback rt = int((get_time() - start) * 1000) break if process_control_events: if _internals.active_exp.mouse.process_quit_event() or \ _internals.active_exp.keyboard.process_control_keys(): break else: _internals.pump_pygame_events() if duration: if int((get_time() - start) * 1000) >= duration: data = None rt = None break if self._logging: _internals.active_exp._event_file_log( "TcpServer,received,{0},wait".format(data)) return data, rt
def wait(self, keys=None, duration=None, wait_for_keyup=False, check_for_control_keys=True): """Wait for keypress(es) (optionally for a certain amount of time). This function will wait for a keypress and returns the found key as well as the reaction time. (This function clears the event queue!) Parameters ---------- keys : int or list, optional a specific key or list of keys to wait for duration : int, optional maximal time to wait in ms wait_for_keyup : bool, optional if True it waits for key-up check_for_control_keys : bool, optional checks if control key has been pressed (default=True) Returns ------- found : char pressed character rt : int reaction time in ms Notes ----- Keys are defined my keyboard constants (please use see misc.constants) See Also -------- design.experiment.register_wait_callback_function """ if expyriment.control.defaults._skip_wait_functions: return None, None if android_show_keyboard is not None: android_show_keyboard() start = get_time() rt = None found_key = None self.clear() if keys is None: keys = self.default_keys else: try: keys = list(keys) except: keys = [keys] if wait_for_keyup: target_event = pygame.KEYUP else: target_event = pygame.KEYDOWN pygame.event.pump() done = False while not done: rtn_callback = expyriment._active_exp._execute_wait_callback() if isinstance(rtn_callback, expyriment.control.CallbackQuitEvent): done = True found_key = rtn_callback rt = int((get_time() - start) * 1000) for event in pygame.event.get([pygame.KEYDOWN, pygame.KEYUP]): if check_for_control_keys and Keyboard.process_control_keys( event): done = True elif event.type == target_event: if keys is not None: if event.key in keys: rt = int((get_time() - start) * 1000) found_key = event.key done = True else: rt = int((get_time() - start) * 1000) found_key = event.key done = True if duration and not done: done = int((get_time() - start) * 1000) >= duration time.sleep(0.0005) if self._logging: expyriment._active_exp._event_file_log("Keyboard,received,{0},wait"\ .format(found_key)) if android_hide_keyboard is not None: android_hide_keyboard() return found_key, rt
def wait_event(self, wait_button=True, wait_motion=True, buttons=None, duration=None, wait_for_buttonup=False): """Wait for a mouse event (i.e., motion, button press or wheel event) Parameters ---------- wait_button : bool, optional set 'False' to ignore for a button presses (default=True) wait_motion : bool, optional set 'False' to ignore for a mouse motions (default=True) buttons : int or list, optional a specific button or list of buttons to wait for duration : int, optional the maximal time to wait in ms wait_for_buttonup : bool, optional if True it waits for button-up default=False) Returns ------- event_id : int id of the event that quited waiting move : bool True if a motion occured pos : (int, int) mouse position (tuple) rt : int reaction time Notes ------ button id coding - None for no mouse button event or - 0,1,2 for left. middle and right button or - 3 for wheel up or - 4 for wheel down (wheel works only for keydown events). See Also -------- design.experiment.register_wait_callback_function """ if expyriment.control.defaults._skip_wait_functions: return None, None, None, None start = get_time() self.clear() old_pos = pygame.mouse.get_pos() btn_id = None rt = None motion_occured = False if buttons is None: buttons = [0, 1, 2, 3, 4] else: try: buttons = list(buttons) except: buttons = [buttons] while True: rtn_callback = expyriment._active_exp._execute_wait_callback() if isinstance(rtn_callback, expyriment.control.CallbackQuitEvent): btn_id = rtn_callback rt = int((get_time() - start) * 1000) break if wait_motion: motion_occured = old_pos != pygame.mouse.get_pos() if wait_button: if wait_for_buttonup: btn_id = self.get_last_button_up_event() else: btn_id = self.get_last_button_down_event() if btn_id ==-1: btn_id = None break elif btn_id in buttons or motion_occured: rt = int((get_time() - start) * 1000) break elif Keyboard.process_control_keys() or (duration is not None and \ int((get_time() - start) * 1000) >= duration): break position_in_expy_coordinates = self.position if self._logging: expyriment._active_exp._event_file_log( "Mouse,received,{0}-{1},wait_event".format(btn_id, motion_occured)) return btn_id, motion_occured, position_in_expy_coordinates, rt
def wait(self, events, duration=None): """Wait for (a) certain event(s). Events to wait for are in the form of a list with 4 elements and do not include a timestamp: [status, data1, data2, data3] Parameters ---------- events : int or list event(s) to wait for duration : int, optional maximal time to wait in ms Returns ------- evt : int found event rt : int reaction time in ms See Also -------- design.experiment.register_wait_callback_function """ if expyriment.control.defaults._skip_wait_functions: return None, None start = get_time() rt = None _event = None self.clear() if type(events) is list and \ len(events) == 4 and \ type(events[0]) is int and \ type(events[1]) is int and \ type(events[2]) is int and \ type(events[3]) is int: events = [events] done = False while not done: rtn_callback = expyriment._active_exp._execute_wait_callback() if isinstance(rtn_callback, expyriment.control.CallbackQuitEvent): return rtn_callback, int((get_time() - start) * 1000) event = self.read(1) if event is not None and event[0][0] in events: rt = int((get_time() - start) * 1000) _event = event[0][0] done = True break if Keyboard.process_control_keys(): done = True break if duration: if int((get_time() - start) * 1000) >= duration: done = True break time.sleep(0.0005) if self._logging: expyriment._active_exp._event_file_log( "MIDI In ({0}),received,{1},wait".format(self.id, _event), 2) return _event, rt
def wait(self, length, package_size=None, duration=None, check_control_keys=True): """Wait for data. Parameters: ----------- length : int The length of the data to be waited for in bytes. If not set, a single package will be waited for. package_size : int, optional The size of the package to be waited for, optional. If not set, the default package size will be used. If length < package_size, package_size = length. duration: int, optional The duration to wait in milliseconds. process_control_keys : bool, optional Check if control key has been pressed (default = True). Returns: -------- data : str The received data. rt : int The time it took to receive the data in milliseconds. """ if expyriment.control.defaults._skip_wait_functions: return None, None start = get_time() data = None rt = None if package_size is None: package_size = self._default_package_size if length is None: length = package_size elif length < package_size: package_size = length while True: try: if data is None: data = self._client[0].recv(package_size) while len(data) < length: if length - len(data) >= package_size: data = data + self._client[0].recv(package_size) else: data = data + self._client[0].recv(length - len(data)) if duration: if int((get_time() - start) * 1000) >= duration: data = None rt = None break rt = int((get_time() - start) * 1000) break except socket.error, e: err = e.args[0] if err == errno.EAGAIN or err == errno.EWOULDBLOCK: rtn_callback = expyriment._active_exp._execute_wait_callback() if isinstance(rtn_callback, expyriment.control.CallbackQuitEvent): return rtn_callback, int((get_time() - start) * 1000) if check_control_keys: if Keyboard.process_control_keys(): break if duration: if int((get_time() - start) * 1000) >= duration: data = None rt = None break
def wait_char(self, char, duration=None, check_for_control_keys=True): """Wait for character(s) (optionally for a certain amount of time). This function will wait for one or more characters and returns the found character as well as the reaction time. (This function clears the event queue!) Parameters ---------- char : int or list a specific character or list of characters to wait for duration : int, optional maximal time to wait in ms check_for_control_keys : bool, optional checks if control key has been pressed (default=True) Returns ------- found : char pressed charater rt : int reaction time in ms See Also -------- design.experiment.register_wait_callback_function """ if expyriment.control.defaults._skip_wait_functions: return None, None start = get_time() rt = None found_char = None self.clear() try: char = list(char) except: char = [char] pygame.event.pump() done = False while not done: rtn_callback = expyriment._active_exp._execute_wait_callback() if isinstance(rtn_callback, expyriment.control.CallbackQuitEvent): done = True rt = int((get_time() - start) * 1000) found_char = rtn_callback for event in pygame.event.get([pygame.KEYUP, pygame.KEYDOWN]): if check_for_control_keys and Keyboard.process_control_keys( event): done = True elif event.type == pygame.KEYDOWN: if event.unicode in char: rt = int((get_time() - start) * 1000) found_char = event.unicode done = True if duration and not done: done = int((get_time() - start) * 1000) >= duration time.sleep(0.0005) if self._logging: expyriment._active_exp._event_file_log( "Keyboard,received,{0},wait_char".format( unicode2str(found_char))) return found_char, rt
def _test1(): info = """This will test the visual stimulus presentation timing specifics of your system. During the test, you will see two squares on the screen. After the test, you will be asked to indicate which (if any) of those two squares were flickering. [Press RETURN to continue]""" # TODO test very slow quit text = stimuli.TextScreen("Visual stimulus presentation test", info) #y = [] #for x in [16, 32, 48, 64]: # y.extend([x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, ]) #graph1 = _make_graph(range(60), y, [0, 255, 0]) #y = range(80) #graph2 = _make_graph(range(60), y, [255, 0, 0]) #graph1.position = (-200, -100) #graph2.position = (200, -100) text.present() #graph1.present(clear=False, update=False) #graph2.present(clear=False) exp.keyboard.wait([constants.K_RETURN]) message = stimuli.TextScreen("Running", "Please wait...") message.present() message.present() message.present() c1 = stimuli.Canvas((400, 400)) c2 = stimuli.Canvas((400, 400)) c3 = stimuli.Canvas((400, 400)) frame1 = stimuli.Rectangle((100, 100), position=(-100, 0)) frame2 = stimuli.Rectangle((100, 100), position=(100, 0)) bg = stimuli.Rectangle((90, 90), colour=exp.background_colour) bg.plot(frame1) bg.plot(frame2) frame1.plot(c1) frame2.plot(c2) frame1.plot(c3) frame2.plot(c3) c1.preload() c2.preload() c3.preload() c1.present(clear=False) c2.present(clear=False) c3.present(clear=False) s1 = stimuli.Circle(1, colour=exp.background_colour) s2 = stimuli.Circle(1, colour=exp.background_colour) s1.preload() s2.preload() todo_time = range(0, 60) * 3 randomize.shuffle_list(todo_time) actual_time = [] for x in todo_time: s1.present(clear=False) start = get_time() exp.clock.wait(x) s2.present(clear=False) actual_time.append((get_time() - start) * 1000) exp.clock.wait(expyriment.design.randomize.rand_int(30, 60)) # determine refresh_rate tmp = [] for _x in range(100): start = get_time() s1.present(clear=False) tmp.append(get_time() - start) start = get_time() s2.present(clear=False) tmp.append(get_time() - start) refresh_rate = 1000 / (statistics.mean(tmp) * 1000) #text = stimuli.TextScreen("Results", "[Press RETURN to continue]") #graph = _make_graph(todo_time, actual_time, [150, 150, 150]) #graph.position = (0, -100) #text.present(update=False) #graph.present(clear=False) #exp.keyboard.wait([constants.K_RETURN]) #text = stimuli.TextScreen( # "Which picture looks most similar to the results?", # "[Press LEFT or RIGHT arrow key]") #y = [] #for x in [16, 32, 48, 64]: # y.extend([x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, ]) #graph1 = _make_graph(range(60), y, [0, 255, 0]) #y = range(80) #graph2 = _make_graph(range(60), y, [255, 0, 0]) #graph1.position = (-200, -100) #graph2.position = (200, -100) #text.present(update=False) #graph1.present(clear=False, update=False) #graph2.present(clear=False) #key, _rt = exp.keyboard.wait([constants.K_LEFT, # constants.K_RIGHT]) #if key == constants.K_LEFT: # response1 = "Steps" #elif key == constants.K_RIGHT: # response1 = "Line" #else: # response1 = None # show histogram of presentation delays def expected_delay(presentation_time, refresh_rate): refresh_time = 1000.0 / refresh_rate return refresh_time - (presentation_time % refresh_time) # delay = map(lambda x: x[1]- x[0], zip(todo_time, actual_time)) unexplained_delay = map( lambda x: x[1] - x[0] - expected_delay(x[0], refresh_rate), zip(todo_time, actual_time)) hist, hist_str = _histogram(unexplained_delay) inaccuracies = [] delayed_presentations = 0 for key in hist.keys(): inaccuracies.extend([key % (1000 / refresh_rate)] * hist[key]) if key != 0: delayed_presentations += hist[key] inaccuracy = int(round(sum(inaccuracies) / float(len(inaccuracies)))) delayed = round(100 * delayed_presentations / 180.0, 2) text = stimuli.TextScreen( "How many of the two squares were flickering?", "[Press 0, 1 or 2]") text.present() key, _rt = exp.keyboard.wait( [constants.K_0, constants.K_1, constants.K_2]) if key == constants.K_0: response = 0 elif key == constants.K_1: response = 1 elif key == constants.K_2: response = 2 info = stimuli.TextScreen("Results", "") results1 = stimuli.TextScreen( "", "Estimated Screen Refresh Rate: {0} Hz (~ every {1} ms)\n\n". format(int(round(refresh_rate)), int(1000.0 / refresh_rate)), text_font="freemono", text_size=16, text_bold=True, text_justification=0, position=(0, 40)) results2 = stimuli.TextScreen( "", "Detected Framebuffer Pages: {0}\n\n".format(response + 1), text_font="freemono", text_size=16, text_bold=True, text_justification=0, position=(0, 20)) if inaccuracy != 0: results3_colour = [255, 0, 0] else: results3_colour = [0, 255, 0] results3 = stimuli.TextScreen( "", "Average Reporting Inaccuracy: {0} ms\n\n".format(inaccuracy), text_font="freemono", text_size=16, text_bold=True, text_justification=0, text_colour=results3_colour, position=(0, -20)) if delayed > 10: results4_colour = [255, 0, 0] elif 10 > delayed > 1: results4_colour = [255, 255, 0] else: results4_colour = [0, 255, 0] results4 = stimuli.TextScreen( "", "Unexplained Presentation Delays: {0} %\n\n\n".format(delayed), text_font="freemono", text_size=16, text_bold=True, text_justification=0, text_colour=results4_colour, position=(0, -40)) results5 = stimuli.TextScreen("", hist_str, text_font="freemono", text_size=16, text_bold=True, text_justification=0, position=(0, -100)) results1.plot(info) results2.plot(info) results3.plot(info) results4.plot(info) results5.plot(info) info2 = stimuli.TextLine("[Press RETURN to continue]", position=(0, -160)) info2.plot(info) info.present() exp.keyboard.wait([constants.K_RETURN]) return todo_time, actual_time, refresh_rate, inaccuracy, delayed, response
exp.add_experiment_info(['Block {} - Test'.format(0)]) # Add listPictures exp.add_experiment_info(presentationOrder) # Add listPictures for nCard in presentationOrder: m._cueCard.setPicture(m._matrix.item( nCard).stimuli[0].filename) # Associate Picture to CueCard m.plotCueCard(True, bs, True) # Show Cue exp.clock.wait(presentationCard) # Wait presentationCard m.plotCueCard(False, bs, True) # Hide Cue mouse.show_cursor(True, True) start = get_time() rt, position = readMouse(start, mouseButton, responseTime) mouse.hide_cursor(True, True) if rt is not None: currentCard = m.checkPosition(position) if currentCard is not None: m._matrix.item(currentCard).color = clickColor m.plotCard(currentCard, False, bs, True) exp.clock.wait(clicPeriod) # Wait 500ms m._matrix.item(currentCard).color = cardColor
def wait(self, codes=None, duration=None, no_clear_buffer=False, bitwise_comparison=False, check_for_control_keys=True): """Wait for responses defined as codes. Notes ----- If bitwise_comparision = True, the function performs a bitwise comparison (logical and) between codes and received input and waits until a certain bit pattern is set. This will also by default check for control keys (quit and pause). Thus, keyboard events will be cleared from the cue and cannot be received by a Keyboard().check() anymore! Parameters ---------- codes : int or list, optional bit pattern to wait for if codes is not set (None) the function returns for any event that differs from the baseline duration : int, optional maximal time to wait in ms no_clear_buffer : bool, optional do not clear the buffer (default = False) bitwise_comparison : bool, optional make a bitwise comparison (default = False) check_for_control_keys : bool, optional checks if control key has been pressed (default=True) Returns ------- key : int key code (or None) that quitted waiting rt : int reaction time See Also -------- design.experiment.register_wait_callback_function """ if expyriment.control.defaults._skip_wait_functions: return None, None start = get_time() rt = None if not no_clear_buffer: self.clear() while True: rtn_callback = expyriment._active_exp._execute_wait_callback() if isinstance(rtn_callback, expyriment.control.CallbackQuitEvent): found = rtn_callback rt = int((get_time() - start) * 1000) break if duration is not None: if int((get_time() - start) * 1000) > duration: return None, None found = self.check(codes, bitwise_comparison) if found is not None: rt = int((get_time() - start) * 1000) break if check_for_control_keys: if Keyboard.process_control_keys(): break if self._logging: expyriment._active_exp._event_file_log( "{0},received,{1},wait".format(self.__class__.__name__, found)) return found, rt
def process_quit_event(click_position=None): """Check if mouse exit action has been performed If Mouse.quit_rect_location is defined (i.e. 0, 1, 2 or 3), clicking quickly three times (i.e., within 1 second) in one of the corners of the screen forces the experiment to quit. The function is called automatically by all mouse get event and wait methods (similar to Keyboard.process_control_keys). If no mouse functions are called by your program, this function can be polled to ensure quitting experiment by mouse. Mouse quit events are especially useful for experiments on devices without hardware keyboard, such as tablet PCs or smartphones. Parameters ---------- click_position : tuple of int (x,y), optional clicked location to be processed. If not defined, the Pygame event queue will be checked for mouse down events and the current position is taken Returns ------- out : bool, optional True if exit action has been performed False otherwise Notes ----- To switch on or off the detection of mouse quit events, please use the static class property `quit_rect_location' (see below). The detection of mouse quit events is activated by default under Android. Static class properties ~~~~~~~~~~~~~~~~~~~~~~~ `Mouse.quit_rect_location` = int, optional Location of the quit click action field or None. 0 = upper left corner, 1 = upper right corner (0) (1) 2 = lower right corner, 3 = lower left corner (3) (2) otherwise the detection of mouse quit events is deactivated. Default value under Android is 1, otherwise None `Mouse.quit_click_rect_size` : tuple (int, int) size of the field (rect) that detects the quit action by triple clicking in one corner of the screen. (default = (30, 30)) Changing the static class properties affects always all mouse instances. """ if Mouse.quit_rect_location not in [0,1,2,3] or \ expyriment._active_exp is None: return False if click_position is None: # check Pygame queu pos = None pygame.event.pump() screen_size = expyriment._active_exp.screen.surface.get_size() for event in pygame.event.get(pygame.MOUSEBUTTONDOWN): if event.button > 0: pos = pygame.mouse.get_pos() pos = (pos[0] - screen_size[0] / 2, -pos[1] + screen_size[1] / 2) break if pos is None: return False else: return Mouse.process_quit_event(click_position=pos) # determine threshold x & y if Mouse.quit_rect_location == 0 or Mouse.quit_rect_location == 3: # left threshold_x = -expyriment._active_exp.screen.center_x + \ Mouse.quit_click_rect_size[0] else:# right threshold_x = expyriment._active_exp.screen.center_x - \ Mouse.quit_click_rect_size[0] if Mouse.quit_rect_location == 0 or Mouse.quit_rect_location == 1: # upper threshold_y = expyriment._active_exp.screen.center_y - \ Mouse.quit_click_rect_size[1] else:# lower threshold_y = -expyriment._active_exp.screen.center_y + \ Mouse.quit_click_rect_size[1] # check if (Mouse.quit_rect_location == 0 and \ click_position[0] < threshold_x and\ click_position[1] > threshold_y) \ or (Mouse.quit_rect_location == 1 and \ click_position[0] > threshold_x and \ click_position[1] > threshold_y) \ or (Mouse.quit_rect_location == 2 and \ click_position[0] > threshold_x and \ click_position[1] < threshold_y) \ or (Mouse.quit_rect_location == 3 and \ click_position[0] < threshold_x and \ click_position[1] < threshold_y): Mouse._quit_action_events.append(get_time()) if len(Mouse._quit_action_events)>=3: diff = get_time()-Mouse._quit_action_events.pop(0) if (diff < 1): # simulate quit key simulated_key = pygame.event.Event(pygame.KEYDOWN,\ {'key': Keyboard.get_quit_key()}) return Keyboard.process_control_keys( key_event=simulated_key) return False
def wait_press(self, buttons=None, duration=None): """Wait for gamepad button press. Returns the found button and the reaction time. Parameters ---------- buttons : int or list, optional specific buttons to wait for duration : int, optional maximal time to wait in ms Returns ------- button : int button _id of the pressed button rt : int reaction time in ms See Also -------- design.experiment.register_wait_callback_function """ if expyriment.control.defaults._skip_wait_functions: return None, None start = get_time() rt = None _button = None self.clear() if buttons is None: buttons = range(self.get_numbuttons()) try: buttons = list(buttons) except: buttons = [buttons] done = False while not done: rtn_callback = expyriment._active_exp._execute_wait_callback() if isinstance(rtn_callback, expyriment.control.CallbackQuitEvent): _button = rtn_callback rt = int((get_time() - start) * 1000) done = True for button in buttons: if self.get_button(button): _button = button rt = int((get_time() - start) * 1000) done = True break if _button is not None or Keyboard.process_control_keys(): done = True break if duration: if int((get_time() - start) * 1000) >= duration: done = True break time.sleep(0.0005) if self._logging: expyriment._active_exp._event_file_log( "Gamepad,received,{0},wait_press".format(_button)) return _button, rt