Commit c5ccffbe by Laura Rodríguez

9 jun

parent 557470ce
## This file is part of PyGaze - the open-source toolbox for eye tracking
##
## PyGaze is a Python module for easily creating gaze contingent experiments
## or other software (as well as non-gaze contingent experiments/software)
## Copyright (C) 2012-2013 Edwin S. Dalmaijer
##
## This program is free software: you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation, either version 3 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program. If not, see <http://www.gnu.org/licenses/>
#
# version: 0.4 (25-03-2013)
# MAIN
DUMMYMODE = False # False for gaze contingent display, True for dummy mode (using mouse or joystick)
LOGFILENAME = 'default' # logfilename, without path
LOGFILE = LOGFILENAME[:] # .txt; adding path before logfilename is optional; logs responses (NOT eye movements, these are stored in an EDF file!)
TRIALS = 5
# UNA OPCION
# # Constants for the display
# DISPSIZE = (1920, 1080) # Resolución en píxeles
# SCREENSIZE = (52.8, 29.7) # Tamaño físico de la pantalla en cm
# # Conversión de centímetros a píxeles basada en el tamaño físico de la pantalla
# def cm_to_px(cm, screen_cm, screen_px):
# return (cm / screen_cm) * screen_px
# # Calcular el centro de la pantalla en píxeles
# CENTER = (DISPSIZE[0] // 2, DISPSIZE[1] // 2)
# # Establecer el tamaño del estímulo en centímetros
# stimulus_diameter_cm = 2.09 # Estímulo de 2.09 cm
# # Convertir el tamaño del estímulo a píxeles
# stimulus_diameter_px = cm_to_px(stimulus_diameter_cm, SCREENSIZE[0], DISPSIZE[0])
# # Display and eye tracker settings (kept as is)
# DUMMYMODE = False
# LOGFILENAME = 'default'
# LOGFILE = LOGFILENAME[:]
# TRIALS = 5
# SCREENNR = 0
# DISPTYPE = 'pygame'
# MOUSEVISIBLE = False
# BGC = (125, 125, 125)
# FGC = (0, 0, 0)
# ---------------------------------------------------------------
# DISPLAY
# used in libscreen, for the *_display functions. The values may be adjusted,
# but not the constant's names
SCREENNR = 0 # number of the screen used for displaying experiment
DISPTYPE = 'pygame' # either 'psychopy' or 'pygame'
DISPSIZE = (1920,1080) # canvas size
CENTER = (DISPSIZE[0] // 2, DISPSIZE[1] // 2) # Centro de la pantalla en píxeles
SCREENSIZE = (52.8, 29.7) # physical display size in cm
MOUSEVISIBLE = False # mouse visibility
BGC = (125, 125, 125) # backgroundcolour
FGC = (0,0,0) # foregroundcolour
# SOUND
# defaults used in libsound. The values may be adjusted, but not the constants'
# names
SOUNDOSCILLATOR = 'sine' # 'sine', 'saw', 'square' or 'whitenoise'
SOUNDFREQUENCY = 440 # Herz
SOUNDLENGTH = 100 # milliseconds (duration)
SOUNDATTACK = 0 # milliseconds (fade-in)
SOUNDDECAY = 5 # milliseconds (fade-out)
SOUNDBUFFERSIZE = 1024 # increase if playback is choppy
SOUNDSAMPLINGFREQUENCY = 48000 # samples per second
SOUNDSAMPLESIZE = -16 # determines bit depth (negative is signed
SOUNDCHANNELS = 2 # 1 = mono, 2 = stereo
# INPUT
# used in libinput. The values may be adjusted, but not the constant names.
MOUSEBUTTONLIST = None # None for all mouse buttons; list of numbers for buttons of choice (e.g. [1,3] for buttons 1 and 3)
MOUSETIMEOUT = None # None for no timeout, or a value in milliseconds
KEYLIST = None # None for all keys; list of keynames for keys of choice (e.g. ['space','9',':'] for space, 9 and ; keys)
KEYTIMEOUT = 1 # None for no timeout, or a value in milliseconds
JOYBUTTONLIST = None # None for all joystick buttons; list of button numbers (start counting at 0) for buttons of choice (e.g. [0,3] for buttons 0 and 3 - may be reffered to as 1 and 4 in other programs)
JOYTIMEOUT = None # None for no timeout, or a value in milliseconds
# EYETRACKER
# general
TRACKERTYPE = 'tobii' # either 'smi', 'eyelink' or 'dummy' (NB: if DUMMYMODE is True, trackertype will be set to dummy automatically)
TRACKERSERIALNUMBER = 'TPFC6-010203158072'
SACCVELTHRESH = 35 # degrees per second, saccade velocity threshold
SACCACCTHRESH = 9500 # degrees per second, saccade acceleration threshold
# EyeLink only
# SMI only
SMIIP = '127.0.0.1'
SMISENDPORT = 4444
SMIRECEIVEPORT = 5555
# FRL
# Used in libgazecon.FRL. The values may be adjusted, but not the constant names.
FRLSIZE = 200 # pixles, FRL-size
FRLDIST = 125 # distance between fixation point and FRL
FRLTYPE = 'gauss' # 'circle', 'gauss', 'ramp' or 'raisedCosine'
FRLPOS = 'center' # 'center', 'top', 'topright', 'right', 'bottomright', 'bottom', 'bottomleft', 'left', or 'topleft'
# CURSOR
# Used in libgazecon.Cursor. The values may be adjusted, but not the constants' names
CURSORTYPE = 'cross' # 'rectangle', 'ellipse', 'plus' (+), 'cross' (X), 'arrow'
CURSORSIZE = 20 # pixels, either an integer value or a tuple for width and height (w,h)
CURSORCOLOUR = 'pink' # colour name (e.g. 'red'), a tuple RGB-triplet (e.g. (255, 255, 255) for white or (0,0,0) for black), or a RGBA-value (e.g. (255,0,0,255) for red)
CURSORFILL = True # True for filled cursor, False for non filled cursor
CURSORPENWIDTH = 3 # cursor edge width in pixels (only if cursor is not filled)
\ No newline at end of file
# -*- coding: utf-8 -*-
#
# This file is part of PyGaze - the open-source toolbox for eye tracking
#
# PyGaze is a Python module for easily creating gaze contingent experiments
# or other software (as well as non-gaze contingent experiments/software)
# Copyright (C) 2012-2013 Edwin S. Dalmaijer
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>
from pygaze.py3compat import *
from pygaze.settings import settings
from distutils.version import StrictVersion
import sys
import os
__version__ = version = u'0.6.0a24'
strict_version = StrictVersion(__version__)
# The version without the prerelease (if any): e.g. 3.0.0
main_version = u'.'.join([str(i) for i in strict_version.version])
# The version following the debian convention: e.g. 3.0.0~a1
if strict_version.prerelease is None:
deb_version = main_version
else:
deb_version = main_version + u'~%s%d' % strict_version.prerelease
# directory stuff
DIR = safe_decode(os.path.dirname(__file__), enc=sys.getfilesystemencoding())
if os.path.exists(u'resources'):
RESDIR = u'resources'
elif os.path.exists(os.path.join(DIR, u'resources')):
RESDIR = os.path.join(DIR, u'resources')
elif os.path.exists(u'/usr/share/pygaze/resources'):
RESDIR = u'/usr/share/pygaze/resources'
elif py3:
RESDIR = os.getcwd()
else:
RESDIR = os.getcwdu()
FONTDIR = os.path.join(RESDIR, u'fonts')
SOUNDDIR = os.path.join(RESDIR, u'sounds')
# fontfiles
FONTFILES = []
if os.path.isdir(FONTDIR):
for fontfile in os.listdir(FONTDIR):
FONTFILES.append(os.path.join(FONTDIR, fontfile))
# -*- coding: utf-8 -*-
#
# This file is part of PyGaze - the open-source toolbox for eye tracking
#
# PyGaze is a Python module for easily creating gaze contingent experiments
# or other software (as well as non-gaze contingent experiments/software)
# Copyright (C) 2012-2013 Edwin S. Dalmaijer
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>
# -*- coding: utf-8 -*-
#
# This file is part of PyGaze - the open-source toolbox for eye tracking
#
# PyGaze is a Python module for easily creating gaze contingent experiments
# or other software (as well as non-gaze contingent experiments/software)
# Copyright (C) 2012-2013 Edwin S. Dalmaijer
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>
# The BaseClasses are meant to store the documentation on all methods of a
# class, but not to contain any functionality whatsoever. BaseClass is
# inherited by all of the subclasses, and the documentation is copied using
# pygaze.copy_docstr. If you intend to make your own subclass for the current
# baseclass, be sure to inherit BaseClass, copy the documentation, and
# redefine the methods as you see fit, e.g.:
#
#import pygaze
#from pygaze._display.basedisplay import BaseDisplay
#
#class DummyDisplay(BaseDisplay):
#
# """An example child of BaseDisplay"""
#
# def __init__(self, *args, **kwargs):
#
# """Initializes a DummyDisplay instance"""
#
# pygaze.copy_docstring(BaseDisplay,DummyDisplay)
#
# def show(self):
#
# # note that here no docstring is provided, as it is copied from
# # the parent class
#
# print("Display.show call at %d" % int(pygaze.clock.get_time()))
#
class BaseDisplay:
"""A class for Display objects, to present Screen objects on a monitor"""
def __init__(self):
"""
Initializes a Display object.
arguments
None
keyword arguments
dispsize -- size of the display in pixels: a (width, height)
tuple (default = DISPSIZE)
fgc -- the foreground colour: a RGB tuple, e.g. (255,0,0)
for red or (0,0,0) for black (default = FGC)
bgc -- the background colour: a RGB tuple, e.g. (0,0,255)
for blue or (255,255,255) for white (default = BGC)
screen -- a screen.Screen instance to be presented on the new
Display (default=None)
"""
pass
def show(self):
"""
Updates ('flips') the display.
arguments
None
keyword arguments
None
returns
time -- the exact refresh time when disptype is PsychoPy,
or an estimate when disptype is PyGame
"""
pass
def show_part(self, rect, screen=None):
"""
Fills AND shows part(s) of the screen to given specified screen
(only works when disptype is PyGame; when this is set to PsychoPy
the entire display is filled and updated)
arguments
rect -- a single or a list of rects; a rect is a (x,y,w,h)
tuple or list
keyword arguments
screen -- the screen of which the specified rects should be
updated to the display (default = None)
returns
time -- the exact refresh time when disptype is PsychoPy,
or an estimate when disptype is PyGame
"""
pass
def fill(self, screen=None):
"""
Fills the screen with the background colour of the Screen, NOT
updating it (call Display.show() to actually show the new contents)
arguments
None
keyword arguments
screen -- the screen that should be drawn to the display or
None to fill the display with its background colour
returns
None
"""
pass
def close(self):
"""
Closes the display
arguments
None
keyword arguments
None
returns
None
"""
pass
def make_screenshot(self, filename='screenshot.png'):
"""
Make a screenshot from the current display.
arguments
None
keyword arguments
filename -- path and filename, under wich the screenshot will be
saved. Defaults to screenshot.png
returns
None
"""
pass
# -*- coding: utf-8 -*-
#
# This file is part of PyGaze - the open-source toolbox for eye tracking
#
# PyGaze is a Python module for easily creating gaze contingent experiments
# or other software (as well as non-gaze contingent experiments/software)
# Copyright (C) 2012-2013 Edwin S. Dalmaijer
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>
from pygaze.py3compat import *
from pygaze import settings
from libopensesame.exceptions import osexception
from openexp.canvas import canvas
from openexp.keyboard import keyboard
from pygaze._display.basedisplay import BaseDisplay
# we try importing the copy_docstr function, but as we do not really need it
# for a proper functioning of the code, we simply ignore it when it fails to
# be imported correctly
try:
from pygaze._misc.misc import copy_docstr
except:
pass
class OSDisplay(BaseDisplay):
# See _display.basedisplay.BaseDisplay for documentation
def __init__(self, **args):
# See _display.basedisplay.BaseDisplay for documentation
# try to import copy docstring (but ignore it if it fails, as we do
# not need it for actual functioning of the code)
try:
copy_docstr(BaseDisplay, OSDisplay)
except:
# we're not even going to show a warning, since the copied
# docstring is useful for code editors; these load the docs
# in a non-verbose manner, so warning messages would be lost
pass
self.experiment = settings.osexperiment
self.canvas = canvas(self.experiment)
self.dispsize = self.experiment.resolution()
def show(self):
# See _display.basedisplay.BaseDisplay for documentation
return self.canvas.show()
def show_part(self, rect, screen=None):
# See _display.basedisplay.BaseDisplay for documentation
return self.canvas.show()
def fill(self, screen=None):
# See _display.basedisplay.BaseDisplay for documentation
if screen != None:
self.canvas = screen.canvas
else:
self.canvas = canvas(self.experiment)
def close(self):
# See _display.basedisplay.BaseDisplay for documentation
pass
# -*- coding: utf-8 -*-
#
# This file is part of PyGaze - the open-source toolbox for eye tracking
#
# PyGaze is a Python module for easily creating gaze contingent experiments
# or other software (as well as non-gaze contingent experiments/software)
# Copyright (C) 2012-2013 Edwin S. Dalmaijer
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>
from pygaze import settings
import pygaze
from pygaze._misc.misc import rgb2psychorgb
from pygaze.libtime import clock
#from pygaze._display.basedisplay import BaseDisplay
from psychopy.visual import Window
# we try importing the copy_docstr function, but as we do not really need it
# for a proper functioning of the code, we simply ignore it when it fails to
# be imported correctly
try:
from pygaze._misc.misc import copy_docstr
except:
pass
#class PsychoPyDisplay(BaseDisplay):
class PsychoPyDisplay:
# See _display.basedisplay.BaseDisplay for documentation
def __init__(self, dispsize=settings.DISPSIZE, fgc=settings.FGC,
bgc=settings.BGC, screennr=settings.SCREENNR, monitor = None, screen=None, **args):
# See _display.basedisplay.BaseDisplay for documentation
# try to import copy docstring (but ignore it if it fails, as we do
# not need it for actual functioning of the code)
try:
copy_docstr(BaseDisplay, PsychoPyDisplay)
except:
# we're not even going to show a warning, since the copied
# docstring is useful for code editors; these load the docs
# in a non-verbose manner, so warning messages would be lost
pass
self.dispsize = dispsize
self.fgc = fgc
self.bgc = bgc
self.screennr = screennr
self.mousevis = False
self.monitor = monitor
# create window
pygaze.expdisplay = Window(size=self.dispsize, pos=None,
color=rgb2psychorgb(self.bgc), colorSpace='rgb',
fullscr=settings.FULLSCREEN, monitor=self.monitor,
screen=self.screennr, units='pix')
# set mouse visibility
pygaze.expdisplay.setMouseVisible(self.mousevis)
# get screen in window
if screen:
for s in screen.screen:
s.draw()
def show(self):
# See _display.basedisplay.BaseDisplay for documentation
pygaze.expdisplay.flip()
return clock.get_time()
def show_part(self, rect, screen=None):
# See _display.basedisplay.BaseDisplay for documentation
self.fill(screen)
self.show()
print("WARNING! screen.Display.show_part not available for PsychoPy display type; fill() and show() are used instead")
return clock.get_time()
def fill(self, screen=None):
# See _display.basedisplay.BaseDisplay for documentation
pygaze.expdisplay.clearBuffer()
if screen != None:
for s in screen.screen:
s.draw()
def close(self):
# See _display.basedisplay.BaseDisplay for documentation
pygaze.expdisplay.close()
def make_screenshot(self, filename='screenshot.png'):
# See _display.basedisplay.BaseDisplay for documentation
pygaze.expdisplay.getMovieFrame(buffer='front')
pygaze.expdisplay.saveMovieFrames(filename)
# -*- coding: utf-8 -*-
#
# This file is part of PyGaze - the open-source toolbox for eye tracking
#
# PyGaze is a Python module for easily creating gaze contingent experiments
# or other software (as well as non-gaze contingent experiments/software)
# Copyright (C) 2012-2013 Edwin S. Dalmaijer
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>
from pygaze import settings
import pygaze
from pygaze.libtime import clock
import copy
import math
import os.path
import pygame
import pygame.display
import pygame.draw
import pygame.image
from pygaze._display.basedisplay import BaseDisplay
# we try importing the copy_docstr function, but as we do not really need it
# for a proper functioning of the code, we simply ignore it when it fails to
# be imported correctly
try:
from pygaze._misc.misc import copy_docstr
except:
pass
class PyGameDisplay(BaseDisplay):
# See _display.basedisplay.BaseDisplay for documentation
def __init__(self, dispsize=settings.DISPSIZE, fgc=settings.FGC,
bgc=settings.BGC, screen=None, **args):
# See _display.basedisplay.BaseDisplay for documentation
# try to import copy docstring (but ignore it if it fails, as we do
# not need it for actual functioning of the code)
try:
copy_docstr(BaseDisplay, PyGameDisplay)
except:
# we're not even going to show a warning, since the copied
# docstring is useful for code editors; these load the docs
# in a non-verbose manner, so warning messages would be lost
pass
self.dispsize = dispsize
self.fgc = fgc
self.bgc = bgc
self.mousevis = False
# initialize PyGame display-module
pygame.display.init()
# make mouse invisible (should be so per default, but you never know)
pygame.mouse.set_visible(self.mousevis)
if settings.FULLSCREEN:
mode = pygame.FULLSCREEN|pygame.HWSURFACE|pygame.DOUBLEBUF
else:
mode = pygame.HWSURFACE|pygame.DOUBLEBUF
# create surface for full screen displaying
pygaze.expdisplay = pygame.display.set_mode(self.dispsize, mode)
# blit screen to display surface (if user entered one)
if screen:
pygaze.expdisplay.blit(screen.screen,(0,0))
else:
pygaze.expdisplay.fill(self.bgc)
def show(self):
# See _display.basedisplay.BaseDisplay for documentation
pygame.display.flip()
return clock.get_time()
def show_part(self, rect, screen=None):
# See _display.basedisplay.BaseDisplay for documentation
if len(rect) > 1:
for r in rect:
pygaze.expdisplay.set_clip(r)
if screen:
pygaze.expdisplay.blit(screen.screen, (0,0))
pygame.display.update(r)
pygaze.expdisplay.set_clip(None)
elif len(rect) == 1:
pygaze.expdisplay.clip(rect)
if screen:
pygaze.expdisplay.blit(screen.screen, (0,0))
pygame.display.update(rect)
pygaze.expdisplay.set_clip(None)
else:
raise Exception("Error in libscreen.Display.show_part: rect should be a single rect (i.e. a (x,y,w,h) tuple) or a list of rects!")
return clock.get_time()
def fill(self, screen=None):
# See _display.basedisplay.BaseDisplay for documentation
pygaze.expdisplay.fill(self.bgc)
if screen != None:
pygaze.expdisplay.blit(screen.screen,(0,0))
def close(self):
# See _display.basedisplay.BaseDisplay for documentation
pygame.display.quit()
def make_screenshot(self, filename='screenshot.png'):
# See _display.basedisplay.BaseDisplay for documentation
pygame.image.save(pygaze.expdisplay, filename)
# -*- coding: utf-8 -*-
#
# This file is part of PyGaze - the open-source toolbox for eye tracking
#
# PyGaze is a Python module for easily creating gaze contingent experiments
# or other software (as well as non-gaze contingent experiments/software)
# Copyright (C) 2012-2013 Edwin S. Dalmaijer
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>
# iViewXAPI.py
#
# Demonstrates features of iView X API
# Defines structures
# Loads in iViewXAPI.dll
# This script shows how to set up an experiment with Python 2.7.1 (with ctypes Library)
#
# Author: SMI GmbH
# Feb. 16, 2011
from ctypes import *
#===========================
# Struct Definition
#===========================
class CSystem(Structure):
_fields_ = [("samplerate", c_int),
("iV_MajorVersion", c_int),
("iV_MinorVersion", c_int),
("iV_Buildnumber", c_int),
("API_MajorVersion", c_int),
("API_MinorVersion", c_int),
("API_Buildnumber", c_int),
("iV_ETDevice", c_int)]
class CCalibration(Structure):
_fields_ = [("method", c_int),
("visualization", c_int),
("displayDevice", c_int),
("speed", c_int),
("autoAccept", c_int),
("foregroundBrightness", c_int),
("backgroundBrightness", c_int),
("targetShape", c_int),
("targetSize", c_int),
("targetFilename", c_char * 256)]
class CEye(Structure):
_fields_ = [("gazeX", c_double),
("gazeY", c_double),
("diam", c_double),
("eyePositionX", c_double),
("eyePositionY", c_double),
("eyePositionZ", c_double)]
class CSample(Structure):
_fields_ = [("timestamp", c_longlong),
("leftEye", CEye),
("rightEye", CEye),
("planeNumber", c_int)]
class CEvent(Structure):
_fields_ = [("eventType", c_char),
("eye", c_char),
("startTime", c_longlong),
("endTime", c_longlong),
("duration", c_longlong),
("positionX", c_double),
("positionY", c_double)]
class CAccuracy(Structure):
_fields_ = [("deviationLX",c_double),
("deviationLY",c_double),
("deviationRX",c_double),
("deviationRY",c_double)]
#===========================
# Loading iViewX.dll
#===========================
iViewXAPI = windll.LoadLibrary("iViewXAPI.dll")
#===========================
# Initializing Structs
#===========================
systemData = CSystem(0, 0, 0, 0, 0, 0, 0, 0)
calibrationData = CCalibration(5, 1, 0, 0, 1, 20, 239, 1, 15, b"")
leftEye = CEye(0,0,0)
rightEye = CEye(0,0,0)
sampleData = CSample(0,leftEye,rightEye,0)
eventData = CEvent('F', 'L', 0, 0, 0, 0, 0)
accuracyData = CAccuracy(0,0,0,0)
# -*- coding: utf-8 -*-
#
# This file is part of PyGaze - the open-source toolbox for eye tracking
#
# PyGaze is a Python module for easily creating gaze contingent experiments
# or other software (as well as non-gaze contingent experiments/software)
# Copyright (C) 2012-2013 Edwin S. Dalmaijer
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>
from pygaze import settings
from pygaze.py3compat import *
from pygaze.libtime import clock
import pygaze
from pygaze.screen import Screen
from pygaze._eyetracker.baseeyetracker import BaseEyeTracker
# we try importing the copy_docstr function, but as we do not really need it
# for a proper functioning of the code, we simply ignore it when it fails to
# be imported correctly
try:
from pygaze._misc.misc import copy_docstr
except:
pass
def message(msg):
"""Prints a timestamp and message to the console"""
print(u"%d\t%s" % (int(clock.get_time()), msg))
class DumbDummy(BaseEyeTracker):
"""A dummy class to run experiments in 'dumb dummy' mode, where nothing happens (NO simulation!)"""
def __init__(self, display):
"""Initiates a 'dumb dummy' object, that doesn't do a thing
arguments
display -- a pygaze display.Display instance
keyword arguments
None
"""
# try to copy docstrings (but ignore it if it fails, as we do
# not need it for actual functioning of the code)
try:
copy_docsafe_decode(BaseEyeTracker, DumbDummy)
except:
# we're not even going to show a warning, since the copied
# docstring is useful for code editors; these load the docs
# in a non-verbose manner, so warning messages would be lost
pass
self.recording = False
self.blinking = False
self.bbpos = (settings.DISPSIZE[0]/2, settings.DISPSIZE[1]/2)
self.display = display
self.screen = Screen(disptype=settings.DISPTYPE, mousevisible=False)
def send_command(self, cmd):
"""Dummy command, messages command instead of sending it to the eyetracker"""
message(u"The following command would have been given to the eyetracker: " + safe_decode(cmd))
def log(self, msg):
"""Dummy log message, messages message instead of sending it to the eyetracker"""
message(u"The following message would have been logged to the EDF: " + safe_decode(msg))
def log_var(self, var, val):
"""Dummy varlog, messages variable and value instead of sending it to the eyetracker"""
message(u"The following variable would have been logged to the EDF: " + safe_decode(var) + u", value: " + safe_decode(val))
def status_msg(self, msg):
"""Dummy status message, messages message instead of sending it to the eyetracker"""
message(u"The following status message would have been visible on the experimentor PC: " + safe_decode(msg))
def connected(self):
"""Dummy connection status"""
message("Dummy mode, eyetracker not connected.")
return True
def calibrate(self):
"""Dummy calibration"""
message("Calibration would now take place")
def drift_correction(self, pos=None, fix_triggered=False):
"""Dummy drift correction"""
message("Drift correction would now take place")
return True
def prepare_drift_correction(self, pos):
"""Dummy drift correction preparation"""
message("Drift correction preparation would now take place")
def fix_triggered_drift_correction(self, pos=None, min_samples=30, max_dev=60, reset_threshold=10):
"""Dummy drift correction (fixation triggered)"""
message("Drift correction (fixation triggered) would now take place")
return True
def start_recording(self):
"""Dummy for starting recording, messages what would have been the recording start"""
self.recording = True
message("Recording would have started now")
def stop_recording(self):
"""Dummy for stopping recording, messages what would have been the recording end"""
self.recording = False
message("Recording would have stopped now")
def close(self):
"""Dummy for closing connection with eyetracker, messages what would have been connection closing time"""
if self.recording:
self.stop_recording()
message("eyetracker connection would have closed now")
def set_eye_used(self):
"""Dummy for setting which eye to track (does nothing)"""
message("Which eye to track would now be set")
def pupil_size(self):
"""Returns dummy pupil size"""
return 19
def sample(self):
"""Returns dummy gaze position"""
return (19,19)
def wait_for_event(self, event):
"""Waits for simulated event (3=STARTBLINK, 4=ENDBLINK, 5=STARTSACC, 6=ENDSACC, 7=STARTFIX, 8=ENDFIX)"""
if event == 5:
outcome = self.wait_for_saccade_start()
elif event == 6:
outcome = self.wait_for_saccade_end()
elif event == 7:
outcome = self.wait_for_fixation_start()
elif event == 8:
outcome = self.wait_for_fixation_end()
elif event == 3:
outcome = self.wait_for_blink_start()
elif event == 4:
outcome = self.wait_for_blink_end()
return outcome
def wait_for_saccade_start(self):
"""Returns starting time and starting position when a simulated saccade is started"""
return clock.get_time(), (19,19)
def wait_for_saccade_end(self):
"""Returns ending time, starting and end position when a simulated saccade is ended"""
# function assumes that a 'saccade' has ended when 'gaze' position remains reasonably
# (i.e.: within maxerr) stable for five samples
# for saccade start algorithm, see wait_for_fixation_start
stime, spos = self.wait_for_saccade_start()
return clock.get_time(), spos, (190,190)
def wait_for_fixation_start(self):
"""Returns starting time and position when a simulated fixation is started"""
return clock.get_time(), (19,19)
def wait_for_fixation_end(self):
"""Returns time and gaze position when a simulated fixation is ended"""
stime, spos = self.wait_for_fixation_start()
return clock.get_time(), spos
def wait_for_blink_start(self):
"""Returns starting time and position of a simulated blink"""
return clock.get_time(), (19,19)
def wait_for_blink_end(self):
"""Returns ending time and position of a simulated blink (mousebuttonup)"""
return clock.get_time(), (19,19)
# -*- coding: utf-8 -*-
#
# This file is part of PyGaze - the open-source toolbox for eye tracking
#
# PyGaze is a Python module for easily creating gaze contingent experiments
# or other software (as well as non-gaze contingent experiments/software)
# Copyright (C) 2012-2013 Edwin S. Dalmaijer
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>
from pygaze._eyetracker.baseeyetracker import BaseEyeTracker
from pygaze.py3compat import *
from pygaze import settings
from pygaze._misc.misc import copy_docstr
from pygaze._eyetracker.libtobii import TobiiProTracker
class EyeTracker(BaseEyeTracker):
"""
Generic EyeTracker class, which morphs into an eye-tracker specific class.
"""
def __init__(self, display, trackertype=settings.TRACKERTYPE, **args):
"""
Initializes an EyeTracker object.
arguments
display -- a pygaze.display.Display instance
keyword arguments
trackertype -- the type of eye tracker; choose from:
'dumbdummy', 'dummy', 'eyelink', 'smi',
'tobii', 'eyetribe' (default = TRACKERTYPE)
**args -- A keyword-argument dictionary that contains
eye-tracker-specific options
"""
# set trackertype to dummy in dummymode
if settings.DUMMYMODE:
trackertype = u'dummy'
# correct wrong input
if trackertype not in [u'dumbdummy', u'dummy', u'eyelink', u'smi', u'eyetribe', u'opengaze', u'tobii', u'tobii-legacy', u'tobiiglasses']:
raise Exception( \
u"Error in eyetracker.EyeTracker: trackertype '%s' not recognized; it should be one of 'dumbdummy', 'dummy', 'eyelink', 'smi', 'eyetribe', 'opengaze', 'tobii', 'tobii-legacy', 'tobiiglasses'" % trackertype)
# EyeLink
if trackertype == u'eyelink':
# import libraries
from pygaze._eyetracker.libeyelink import libeyelink
# morph class
self.__class__ = libeyelink
# initialize
self.__class__.__init__(self, display, **args)
# SMI
elif trackertype == u'smi':
# import libraries
from pygaze._eyetracker.libsmi import SMItracker
# morph class
self.__class__ = SMItracker
# initialize
self.__class__.__init__(self, display, **args)
# Tobii Legacy
elif trackertype == u'tobii-legacy':
# import libraries
from pygaze._eyetracker.libtobiilegacy import TobiiTracker
# morph class
self.__class__ = TobiiTracker
# initialize
self.__class__.__init__(self, display, **args)
# Tobii
elif trackertype == u'tobii':
# import libraries
from pygaze._eyetracker.libtobii import TobiiProTracker
# morph class
self.__class__ = TobiiProTracker
# initialize
self.__class__.__init__(self, display, **args)
# Tobii Pro Glasses 2
elif trackertype == u'tobiiglasses':
# import libraries
from pygaze._eyetracker.libtobiiglasses import TobiiGlassesTracker
# morph class
self.__class__ = TobiiGlassesTracker
# initialize
self.__class__.__init__(self, display, **args)
# EyeTribe
elif trackertype == u'eyetribe':
# import libraries
from pygaze._eyetracker.libeyetribe import EyeTribeTracker
# morph class
self.__class__ = EyeTribeTracker
# initialize
self.__class__.__init__(self, display, **args)
# OpenGaze
elif trackertype == u'opengaze':
# import libraries
from pygaze._eyetracker.libopengaze import OpenGazeTracker
# morph class
self.__class__ = OpenGazeTracker
# initialize
self.__class__.__init__(self, display, **args)
# dummy mode
elif trackertype == u'dummy':
# import libraries
from pygaze._eyetracker.libdummytracker import Dummy
# morph class
self.__class__ = Dummy
# initialize
self.__class__.__init__(self, display)
# dumb dummy mode
elif trackertype == u'dumbdummy':
# import libraries
from pygaze._eyetracker.libdumbdummy import DumbDummy
# morph class
self.__class__ = DumbDummy
# initialize
self.__class__.__init__(self, display)
else:
raise Exception( \
u"Error in eyetracker.EyeTracker.__init__: trackertype '%s' not recognized, this should not happen!" % trackertype)
# copy docstrings
copy_docstr(BaseEyeTracker, EyeTracker)
# -*- coding: utf-8 -*-
#
# This file is part of PyGaze - the open-source toolbox for eye tracking
#
# PyGaze is a Python module for easily creating gaze contingent experiments
# or other software (as well as non-gaze contingent experiments/software)
# Copyright (C) 2012-2013 Edwin S. Dalmaijer
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>
# -*- coding: utf-8 -*-
#
# This file is part of PyGaze - the open-source toolbox for eye tracking
#
# PyGaze is a Python module for easily creating gaze contingent experiments
# or other software (as well as non-gaze contingent experiments/software)
# Copyright (C) 2012-2013 Edwin S. Dalmaijer
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>
# The BaseClasses are meant to store the documentation on all methods of a
# class, but not to contain any functionality whatsoever. BaseClass is
# inherited by all of the subclasses, and the documentation is copied using
# pygaze.copy_docstr. If you intend to make your own subclass for the current
# baseclass, be sure to inherit BaseClass, copy the documentation, and
# redefine the methods as you see fit, e.g.:
#
#import pygaze
#from pygaze._display.basedisplay import BaseDisplay
#
#class DummyDisplay(BaseDisplay):
#
# """An example child of BaseDisplay"""
#
# def __init__(self, *args, **kwargs):
#
# """Initializes a DummyDisplay instance"""
#
# pygaze.copy_docstring(BaseDisplay,DummyDisplay)
#
# def show(self):
#
# # note that here no docstring is provided, as it is copied from
# # the parent class
#
# print("Display.show call at %d" % int(clock.get_time()))
#
class BaseJoystick:
"""A joystick for collecting responses"""
def __init__(self):
"""
Initializes a Joystick instance
arguments
None
keyword arguments
joybuttonlist -- list of joystick buttons that are allowed (e.g.
[0,2,4]) or None to allow all buttons
(default = JOYBUTTONLIST)
timeout -- time in milliseconds after which None is returned
on a call to a get_* method when no input is
registered (default = JOYTIMEOUT)
"""
pass
def set_joybuttonlist(self):
"""
Set a list of accepted joystick buttons
arguments
None
keyword arguments
joybuttonlist -- list of joystick buttons that are allowed (e.g.
[0,2,4]) or None to allow all buttons
(default = None)
returns
None -- sets the jbuttonlist property
"""
pass
def set_timeout(self):
"""
Set a timeout (in milliseconds)
arguments
None
keyword arguments
timeout -- time in milliseconds after which None is returned
on a call to get_clicked method when no click is
registered (default = None)
returns
None -- sets timeout property
"""
pass
def get_joybutton(self):
"""
Waits for joystick buttonpress
arguments
None
keyword arguments
joybuttonlist -- list of buttons that are allowed (e.g.
[0,2,4]), None to allow all buttons or
'default' to use jbuttonlist property
(default = 'default')
timeout -- time in milliseconds after which None is returned
when no buttonpress is registered; None for no
timeout or 'default' to use the timeout property
(default = 'default')
returns
button, presstime -- button is an integer, indicating which button
has been pressed or None when no button has
been pressed
presstime is the time (measured from
expbegintime) a buttonpress or a timeout
occured
"""
pass
def get_joyaxes(self):
"""
Waits for joystick axis movement
arguments
None
keyword arguments
timeout -- time in milliseconds after which None is returned
when no buttonpress is registered; None for no
timeout or 'default' to use the timeout property
(default = 'default')
returns
axespos, time -- axespos is a [x,y,z] position list for the
positions of the joystick axes (usually [x,y,z]
for the main stick); x, y and z are floats
time is the time (measured from expbegintime)
an axismovement or a timeout occured
"""
pass
def get_joyballs(self):
"""
Waits for joystick trackball movement
arguments
None
keyword arguments
timeout -- time in milliseconds after which None is returned
when no buttonpress is registered; None for no
timeout or 'default' to use the timeout property
(default = 'default')
returns
ballpos, time -- ballpos is a [ball1,ball2,...,ballN] position
list for the positions of the joystick balls;
each ball position is a (x,y) tuple
time is the time (measured from expbegintime) a
ballmovement or a timeout occured
"""
pass
def get_joyhats(self):
"""
Waits for joystick hat movement
arguments
None
keyword arguments
timeout -- time in milliseconds after which None is returned
when no buttonpress is registered; None for no
timeout or 'default' to use the timeout property
(default = 'default')
returns
hatpos, time -- hatpos is a [hat1,hat2,...,hatN] position list
for the positions of the joystick hats; each
hat position is a (x,y) tuple
time is the time (measured from expbegintime) a
hatmovement or a timeout occured
"""
pass
def get_joyinput(self):
"""
Waits for any kind of joystick input
arguments
None
keyword arguments
joybuttonlist -- list of buttons that are allowed (e.g.
[0,2,4]), None to allow all buttons or
'default' to use jbuttonlist property
(default = 'default')
timeout -- time in milliseconds after which None is returned
when no buttonpress is registered; None for no
timeout or 'default' to use the timeout property
(default = 'default')
returns
event, input, time -- event is a string or None on a timeout,
indicating what kind of input was given:
'joybuttonpress', input is an integer
button number
'joyaxismotion', input is a [x,y,z]
position list for the positions of the
joystick axes (usually [x,y,z] for the
main stick); x, y and z are floats
'joyballmotion', input is a
[ball1,ball2,...,ballN] position list for
the positions of the joystick balls; each
ball position is a (x,y) tuple
'joyhatmotion', input is a
[hat1,hat2,...,hatN] position list for
the positions of the joystick hats; each
hat position is a (x,y) tuple
time is the time (measured from
expbegintime) any input or a timeout
occured
"""
pass
# -*- coding: utf-8 -*-
#
# This file is part of PyGaze - the open-source toolbox for eye tracking
#
# PyGaze is a Python module for easily creating gaze contingent experiments
# or other software (as well as non-gaze contingent experiments/software)
# Copyright (C) 2012-2013 Edwin S. Dalmaijer
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>
# -*- coding: utf-8 -*-
#
# This file is part of PyGaze - the open-source toolbox for eye tracking
#
# PyGaze is a Python module for easily creating gaze contingent experiments
# or other software (as well as non-gaze contingent experiments/software)
# Copyright (C) 2012-2013 Edwin S. Dalmaijer
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>
# The BaseClasses are meant to store the documentation on all methods of a
# class, but not to contain any functionality whatsoever. BaseClass is
# inherited by all of the subclasses, and the documentation is copied using
# pygaze.copy_docstr. If you intend to make your own subclass for the current
# baseclass, be sure to inherit BaseClass, copy the documentation, and
# redefine the methods as you see fit, e.g.:
#
#import pygaze
#from pygaze._display.basedisplay import BaseDisplay
#
#class DummyDisplay(BaseDisplay):
#
# """An example child of BaseDisplay"""
#
# def __init__(self, *args, **kwargs):
#
# """Initializes a DummyDisplay instance"""
#
# pygaze.copy_docstring(BaseDisplay,DummyDisplay)
#
# def show(self):
#
# # note that here no docstring is provided, as it is copied from
# # the parent class
#
# print("Display.show call at %d" % int(clock.get_time()))
#
class BaseKeyboard:
"""A keyboard for collecting responses"""
def __init__(self):
"""
Initializes the Keyboard object
arguments
None
keyword arguments
keylist -- list of keys that are allowed, e.g. ['1','a','enter']
for the 1, A and Enter keys (default =
KEYLIST)
timeout -- time in milliseconds after which None is returned
on a call to the get_key method when no keypress is
registered (default = KEYTIMEOUT)
"""
pass
def set_keylist(self):
"""
Set a list of accepted keys
arguments
None
keyword arguments
keylist -- list of keys that are allowed (e.g.
['1','a','enter']) or None to allow all keys
(default = None)
returns
None -- sets klist property
"""
pass
def set_timeout(self):
"""
Set a timeout (in milliseconds)
arguments
None
keyword arguments
timeout -- time in milliseconds after which None is returned
on a call to get_key method when no keypress is
registered (default = None)
returns
None -- sets timeout property
"""
pass
def get_key(self):
"""
Wait for keyboard input
arguments
None
keyword arguments
keylist -- list of keys that are allowed (e.g.
['1','a','enter']), None to allow all keys or
'default' to use klist property (default = 'default')
timeout -- time in milliseconds after which None is returned
when no keypress is registered (default = None);
None for no timeout or 'default' to use the timeout
property (default = 'default')
flush -- Boolean indicating if all input from before
calling get_key should be ignored, if set to
False keypresses from before calling this
function will be registered, otherwise every
keyboard input from before calling this function
will be flushed (default = False)
returns
key, presstime -- key is a string, indicating which button has
been pressed or None when no key has been
pressed
presstime is the time (measured from
expbegintime) a keypress or a timeout occured
"""
pass
# -*- coding: utf-8 -*-
#
# This file is part of PyGaze - the open-source toolbox for eye tracking
#
# PyGaze is a Python module for easily creating gaze contingent experiments
# or other software (as well as non-gaze contingent experiments/software)
# Copyright (C) 2012-2013 Edwin S. Dalmaijer
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>
from libopensesame.exceptions import osexception
from openexp.keyboard import keyboard
from pygaze.py3compat import *
from pygaze import settings
from pygaze._keyboard.basekeyboard import BaseKeyboard
# we try importing the copy_docstr function, but as we do not really need it
# for a proper functioning of the code, we simply ignore it when it fails to
# be imported correctly
try:
from pygaze._misc.misc import copy_docstr
except:
pass
class OSKeyboard(BaseKeyboard):
# See _keyboard.basekeyboard.BaseKeyboard
def __init__(self, keylist=settings.KEYLIST, timeout=settings.KEYTIMEOUT):
# See _keyboard.basekeyboard.BaseKeyboard
# try to copy docstring (but ignore it if it fails, as we do
# not need it for actual functioning of the code)
try:
copy_docstr(BaseKeyboard, OSKeyboard)
except:
# we're not even going to show a warning, since the copied
# docstring is useful for code editors; these load the docs
# in a non-verbose manner, so warning messages would be lost
pass
self.experiment = settings.osexperiment
self.keyboard = keyboard(self.experiment, keylist=keylist,
timeout=timeout)
def set_keylist(self, keylist=None):
# See _keyboard.basekeyboard.BaseKeyboard
self.keyboard.keylist = keylist
def set_timeout(self, timeout=None):
# See _keyboard.basekeyboard.BaseKeyboard
self.keyboard.timeout = timeout
def get_key(self, keylist=u'default', timeout=u'default', flush=False):
# See _keyboard.basekeyboard.BaseKeyboard
kwdict = {}
if keylist != u'default':
kwdict[u'keylist'] = keylist
if timeout != u'default':
kwdict[u'timeout'] = timeout
# flush if necessary
if flush:
self.keyboard.flush()
return self.keyboard.get_key(**kwdict)
# -*- coding: utf-8 -*-
#
# This file is part of PyGaze - the open-source toolbox for eye tracking
#
# PyGaze is a Python module for easily creating gaze contingent experiments
# or other software (as well as non-gaze contingent experiments/software)
# Copyright (C) 2012-2013 Edwin S. Dalmaijer
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>
from pygaze.libtime import clock
from pygaze import settings
from pygaze._keyboard.basekeyboard import BaseKeyboard
# we try importing the copy_docstr function, but as we do not really need it
# for a proper functioning of the code, we simply ignore it when it fails to
# be imported correctly
try:
from pygaze._misc.misc import copy_docstr
except:
pass
import pygaze
import psychopy.event
class PsychoPyKeyboard(BaseKeyboard):
# See _keyboard.basekeyboard.BaseKeyboard
def __init__(self, keylist=settings.KEYLIST, timeout=settings.KEYTIMEOUT):
# See _keyboard.basekeyboard.BaseKeyboard
# try to copy docstring (but ignore it if it fails, as we do
# not need it for actual functioning of the code)
try:
copy_docstr(BaseKeyboard, PsychoPyKeyboard)
except:
# we're not even going to show a warning, since the copied
# docstring is useful for code editors; these load the docs
# in a non-verbose manner, so warning messages would be lost
pass
# keymap
self.keymap = {
'!' : 'exclamation',
'"' : 'doublequote',
'#' : 'hash',
'$' : 'dollar',
'&' : 'ampersand',
'\'' : 'quoteleft',
'(' : None,
')' : None,
'*' : 'asterisk',
'+' : 'plus',
',' : 'comma',
'-' : 'minus',
'.' : None,
'/' : 'slash',
':' : 'colin',
';' : 'semicolon',
'=' : 'equal',
'>' : 'greater',
'?' : 'question',
'@' : 'at',
'[' : 'bracketleft',
'\\' : 'backslash',
']' : 'bracketright',
'^' : None,
'_' : 'underscore'
}
# set keyboard characteristics
self.set_keylist(keylist)
self.set_timeout(timeout)
def set_keylist(self, keylist=None):
# See _keyboard.basekeyboard.BaseKeyboard
if keylist == None or keylist == []:
self.klist = None
else:
self.klist = []
for key in keylist:
if key in self.keymap:
self.klist.append(self.keymap[key])
else:
self.klist.append(key)
def set_timeout(self, timeout=None):
# See _keyboard.basekeyboard.BaseKeyboard
self.timeout = timeout
def get_key(self, keylist='default', timeout='default', flush=False):
# See _keyboard.basekeyboard.BaseKeyboard
# set keylist and timeout
if keylist == 'default':
keylist = self.klist
if timeout == 'default':
timeout = self.timeout
# flush if necessary
if flush:
psychopy.event.clearEvents(eventType='keyboard')
# starttime
starttime = clock.get_time()
time = clock.get_time()
# wait for input
while timeout == None or time - starttime <= timeout:
keys = psychopy.event.getKeys(keyList=keylist,timeStamped=False)
for key in keys:
if keylist == None or key in keylist:
return key, clock.get_time()
time = clock.get_time()
return None, time
# -*- coding: utf-8 -*-
#
# This file is part of PyGaze - the open-source toolbox for eye tracking
#
# PyGaze is a Python module for easily creating gaze contingent experiments
# or other software (as well as non-gaze contingent experiments/software)
# Copyright (C) 2012-2013 Edwin S. Dalmaijer
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>
from pygaze.libtime import clock
from pygaze import settings
from pygaze._keyboard.basekeyboard import BaseKeyboard
# we try importing the copy_docstr function, but as we do not really need it
# for a proper functioning of the code, we simply ignore it when it fails to
# be imported correctly
try:
from pygaze._misc.misc import copy_docstr
except:
pass
import pygaze
import pygame
class PyGameKeyboard(BaseKeyboard):
# See _keyboard.basekeyboard.BaseKeyboard
import pygame.key
def __init__(self, keylist=settings.KEYLIST, timeout=settings.KEYTIMEOUT):
# See _keyboard.basekeyboard.BaseKeyboard
# try to copy docstring (but ignore it if it fails, as we do
# not need it for actual functioning of the code)
try:
copy_docstr(BaseKeyboard, PyGameKeyboard)
except:
# we're not even going to show a warning, since the copied
# docstring is useful for code editors; these load the docs
# in a non-verbose manner, so warning messages would be lost
pass
# dictionary for keynames and codes
self.key_codes = {}
for i in dir(pygame):
if i[:2] == "K_":
code = eval("pygame.%s" % i)
name1 = pygame.key.name(code).lower()
name2 = name1.upper()
name3 = i[2:].lower()
name4 = name3.upper()
self.key_codes[name1] = code
self.key_codes[name2] = code
self.key_codes[name3] = code
self.key_codes[name4] = code
# set keyboard characteristics
self.set_keylist(keylist)
self.set_timeout(timeout)
def set_keylist(self, keylist=None):
# See _keyboard.basekeyboard.BaseKeyboard
if keylist == None or keylist == []:
self.klist = None
else:
self.klist = []
for key in keylist:
self.klist.append(key)
def set_timeout(self, timeout=None):
# See _keyboard.basekeyboard.BaseKeyboard
self.timeout = timeout
def get_key(self, keylist='default', timeout='default', flush=False):
# See _keyboard.basekeyboard.BaseKeyboard
# set keylist and timeout
if keylist == 'default':
keylist = self.klist
if timeout == 'default':
timeout = self.timeout
# flush if necessary
if flush:
pygame.event.get(pygame.KEYDOWN)
# starttime
starttime = clock.get_time()
time = clock.get_time()
# wait for input
while timeout == None or time - starttime <= timeout:
time = clock.get_time()
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
time = clock.get_time()
key = pygame.key.name(event.key)
if keylist == None or key in keylist:
return key, time
# in case of timeout
return None, time
# -*- coding: utf-8 -*-
#
# This file is part of PyGaze - the open-source toolbox for eye tracking
#
# PyGaze is a Python module for easily creating gaze contingent experiments
# or other software (as well as non-gaze contingent experiments/software)
# Copyright (C) 2012-2013 Edwin S. Dalmaijer
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>
# -*- coding: utf-8 -*-
#
# This file is part of PyGaze - the open-source toolbox for eye tracking
#
# PyGaze is a Python module for easily creating gaze contingent experiments
# or other software (as well as non-gaze contingent experiments/software)
# Copyright (C) 2012-2013 Edwin S. Dalmaijer
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>
# The BaseClasses are meant to store the documentation on all methods of a
# class, but not to contain any functionality whatsoever. BaseClass is
# inherited by all of the subclasses, and the documentation is copied using
# pygaze.copy_docstr. If you intend to make your own subclass for the current
# baseclass, be sure to inherit BaseClass, copy the documentation, and
# redefine the methods as you see fit, e.g.:
#
#import pygaze
#from pygaze._display.basedisplay import BaseDisplay
#
#class DummyDisplay(BaseDisplay):
#
# """An example child of BaseDisplay"""
#
# def __init__(self, *args, **kwargs):
#
# """Initializes a DummyDisplay instance"""
#
# pygaze.copy_docstring(BaseDisplay,DummyDisplay)
#
# def show(self):
#
# # note that here no docstring is provided, as it is copied from
# # the parent class
#
# print("Display.show call at %d" % int(clock.get_time()))
#
class BaseLogfile:
"""Logfile object for saving data"""
def __init__(self):
"""Initiates logfile object
arguments
None
keyword arguments
filename -- name (possibly including path) for the logfile;
WITHOUT extension! (default = LOGFILE)
returns
None -- sets filename and logfile properties
"""
pass
def write(self):
"""
Writes given values to logfile (each value separated with a tab)
arguments
vallist -- list of values to be written to logfile
keyword arguments
None
returns
None -- writes each value to the logfile, adding tabs between
the values
"""
pass
def close(self):
"""
Closes logfile (do this after writing everything to the file!)
arguments
None
keyword arguments
None
returns
None -- closes logfile; calling write method after calling
close method will result in an error!
"""
pass
\ No newline at end of file
# -*- coding: utf-8 -*-
#
# This file is part of PyGaze - the open-source toolbox for eye tracking
#
# PyGaze is a Python module for easily creating gaze contingent experiments
# or other software (as well as non-gaze contingent experiments/software)
# Copyright (C) 2012-2013 Edwin S. Dalmaijer
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>
import os
from pygaze import settings
from pygaze._logfile.baselogfile import BaseLogfile
# we try importing the copy_docstr function, but as we do not really need it
# for a proper functioning of the code, we simply ignore it when it fails to
# be imported correctly
try:
from pygaze._misc.misc import copy_docstr
except:
pass
class Logfile(BaseLogfile):
# See _logfile.baselogfile.BaseLogfile
def __init__(self, filename=settings.LOGFILE):
# See _logfile.baselogfile.BaseLogfile
# try to copy docstring (but ignore it if it fails, as we do
# not need it for actual functioning of the code)
try:
copy_docstr(BaseLogfile, Logfile)
except:
# we're not even going to show a warning, since the copied
# docstring is useful for code editors; these load the docs
# in a non-verbose manner, so warning messages would be lost
pass
self.filename = filename + ".txt"
self.logfile = open(self.filename, "w")
def write(self, vallist):
# See _logfile.baselogfile.BaseLogfile
# empty string
line = ""
# all values to string
vallist = map(str, vallist)
# insert tabs between values, end with newline character
line = "\t".join(vallist) + "\n"
# write line to file (on disk)
self.logfile.write(line) # write to internal buffer
self.logfile.flush() # internal buffer to RAM
os.fsync(self.logfile.fileno()) # RAM file cache to disk
def close(self):
# See _logfile.baselogfile.BaseLogfile
self.logfile.close()
# -*- coding: utf-8 -*-
#
# This file is part of PyGaze - the open-source toolbox for eye tracking
#
# PyGaze is a Python module for easily creating gaze contingent experiments
# or other software (as well as non-gaze contingent experiments/software)
# Copyright (C) 2012-2013 Edwin S. Dalmaijer
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>
# -*- coding: utf-8 -*-
#
# This file is part of PyGaze - the open-source toolbox for eye tracking
#
# PyGaze is a Python module for easily creating gaze contingent experiments
# or other software (as well as non-gaze contingent experiments/software)
# Copyright (C) 2012-2013 Edwin S. Dalmaijer
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>
from inspect import ismethod
from pygaze import settings
# # # # #
# helper functions
def copy_docstr(src, target):
"""
Copies docstrings from the methods of a source class to the methods of a
target class.
arguments:
src -- source class (e.g. BaseDisplay)
target -- target class (e.g. PyGameDisplay)
"""
for attr_name in dir(target):
if not hasattr(src, attr_name) or not ismethod(getattr(src, attr_name)):
continue
getattr(target, attr_name).__func__.__doc__ = getattr(src, attr_name).__func__.__doc__
def pos2psychopos(pos, dispsize=None):
"""Returns a converted position tuple (x,y) (internal use)
arguments
pos -- a (x,y) position tuple, assuming (0,0) is top left
keyword arguments
dispsize -- a (width, height) tuple for the display resolution or None
for autodetecting the size of current active window
(default = None)
returns
pos -- a (x,y) tuple that makes sense to PsychoPy (i.e. (0,0) is
display center; bottom left is (-,-) and top right is
(+,+))
"""
if dispsize == None:
dispsize = settings.DISPSIZE[:]
x = pos[0] - dispsize[0]/2
y = (pos[1] - dispsize[1]/2) * -1
return (x,y)
def psychopos2pos(pos, dispsize=None):
"""Returns a converted position tuple (x,y) (internal use)
arguments
pos -- a (x,y) tuple that makes sense to PsychoPy (i.e. (0,0) is
display center; bottom left is (-,-) and top right is
(+,+))
keyword arguments
dispsize -- a (width, height) tuple for the display resolution or None
for autodetecting the size of current active window
(default = None)
returns
pos -- a (x,y) position tuple, assuming (0,0) is top left
"""
if dispsize == None:
dispsize = settings.DISPSIZE[:]
x = pos[0] + dispsize[0]/2
y = (pos[1] * -1) + dispsize[1]/2
return (x,y)
def rgb2psychorgb(rgbgun):
"""Returns a converted RGB gun
arguments
rgbgun -- a (R,G,B) or (R,G,B,A) tuple containing values between 0
and 255; other values (e.g. 'red' or hex values) may be
passed as well, but will be returned as they were
returns
psyrgb -- a (R,G,B) tuple containing values between -1 and 1; or
rgbgun when passed rgbgun was not a tuple or a list
"""
if type(rgbgun) not in [tuple,list]:
return rgbgun
psyrgb = []
for val in rgbgun:
psyrgb.append((val/127.5)-1)
# return (R,G,B), since PsychoPy does not like alpha channels anymore
return tuple(psyrgb[0:3])
# -*- coding: utf-8 -*-
#
# This file is part of PyGaze - the open-source toolbox for eye tracking
#
# PyGaze is a Python module for easily creating gaze contingent experiments
# or other software (as well as non-gaze contingent experiments/software)
# Copyright (C) 2012-2013 Edwin S. Dalmaijer
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>
# -*- coding: utf-8 -*-
#
# This file is part of PyGaze - the open-source toolbox for eye tracking
#
# PyGaze is a Python module for easily creating gaze contingent experiments
# or other software (as well as non-gaze contingent experiments/software)
# Copyright (C) 2012-2013 Edwin S. Dalmaijer
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>
# The BaseClasses are meant to store the documentation on all methods of a
# class, but not to contain any functionality whatsoever. BaseClass is
# inherited by all of the subclasses, and the documentation is copied using
# pygaze.copy_docstr. If you intend to make your own subclass for the current
# baseclass, be sure to inherit BaseClass, copy the documentation, and
# redefine the methods as you see fit, e.g.:
#
#import pygaze
#from pygaze._display.basedisplay import BaseDisplay
#
#class DummyDisplay(BaseDisplay):
#
# """An example child of BaseDisplay"""
#
# def __init__(self, *args, **kwargs):
#
# """Initializes a DummyDisplay instance"""
#
# pygaze.copy_docstring(BaseDisplay,DummyDisplay)
#
# def show(self):
#
# # note that here no docstring is provided, as it is copied from
# # the parent class
#
# print("Display.show call at %d" % int(clock.get_time()))
#
class BaseMouse:
"""A mouse for collecting responses"""
def __init__(self):
"""
Initializes mouse object
arguments
None
keyword arguments
mousebuttonlist -- list of mouse buttons that are allowed (e.g.
[1,3] for left and right button) or None to
allow all buttons (default =
MOUSEBUTTONLIST)
timeout -- time in milliseconds after which None is returned
on a call to get_clicked method when no click is
registered (default = MOUSETIMEOUT)
visible -- Boolean indicating if mouse should be visible or not
(default = False)
"""
pass
def set_mousebuttonlist(self):
"""
Set a list of accepted mouse buttons
arguments
None
keyword arguments
mousebuttonlist -- list of mouse buttons that are allowed (e.g.
[1,3] for left and right button) or None to
allow all buttons (default = None)
returns
None -- sets mbuttonlist property
"""
pass
def set_timeout(self):
"""
Set a timeout (in milliseconds)
arguments
None
keyword arguments
timeout -- time in milliseconds after which None is returned
on a call to get_clicked method when no click is
registered (default = None)
returns
None -- sets timeout property
"""
pass
def set_visible(self):
"""
Sets the visibility of the cursor
arguments
None
keyword arguments
visible -- Boolean indicating if mouse should be visible or not
(default = False)
returns
None -- sets visible property
"""
pass
def set_pos(self):
"""
Set the mouse position
arguments
None
keyword arguments
pos -- an (x,y) position tuple, assuming top left is (0,0)
(default = (0,0))
returns
None -- sets the mouse position
"""
pass
def get_pos(self):
"""
Returns mouse position
arguments
None
keyword arguments
None
returns
mpos -- a (x,y) position tuple, assuming top left is (0,0)
"""
pass
def get_clicked(self):
"""
Waits for mouse clicks
arguments
None
keyword arguments
mousebuttonlist -- list of mouse buttons that are allowed (e.g.
[1,3] for left and right button); None to
allow all buttons or 'default' to use the
mbuttonlist property (default = 'default')
timeout -- time in milliseconds after which None is returned
when no click is registered; None for no timeout or
'default' to use the timeout property (default =
'default')
returns
mousebutton, clickpos, clicktime -- mousebutton is an integer,
indicating which button has
been pressed or None when no
button has been pressed;
clickpos is an (x,y) position
tuple or None when no click
was registered;
clicktime is the time
(measured from expbegintime) a
buttonpress or a timeout
occured
"""
pass
def get_pressed(self):
"""
Returns the current state of the mouse buttons
arguments
None
keyword arguments
None
returns
statelist -- a list of Booleans indicating which mousebutton is
down (e.g. [1,0,0] when only button 1 is pressed on
a three-button-mouse)
"""
pass
\ No newline at end of file
# -*- coding: utf-8 -*-
#
# This file is part of PyGaze - the open-source toolbox for eye tracking
#
# PyGaze is a Python module for easily creating gaze contingent experiments
# or other software (as well as non-gaze contingent experiments/software)
# Copyright (C) 2012-2013 Edwin S. Dalmaijer
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>
from pygaze.py3compat import *
from pygaze import settings
from libopensesame.exceptions import osexception
from openexp.mouse import mouse
from pygaze._mouse.basemouse import BaseMouse
# we try importing the copy_docstr function, but as we do not really need it
# for a proper functioning of the code, we simply ignore it when it fails to
# be imported correctly
try:
from pygaze._misc.misc import copy_docstr
except:
pass
class OSMouse(BaseMouse):
# See _mouse.basemouse.BaseMouse
def __init__(self, mousebuttonlist=settings.MOUSEBUTTONLIST,
timeout=settings.MOUSETIMEOUT, visible=False):
# See _mouse.basemouse.BaseMouse
# try to copy docstring (but ignore it if it fails, as we do
# not need it for actual functioning of the code)
try:
copy_docstr(BaseMouse, OSMouse)
except:
# we're not even going to show a warning, since the copied
# docstring is useful for code editors; these load the docs
# in a non-verbose manner, so warning messages would be lost
pass
self.experiment = settings.osexperiment
self.uniform_coordinates = \
self.experiment.var.uniform_coordinates == u'yes'
self.mouse = mouse(self.experiment, buttonlist=mousebuttonlist,
timeout=timeout)
def _from_pos(self, pos):
"""Convert OpenSesame coordinates to PyGaze coordinates."""
if pos is None or not self.uniform_coordinates:
return pos
return pos[0]+self.mouse._xcenter, pos[1]+self.mouse._ycenter
def _to_pos(self, pos):
"""Convert PyGaze coordinates to OpenSesame coordinates."""
if pos is None or not self.uniform_coordinates:
return pos
return pos[0]-self.mouse._xcenter, pos[1]-self.mouse._ycenter
def set_mousebuttonlist(self, mousebuttonlist=None):
# See _mouse.basemouse.BaseMouse
self.mouse.buttonlist = mousebuttonlist
def set_timeout(self, timeout=None):
# See _mouse.basemouse.BaseMouse
self.mouse.timeout = timeout
def set_visible(self, visible=True):
# See _mouse.basemouse.BaseMouse
self.mouse.show_cursor(visible)
def set_pos(self, pos=(0,0)):
# See _mouse.basemouse.BaseMouse
self.mouse.set_pos(self._to_pos(pos))
def get_pos(self):
# See _mouse.basemouse.BaseMouse
return self._from_pos(self.mouse.get_pos()[0])
def get_clicked(self, mousebuttonlist=u'default', timeout=u'default'):
# See _mouse.basemouse.BaseMouse
# set buttonlist and timeout
kwdict = {}
if mousebuttonlist != u'default':
kwdict[u'buttonlist'] = mousebuttonlist
if timeout != u'default':
kwdict[u'buttonlist'] = timeout
button, pos, t = self.mouse.get_click(**kwdict)
return button, self._from_pos(pos), t
def get_pressed(self):
# See _mouse.basemouse.BaseMouse
return self.mouse.get_pressed()
# -*- coding: utf-8 -*-
#
# This file is part of PyGaze - the open-source toolbox for eye tracking
#
# PyGaze is a Python module for easily creating gaze contingent experiments
# or other software (as well as non-gaze contingent experiments/software)
# Copyright (C) 2012-2013 Edwin S. Dalmaijer
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>
from pygaze.libtime import clock
from pygaze import settings
from pygaze._mouse.basemouse import BaseMouse
# we try importing the copy_docstr function, but as we do not really need it
# for a proper functioning of the code, we simply ignore it when it fails to
# be imported correctly
try:
from pygaze._misc.misc import copy_docstr
except:
pass
import pygaze
from pygaze._misc.misc import pos2psychopos, psychopos2pos
import psychopy.event
class PsychoPyMouse(BaseMouse):
# See _mouse.basemouse.BaseMouse
def __init__(self, mousebuttonlist=settings.MOUSEBUTTONLIST,
timeout=settings.MOUSETIMEOUT, visible=False):
# See _mouse.basemouse.BaseMouse
# try to copy docstring (but ignore it if it fails, as we do
# not need it for actual functioning of the code)
try:
copy_docstr(BaseMouse, PsychoPyMouse)
except:
# we're not even going to show a warning, since the copied
# docstring is useful for code editors; these load the docs
# in a non-verbose manner, so warning messages would be lost
pass
# create mouse object
self.mouse = psychopy.event.Mouse(visible=False,
win=pygaze.expdisplay)
# set mouse characteristics
self.set_mousebuttonlist(mousebuttonlist)
self.set_timeout(timeout)
self.set_visible(visible=visible)
def set_mousebuttonlist(self, mousebuttonlist=None):
# See _mouse.basemouse.BaseMouse
if mousebuttonlist == None or mousebuttonlist == []:
self.mbuttonlist = None
else:
self.mbuttonlist = []
for mbutton in mousebuttonlist:
self.mbuttonlist.append(mbutton)
def set_timeout(self, timeout=None):
# See _mouse.basemouse.BaseMouse
self.timeout = timeout
def set_visible(self, visible=True):
# See _mouse.basemouse.BaseMouse
self.visible = visible
self.mouse.setVisible(self.visible)
def set_pos(self, pos=(0,0)):
# See _mouse.basemouse.BaseMouse
self.mouse.setPos(newPos=pos2psychopos(pos))
def get_pos(self):
# See _mouse.basemouse.BaseMouse
return psychopos2pos(self.mouse.getPos())
def get_clicked(self, mousebuttonlist='default', timeout='default'):
# See _mouse.basemouse.BaseMouse
# set buttonlist and timeout
if mousebuttonlist == 'default':
mousebuttonlist = self.mbuttonlist
if timeout == 'default':
timeout = self.timeout
# starttime
starttime = clock.get_time()
time = clock.get_time()
# wait for mouse clicks
while timeout == None or time - starttime <= timeout:
time = clock.get_time()
pressed = self.mouse.getPressed()
if sum(pressed) > 0:
for b in range(0,len(pressed)):
if pressed[b] == 1:
if mousebuttonlist == None or b+1 in mousebuttonlist:
return b+1, self.get_pos(), time
# in case of timeout
return None, None, time
def get_pressed(self):
# See _mouse.basemouse.BaseMouse
return self.mouse.getPressed()
# -*- coding: utf-8 -*-
#
# This file is part of PyGaze - the open-source toolbox for eye tracking
#
# PyGaze is a Python module for easily creating gaze contingent experiments
# or other software (as well as non-gaze contingent experiments/software)
# Copyright (C) 2012-2013 Edwin S. Dalmaijer
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>
from pygaze.libtime import clock
from pygaze import settings
from pygaze._mouse.basemouse import BaseMouse
# we try importing the copy_docstr function, but as we do not really need it
# for a proper functioning of the code, we simply ignore it when it fails to
# be imported correctly
try:
from pygaze._misc.misc import copy_docstr
except:
pass
import pygaze
import pygame
class PyGameMouse(BaseMouse):
# See _mouse.basemouse.BaseMouse
import pygame.mouse
def __init__(self, mousebuttonlist=settings.MOUSEBUTTONLIST,
timeout=settings.MOUSETIMEOUT, visible=False):
# See _mouse.basemouse.BaseMouse
# try to copy docstring (but ignore it if it fails, as we do
# not need it for actual functioning of the code)
try:
copy_docstr(BaseMouse, PyGameMouse)
except:
# we're not even going to show a warning, since the copied
# docstring is useful for code editors; these load the docs
# in a non-verbose manner, so warning messages would be lost
pass
# set mouse characteristics
self.set_mousebuttonlist(mousebuttonlist)
self.set_timeout(timeout)
self.set_visible(visible=visible)
def set_mousebuttonlist(self, mousebuttonlist=None):
# See _mouse.basemouse.BaseMouse
if mousebuttonlist == None or mousebuttonlist == []:
self.mbuttonlist = None
else:
self.mbuttonlist = []
for mbutton in mousebuttonlist:
self.mbuttonlist.append(mbutton)
def set_timeout(self, timeout=None):
# See _mouse.basemouse.BaseMouse
self.timeout = timeout
def set_visible(self, visible=True):
# See _mouse.basemouse.BaseMouse
self.visible = visible
pygame.mouse.set_visible(visible)
def set_pos(self, pos=(0,0)):
# See _mouse.basemouse.BaseMouse
pygame.mouse.set_pos(pos)
def get_pos(self):
# See _mouse.basemouse.BaseMouse
mpos = pygame.mouse.get_pos()
return mpos
def get_clicked(self, mousebuttonlist='default', timeout='default'):
# See _mouse.basemouse.BaseMouse
# set buttonlist and timeout
if mousebuttonlist == 'default':
mousebuttonlist = self.mbuttonlist
if timeout == 'default':
timeout = self.timeout
# starttime
starttime = clock.get_time()
time = clock.get_time()
# wait for mouse clicks
while timeout == None or time - starttime <= timeout:
time = clock.get_time()
for event in pygame.event.get():
if event.type == pygame.MOUSEBUTTONDOWN:
time = clock.get_time()
clickpos = self.get_pos()
if mousebuttonlist == None or event.button in mousebuttonlist:
pressed = event.button
return pressed, clickpos, time
# in case of timeout
return None, None, time
def get_pressed(self):
# See _mouse.basemouse.BaseMouse
pygame.event.get()
return pygame.mouse.get_pressed()
# -*- coding: utf-8 -*-
#
# This file is part of PyGaze - the open-source toolbox for eye tracking
#
# PyGaze is a Python module for easily creating gaze contingent experiments
# or other software (as well as non-gaze contingent experiments/software)
# Copyright (C) 2012-2013 Edwin S. Dalmaijer
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>
# -*- coding: utf-8 -*-
#
# This file is part of PyGaze - the open-source toolbox for eye tracking
#
# PyGaze is a Python module for easily creating gaze contingent experiments
# or other software (as well as non-gaze contingent experiments/software)
# Copyright (C) 2012-2013 Edwin S. Dalmaijer
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>
from pygaze.py3compat import *
from pygaze import settings
from libopensesame.exceptions import osexception
from openexp.canvas import canvas
from pygaze._screen.basescreen import BaseScreen
# we try importing the copy_docstr function, but as we do not really need it
# for a proper functioning of the code, we simply ignore it when it fails to
# be imported correctly
try:
from pygaze._misc.misc import copy_docstr
except:
pass
class OSScreen(BaseScreen):
"""See _display.pygamescreen.PyGameScreen"""
def __init__(self, screen=None, **args):
"""See _display.pygamescreen.PyGameScreen"""
# try to copy docstring (but ignore it if it fails, as we do
# not need it for actual functioning of the code)
try:
copy_docstr(BaseScreen, OSScreen)
except:
# we're not even going to show a warning, since the copied
# docstring is useful for code editors; these load the docs
# in a non-verbose manner, so warning messages would be lost
pass
self.experiment = settings.osexperiment
self.uniform_coordinates = \
self.experiment.var.uniform_coordinates == u'yes'
self.create(screen=screen)
def _pos(self, pos):
"""Convert PyGaze coordinates to OpenSesame coordinates."""
if pos in (None, (None, None)):
return None, None
x, y = pos
if self.uniform_coordinates:
x -= self.canvas._xcenter
y -= self.canvas._ycenter
return x, y
def create(self, screen=None):
"""See _display.pygamescreen.PyGameScreen"""
if screen != None:
self.canvas = screen.copy()
else:
self.canvas = canvas(self.experiment)
def clear(self, colour=None):
"""See _display.pygamescreen.PyGameScreen"""
self.canvas.clear()
def copy(self, screen):
"""See _display.pygamescreen.PyGameScreen"""
self.canvas = screen.copy()
def draw_circle(self, colour=None, pos=None, r=50, pw=1, fill=False):
"""See _display.pygamescreen.PyGameScreen"""
x, y = self._pos(pos)
kwdict = {}
if colour is not None:
kwdict[u'color'] = colour
self.canvas.set_penwidth(pw)
self.canvas.circle(x, y, r, fill=fill, **kwdict)
def draw_ellipse(self, colour=None, x=None, y=None, w=50, h=50, pw=1,
fill=False):
"""See _display.pygamescreen.PyGameScreen"""
x, y = self._pos((x, y))
kwdict = {}
if colour is not None:
kwdict[u'color'] = colour
if pw is not None:
kwdict[u'penwidth'] = pw
self.canvas.ellipse(x, y, w, h, fill=fill, **kwdict)
def draw_rect(self, colour=None, x=None, y=None, w=50, h=50, pw=1,
fill=False):
"""See _display.pygamescreen.PyGameScreen"""
x, y = self._pos((x, y))
kwdict = {}
if colour is not None:
kwdict[u'color'] = colour
if pw is not None:
kwdict[u'penwidth'] = pw
self.canvas.rect(x, y, w, h, fill=fill, **kwdict)
def draw_line(self, colour=None, spos=None, epos=None, pw=1):
"""See _display.pygamescreen.PyGameScreen"""
sx, sy = self._pos(spos)
ex, ey = self._pos(epos)
kwdict = {}
if colour is not None:
kwdict[u'color'] = colour
if pw is not None:
kwdict[u'penwidth'] = pw
self.canvas.line(sx, sy, ex, ey, **kwdict)
def draw_polygon(self, pointlist, colour=None, pw=1, fill=True):
"""See _display.pygamescreen.PyGameScreen"""
kwdict = {}
if colour is not None:
kwdict[u'color'] = colour
if pw is not None:
kwdict[u'penwidth'] = pw
pointlist = [self._pos(pt) for pt in pointlist]
self.canvas.polygon(pointlist, fill=fill, **kwdict)
def draw_fixation(self, fixtype='cross', colour=None, pos=None, pw=1,
diameter=12):
"""See _display.pygamescreen.PyGameScreen"""
# TODO: Respect the fixtype argument
x, y = self._pos(pos)
kwdict = {}
if colour is not None:
kwdict[u'color'] = colour
self.canvas.fixdot(x, y, **kwdict)
def draw_text(self, text='text', colour=None, pos=None, center=True,
font='mono', fontsize=12, antialias=True):
"""See _display.pygamescreen.PyGameScreen"""
x, y = self._pos(pos)
kwdict = {}
if colour is not None:
kwdict[u'color'] = colour
self.canvas.set_font(style=font, size=fontsize)
self.canvas.text(text, center=center, x=x, y=y, html=False, **kwdict)
def draw_image(self, image, pos=None, scale=None):
"""See _display.pygamescreen.PyGameScreen"""
x, y = self._pos(pos)
self.canvas.image(image, x=x, y=y, scale=scale)
def set_background_colour(self, colour=None):
"""See _display.pygamescreen.PyGameScreen"""
if colour != None:
self.canvas.set_bgcolor(colour)
# -*- coding: utf-8 -*-
#
# This file is part of PyGaze - the open-source toolbox for eye tracking
#
# PyGaze is a Python module for easily creating gaze contingent experiments
# or other software (as well as non-gaze contingent experiments/software)
# Copyright (C) 2012-2013 Edwin S. Dalmaijer
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>
# -*- coding: utf-8 -*-
#
# This file is part of PyGaze - the open-source toolbox for eye tracking
#
# PyGaze is a Python module for easily creating gaze contingent experiments
# or other software (as well as non-gaze contingent experiments/software)
# Copyright (C) 2012-2013 Edwin S. Dalmaijer
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>
# The BaseClasses are meant to store the documentation on all methods of a
# class, but not to contain any functionality whatsoever. BaseClass is
# inherited by all of the subclasses, and the documentation is copied using
# pygaze.copy_docstr. If you intend to make your own subclass for the current
# baseclass, be sure to inherit BaseClass, copy the documentation, and
# redefine the methods as you see fit, e.g.:
#
#import pygaze
#from pygaze._display.basedisplay import BaseDisplay
#
#class DummyDisplay(BaseDisplay):
#
# """An example child of BaseDisplay"""
#
# def __init__(self, *args, **kwargs):
#
# """Initializes a DummyDisplay instance"""
#
# pygaze.copy_docstring(BaseDisplay,DummyDisplay)
#
# def show(self):
#
# # note that here no docstring is provided, as it is copied from
# # the parent class
#
# print("Display.show call at %d" % int(pygaze.clock.get_time()))
#
class BaseSound:
"""A Sound class for creating and playing sounds"""
def __init__(self):
"""
Initializes a Sound Instance
arguments
None
keyword arguments
osc -- type of oscillator; allowed: 'sine', 'saw', 'square',
'whitenoise' (default = SOUNDOSCILLATOR)
freq -- sound frequency in Herz, either float or integer
(default = SOUNDFREQUENCY)
length -- sound length in milliseconds (default =
SOUNDLENGTH)
attack -- sound attack ('fade in') in milliseconds (default =
SOUNDATTACK)
decay -- sound decay ('fade out') in milliseconds (default =
SOUNDDECAY)
soundfile -- full path to soundfile with .ogg or .wav extension
or None for no file; if a file is specified, all
other keyword arguments will be ignored (default =
None)
"""
pass
def pan(self):
"""
Sets the panning of a sound (the volume of the 'unpanned'
channel decreases, while the other channel remaines the same)
arguments
panning -- either a float between -1 and 1, 'left' or 'right':
'left': full panning to left (same as -1)
< 0: panning to left
0: no panning
> 0: panning to right
'right': full panning to left (same as 1)
keyword arguments
None
returns
None -- self.sound is panned
"""
pass
def play(self):
"""
Plays specified sound (keyword argument loops specifies how many
repeats after being played once, -1 is infinite); function does not
wait for playback end, but returns immediately
arguments
None
keyword arguments
repeats -- specifies the amount of repeats after being played
once (-1 is infinite) (default = 0)
returns
None -- self.sound is played
"""
pass
def stop(self):
"""
Stops sound playback
arguments
None
keyword arguments
None
returns
None -- self.sound stops playing
"""
pass
def set_volume(self):
"""
Set the playback volume (loudness) to specified value
arguments
volume -- float between 0 and 1
keyword arguments
None
returns
None -- sets self.sound volume to specified value
"""
pass
\ No newline at end of file
# -*- coding: utf-8 -*-
#
# This file is part of PyGaze - the open-source toolbox for eye tracking
#
# PyGaze is a Python module for easily creating gaze contingent experiments
# or other software (as well as non-gaze contingent experiments/software)
# Copyright (C) 2012-2013 Edwin S. Dalmaijer
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>
from pygaze import settings
from pygaze._sound.basesound import BaseSound
# we try importing the copy_docstr function, but as we do not really need it
# for a proper functioning of the code, we simply ignore it when it fails to
# be imported correctly
try:
from pygaze._misc.misc import copy_docstr
except:
pass
import math
import numpy
import os.path
import pygame
import random
class PyGameSound(BaseSound):
# see pygaze._sound.basesound.BaseSound
def __init__(self, osc=settings.SOUNDOSCILLATOR,
freq=settings.SOUNDFREQUENCY, length=settings.SOUNDLENGTH,
attack=settings.SOUNDATTACK, decay=settings.SOUNDDECAY, soundfile=None):
# see pygaze._sound.basesound.BaseSound
# try to copy docstring (but ignore it if it fails, as we do
# not need it for actual functioning of the code)
try:
copy_docstr(BaseSound, PyGameSound)
except:
# we're not even going to show a warning, since the copied
# docstring is useful for code editors; these load the docs
# in a non-verbose manner, so warning messages would be lost
pass
pygame.mixer.init(frequency=settings.SOUNDSAMPLINGFREQUENCY,
size=settings.SOUNDSAMPLESIZE, channels=settings.SOUNDCHANNELS,
buffer=settings.SOUNDBUFFERSIZE)
# if a sound file was specified, use soundfile and ignore other keyword arguments
if soundfile != None:
if not os.path.exists(soundfile):
raise Exception("Error in libsound.Player.__init__(): Sound file %s not found!" % soundfile)
if os.path.splitext(soundfile)[1].lower() not in (".ogg", ".wav"):
raise Exception("Error in libsound.Player.__init__(): Sound file %s is not in .ogg or .wav format!" % soundfile)
self.sound = pygame.mixer.Sound(soundfile)
# if no soundfile was specified, use keyword arguments to create sound
else:
if osc == 'sine':
_func = math.sin
elif osc == 'saw':
_func = self.saw
elif osc == 'square':
_func = self.square
elif osc == 'whitenoise':
_func = self.white_noise
else:
raise Exception("Error in libsound.Sound.__init__(): oscillator %s could not be recognized; oscillator is set to 'sine'." % osc)
l = []
attack = attack * settings.SOUNDSAMPLINGFREQUENCY / 1000
decay = decay * settings.SOUNDSAMPLINGFREQUENCY / 1000
amp = 32767 / 2
sps = settings.SOUNDSAMPLINGFREQUENCY
cps = float(sps/freq) # cycles per sample
slen = settings.SOUNDSAMPLINGFREQUENCY * length / 1000 # number of samples
for i in range(slen):
p = float((i % cps)) / cps * 2 * math.pi
v = int(amp * (_func(p)))
if i < attack:
v = int(v * float(i) / attack)
if i > slen - decay:
v = int(v * (float(slen) - float(i)) / decay)
l.append(v)
l.append(v)
b = numpy.array(l, dtype="int16").reshape(len(l) / 2, 2)
self.sound = pygame.mixer.Sound(b)
def saw(self, phase):
"""
Returns a point in a saw wave (for internal use)
arguments
phase -- phase of the wave
keyword arguments
None
returns
p -- point in a saw wave
"""
phase = phase % math.pi
return float(phase) / (0.5 * math.pi) - 1.0
def square(self, phase):
"""
Returns a point in a square wave (for internal use)
arguments
phase -- phase of the wave
keyword arguments
None
returns
p -- point in a square wave
"""
if phase < math.pi:
return 1
return -1
def white_noise(self, phase):
"""
Returns a point in random noise (for internal use)
arguments
phase -- phase of the sound (ignored, but necessary for
internal reasons; see __init__)
keyword arguments
None
returns
p -- random number (i.e. a point in white noise sound)
"""
return random.random()
def pan(self, panning):
# see pygaze._sound.basesound.BaseSound
# raise exception on wrong input
if type(panning) not in (int, float) and panning not in ['left','right']:
raise Exception("Error in libsound.Sound.pan(): panning must be a value between -1.0 and 1.0 or either 'left' or 'right'.")
# correct wrong inputs
if panning < -1:
panning = -1
elif panning > 1:
panning = 1
# round off, to prevent too long numbers
panning = numpy.round(panning, decimals=8)
buf = pygame.sndarray.array(self.sound)
for i in range(len(buf)):
l = buf[i][0]
r = buf[i][1]
if panning == 'left':
r = 0
elif panning == 'right':
l = 0
elif panning < 0:
r = int(float(r) / abs(panning))
elif panning > 0:
l = int(float(l) / abs(panning))
buf[i][0] = l
buf[i][1] = r
self.sound = pygame.sndarray.make_sound(numpy.array(buf))
def play(self, repeats=0):
# see pygaze._sound.basesound.BaseSound
self.sound.play(loops=repeats)
def stop(self):
# see pygaze._sound.basesound.BaseSound
self.sound.stop()
def set_volume(self, volume):
# see pygaze._sound.basesound.BaseSound
if volume <= 1.0 and volume >= 0.0:
self.sound.set_volume(volume)
else:
raise Exception("Error in libsound.Sound.set_volume(): Volume must be a value between 0.0 and 1.0.")
# -*- coding: utf-8 -*-
#
# This file is part of PyGaze - the open-source toolbox for eye tracking
#
# PyGaze is a Python module for easily creating gaze contingent experiments
# or other software (as well as non-gaze contingent experiments/software)
# Copyright (C) 2012-2013 Edwin S. Dalmaijer
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>
# -*- coding: utf-8 -*-
#
# This file is part of PyGaze - the open-source toolbox for eye tracking
#
# PyGaze is a Python module for easily creating gaze contingent experiments
# or other software (as well as non-gaze contingent experiments/software)
# Copyright (C) 2012-2013 Edwin S. Dalmaijer
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>
# The BaseClasses are meant to store the documentation on all methods of a
# class, but not to contain any functionality whatsoever. BaseClass is
# inherited by all of the subclasses, and the documentation is copied using
# pygaze.copy_docstr. If you intend to make your own subclass for the current
# baseclass, be sure to inherit BaseClass, copy the documentation, and
# redefine the methods as you see fit, e.g.:
#
#import pygaze
#from pygaze._display.basedisplay import BaseDisplay
#
#class DummyDisplay(BaseDisplay):
#
# """An example child of BaseDisplay"""
#
# def __init__(self, *args, **kwargs):
#
# """Initializes a DummyDisplay instance"""
#
# pygaze.copy_docstring(BaseDisplay,DummyDisplay)
#
# def show(self):
#
# # note that here no docstring is provided, as it is copied from
# # the parent class
#
# print("Display.show call at %d" % int(pygaze.clock.get_time()))
#
class BaseTime:
"""Class for keeping track of time"""
def __init__(self):
"""
Initializes a Time instance
arguments
None
keyword arguments
None
"""
pass
def expstart(self):
"""
Time is set to 0 when calling this
arguments
None
keyword arguments
None
returns
Nothing
"""
pass
def get_time(self):
"""
Returns current time in milliseconds
arguments
None
keyword arguments
None
returns
time -- current time in milliseconds, as measured since
expbegintime
"""
pass
def pause(self):
"""
Pauses the experiment for given number of milliseconds
arguments
pausetime -- time to pause in milliseconds
keyword arguments
None
returns
pausetime -- actual time the system paused (in milliseconds)
"""
pass
def expend(self):
"""
Completely ends the experiment (only call this at the end!)
arguments
None
keyword arguments
None
returns
endtime -- ending time of the experiment (in milliseconds since
expbegintime
"""
pass
\ No newline at end of file
# -*- coding: utf-8 -*-
#
# This file is part of PyGaze - the open-source toolbox for eye tracking
#
# PyGaze is a Python module for easily creating gaze contingent experiments
# or other software (as well as non-gaze contingent experiments/software)
# Copyright (C) 2012-2013 Edwin S. Dalmaijer
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>
from pygaze.py3compat import *
from pygaze import settings
from pygaze._time.basetime import BaseTime
# we try importing the copy_docstr function, but as we do not really need it
# for a proper functioning of the code, we simply ignore it when it fails to
# be imported correctly
try:
from pygaze._misc.misc import copy_docstr
except:
pass
class OSTime(BaseTime):
# see pygaze._time.basetime.BaseTime
def __init__(self):
# see pygaze._time.basetime.BaseTime
# try to copy docstring (but ignore it if it fails, as we do
# not need it for actual functioning of the code)
try:
copy_docstr(BaseTime, OSTime)
except:
# we're not even going to show a warning, since the copied
# docstring is useful for code editors; these load the docs
# in a non-verbose manner, so warning messages would be lost
pass
pass
def expstart(self):
# see pygaze._time.basetime.BaseTime
global expbegintime
expbegintime = 0
def get_time(self):
# see pygaze._time.basetime.BaseTime
return settings.osexperiment.time()
def pause(self, pausetime):
# see pygaze._time.basetime.BaseTime
return settings.osexperiment.sleep(pausetime)
def expend(self):
# see pygaze._time.basetime.BaseTime
return settings.osexperiment.time()
# -*- coding: utf-8 -*-
#
# This file is part of PyGaze - the open-source toolbox for eye tracking
#
# PyGaze is a Python module for easily creating gaze contingent experiments
# or other software (as well as non-gaze contingent experiments/software)
# Copyright (C) 2012-2013 Edwin S. Dalmaijer
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>
import psychopy.core
from pygaze._time.basetime import BaseTime
# we try importing the copy_docstr function, but as we do not really need it
# for a proper functioning of the code, we simply ignore it when it fails to
# be imported correctly
try:
from pygaze._misc.misc import copy_docstr
except:
pass
class PsychoPyTime(BaseTime):
# see pygaze._time.basetime.BaseTime
def __init__(self):
# see pygaze._time.basetime.BaseTime
# try to copy docstring (but ignore it if it fails, as we do
# not need it for actual functioning of the code)
try:
copy_docstr(BaseTime, PsychoPyTime)
except:
# we're not even going to show a warning, since the copied
# docstring is useful for code editors; these load the docs
# in a non-verbose manner, so warning messages would be lost
pass
pass
def expstart(self):
# see pygaze._time.basetime.BaseTime
global expbegintime
expbegintime = psychopy.core.getTime() * 1000
def get_time(self):
# see pygaze._time.basetime.BaseTime
return psychopy.core.getTime() * 1000 - expbegintime
def pause(self, pausetime):
# see pygaze._time.basetime.BaseTime
t0 = psychopy.core.getTime()
psychopy.core.wait(pausetime/1000.0)
t1 = psychopy.core.getTime()
return t1-t0
def expend(self):
# see pygaze._time.basetime.BaseTime
endtime = self.get_time() * 1000
psychopy.core.quit()
return endtime
# -*- coding: utf-8 -*-
#
# This file is part of PyGaze - the open-source toolbox for eye tracking
#
# PyGaze is a Python module for easily creating gaze contingent experiments
# or other software (as well as non-gaze contingent experiments/software)
# Copyright (C) 2012-2013 Edwin S. Dalmaijer
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>
import pygame
import time
import sys
from pygaze._time.basetime import BaseTime
# we try importing the copy_docstr function, but as we do not really need it
# for a proper functioning of the code, we simply ignore it when it fails to
# be imported correctly
try:
from pygaze._misc.misc import copy_docstr
except:
pass
class PyGameTime(BaseTime):
# see pygaze._time.basetime.BaseTime
def __init__(self):
# see pygaze._time.basetime.BaseTime
# try to copy docstring (but ignore it if it fails, as we do
# not need it for actual functioning of the code)
try:
copy_docstr(BaseTime, PyGameTime)
except:
# we're not even going to show a warning, since the copied
# docstring is useful for code editors; these load the docs
# in a non-verbose manner, so warning messages would be lost
pass
# On Windows, `time.clock()` provides higher accuracy than
# `time.time()`.
if sys.platform == 'win32':
self._cpu_time = time.perf_counter
else:
self._cpu_time = time.time
pygame.init()
def expstart(self):
# see pygaze._time.basetime.BaseTime
global expbegintime
expbegintime = self._cpu_time() * 1000
def get_time(self):
# see pygaze._time.basetime.BaseTime
ctime = self._cpu_time()*1000 - expbegintime
return ctime
def pause(self, pausetime):
# see pygaze._time.basetime.BaseTime
realpause = pygame.time.delay(pausetime)
return realpause
def expend(self):
# see pygaze._time.basetime.BaseTime
endtime = self.get_time()
pygame.quit()
return endtime
\ No newline at end of file
# -*- coding: utf-8 -*-
#
# This file is part of PyGaze - the open-source toolbox for eye tracking
#
# PyGaze is a Python module for easily creating gaze contingent experiments
# or other software (as well as non-gaze contingent experiments/software)
# Copyright (C) 2012-2013 Edwin S. Dalmaijer
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>
# MAIN
DUMMYMODE = False # False for gaze contingent display, True for dummy mode (using mouse or joystick)
LOGFILENAME = 'default' # logfilename, without path
LOGFILE = LOGFILENAME[:] # .txt; adding path before logfilename is optional; logs responses (NOT eye movements, these are stored in a different (EDF/IDF/TXT) file!)
# DISPLAY
SCREENNR = 0 # number of the screen used for displaying experiment
DISPTYPE = 'psychopy' # either 'psychopy' or 'pygame'
DISPSIZE = (1280,1024) # canvas size
SCREENSIZE = (33.8,27.1) # physical screen size in centimeters
SCREENDIST = 57.0 # centimeters; distance between screen and participant's eyes
FULLSCREEN = True # Indicates whether the experiment is executed in fullscreen
MOUSEVISIBLE = False # mouse visibility
BGC = (125,125,125) # backgroundcolour
FGC = (0,0,0) # foregroundcolour
# SOUND
SOUNDOSCILLATOR = 'sine' # 'sine', 'saw', 'square' or 'whitenoise'
SOUNDFREQUENCY = 440 # Herz
SOUNDLENGTH = 100 # milliseconds (duration)
SOUNDATTACK = 0 # milliseconds (fade-in)
SOUNDDECAY = 5 # milliseconds (fade-out)
SOUNDBUFFERSIZE = 1024 # increase if playback is choppy
SOUNDSAMPLINGFREQUENCY = 48000 # samples per second
SOUNDSAMPLESIZE = -16 # determines bit depth (negative is signed
SOUNDCHANNELS = 2 # 1 = mono, 2 = stereo
# INPUT
MOUSEBUTTONLIST = None # None for all mouse buttons; list of numbers for buttons of choice (e.g. [1,3] for buttons 1 and 3)
MOUSETIMEOUT = None # None for no timeout, or a value in milliseconds
KEYLIST = None # None for all keys; list of keynames for keys of choice (e.g. ['space','9',':'] for space, 9 and ; keys)
KEYTIMEOUT = 1 # None for no timeout, or a value in milliseconds
JOYBUTTONLIST = None # None for all joystick buttons; list of button numbers (start counting at 0) for buttons of choice (e.g. [0,3] for buttons 0 and 3 - may be reffered to as 1 and 4 in other programs)
JOYTIMEOUT = None # None for no timeout, or a value in milliseconds
# EYETRACKER
# general
TRACKERTYPE = 'eyelink' # either 'smi', 'eyelink' or 'dummy' (NB: if DUMMYMODE is True, trackertype will be set to dummy automatically)
SACCVELTHRESH = 35 # degrees per second, saccade velocity threshold
SACCACCTHRESH = 9500 # degrees per second, saccade acceleration threshold
BLINKTHRESH = 150 # milliseconds, blink detection threshold used in PyGaze method
EVENTDETECTION = 'pygaze' # either 'pygaze' for PyGaze detection algorithms, or 'native' for manufacturer's event detection (if available)
# EyeLink only
EYELINKCALBEEP = True # Calibration beep with each jump
EYELINKPUPILSIZEMODE = 'area'
# EyeTribe only
EYETRIBECALIBDUR = 750
EYETRIBEPRECALIBDUR = 500
# SMI only
SMIIP = '127.0.0.1'
SMISENDPORT = 4444
SMIRECEIVEPORT = 5555
# -*- coding: utf-8 -*-
#
# This file is part of PyGaze - the open-source toolbox for eye tracking
#
# PyGaze is a Python module for easily creating gaze contingent experiments
# or other software (as well as non-gaze contingent experiments/software)
# Copyright (C) 2012-2013 Edwin S. Dalmaijer
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>
from pygaze.py3compat import *
from pygaze import settings
from pygaze._misc.misc import copy_docstr
from pygaze._display.basedisplay import BaseDisplay
class Display(BaseDisplay):
# see BaseDisplay
def __init__(self, disptype=settings.DISPTYPE, **args):
# see BaseDisplay
if disptype == u'pygame':
from pygaze._display.pygamedisplay import PyGameDisplay as Display
elif disptype == u'psychopy':
from pygaze._display.psychopydisplay import PsychoPyDisplay as Display
elif disptype == u'opensesame':
from pygaze._display.osdisplay import OSDisplay as Display
else:
raise Exception(u'Unexpected disptype : %s' % disptype)
self.__class__ = Display
self.__class__.__init__(self, **args)
copy_docstr(BaseDisplay, Display)
# -*- coding: utf-8 -*-
#
# This file is part of PyGaze - the open-source toolbox for eye tracking
#
# PyGaze is a Python module for easily creating gaze contingent experiments
# or other software (as well as non-gaze contingent experiments/software)
# Copyright (C) 2012-2013 Edwin S. Dalmaijer
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>
from pygaze._eyetracker.baseeyetracker import BaseEyeTracker
from pygaze.py3compat import *
from pygaze import settings
from pygaze._misc.misc import copy_docstr
from pygaze._eyetracker.libtobii import TobiiProTracker
class EyeTracker(BaseEyeTracker):
"""
Generic EyeTracker class, which morphs into an eye-tracker specific class.
"""
def __init__(self, display, trackertype=settings.TRACKERTYPE, **args):
"""
Initializes an EyeTracker object.
arguments
display -- a pygaze.display.Display instance
keyword arguments
trackertype -- the type of eye tracker; choose from:
"dumbdummy", "dummy", "eyelink", "eyelogic", "smi",
"tobii", "eyetribe" (default = TRACKERTYPE)
**args -- A keyword-argument dictionary that contains
eye-tracker-specific options
"""
# set trackertype to dummy in dummymode
if settings.DUMMYMODE:
trackertype = "dummy"
# correct wrong input
allowed_trackers = ["dumbdummy", "dummy", "eyelink", "eyelogic", "smi", \
"eyetribe", "opengaze", "alea", "tobii", "tobii-legacy", \
"tobiiglasses"]
if trackertype not in allowed_trackers:
raise Exception( \
"Error in eyetracker.EyeTracker: trackertype {} not recognized; it should be one of {}".format(trackertype,allowed_trackers))
# EyeLink
if trackertype == "eyelink":
# import libraries
from pygaze._eyetracker.libeyelink import libeyelink
# morph class
self.__class__ = libeyelink
# initialize
self.__class__.__init__(self, display, **args)
# EyeLogic
elif trackertype == "eyelogic":
# import libraries
from pygaze._eyetracker.libeyelogic import EyeLogicTracker
# morph class
self.__class__ = EyeLogicTracker
# initialize
self.__class__.__init__(self, display, **args)
# SMI
elif trackertype == "smi":
# import libraries
from pygaze._eyetracker.libsmi import SMItracker
# morph class
self.__class__ = SMItracker
# initialize
self.__class__.__init__(self, display, **args)
# Tobii Legacy
elif trackertype == "tobii-legacy":
# import libraries
from pygaze._eyetracker.libtobiilegacy import TobiiTracker
# morph class
self.__class__ = TobiiTracker
# initialize
self.__class__.__init__(self, display, **args)
# Tobii
elif trackertype == "tobii":
# import libraries
from pygaze._eyetracker.libtobii import TobiiProTracker
# morph class
self.__class__ = TobiiProTracker
# initialize
self.__class__.__init__(self, display, **args)
# Tobii Pro Glasses 2
elif trackertype == "tobiiglasses":
# import libraries
from pygaze._eyetracker.libtobiiglasses import TobiiGlassesTracker
# morph class
self.__class__ = TobiiGlassesTracker
# initialize
self.__class__.__init__(self, display, **args)
# EyeTribe
elif trackertype == "eyetribe":
# import libraries
from pygaze._eyetracker.libeyetribe import EyeTribeTracker
# morph class
self.__class__ = EyeTribeTracker
# initialize
self.__class__.__init__(self, display, **args)
# OpenGaze
elif trackertype == "opengaze":
# import libraries
from pygaze._eyetracker.libopengaze import OpenGazeTracker
# morph class
self.__class__ = OpenGazeTracker
# initialize
self.__class__.__init__(self, display, **args)
# OpenGaze
elif trackertype == "alea":
# import libraries
from pygaze._eyetracker.libalea import AleaTracker
# morph class
self.__class__ = AleaTracker
# initialize
self.__class__.__init__(self, display, **args)
# dummy mode
elif trackertype == "dummy":
# import libraries
from pygaze._eyetracker.libdummytracker import Dummy
# morph class
self.__class__ = Dummy
# initialize
self.__class__.__init__(self, display)
# dumb dummy mode
elif trackertype == "dumbdummy":
# import libraries
from pygaze._eyetracker.libdumbdummy import DumbDummy
# morph class
self.__class__ = DumbDummy
# initialize
self.__class__.__init__(self, display)
else:
raise Exception( \
"Error in eyetracker.EyeTracker.__init__: trackertype {} not recognized, this should not happen!".format(trackertype))
# copy docstrings
copy_docstr(BaseEyeTracker, EyeTracker)
\ No newline at end of file
# -*- coding: utf-8 -*-
#
# This file is part of PyGaze - the open-source toolbox for eye tracking
#
# PyGaze is a Python module for easily creating gaze contingent experiments
# or other software (as well as non-gaze contingent experiments/software)
# Copyright (C) 2012-2013 Edwin S. Dalmaijer
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>
from pygaze.py3compat import *
from pygaze import settings
from pygaze._misc.misc import copy_docstr
from pygaze._joystick.basejoystick import BaseJoystick
class Joystick(BaseJoystick):
# see BaseJoystick
def __init__(self, disptype=settings.DISPTYPE, **args):
# see BaseJoystick
if disptype in (u'pygame', u'psychopy'):
from pygaze._joystick.pygamejoystick import PyGameJoystick
self.__class__ = PyGameJoystick
else:
raise Exception(u'Unexpected disptype : %s' % disptype)
self.__class__.__init__(self, **args)
copy_docstr(BaseJoystick, Joystick)
# -*- coding: utf-8 -*-
#
# This file is part of PyGaze - the open-source toolbox for eye tracking
#
# PyGaze is a Python module for easily creating gaze contingent experiments
# or other software (as well as non-gaze contingent experiments/software)
# Copyright (C) 2012-2013 Edwin S. Dalmaijer
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>
from pygaze.py3compat import *
from pygaze import settings
from pygaze._misc.misc import copy_docstr
from pygaze._keyboard.basekeyboard import BaseKeyboard
class Keyboard(BaseKeyboard):
# see BaseKeyboard
def __init__(self, disptype=settings.DISPTYPE, **args):
# see BaseKeyboard
if disptype == u'pygame':
from pygaze._keyboard.pygamekeyboard import PyGameKeyboard as \
Keyboard
elif disptype == u'psychopy':
from pygaze._keyboard.psychopykeyboard import PsychoPyKeyboard as \
Keyboard
elif disptype == u'opensesame':
from pygaze._keyboard.oskeyboard import OSKeyboard as \
Keyboard
else:
raise Exception(u'Unexpected disptype : %s' % disptype)
self.__class__ = Keyboard
self.__class__.__init__(self, **args)
copy_docstr(BaseKeyboard, Keyboard)
# -*- coding: utf-8 -*-
#
# This file is part of PyGaze - the open-source toolbox for eye tracking
#
# PyGaze is a Python module for easily creating gaze contingent experiments
# or other software (as well as non-gaze contingent experiments/software)
# Copyright (C) 2012-2013 Edwin S. Dalmaijer
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>
from plugins.aoi import AOI
from plugins.frl import FRL
from plugins.gazecursor import GazeCursor as Cursor
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment