def compareXlsxFiles(pathToActual, pathToCorrect): from openpyxl.reader.excel import load_workbook # Make sure the file is there expBook = load_workbook(pathToCorrect) actBook = load_workbook(pathToActual) error = None for wsN, expWS in enumerate(expBook.worksheets): actWS = actBook.worksheets[wsN] for key, expVal in expWS._cells.items(): actVal = actWS._cells[key].value expVal = expVal.value # determine whether there will be errors try: # convert to float if possible and compare with a reasonable # (default) precision expVal = float(expVal) isFloatable = True except: isFloatable = False if isFloatable and abs(expVal - float(actVal)) > 0.0001: error = "Cell %s: %f != %f" % (key, expVal, actVal) break elif not isFloatable and expVal != actVal: error = "Cell %s: %s != %s" % (key, expVal, actVal) break if error: pathToLocal, ext = os.path.splitext(pathToCorrect) pathToLocal = pathToLocal + "_local" + ext shutil.copyfile(pathToActual, pathToLocal) logging.warning("xlsxActual!=xlsxCorr: Saving local copy to %s" % pathToLocal) raise IOError, error
def _handleFileCollision(fileName, fileCollisionMethod): """ Handle filename collisions by overwriting, renaming, or failing hard. :Parameters: fileCollisionMethod: 'overwrite', 'rename', 'fail' If a file with the requested name already exists, specify how to deal with it. 'overwrite' will overwite existing files in place, 'rename' will append an integer to create a new file ('trials1.psydat', 'trials2.pysdat' etc) and 'error' will raise an IOError. """ if fileCollisionMethod == 'overwrite': logging.warning('Data file, %s, will be overwritten' % fileName) elif fileCollisionMethod == 'fail': raise IOError("Data file %s already exists. Set argument fileCollisionMethod to overwrite." % fileName) elif fileCollisionMethod == 'rename': rootName, extension = os.path.splitext(fileName) matchingFiles = glob.glob("%s*%s" % (rootName, extension)) count = len(matchingFiles) fileName = "%s_%d%s" % (rootName, count, extension) # Build the renamed string. if os.path.exists(fileName): # Check to make sure the new fileName hasn't been taken too. raise IOError("New fileName %s has already been taken. Something is wrong with the append counter." % fileName) else: raise ValueError("Argument fileCollisionMethod was invalid: %s" % str(fileCollisionMethod)) return fileName
def rgb2dklCart(picture, conversionMatrix=None): """Convert an RGB image into Cartesian DKL space""" #Turn the picture into an array so we can do maths picture=numpy.array(picture) #Find the original dimensions of the picture origShape = picture.shape #this is the inversion of the dkl2rgb conversion matrix if conversionMatrix==None: conversionMatrix = numpy.asarray([\ #LUMIN->%L-M->L+M-S [ 0.25145542, 0.64933633, 0.09920825], [ 0.78737943, -0.55586618, -0.23151325], [ 0.26562825, 0.63933074, -0.90495899]]) logging.warning('This monitor has not been color-calibrated. Using default DKL conversion matrix.') else: conversionMatrix = numpy.linalg.inv(conversionMatrix) #Reshape the picture so that it can multiplied by the conversion matrix red = picture[:,:,0] green = picture[:,:,1] blue = picture[:,:,2] dkl = numpy.asarray([red.reshape([-1]), green.reshape([-1]), blue.reshape([-1])]) #Multiply the picture by the conversion matrix dkl=numpy.dot(conversionMatrix, dkl) #Reshape the picture so that it's back to it's original shape dklPicture = numpy.reshape(numpy.transpose(dkl), origShape) return dklPicture
def onResize(self, width, height): """A method that will be called if the window detects a resize event """ logging.warning("dispatchEvents() method in {} was called " "but is not implemented. Is it needed?" .format(self.win.winType) )
def lms2rgb(lms_Nx3, conversionMatrix=None): """Convert from cone space (Long, Medium, Short) to RGB. Requires a conversion matrix, which will be generated from generic Sony Trinitron phosphors if not supplied (note that you will not get an accurate representation of the color space unless you supply a conversion matrix) usage:: rgb_Nx3 = lms2rgb(dkl_Nx3(el,az,radius), conversionMatrix) """ lms_3xN = numpy.transpose(lms_Nx3)#its easier to use in the other orientation! if conversionMatrix==None: cones_to_rgb = numpy.asarray([ \ #L M S [ 4.97068857, -4.14354132, 0.17285275],#R [-0.90913894, 2.15671326, -0.24757432],#G [-0.03976551, -0.14253782, 1.18230333]#B ]) logging.warning('This monitor has not been color-calibrated. Using default LMS conversion matrix.') else: cones_to_rgb=conversionMatrix rgb_to_cones = numpy.linalg.pinv(cones_to_rgb)#get inverse rgb = numpy.dot(cones_to_rgb, lms_3xN) return numpy.transpose(rgb)#return in the shape we received it
def _getNextFrame(self): """get next frame info ( do not decode frame yet) """ while self.status == PLAYING: if self._video_stream.grab(): self._prev_frame_index = self._next_frame_index self._prev_frame_sec = self._next_frame_sec self._next_frame_index = self._video_stream.get( cv2.CAP_PROP_POS_FRAMES) self._next_frame_sec = self._video_stream.get( cv2.CAP_PROP_POS_MSEC)/1000.0 self._video_perc_done = self._video_stream.get( cv2.CAP_PROP_POS_AVI_RATIO) self._next_frame_displayed = False halfInterval = self._inter_frame_interval/2.0 if self.getTimeToNextFrameDraw() > -halfInterval: return self._next_frame_sec else: self.nDroppedFrames += 1 if self.nDroppedFrames < reportNDroppedFrames: msg = "MovieStim2 dropping video frame index: %d" logging.warning(msg % self._next_frame_index) elif self.nDroppedFrames == reportNDroppedFrames: msg = ("Multiple Movie frames have occurred - " "I'll stop bothering you about them!") logging.warning(msg) else: self._onEos() break
def __init__(self, win, contrast=1.0, gamma=None, nEntries=256, mode='bits++', rampType = 'configFile'): self.win = win self.contrast=contrast self.nEntries=nEntries self.mode = mode self.method = 'fast' #used to allow setting via USB which was 'slow' self.gammaCorrect = 'software' #Bits++ doesn't do its own correction so we need to #import pyglet.GL late so that we can import bits.py without it initially global GL, visual from psychopy import visual import pyglet.gl as GL if self.gammaCorrect=='software': if gamma is None: self.gamma = win.gamma #inherit from window elif len(gamma)>2: # [Lum,R,G,B] or [R,G,B] self.gamma=gamma[-3:] else: self.gamma = [gamma, gamma, gamma] if init(): setVideoMode(NOGAMMACORRECT|VIDEOENCODEDCOMMS) self.initialised=True logging.debug('found and initialised bits++') else: self.initialised=False logging.warning("couldn't initialise bits++") #do the processing self._HEADandLUT = np.zeros((524,1,3),np.uint8) self._HEADandLUT[:12,:,0] = np.asarray([ 36, 63, 8, 211, 3, 112, 56, 34,0,0,0,0]).reshape([12,1])#R self._HEADandLUT[:12,:,1] = np.asarray([ 106, 136, 19, 25, 115, 68, 41, 159,0,0,0,0]).reshape([12,1])#G self._HEADandLUT[:12,:,2] = np.asarray([ 133, 163, 138, 46, 164, 9, 49, 208,0,0,0,0]).reshape([12,1])#B self.LUT=np.zeros((256,3),'d') #just a place holder self.setLUT()#this will set self.LUT and update self._LUTandHEAD self._setupShaders() #replace window methods with our custom ones self.win._prepareFBOrender = self._prepareFBOrender self.win._finishFBOrender = self._finishFBOrender self.win._afterFBOrender = self._afterFBOrender #set gamma of the window to the identity LUT if rampType == 'configFile': #now check that we have a valid configuration of the box self.config = Config(self) #check that this matches the prev config for our graphics card etc ok=False #until we find otherwise ok = self.config.quickCheck() if ok: self.win.gammaRamp = self.config.identityLUT else: rampType = None if not rampType == 'configFile': #'this must NOT be an `else` from the above `if` because can be overidden #possibly we were given a numerical rampType (as in the :func:`psychopy.gamma.setGamma()`) self.win.winHandle.setGamma(self.win.winHandle, rampType=rampType)
def __init__(self, win=None, portName=None, mode=''): serialdevice.SerialDevice.__init__(self, port=portName, baudrate=19200, byteSize=8, stopBits=1, parity="N", #'N'one, 'E'ven, 'O'dd, 'M'ask, eol='\n', maxAttempts=1, pauseDuration=0.1, checkAwake=True) if not self.OK: return #we have a confirmed connection. Now check details about device and system if not hasattr(self, 'info'): self.info = self.getInfo() self.config = None self.mode = mode self.win = win if self.win is not None: if not hasattr(self.win, '_prepareFBOrender'): logging.error("BitsSharp was given an object as win argument but this is not a visual.Window") self.win._prepareFBOrender = self._prepareFBOrender self.win._finishFBOrender = self._finishFBOrender self._setupShaders() #now check that we have a valid configuration of the box self.checkConfig(level=0) else: self.config = None # makes no sense if we have a window? logging.warning("%s was not given any PsychoPy win" %(self))
def __init__(self, win, contrast=1.0, gamma=[1.0,1.0,1.0], nEntries=256, mode='bits++',): self.win = win self.contrast=contrast self.nEntries=nEntries self.mode = mode self.method = 'fast' #used to allow setting via USB which was 'slow' if len(gamma)>2: # [Lum,R,G,B] or [R,G,B] self.gamma=gamma[-3:] else: self.gamma = [gamma, gamma, gamma] if init(): setVideoMode(NOGAMMACORRECT|VIDEOENCODEDCOMMS) self.initialised=True logging.debug('found and initialised bits++') else: self.initialised=False logging.warning("couldn't initialise bits++") #do the processing self._HEADandLUT = np.zeros((524,1,3),np.uint8) self._HEADandLUT[:12,:,0] = np.asarray([ 36, 63, 8, 211, 3, 112, 56, 34,0,0,0,0]).reshape([12,1])#R self._HEADandLUT[:12,:,1] = np.asarray([ 106, 136, 19, 25, 115, 68, 41, 159,0,0,0,0]).reshape([12,1])#G self._HEADandLUT[:12,:,2] = np.asarray([ 133, 163, 138, 46, 164, 9, 49, 208,0,0,0,0]).reshape([12,1])#B self.LUT=np.zeros((256,3),'d') #just a place holder self.setLUT()#this will set self.LUT and update self._LUTandHEAD self._setupShaders()
def push(self, infoStream=None): """Push to remote from local copy of the repository Parameters ---------- infoStream Returns ------- 1 if successful -1 if project deleted on remote """ if infoStream: infoStream.write("\nPushing changes from remote...") try: info = self.repo.git.push(self.remoteWithToken, 'master') infoStream.write("\n{}".format(info)) except git.exc.GitCommandError as e: if ("The project you were looking for could not be found" in traceback.format_exc()): # pointing to a project at pavlovia but it doesn't exist logging.warning("Project not found on gitlab.pavlovia.org") return MISSING_REMOTE else: raise e logging.debug('push complete: {}'.format(self.remoteHTTPS)) if infoStream: infoStream.write("done") return 1
def calibrateZero(self): """Perform a calibration to zero light. For early versions of the ColorCAL this had to be called after connecting to the device. For later versions the dark calibration was performed at the factory and stored in non-volatile memory. You can check if you need to run a calibration with:: ColorCAL.getNeedsCalibrateZero() """ val = self.sendMessage(b"UZC", timeout=1.0) if val == 'OK00': pass elif val == 'ER11': logging.error( "Could not calibrate ColorCAL2. Is it properly covered?") return False else: # unlikely logging.warning( "Received surprising result from ColorCAL2: %s" % val) return False # then take a measurement to see if we are close to zero lum (ie is it # covered?) self.ok, x, y, z = self.measure() if y > 3: logging.error('There seems to be some light getting to the ' 'detector. It should be well-covered for zero ' 'calibration') return False self._zeroCalibrated = True self.calibMatrix = self.getCalibMatrix() return True
def _setupFrameBuffer(self): # Setup framebuffer self.frameBuffer = FB.glGenFramebuffersEXT(1) FB.glBindFramebufferEXT(FB.GL_FRAMEBUFFER_EXT, self.frameBuffer) # Setup depthbuffer self.depthBuffer = FB.glGenRenderbuffersEXT(1) FB.glBindRenderbufferEXT (FB.GL_RENDERBUFFER_EXT,self.depthBuffer) FB.glRenderbufferStorageEXT (FB.GL_RENDERBUFFER_EXT, GL.GL_DEPTH_COMPONENT, int(self.size[0]), int(self.size[1])) # Create texture to render to self.frameTexture = c_uint(0) GL.glGenTextures (1, self.frameTexture) GL.glBindTexture (GL.GL_TEXTURE_2D, self.frameTexture) GL.glTexParameteri (GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR) GL.glTexParameteri (GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR) GL.glTexImage2D (GL.GL_TEXTURE_2D, 0, GL.GL_RGBA32F_ARB, int(self.size[0]), int(self.size[1]), 0, GL.GL_RGBA, GL.GL_FLOAT, None) #attach texture to the frame buffer FB.glFramebufferTexture2DEXT (FB.GL_FRAMEBUFFER_EXT, GL.GL_COLOR_ATTACHMENT0_EXT, GL.GL_TEXTURE_2D, self.frameTexture, 0) FB.glFramebufferRenderbufferEXT(FB.GL_FRAMEBUFFER_EXT, GL.GL_DEPTH_ATTACHMENT_EXT, FB.GL_RENDERBUFFER_EXT, self.depthBuffer) status = FB.glCheckFramebufferStatusEXT (FB.GL_FRAMEBUFFER_EXT) if status != FB.GL_FRAMEBUFFER_COMPLETE_EXT: logging.warning("Error in framebuffer activation") return GL.glDisable(GL.GL_TEXTURE_2D)
def pavlovia(self, proj): global knownProjects self.__dict__['pavlovia'] = proj if not hasattr(proj, 'attributes'): return thisID = proj.attributes['path_with_namespace'] if thisID in knownProjects \ and os.path.exists(knownProjects[thisID]['localRoot']): rememberedProj = knownProjects[thisID] if rememberedProj['idNumber'] != proj.attributes['id']: logging.warning("Project {} has changed gitlab ID since last " "use (was {} now {})" .format(thisID, rememberedProj['idNumber'], proj.attributes['id'])) self.update(rememberedProj) elif 'localRoot' in self: # this means the local root was set before the remote was known self['id'] = proj.attributes['path_with_namespace'] self['idNumber'] = proj.attributes['id'] knownProjects[self['id']] = self else: self['localRoot'] = '' self['id'] = proj.attributes['path_with_namespace'] self['idNumber'] = proj.attributes['id'] self['remoteSSH'] = proj.ssh_url_to_repo self['remoteHTTPS'] = proj.http_url_to_repo
def setMarker(self, tone=19000, secs=0.015, volume=0.03, log=True): """Sets the onset marker, where `tone` is either in hz or a custom sound. The default tone (19000 Hz) is recommended for auto-detection, as being easier to isolate from speech sounds (and so reliable to detect). The default duration and volume are appropriate for a quiet setting such as a lab testing room. A louder volume, longer duration, or both may give better results when recording loud sounds or in noisy environments, and will be auto-detected just fine (even more easily). If the hardware microphone in use is not physically near the speaker hardware, a louder volume is likely to be required. Custom sounds cannot be auto-detected, but are supported anyway for presentation purposes. E.g., a recording of someone saying "go" or "stop" could be passed as the onset marker. """ if hasattr(tone, "play"): self.marker_hz = 0 self.marker = tone if log and self.autoLog: logging.exp("custom sound set as marker; getMarkerOnset() will not be able to auto-detect onset") else: self.marker_hz = float(tone) sampleRate = sound.pyoSndServer.getSamplingRate() if sampleRate < 2 * self.marker_hz: # NyquistError logging.warning( "Recording rate (%i Hz) too slow for %i Hz-based marker detection." % (int(sampleRate), self.marker_hz) ) if log and self.autoLog: logging.exp("frequency of recording onset marker: %.1f" % self.marker_hz) self.marker = sound.Sound(self.marker_hz, secs, volume=volume, name=self.name + ".marker_tone")
def __init__(self, name='', autoLog=True): self.name = name self.status = NOT_STARTED self.autoLog = autoLog if self.autoLog: logging.warning("%s is calling MinimalStim.__init__() with autolog=True. Set autoLog to True only at the end of __init__())" \ %(self.__class__.__name__))
def __init__(self, win, contrast=1.0, gamma=[1.0,1.0,1.0], nEntries=256, mode='bits++',): self.win = win self.contrast=contrast self.nEntries=nEntries #set standardised name for mode if mode in ['bits','bits++']: self.mode = 'bits++' elif mode in ['color','color++','colour','colour++']: self.mode = 'color++' elif mode in ['mono','mono++']: self.mode = 'mono++' else: logging.error("Unknown mode '%s' for BitsBox" %mode) if len(gamma)>2: # [Lum,R,G,B] or [R,G,B] self.gamma=gamma[-3:] else: self.gamma = [gamma, gamma, gamma] if init(): setVideoMode(NOGAMMACORRECT|VIDEOENCODEDCOMMS) self.initialised=True logging.debug('found and initialised bits++') else: self.initialised=False logging.warning("couldn't initialise bits++") if self.mode == 'bits++': #do the processing self._HEADandLUT = numpy.zeros((524,1,3),numpy.uint8) self._HEADandLUT[:12,:,0] = numpy.asarray([ 36, 63, 8, 211, 3, 112, 56, 34,0,0,0,0]).reshape([12,1])#R self._HEADandLUT[:12,:,1] = numpy.asarray([ 106, 136, 19, 25, 115, 68, 41, 159,0,0,0,0]).reshape([12,1])#G self._HEADandLUT[:12,:,2] = numpy.asarray([ 133, 163, 138, 46, 164, 9, 49, 208,0,0,0,0]).reshape([12,1])#B self.LUT=numpy.zeros((256,3),'d')#just a place holder self.setLUT()#this will set self.LUT and update self._LUTandHEAD elif haveShaders: self.monoModeShader = shaders.compileProgram(fragment=shaders.bitsMonoModeFrag, attachments=[shaders.gammaCorrectionFrag]) self.colorModeShader =shaders.compileProgram(fragment=shaders.bitsColorModeFrag, attachments=[shaders.gammaCorrectionFrag]) GL.glUseProgram(self.colorModeShader) prog = self.colorModeShader GL.glUniform1f(GL.glGetUniformLocation(prog, 'sampleSpacing'), 1.0) #Set default encoding gamma for power-law shader to (1.0, 1.0, 1.0): GL.glUniform3f(GL.glGetUniformLocation(prog, 'ICMEncodingGamma'), 1.0, 1.0, 1.0) # Default min and max luminance is 0.0 to 1.0, therefore reciprocal 1/range is also 1.0: GL.glUniform3f(GL.glGetUniformLocation(prog, 'ICMMinInLuminance'), 0.0, 0.0, 0.0) GL.glUniform3f(GL.glGetUniformLocation(prog, 'ICMMaxInLuminance'), 1.0, 1.0, 1.0) GL.glUniform3f(GL.glGetUniformLocation(prog, 'ICMReciprocalLuminanceRange'), 1.0, 1.0, 1.0) # Default gain to postmultiply is 1.0: GL.glUniform3f(GL.glGetUniformLocation(prog, 'ICMOutputGain'), 1.0, 1.0, 1.0) # Default bias to is 0.0: GL.glUniform3f(GL.glGetUniformLocation(prog, 'ICMOutputBias'), 0.0, 0.0, 0.0) GL.glUniform2f(GL.glGetUniformLocation(prog, 'ICMClampToColorRange'), 0.0, 1.0) GL.glUseProgram(0)
def setPortAddress(address=0x0378): """Set the memory address or device node for your parallel port of your parallel port, to be used in subsequent commands common port addresses:: LPT1 = 0x0378 or 0x03BC LPT2 = 0x0278 or 0x0378 LPT3 = 0x0278 or for Linux:: /dev/parport0 This routine will attempt to find a usable driver depending on your platform """ global PORT # convert u"0x0378" into 0x0378 if isinstance(address, basestring) and address.startswith('0x'): address = int(address, 16) # This is useful with the Linux-based driver where deleting # the port object ensures that we're not longer holding the # device node open and that we won't error if we end up # re-opening it if PORT is not None: del PORT try: PORT = ParallelPort(address=address) except Exception as exp: logging.warning('Could not initiate port: %s' % str(exp)) PORT = None
def openOutputFile(fileName=None, append=False, fileCollisionMethod='rename', encoding='utf-8-sig'): """Open an output file (or standard output) for writing. :Parameters: fileName : None, 'stdout', or str The desired output file name. If `None` or `stdout`, return `sys.stdout`. Any other string will be considered a filename. append : bool, optional If ``True``, append data to an existing file; otherwise, overwrite it with new data. Defaults to ``True``, i.e. appending. fileCollisionMethod : string, optional How to handle filename collisions. Valid values are `'rename'`, `'overwrite'`, and `'fail'`. This parameter is ignored if ``append`` is set to ``True``. Defaults to `rename`. encoding : string, optional The encoding to use when writing the file. This parameter will be ignored if `append` is `False` and `fileName` ends with `.psydat` or `.npy` (i.e. if a binary file is to be written). Defaults to ``'utf-8'``. :Returns: f : file A writable file handle. """ fileName = pathToString(fileName) if (fileName is None) or (fileName == 'stdout'): return sys.stdout if append: mode = 'a' else: if fileName.endswith(('.psydat', '.npy')): mode = 'wb' else: mode = 'w' # Rename the output file if a file of that name already exists # and it should not be appended. if os.path.exists(fileName) and not append: fileName = handleFileCollision( fileName, fileCollisionMethod=fileCollisionMethod) # Do not use encoding when writing a binary file. if 'b' in mode: encoding = None if os.path.exists(fileName) and mode in ['w', 'wb']: logging.warning('Data file %s will be overwritten!' % fileName) # The file wil always be opened in binary writing mode, # see https://docs.python.org/2/library/codecs.html#codecs.open f = codecs.open(fileName, mode=mode, encoding=encoding) return f
def rush(value=True): """Raise the priority of the current thread/process using - sched_setscheduler NB for rush() to work on (debian-based?) linux requires that the script is run using a copy of python that is allowed to change priority, eg: sudo setcap cap_sys_nice=eip <sys.executable>, and maybe restart PsychoPy. If <sys.executable> is the system python, its important to restore it back to normal to avoid possible side-effects. Alternatively, use a different python executable, and change its cap_sys_nice. For RedHat-based systems, 'sudo chrt ...' at run-time might be needed instead, not sure. see http://rt.et.redhat.com/wiki/images/8/8e/Rtprio.pdf """ if importCtypesFailed: return False if value:#set to RR with max priority schedParams = _SchedParams() schedParams.sched_priority = c.sched_get_priority_max(SCHED_RR) err = c.sched_setscheduler(0,SCHED_RR, ctypes.byref(schedParams)) if err==-1:#returns 0 if OK logging.warning("""Failed to raise thread priority with sched_setscheduler. To enable rush(), if you are using a debian-based linux, try this in a terminal window: 'sudo setcap cap_sys_nice=eip %s' [NB: You may need to install 'setcap' first.] If you are using the system's python (eg /usr/bin/python2.x), its highly recommended to change cap_sys_nice back to normal afterwards: 'sudo setcap cap_sys_nice= %s'""" % (sys.executable,sys.executable)) else:#set to RR with normal priority schedParams = _SchedParams() schedParams.sched_priority = c.sched_get_priority_min(SCHED_NORMAL) err = c.sched_setscheduler(0,SCHED_NORMAL, ctypes.byref(schedParams)) if err==-1:#returns 0 if OK logging.warning("""Failed to set thread priority back to normal level with sched_setscheduler. Try: 'sudo setcap cap_sys_nice= %s'""" % (sys.executable)) return True
def compareScreenshot(fileName, win, crit=5.0): """Compare the current back buffer of the given window with the file Screenshots are stored and compared against the files under path kept in TESTS_DATA_PATH. Thus specify relative path to that directory """ #if we start this from a folder below run.py the data folder won't be found fileName = pjoin(TESTS_DATA_PATH, fileName) #get the frame from the window win.getMovieFrame(buffer='back') frame=win.movieFrames[-1] win.movieFrames=[] #if the file exists run a test, if not save the file if not isfile(fileName): frame.save(fileName, optimize=1) skip("Created %s" % basename(fileName)) else: expected = Image.open(fileName) expDat = np.array(expected.getdata()) imgDat = np.array(frame.getdata()) rms = (((imgDat-expDat)**2).sum()/len(imgDat))**0.5 filenameLocal = fileName.replace('.png','_local.png') if rms >= crit/2: #there was SOME discrepency logging.warning('PsychoPyTests: RMS=%.3g at threshold=%3.g' % (rms, crit)) if not rms<crit: #don't do `if rms>=crit because that doesn't catch rms=nan frame.save(filenameLocal, optimize=1) logging.warning('PsychoPyTests: Saving local copy into %s' % filenameLocal) assert rms<crit, \ "RMS=%.3g at threshold=%.3g. Local copy in %s" % (rms, crit, filenameLocal)
def _getCtrlValue(self, ctrl): """Retrieve the current value from the control (whatever type of ctrl it is, e.g. checkbox.GetValue, textctrl.GetStringSelection Different types of control have different methods for retrieving the value. This function checks them all and returns the value or None. """ if ctrl is None: return None elif hasattr(ctrl, '_choices'): # for wx.Choice if self.name == 'audioDevice': # convert the option back to a list with preferred at top val = ctrl._choices preferred = ctrl._choices.pop(ctrl.GetSelection()) val.insert(0, preferred) return val else: return ctrl._choices[ctrl.GetSelection()] elif hasattr(ctrl, 'GetValue'): # e.g. TextCtrl return ctrl.GetValue() elif hasattr(ctrl, 'GetLabel'): # for wx.StaticText return ctrl.GetLabel() else: msg = "failed to retrieve the value for pref: %s" logging.warning(msg % ctrl.valueCtrl) return None
def OnDropFiles(self, x, y, filenames): logging.debug('PsychoPyBuilder: received dropped files: %s' % filenames) for filename in filenames: if filename.endswith('.psyexp') or filename.lower().endswith('.py'): self.builder.fileOpen(filename=filename) else: logging.warning('dropped file ignored: did not end in .psyexp or .py')
def __init__(self, port=None, sendBreak=False, smoothing=False, bufferSize=262144): # if we're trying to send the break signal then presumably the device # is sleeping if sendBreak: checkAwake = False else: checkAwake = True # run initialisation; parity = enable parity checking super(BlackBoxToolkit, self).__init__(port, baudrate=230400, eol="\r\n", parity='N', pauseDuration=1.0, # 1 second pause!! slow device checkAwake=checkAwake) if sendBreak: self.sendBreak() time.sleep(3.0) # give time to reset if smoothing == False: # For use with CRT monitors which require smoothing. LCD monitors do not. # Remove smoothing for optos, but keep mic smoothing - refer to BBTK handbook re: mic smoothing latency # Important to remove smoothing for optos, as smoothing adds 20ms delay to timing. logging.info("Opto sensor smoothing removed. Mic1 and Mic2 smoothing still active.") self.setSmoothing('11000000') self.pause() try: # set buffer size - can make proportional to size of data (32 bytes per line * events)+1000 self.com.set_buffer_size(bufferSize) except Exception: logging.warning("Could not set buffer size. The default buffer size for Windows is 4096 bytes.")
def rgb2lms(rgb_Nx3, conversionMatrix=None): """Convert from RGB to cone space (LMS). Requires a conversion matrix, which will be generated from generic Sony Trinitron phosphors if not supplied (note that you will not get an accurate representation of the color space unless you supply a conversion matrix) usage:: lms_Nx3 = rgb2lms(rgb_Nx3(el,az,radius), conversionMatrix) """ # its easier to use in the other orientation! rgb_3xN = numpy.transpose(rgb_Nx3) if conversionMatrix is None: cones_to_rgb = numpy.asarray([ # L M S [4.97068857, -4.14354132, 0.17285275], # R [-0.90913894, 2.15671326, -0.24757432], # G [-0.03976551, -0.14253782, 1.18230333]]) # B logging.warning('This monitor has not been color-calibrated. ' 'Using default LMS conversion matrix.') else: cones_to_rgb = conversionMatrix rgb_to_cones = numpy.linalg.inv(cones_to_rgb) lms = numpy.dot(rgb_to_cones, rgb_3xN) return numpy.transpose(lms) # return in the shape we received it
def _getNextFrame(self): # get next frame info ( do not decode frame yet) while self.status == PLAYING: if self._video_stream.grab(): self._prev_frame_index = self._next_frame_index self._prev_frame_sec = self._next_frame_sec self._next_frame_index = self._video_stream.get(cv2.cv.CV_CAP_PROP_POS_FRAMES) if self._requested_fps and self._no_audio: self._next_frame_sec = self._next_frame_index/self._requested_fps#*self._video_stream.get(cv2.cv.CV_CAP_PROP_POS_MSEC)/1000.0 else: self._next_frame_sec = self._video_stream.get(cv2.cv.CV_CAP_PROP_POS_MSEC)/1000.0 self._video_perc_done = self._video_stream.get(cv2.cv.CV_CAP_PROP_POS_AVI_RATIO) self._next_frame_displayed = False if self.getTimeToNextFrameDraw() > -self._inter_frame_interval/2.0: return self._next_frame_sec else: self.nDroppedFrames += 1 if self.nDroppedFrames < reportNDroppedFrames: logging.warning("MovieStim2 dropping video frame index: %d"%(self._next_frame_index)) elif self.nDroppedFrames == reportNDroppedFrames: logging.warning("Multiple Movie frames have " "occurred - I'll stop bothering you " "about them!") else: self._onEos() break
def sendUsageStats(app=None): """Sends anonymous, very basic usage stats to psychopy server: the version of PsychoPy the system used (platform and version) the date """ v = psychopy.__version__ dateNow = time.strftime("%Y-%m-%d_%H:%M") miscInfo = '' # get platform-specific info if sys.platform == 'darwin': OSXver, junk, architecture = platform.mac_ver() systemInfo = "OSX_%s_%s" % (OSXver, architecture) elif sys.platform.startswith('linux'): systemInfo = '%s_%s_%s' % ( 'Linux', ':'.join([x for x in platform.dist() if x != '']), platform.release()) if len(systemInfo) > 30: # if it's too long PHP/SQL fails to store!? systemInfo = systemInfo[0:30] elif sys.platform == 'win32': systemInfo = "win32_v" + platform.version() else: systemInfo = platform.system() + platform.release() u = "http://www.psychopy.org/usage.php?date=%s&sys=%s&version=%s&misc=%s" URL = u % (dateNow, systemInfo, v, miscInfo) try: page = requests.get(URL, proxies=web.proxies) # proxies except Exception: logging.warning("Couldn't connect to psychopy.org\n" "Check internet settings (and proxy " "setting in PsychoPy Preferences.")
def push(self, syncPanel=None, progressHandler=None): """Push to remote from local copy of the repository Parameters ---------- syncPanel progressHandler Returns ------- 1 if successful -1 if project deleted on remote """ if syncPanel: syncPanel.statusAppend("\nPushing changes to remote...") origin = self.repo.remotes.origin try: info = self.repo.git.push() # progress=progressHandler except git.exc.GitCommandError as e: if ("The project you were looking for could not be found" in traceback.format_exc()): # we are pointing to a project at pavlovia but it doesn't exist # suggest we create it logging.warning("Project not found on gitlab.pavlovia.org") return MISSING_REMOTE else: raise e logging.debug('push report: {}'.format(info)) if syncPanel: syncPanel.statusAppend("done") if info: syncPanel.statusAppend("\n{}".format(info)) return 1
def add(self, thisType, value, position=None): """Add data to an existing data type (and add a new one if necess) """ if not thisType in self: self.addDataType(thisType) if position is None: # 'ran' is always the first thing to update repN = sum(self['ran'][self.trials.thisIndex]) if thisType != 'ran': # because it has already been updated repN -= 1 # make a list where 1st digit is trial number position = [self.trials.thisIndex] position.append(repN) # check whether data falls within bounds posArr = np.asarray(position) shapeArr = np.asarray(self.dataShape) if not np.alltrue(posArr < shapeArr): # array isn't big enough logging.warning('need a bigger array for: ' + thisType) # not implemented yet! self[thisType] = extendArr(self[thisType], posArr) # check for ndarrays with more than one value and for non-numeric data if (self.isNumeric[thisType] and ((type(value) == np.ndarray and len(value) > 1) or (type(value) not in [float, int]))): self._convertToObjectArray(thisType) # insert the value self[thisType][position[0], int(position[1])] = value
def _setVersion(version): """ Sets the version to be used for compiling using the useVersion function Parameters ---------- version: string The version requested """ # Set version if version: from psychopy import useVersion useVersion(version) global logging from psychopy import logging if __name__ != '__main__' and version not in [None, 'None', 'none', '']: version = None msg = "You cannot set version by calling compileScript() manually. Setting 'version' to None." logging.warning(msg) return version
def rush(value=True, realtime=False): """Raise the priority of the current thread/process using - sched_setscheduler realtime arg is not used in Linux implementation. NB for rush() to work on (debian-based?) Linux requires that the script is run using a copy of python that is allowed to change priority, eg: sudo setcap cap_sys_nice=eip <sys.executable>, and maybe restart PsychoPy. If <sys.executable> is the system python, it's important to restore it back to normal to avoid possible side-effects. Alternatively, use a different python executable, and change its cap_sys_nice. For RedHat-based systems, 'sudo chrt ...' at run-time might be needed instead, not sure. see http://rt.et.redhat.com/wiki/images/8/8e/Rtprio.pdf """ if importCtypesFailed: return False if value: # set to RR with max priority schedParams = _SchedParams() schedParams.sched_priority = c.sched_get_priority_max(SCHED_RR) err = c.sched_setscheduler(0, SCHED_RR, ctypes.byref(schedParams)) if err == -1: # returns 0 if OK logging.warning(warnMax % (sys.executable, sys.executable)) else: # set to RR with normal priority schedParams = _SchedParams() schedParams.sched_priority = c.sched_get_priority_min(SCHED_NORMAL) err = c.sched_setscheduler(0, SCHED_NORMAL, ctypes.byref(schedParams)) if err == -1: # returns 0 if OK logging.warning(warnNormal % sys.executable) return True
def stop(self): """Start recording from this keyboard""" logging.warning("Stopping key buffers but this could be dangerous if" "other keyboards rely on the same.") for buffer in self._buffers.values(): buffer.stop()
def installZipFile(self, zfile, v=None): """If v is provided this will be used as new version number; otherwise try and retrieve a version number from zip file name """ info = "" # return this at the end if py3: zfileIsName = type(zfile) == str else: zfileIsName = type(zfile) in (str, unicode) if os.path.isfile(zfile) and zfileIsName: # zfile is filename not an actual file if v is None: # try and deduce it zFilename = os.path.split(zfile)[-1] searchName = re.search('[0-9]*\.[0-9]*\.[0-9]*.', zFilename) if searchName != None: v = searchName.group(0)[:-1] else: msg = "Couldn't deduce version from zip file: %s" logging.warning(msg % zFilename) f = open(zfile, 'rb') zfile = zipfile.ZipFile(f) else: # assume here that zfile is a ZipFile pass # todo: error checking - is it a zipfile? currPath = self.app.prefs.paths['psychopy'] currVer = psychopy.__version__ # any commands that are successfully executed may need to be undone if # a later one fails undoStr = "" # depending on install method, needs diff handling # if path ends with 'psychopy' then move it to 'psychopy-version' and # create a new 'psychopy' folder for new version # does the path contain any version number? versionLabelsInPath = re.findall('PsychoPy-.*/', currPath) # e.g. the mac standalone app, no need to refer to new version number onWin32 = bool(sys.platform == 'win32' and int(sys.getwindowsversion()[1]) > 5) if len(versionLabelsInPath) == 0: unzipTarget = currPath try: # to move existing PsychoPy os.rename(currPath, "%s-%s" % (currPath, currVer)) undoStr += 'os.rename("%s-%s" %(currPath, currVer),currPath)\n' except Exception: if onWin32: msg = _translate("To upgrade you need to restart the app" " as admin (Right-click the app and " "'Run as admin')") else: msg = _translate("Could not move existing PsychoPy " "installation (permissions error?)") return msg else: # setuptools-style installation # generate new target path unzipTarget = currPath for thisVersionLabel in versionLabelsInPath: # remove final slash from the re.findall pathVersion = thisVersionLabel[:-1] unzipTarget = unzipTarget.replace(pathVersion, "PsychoPy-%s" % v) # find the .pth file that specifies the python dir # create the new installation directory BEFORE changing pth # file nUpdates, newInfo = self.updatePthFile(pathVersion, "PsychoPy-%s" % v) if nUpdates == -1: # there was an error (likely permissions) undoStr += 'self.updatePthFile(unzipTarget, currPath)\n' exec(undoStr) # undo previous changes return newInfo try: # create the new installation dir AFTER renaming existing dir os.makedirs(unzipTarget) undoStr += 'os.remove(%s)\n' % unzipTarget except Exception: # revert path rename and inform user exec(undoStr) # undo previous changes if onWin32: msg = _translate( "Right-click the app and 'Run as admin'):\n%s") else: msg = _translate("Failed to create directory for new version" " (permissions error?):\n%s") return msg % unzipTarget # do the actual extraction for name in zfile.namelist(): # for each file within the zip # check that this file is part of psychopy (not metadata or docs) if name.count('/psychopy/') < 1: continue try: targetFile = os.path.join(unzipTarget, name.split('/psychopy/')[1]) targetContainer = os.path.split(targetFile)[0] if not os.path.isdir(targetContainer): os.makedirs(targetContainer) # make the containing folder if targetFile.endswith('/'): os.makedirs(targetFile) # it's a folder else: outfile = open(targetFile, 'wb') outfile.write(zfile.read(name)) outfile.close() except Exception: exec(undoStr) # undo previous changes logging.error('failed to unzip file: ' + name) logging.error(sys.exc_info()[0]) info += _translate('Success. \nChanges to PsychoPy will be completed' ' when the application is next run') self.cancelBtn.SetDefault() self.installBtn.Disable() return info
def __init__(self, win, image="", units="", pos=(0.0, 0.0), flipHoriz=False, flipVert=False, name='', autoLog=True): """ :Parameters: win : a :class:`~psychopy.visual.Window` object (required) image : The filename, including relative or absolute path. The image can be any format that the Python Imagin Library can import (almost any). Can also be an image already loaded by PIL. units : **None**, 'height', 'norm', 'cm', 'deg' or 'pix' If None then the current units of the :class:`~psychopy.visual.Window` will be used. See :ref:`units` for explanation of other options. pos : The centre of the stimulus, as a tuple (0., 0.) or a list [0., 0.] for the x and y. The origin is the screen centre, the units are determined by units (see above). Stimuli can be positioned off-screen, beyond the window! name : string The name of the object to be using during logged messages about this stim """ #what local vars are defined (these are the init params) for use by __repr__ self._initParams = dir() self._initParams.remove('self') self.autoLog = False self.win = win self.name = name super(SimpleImageStim, self).__init__() #unit conversions if units != None and len(units): self.units = units else: self.units = win.units self.useShaders = win._haveShaders #use shaders if available by default, this is a good thing self.pos = numpy.array(pos, float) self.setImage(image) #check image size against window size if (self.size[0] > self.win.size[0]) or (self.size[1] > self.win.size[1]): logging.warning( "Image size (%s, %s) was larger than window size (%s, %s). Will draw black screen." % (self.size[0], self.size[1], self.win.size[0], self.win.size[1])) #check position with size, warn if stimuli not fully drawn if ((self.pos[0] + (self.size[0] / 2.0) > self.win.size[0] / 2.0) or (self.pos[0] - (self.size[0] / 2.0) < -self.win.size[0] / 2.0)): logging.warning( "The image does not completely fit inside the window in the X direction." ) if ((self.pos[1] + (self.size[1] / 2.0) > self.win.size[1] / 2.0) or (self.pos[1] - (self.size[1] / 2.0) < -self.win.size[1] / 2.0)): logging.warning( "The image does not completely fit inside the window in the Y direction." ) #flip if necessary self.flipHoriz = False #initially it is false, then so the flip according to arg above self.setFlipHoriz(flipHoriz) self.flipVert = False #initially it is false, then so the flip according to arg above self.setFlipVert(flipVert) self._calcPosRendered() #set autoLog (now that params have been initialised) self.autoLog = autoLog if autoLog: logging.exp("Created %s = %s" % (self.name, repr(self)))
#!/usr/bin/env python2 """filters.py placeholder file for backwards compatibility; Dec 2015 """ from psychopy import logging logging.warning('Deprecated v1.84.00: instead of `from psychopy import ' 'filters`, now do `from psychopy.visual import filters`') from psychopy.visual.filters import * # pylint: disable=0401,W0614
def __init__(self, win, filename="", units='pix', size=None, pos=(0.0, 0.0), ori=0.0, flipVert=False, flipHoriz=False, color=(1.0, 1.0, 1.0), colorSpace='rgb', opacity=1.0, volume=1.0, name='', loop=False, autoLog=True, depth=0.0, noAudio=False, vframe_callback=None, fps=None, interpolate=True): """ :Parameters: filename : a string giving the relative or absolute path to the movie. flipVert : True or *False* If True then the movie will be top-bottom flipped flipHoriz : True or *False* If True then the movie will be right-left flipped volume : The nominal level is 100, and 0 is silence. loop : bool, optional Whether to start the movie over from the beginning if draw is called and the movie is done. """ # what local vars are defined (these are the init params) for use # by __repr__ self._initParams = dir() self._initParams.remove('self') super(MovieStim3, self).__init__(win, units=units, name=name, autoLog=False) retraceRate = win._monitorFrameRate if retraceRate is None: retraceRate = win.getActualFrameRate() if retraceRate is None: logging.warning("FrameRate could not be supplied by psychopy; " "defaulting to 60.0") retraceRate = 60.0 self._retraceInterval = 1.0 / retraceRate self.filename = filename self.loop = loop self.flipVert = flipVert self.flipHoriz = flipHoriz self.pos = numpy.asarray(pos, float) self.depth = depth self.opacity = float(opacity) self.interpolate = interpolate self.noAudio = noAudio self._audioStream = None self.useTexSubImage2D = True self._videoClock = Clock() self.loadMovie(self.filename) self.setVolume(volume) self.nDroppedFrames = 0 # size if size is None: self.size = numpy.array([self._mov.w, self._mov.h], float) else: self.size = val2array(size) self.ori = ori self._updateVertices() # set autoLog (now that params have been initialised) self.autoLog = autoLog if autoLog: logging.exp("Created %s = %s" % (self.name, str(self)))
button_resp.device_number = 0 try: numJoysticks = joysticklib.getNumJoysticks() if numJoysticks > 0: button_resp.device = joysticklib.Joystick(0) try: joystickCache except NameError: joystickCache={} if not 0 in joystickCache: joystickCache[0] = joysticklib.Joystick(0) button_resp.device = joystickCache[0] else: button_resp.device = virtualjoybuttonslib.VirtualJoyButtons(0) logging.warning("joystick_{}: Using keyboard emulation 'ctrl' + 'Alt' + digit.".format(button_resp.device_number)) except Exception: pass if not button_resp.device: logging.error('No joystick/gamepad device found.') core.quit() button_resp.status = None button_resp.clock = core.Clock() button_resp.numButtons = button_resp.device.getNumButtons() # Create some handy timers globalClock = core.Clock() # to track the time since experiment started routineTimer = core.CountdownTimer() # to track time remaining of each (non-slip) routine
def loadFromXML(self, filename): """Loads an xml file and parses the builder Experiment from it """ self._doc.parse(filename) root = self._doc.getroot() # some error checking on the version (and report that this isn't valid # .psyexp)? filenameBase = os.path.basename(filename) if root.tag != "PsychoPy2experiment": logging.error('%s is not a valid .psyexp file, "%s"' % (filenameBase, root.tag)) # the current exp is already vaporized at this point, oops return self.psychopyVersion = root.get('version') versionf = float(self.psychopyVersion.rsplit('.', 1)[0]) if versionf < 1.63: msg = 'note: v%s was used to create %s ("%s")' vals = (self.psychopyVersion, filenameBase, root.tag) logging.warning(msg % vals) # Parse document nodes # first make sure we're empty self.flow = Flow(exp=self) # every exp has exactly one flow self.routines = {} self.namespace = NameSpace(self) # start fresh modifiedNames = [] duplicateNames = [] # fetch exp settings settingsNode = root.find('Settings') for child in settingsNode: self._getXMLparam(params=self.settings.params, paramNode=child, componentNode=settingsNode) # name should be saved as a settings parameter (only from 1.74.00) if self.settings.params['expName'].val in ['', None, 'None']: shortName = os.path.splitext(filenameBase)[0] self.setExpName(shortName) # fetch routines routinesNode = root.find('Routines') allCompons = getAllComponents(self.prefsBuilder['componentsFolders'], fetchIcons=False) # get each routine node from the list of routines for routineNode in routinesNode: routineGoodName = self.namespace.makeValid(routineNode.get('name')) if routineGoodName != routineNode.get('name'): modifiedNames.append(routineNode.get('name')) self.namespace.user.append(routineGoodName) routine = Routine(name=routineGoodName, exp=self) # self._getXMLparam(params=routine.params, paramNode=routineNode) self.routines[routineNode.get('name')] = routine for componentNode in routineNode: componentType = componentNode.tag if componentType in allCompons: # create an actual component of that type component = allCompons[componentType]( name=componentNode.get('name'), parentName=routineNode.get('name'), exp=self) else: # create UnknownComponent instead component = allCompons['UnknownComponent']( name=componentNode.get('name'), parentName=routineNode.get('name'), exp=self) # check for components that were absent in older versions of # the builder and change the default behavior # (currently only the new behavior of choices for RatingScale, # HS, November 2012) # HS's modification superceded Jan 2014, removing several # RatingScale options if componentType == 'RatingScaleComponent': if (componentNode.get('choiceLabelsAboveLine') or componentNode.get('lowAnchorText') or componentNode.get('highAnchorText')): pass # if not componentNode.get('choiceLabelsAboveLine'): # # this rating scale was created using older version # component.params['choiceLabelsAboveLine'].val=True # populate the component with its various params for paramNode in componentNode: self._getXMLparam(params=component.params, paramNode=paramNode, componentNode=componentNode) compGoodName = self.namespace.makeValid( componentNode.get('name')) if compGoodName != componentNode.get('name'): modifiedNames.append(componentNode.get('name')) self.namespace.add(compGoodName) component.params['name'].val = compGoodName routine.append(component) # for each component that uses a Static for updates, we need to set # that for thisRoutine in list(self.routines.values()): for thisComp in thisRoutine: for thisParamName in thisComp.params: thisParam = thisComp.params[thisParamName] if thisParamName == 'advancedParams': continue # advanced isn't a normal param elif thisParam.updates and "during:" in thisParam.updates: # remove the part that says 'during' updates = thisParam.updates.split(': ')[1] routine, static = updates.split('.') if routine not in self.routines: msg = ("%s was set to update during %s Static " "Component, but that component no longer " "exists") logging.warning(msg % (thisParamName, static)) else: self.routines[routine].getComponentFromName( static).addComponentUpdate( thisRoutine.params['name'], thisComp.params['name'], thisParamName) # fetch flow settings flowNode = root.find('Flow') loops = {} for elementNode in flowNode: if elementNode.tag == "LoopInitiator": loopType = elementNode.get('loopType') loopName = self.namespace.makeValid(elementNode.get('name')) if loopName != elementNode.get('name'): modifiedNames.append(elementNode.get('name')) self.namespace.add(loopName) loop = eval('%s(exp=self,name="%s")' % (loopType, loopName)) loops[loopName] = loop for paramNode in elementNode: self._getXMLparam(paramNode=paramNode, params=loop.params) # for conditions convert string rep to list of dicts if paramNode.get('name') == 'conditions': param = loop.params['conditions'] # e.g. param.val=[{'ori':0},{'ori':3}] try: param.val = eval('%s' % (param.val)) except SyntaxError: # This can occur if Python2.7 conditions string # contained long ints (e.g. 8L) and these can't be # parsed by Py3. But allow the file to carry on # loading and the conditions will still be loaded # from the xlsx file pass # get condition names from within conditionsFile, if any: try: # psychophysicsstaircase demo has no such param conditionsFile = loop.params['conditionsFile'].val except Exception: conditionsFile = None if conditionsFile in ['None', '']: conditionsFile = None if conditionsFile: try: trialList, fieldNames = data.importConditions( conditionsFile, returnFieldNames=True) for fname in fieldNames: if fname != self.namespace.makeValid(fname): duplicateNames.append(fname) else: self.namespace.add(fname) except Exception: pass # couldn't load the conditions file for now self.flow.append(LoopInitiator(loop=loops[loopName])) elif elementNode.tag == "LoopTerminator": self.flow.append( LoopTerminator(loop=loops[elementNode.get('name')])) elif elementNode.tag == "Routine": if elementNode.get('name') in self.routines: self.flow.append(self.routines[elementNode.get('name')]) else: logging.error("A Routine called '{}' was on the Flow but " "could not be found (failed rename?). You " "may need to re-insert it".format( elementNode.get('name'))) logging.flush() if modifiedNames: msg = 'duplicate variable name(s) changed in loadFromXML: %s\n' logging.warning(msg % ', '.join(list(set(modifiedNames)))) if duplicateNames: msg = 'duplicate variable names: %s' logging.warning(msg % ', '.join(list(set(duplicateNames)))) # if we succeeded then save current filename to self self.filename = filename
def __init__(self, win, *args, **kwargs): """Set up the backend window according the params of the PsychoPy win Before PsychoPy 1.90.0 this code was executed in Window._setupPygame() :param: win is a PsychoPy Window (usually not fully created yet) """ BaseBackend.__init__(self, win) # sets up self.win=win as weakref if win.allowStencil: stencil_size = 8 else: stencil_size = 0 vsync = 0 # provide warning if stereo buffers are requested but unavailable if win.stereo and not GL.gl_info.have_extension('GL_STEREO'): logging.warning('A stereo window was requested but the graphics ' 'card does not appear to support GL_STEREO') win.stereo = False if sys.platform == 'darwin' and not win.useRetina and pyglet.version >= "1.3": raise ValueError("As of PsychoPy 1.85.3 OSX windows should all be " "set to useRetina=True (or remove the argument). " "Pyglet 1.3 appears to be forcing " "us to use retina on any retina-capable screen " "so setting to False has no effect.") # multisampling sample_buffers = 0 aa_samples = 0 if win.multiSample: sample_buffers = 1 # get maximum number of samples the driver supports max_samples = (GL.GLint)() GL.glGetIntegerv(GL.GL_MAX_SAMPLES, max_samples) if (win.numSamples >= 2) and (win.numSamples <= max_samples.value): # NB - also check if divisible by two and integer? aa_samples = win.numSamples else: logging.warning( 'Invalid number of MSAA samples provided, must be ' 'integer greater than two. Disabling.') win.multiSample = False # options that the user might want config = GL.Config(depth_size=8, double_buffer=True, sample_buffers=sample_buffers, samples=aa_samples, stencil_size=stencil_size, stereo=win.stereo, vsync=vsync) defDisp = pyglet.window.get_platform().get_default_display() allScrs = defDisp.get_screens() # Screen (from Exp Settings) is 1-indexed, # so the second screen is Screen 1 if len(allScrs) < int(win.screen) + 1: logging.warn("Requested an unavailable screen number - " "using first available.") thisScreen = allScrs[0] else: thisScreen = allScrs[win.screen] if win.autoLog: logging.info('configured pyglet screen %i' % self.screen) # if fullscreen check screen size if win._isFullScr: win._checkMatchingSizes(win.size, [thisScreen.width, thisScreen.height]) w = h = None else: w, h = win.size if win.allowGUI: style = None else: style = 'borderless' try: self.winHandle = pyglet.window.Window(width=w, height=h, caption="PsychoPy", fullscreen=win._isFullScr, config=config, screen=thisScreen, style=style) except pyglet.gl.ContextException: # turn off the shadow window an try again pyglet.options['shadow_window'] = False self.winHandle = pyglet.window.Window(width=w, height=h, caption="PsychoPy", fullscreen=self._isFullScr, config=config, screen=thisScreen, style=style) logging.warning( "Pyglet shadow_window has been turned off. This is " "only an issue for you if you need multiple " "stimulus windows, in which case update your " "graphics card and/or graphics drivers.") if sys.platform == 'win32': # pyHook window hwnd maps to: # pyglet 1.14 -> window._hwnd # pyglet 1.2a -> window._view_hwnd if pyglet.version > "1.2": win._hw_handle = self.winHandle._view_hwnd else: win._hw_handle = self.winHandle._hwnd elif sys.platform == 'darwin': if win.useRetina: global retinaContext retinaContext = self.winHandle.context._nscontext view = retinaContext.view() bounds = view.convertRectToBacking_(view.bounds()).size if win.size[0] == bounds.width: win.useRetina = False # the screen is not a retina display win.size = np.array([int(bounds.width), int(bounds.height)]) try: # python 32bit (1.4. or 1.2 pyglet) win._hw_handle = self.winHandle._window.value except Exception: # pyglet 1.2 with 64bit python? win._hw_handle = self.winHandle._nswindow.windowNumber() elif sys.platform.startswith('linux'): win._hw_handle = self.winHandle._window if win.useFBO: # check for necessary extensions if not GL.gl_info.have_extension('GL_EXT_framebuffer_object'): msg = ("Trying to use a framebuffer object but " "GL_EXT_framebuffer_object is not supported. Disabled") logging.warn(msg) win.useFBO = False if not GL.gl_info.have_extension('GL_ARB_texture_float'): msg = ("Trying to use a framebuffer object but " "GL_ARB_texture_float is not supported. Disabling") logging.warn(msg) win.useFBO = False if pyglet.version < "1.2" and sys.platform == 'darwin': platform_specific.syncSwapBuffers(1) # add these methods to the pyglet window self.winHandle.setGamma = setGamma self.winHandle.setGammaRamp = setGammaRamp self.winHandle.getGammaRamp = getGammaRamp self.winHandle.set_vsync(True) self.winHandle.on_text = event._onPygletText self.winHandle.on_key_press = event._onPygletKey self.winHandle.on_mouse_press = event._onPygletMousePress self.winHandle.on_mouse_release = event._onPygletMouseRelease self.winHandle.on_mouse_scroll = event._onPygletMouseWheel if not win.allowGUI: # make mouse invisible. Could go further and make it 'exclusive' # (but need to alter x,y handling then) self.winHandle.set_mouse_visible(False) self.winHandle.on_resize = _onResize # avoid circular reference if not win.pos: # work out where the centre should be if win.useRetina: win.pos = [(thisScreen.width - win.size[0] / 2) / 2, (thisScreen.height - win.size[1] / 2) / 2] else: win.pos = [(thisScreen.width - win.size[0]) / 2, (thisScreen.height - win.size[1]) / 2] if not win._isFullScr: # add the necessary amount for second screen self.winHandle.set_location(int(win.pos[0] + thisScreen.x), int(win.pos[1] + thisScreen.y)) try: # to load an icon for the window iconFile = os.path.join(psychopy.prefs.paths['resources'], 'psychopy.ico') icon = pyglet.image.load(filename=iconFile) self.winHandle.set_icon(icon) except Exception: pass # doesn't matter # store properties of the system self._driver = pyglet.gl.gl_info.get_renderer() self._gammaErrorPolicy = win.gammaErrorPolicy try: self._origGammaRamp = self.getGammaRamp() self._rampSize = getGammaRampSize( self.screenID, self.xDisplay, gammaErrorPolicy=self._gammaErrorPolicy) except OSError: self.close() raise self._TravisTesting = (os.environ.get('TRAVIS') == 'true')
def lineColorSpace(self, value): logging.warning( "Setting color space by attribute rather than by object is deprecated. Value of lineColorSpace has been assigned to colorSpace." ) self.colorSpace = value
from collections import deque import sys import copy import psychopy.core import psychopy.clock from psychopy import logging from psychopy.constants import NOT_STARTED try: import psychtoolbox as ptb from psychtoolbox import hid havePTB = True except ImportError as err: logging.warning(("Import Error: " + err.args[0] + ". Using event module for keyboard component.")) from psychopy import event havePTB = False defaultBufferSize = 10000 def getKeyboards(): """Get info about the available keyboards. Only really useful on Mac/Linux because on these the info can be used to select a particular physical device when calling :class:`Keyboard`. On Win this function does return information correctly but the :class:Keyboard can't make use of it. Returns
def initPyo(rate=44100, stereo=True, buffer=128): """setup the pyo (sound) server """ global pyoSndServer, Sound, audioDriver, duplex, maxChnls Sound = SoundPyo global pyo try: assert pyo except NameError: # pragma: no cover import pyo # microphone.switchOn() calls initPyo even if audioLib is something else #subclass the pyo.Server so that we can insert a __del__ function that shuts it down # skip coverage since the class is never used if we have a recent version of pyo class _Server(pyo.Server): # pragma: no cover core = core #make libs class variables so they don't get deleted first logging = logging def __del__(self): self.stop() self.core.wait( 0.5) #make sure enough time passes for the server to shutdown self.shutdown() self.core.wait( 0.5) #make sure enough time passes for the server to shutdown self.logging.debug( 'pyo sound server shutdown') #this may never get printed if '.'.join(map(str, pyo.getVersion())) < '0.6.4': Server = _Server else: Server = pyo.Server # if we already have a server, just re-initialize it if 'pyoSndServer' in globals() and hasattr(pyoSndServer, 'shutdown'): pyoSndServer.stop() core.wait( 0.5) #make sure enough time passes for the server to shutdown pyoSndServer.shutdown() core.wait(0.5) pyoSndServer.reinit(sr=rate, nchnls=maxChnls, buffersize=buffer, audio=audioDriver) pyoSndServer.boot() else: if platform == 'win32': #check for output device/driver devNames, devIDs = pyo.pa_get_output_devices() audioDriver, outputID = _bestDriver(devNames, devIDs) if outputID is None: audioDriver = 'Windows Default Output' #using the default output because we didn't find the one(s) requested outputID = pyo.pa_get_default_output() if outputID is not None: logging.info('Using sound driver: %s (ID=%i)' % (audioDriver, outputID)) maxOutputChnls = pyo.pa_get_output_max_channels(outputID) else: logging.warning( 'No audio outputs found (no speakers connected?') return -1 #check for valid input (mic) devNames, devIDs = pyo.pa_get_input_devices() audioInputName, inputID = _bestDriver(devNames, devIDs) if inputID is None: audioInputName = 'Windows Default Input' #using the default input because we didn't find the one(s) requested inputID = pyo.pa_get_default_input() if inputID is not None: logging.info('Using sound-input driver: %s (ID=%i)' % (audioInputName, inputID)) maxInputChnls = pyo.pa_get_input_max_channels(inputID) duplex = bool(maxInputChnls > 0) else: maxInputChnls = 0 duplex = False else: #for other platforms set duplex to True (if microphone is available) audioDriver = prefs.general['audioDriver'][0] maxInputChnls = pyo.pa_get_input_max_channels( pyo.pa_get_default_input()) maxOutputChnls = pyo.pa_get_output_max_channels( pyo.pa_get_default_output()) duplex = bool(maxInputChnls > 0) maxChnls = min(maxInputChnls, maxOutputChnls) if maxInputChnls < 1: # pragma: no cover logging.warning( '%s.initPyo could not find microphone hardware; recording not available' % __name__) maxChnls = maxOutputChnls if maxOutputChnls < 1: # pragma: no cover logging.error( '%s.initPyo could not find speaker hardware; sound not available' % __name__) return -1 # create the instance of the server: if platform in ['darwin', 'linux2']: #for mac/linux we set the backend using the server audio param pyoSndServer = Server(sr=rate, nchnls=maxChnls, buffersize=buffer, audio=audioDriver) else: #with others we just use portaudio and then set the OutputDevice below pyoSndServer = Server(sr=rate, nchnls=maxChnls, buffersize=buffer) pyoSndServer.setVerbosity(1) if platform == 'win32': pyoSndServer.setOutputDevice(outputID) if inputID is not None: pyoSndServer.setInputDevice(inputID) #do other config here as needed (setDuplex? setOutputDevice?) pyoSndServer.setDuplex(duplex) pyoSndServer.boot() core.wait(0.5) #wait for server to boot before starting te sound stream pyoSndServer.start() try: Sound() # test creation, no play except pyo.PyoServerStateException: msg = "Failed to start pyo sound Server" if platform == 'darwin' and audioDriver != 'portaudio': msg += "; maybe try prefs.general.audioDriver 'portaudio'?" logging.error(msg) core.quit() logging.debug('pyo sound server started') logging.flush()
def __init__( self, win, units='', lineWidth=1.5, lineColor=False, # uses False in place of None to distinguish between "not set" and "transparent" fillColor=False, # uses False in place of None to distinguish between "not set" and "transparent" colorSpace='rgb', vertices=((-0.5, 0), (0, +0.5), (+0.5, 0)), closeShape=True, pos=(0, 0), size=1, ori=0.0, opacity=None, contrast=1.0, depth=0, interpolate=True, name=None, autoLog=None, autoDraw=False, # legacy color=False, lineRGB=False, fillRGB=False, fillColorSpace=None, lineColorSpace=None): """ """ # all doc is in the attributes # what local vars are defined (these are the init params) for use by # __repr__ self._initParams = dir() self._initParams.remove('self') # Initialize inheritance and remove unwanted methods; autoLog is set # later super(BaseShapeStim, self).__init__(win, units=units, name=name, autoLog=False) self.pos = numpy.array(pos, float) self.closeShape = closeShape self.lineWidth = lineWidth self.interpolate = interpolate # Appearance self.colorSpace = colorSpace if fillColor is not False: self.fillColor = fillColor elif color is not False: # Override fillColor with color if not set self.fillColor = color else: # Default to None if neither are set self.fillColor = None if lineColor is not False: self.lineColor = lineColor elif color is not False: # Override lineColor with color if not set self.lineColor = color else: # Default to black if neither are set self.lineColor = 'black' if lineRGB is not False: # Override with RGB if set logging.warning("Use of rgb arguments to stimuli are deprecated." " Please use color and colorSpace args instead") self.setLineColor(lineRGB, colorSpace='rgb', log=None) if fillRGB is not False: # Override with RGB if set logging.warning("Use of rgb arguments to stimuli are deprecated." " Please use color and colorSpace args instead") self.setFillColor(fillRGB, colorSpace='rgb', log=None) self.contrast = contrast if opacity: self.opacity = opacity # Other stuff self.depth = depth self.ori = numpy.array(ori, float) self.size = numpy.array([0.0, 0.0]) + size # make sure that it's 2D if vertices != (): # flag for when super-init'ing a ShapeStim self.vertices = vertices # call attributeSetter self.autoDraw = autoDraw # call attributeSetter # set autoLog now that params have been initialised wantLog = autoLog is None and self.win.autoLog self.__dict__['autoLog'] = autoLog or wantLog if self.autoLog: logging.exp("Created %s = %s" % (self.name, str(self)))
def main(info): # save log of subjects write_subjectlog(subjectlog, info) run_nr = int(info['run_nr']) subj = info['subject_id'] fullscr = info['fullscr'] time = core.Clock() subj_dir = pjoin(RESDIR, 'sub-' + subj) if not pexists(subj_dir): os.makedirs(subj_dir) log_fn = config['log_template'].format( subj=subj, task_name=config['task_name'], runnr=run_nr, timestamp=ptime.strftime(time_template), ) log_fn = pjoin(subj_dir, log_fn) log_responses = logging.LogFile(log_fn, level=logging.INFO) # set up global key for quitting; if that happens, log will be moved to # {log_fn}__halted.txt event.globalKeys.add(key='q', modifiers=['ctrl'], func=move_halted_log, func_args=[log_fn], name='quit experiment gracefully') # --- LOAD STIMULI ORDER FOR THIS PARTICIPANT --- stim_json = pjoin(PWD, 'cfg', 'sub-{0}_task-localizer_4runs.json'.format(subj)) # create stimulus order if not existing if not os.path.exists(stim_json): logging.warning("Creating stimulus order for {0}".format(subj)) MAKESTIMPY = pjoin(HERE, 'make_stim_order.py') cmd = "python {cmd} --subid {subj} --output {output} " \ "--nruns 4".format(cmd=MAKESTIMPY, subj=subj, output=dirname(stim_json)) logging.warning("Running '{0}'".format(cmd)) sp.check_call(cmd.split()) with open(stim_json, 'rb') as f: stimuli = json.load(f)[str(run_nr)] # ------------------------ print "Opening screen" tbegin = time.getTime() using_scanner = info['scanner?'] # Setting up visual size = [1280, 1024] scrwin = visual.Window(size=size, allowGUI=False, units='pix', screen=1, rgb=[-1, -1, -1], fullscr=fullscr) # load clips print "Loading stimuli" loading = visual.TextStim(scrwin, text="Loading stimuli...", height=31) loading.draw() scrwin.flip() stimuli_clip = dict() for stim in stimuli: if stim['stim_type'] != 'fixation': stim_fn = stim['stim_fn'] print("Loading {0}".format(stim_fn)) stimuli_clip[stim_fn] = \ visual.MovieStim3(scrwin, pjoin(PWD, stim_fn), size=(1280, 940), name=stim_fn, noAudio=True, loop=True) scrwin.flip() cross_hair = visual.TextStim(scrwin, text='+', height=31, pos=(0, 0), color='#FFFFFF') if using_scanner: intro_msg = "Waiting for trigger..." else: intro_msg = "Press Enter to start" intro_msg = instructions + '\n' + intro_msg intro = visual.TextStim(scrwin, text=intro_msg, height=31, wrapWidth=900) # Start of experiment intro.draw() scrwin.flip() # open up serial port and wait for first trigger if using_scanner: ser_port = '/dev/ttyUSB0' ser = serial.Serial(ser_port, 115200, timeout=.0001) ser.flushInput() trigger = '' while trigger != '5': trigger = ser.read() else: from psychopy.hardware.emulator import launchScan event.waitKeys(keyList=['return']) # XXX: set up TR here MR_settings = { 'TR': 1, 'volumes': 280, 'sync': '5', 'skip': 3, 'sound': False, } vol = launchScan(scrwin, MR_settings, globalClock=time, mode='Test') class FakeSerial(object): @staticmethod def read(): k = event.getKeys(['1', '2', '5']) return k[-1] if k else '' ser = FakeSerial() # set up timer for experiment starting from first trigger timer_exp = core.Clock() trunbegin = timer_exp.getTime() # setup bids log logbids("onset\tduration\tstim_type\trepetition") # duration will be filled later template_bids = '{onset:.3f}\t{duration:.3f}\t{stim_type}\t{stim_fn}\t' \ '{repetition}' # and now we just loop through the trials for trial in stimuli: stim_type = trial['stim_type'] stim_fn = trial['stim_fn'] duration = trial['duration'] logbids(template_bids.format( onset=timer_exp.getTime(), duration=duration, stim_type=stim_type, stim_fn=stim_fn, repetition=trial.get('repetition', 0)), ) trial_counter = core.CountdownTimer(duration) if stim_type == 'fixation': cross_hair.draw() scrwin.flip() logging.flush() while trial_counter.getTime() > 0: pass else: movie = stimuli_clip[stim_fn] while trial_counter.getTime() > 0: key = ser.read() if key in ['1', '2']: logbids(template_bids.format( onset=timer_exp.getTime(), duration=0., stim_type='button_press', stim_fn=None, repetition=0) ) if movie.status != visual.FINISHED: movie.draw() scrwin.flip() else: cross_hair.draw() scrwin.flip() logging.exp("Done in {0:.2f}s".format(timer_exp.getTime())) logging.flush() scrwin.close() core.quit()
def __init__(self, win, *args, **kwargs): """Set up the backend window according the params of the PsychoPy win Before PsychoPy 1.90.0 this code was executed in Window._setupPygame() Parameters ---------- win : psychopy.visual.Window instance PsychoPy Window (usually not fully created yet). share : psychopy.visual.Window instance PsychoPy Window to share a context with bpc : array_like Bits per color (R, G, B). refreshHz : int Refresh rate in Hertz. depthBits : int, Framebuffer (back buffer) depth bits. swapInterval : int Swap interval for the current OpenGL context. stencilBits : int Framebuffer (back buffer) stencil bits. winTitle : str Optional window title string. *args Additional position arguments. **kwargs Additional keyword arguments. """ BaseBackend.__init__(self, win) # window to share a context with shareWin = kwargs.get('share', None) if shareWin is not None: if shareWin.winType == 'glfw': shareContext = shareWin.winHandle else: logging.warning( 'Cannot share a context with a non-GLFW window. Disabling.' ) shareContext = None else: shareContext = None if sys.platform == 'darwin' and not win.useRetina and pyglet.version >= "1.3": raise ValueError("As of PsychoPy 1.85.3 OSX windows should all be " "set to useRetina=True (or remove the argument). " "Pyglet 1.3 appears to be forcing " "us to use retina on any retina-capable screen " "so setting to False has no effect.") # window framebuffer configuration bpc = kwargs.get('bpc', (8, 8, 8)) if isinstance(bpc, int): win.bpc = (bpc, bpc, bpc) else: win.bpc = bpc win.refreshHz = int(kwargs.get('refreshHz', 60)) win.depthBits = int(kwargs.get('depthBits', 8)) win.stencilBits = int(kwargs.get('stencilBits', 8)) # win.swapInterval = int(kwargs.get('swapInterval', 1)) # vsync ON if 1 # get monitors, with GLFW the primary display is ALWAYS at index 0 allScrs = glfw.get_monitors() if len(allScrs) < int(win.screen) + 1: logging.warn("Requested an unavailable screen number - " "using first available.") win.screen = 0 thisScreen = allScrs[win.screen] if win.autoLog: logging.info('configured GLFW screen %i' % win.screen) # find a matching video mode (can we even support this configuration?) isVidmodeSupported = False for vidmode in glfw.get_video_modes(thisScreen): size, bpc, hz = vidmode if win._isFullScr: # size and refresh rate are ignored if windowed hasSize = size == tuple(win.size) hasHz = hz == win.refreshHz else: hasSize = hasHz = True hasBpc = bpc == tuple(win.bpc) if hasSize and hasBpc and hasHz: isVidmodeSupported = True break nativeVidmode = glfw.get_video_mode(thisScreen) if not isVidmodeSupported: # the requested video mode is not supported, use current logging.warning( ("The specified video mode is not supported by this display, " "using native mode ...")) actualWidth, actualHeight = nativeVidmode.size redBits, greenBits, blueBits = nativeVidmode.bits # change the window settings if win._isFullScr: logging.warning(( "Overriding user video settings: size {} -> {}, bpc {} -> " "{}, refreshHz {} -> {}".format( tuple(win.size), (actualWidth, actualHeight), tuple(win.bpc), (redBits, greenBits, blueBits), win.refreshHz, nativeVidmode.refresh_rate))) win.clientSize = np.array((actualWidth, actualHeight), np.int) else: logging.warning( ("Overriding user video settings: bpc {} -> " "{}, refreshHz {} -> {}".format( tuple(win.bpc), (redBits, greenBits, blueBits), win.refreshHz, nativeVidmode.refresh_rate))) win.bpc = (redBits, greenBits, blueBits) win.refreshHz = nativeVidmode.refresh_rate if win._isFullScr: useDisplay = thisScreen else: useDisplay = None # configure stereo useStereo = 0 if win.stereo: # provide warning if stereo buffers are requested but unavailable if not glfw.extension_supported('GL_STEREO'): logging.warning( 'A stereo window was requested but the graphics ' 'card does not appear to support GL_STEREO') win.stereo = False else: useStereo = 1 # setup multisampling # This enables multisampling on the window backbuffer, not on other # framebuffers. msaaSamples = 0 if win.multiSample: maxSamples = (GL.GLint)() GL.glGetIntegerv(GL.GL_MAX_SAMPLES, maxSamples) if (win.numSamples & (win.numSamples - 1)) != 0: # power of two? logging.warning( 'Invalid number of MSAA samples provided, must be ' 'power of two. Disabling.') elif 0 > win.numSamples > maxSamples.value: # check if within range logging.warning( 'Invalid number of MSAA samples provided, outside of valid ' 'range. Disabling.') else: msaaSamples = win.numSamples win.multiSample = msaaSamples > 0 # disable stencil buffer if not win.allowStencil: win.stencilBits = 0 # set buffer configuration hints glfw.window_hint(glfw.RED_BITS, win.bpc[0]) glfw.window_hint(glfw.GREEN_BITS, win.bpc[1]) glfw.window_hint(glfw.BLUE_BITS, win.bpc[2]) glfw.window_hint(glfw.REFRESH_RATE, win.refreshHz) glfw.window_hint(glfw.STEREO, useStereo) glfw.window_hint(glfw.SAMPLES, msaaSamples) glfw.window_hint(glfw.STENCIL_BITS, win.stencilBits) glfw.window_hint(glfw.DEPTH_BITS, win.depthBits) glfw.window_hint(glfw.AUTO_ICONIFY, 0) # window appearance and behaviour hints if not win.allowGUI: glfw.window_hint(glfw.DECORATED, 0) # create the window self.winHandle = glfw.create_window( width=win.clientSize[0], height=win.clientSize[1], title=str(kwargs.get('winTitle', "PsychoPy (GLFW)")), monitor=useDisplay, share=shareContext) # The window's user pointer maps the Python Window object to its GLFW # representation. glfw.set_window_user_pointer(self.winHandle, win) glfw.make_context_current(self.winHandle) # ready to use # set the position of the window if not fullscreen if not win._isFullScr: # if no window position is specified, centre it on-screen if win.pos is None: size, bpc, hz = nativeVidmode win.pos = [(size[0] - win.clientSize[0]) / 2.0, (size[1] - win.clientSize[1]) / 2.0] # get the virtual position of the monitor, apply offset to the # window position px, py = glfw.get_monitor_pos(thisScreen) glfw.set_window_pos(self.winHandle, int(win.pos[0] + px), int(win.pos[1] + py)) elif win._isFullScr and win.pos is not None: logging.warn("Ignoring window 'pos' in fullscreen mode.") # set the window icon if hasattr(glfw, 'set_window_icon'): glfw.set_window_icon(self.winHandle, 1, _WINDOW_ICON_) # set the window size to the framebuffer size self._frameBufferSize = np.array( glfw.get_framebuffer_size(self.winHandle)) if win.useFBO: # check for necessary extensions if not glfw.extension_supported('GL_EXT_framebuffer_object'): msg = ("Trying to use a framebuffer object but " "GL_EXT_framebuffer_object is not supported. Disabled") logging.warn(msg) win.useFBO = False if not glfw.extension_supported('GL_ARB_texture_float'): msg = ("Trying to use a framebuffer object but " "GL_ARB_texture_float is not supported. Disabling") logging.warn(msg) win.useFBO = False # Assign event callbacks, these are dispatched when 'poll_events' is # called. glfw.set_mouse_button_callback(self.winHandle, event._onGLFWMouseButton) glfw.set_scroll_callback(self.winHandle, event._onGLFWMouseScroll) glfw.set_key_callback(self.winHandle, event._onGLFWKey) glfw.set_char_mods_callback(self.winHandle, event._onGLFWText) # set swap interval to manual setting, independent of waitBlanking self.setSwapInterval(int(kwargs.get('swapInterval', 1))) # give the window class GLFW specific methods win.setMouseType = self.setMouseType if not win.allowGUI: self.setMouseVisibility(False)
def __init__(self, win, tex ="sqrXsqr", mask ="none", units ="", pos =(0.0,0.0), size =(1.0,1.0), radialCycles=3, angularCycles=4, radialPhase=0, angularPhase=0, ori =0.0, texRes =64, angularRes=100, visibleWedge=(0, 360), rgb =None, color=(1.0,1.0,1.0), colorSpace='rgb', dkl=None, lms=None, contrast=1.0, opacity=1.0, depth=0, rgbPedestal = (0.0,0.0,0.0), interpolate=False, name=None, autoLog=None, maskParams=None): """ """ # Empty docstring on __init__, Simply inherits methods from GratingStim #what local vars are defined (these are the init params) for use by __repr__ self._initParams = dir() self._initParams.remove('self') super(RadialStim, self).__init__(win, units=units, name=name, autoLog=False) #autolog should start off false self.useShaders = win._haveShaders #use shaders if available by default, this is a good thing # UGLY HACK again. (See same section in GratingStim for ideas) self.__dict__['contrast'] = 1 self.__dict__['size'] = 1 self.__dict__['sf'] = 1 self.__dict__['tex'] = tex #initialise textures for stimulus self._texID = GL.GLuint() GL.glGenTextures(1, ctypes.byref(self._texID)) self._maskID = GL.GLuint() GL.glGenTextures(1, ctypes.byref(self._maskID)) self.__dict__['maskParams'] = maskParams self.maskRadialPhase = 0 self.texRes = texRes #must be power of 2 self.interpolate = interpolate self.rgbPedestal = val2array(rgbPedestal, False, length=3) #these are defined by the GratingStim but will just cause confusion here! self.setSF = None self.setPhase = None self.colorSpace=colorSpace if rgb!=None: logging.warning("Use of rgb arguments to stimuli are deprecated. Please use color and colorSpace args instead") self.setColor(rgb, colorSpace='rgb', log=False) elif dkl!=None: logging.warning("Use of dkl arguments to stimuli are deprecated. Please use color and colorSpace args instead") self.setColor(dkl, colorSpace='dkl', log=False) elif lms!=None: logging.warning("Use of lms arguments to stimuli are deprecated. Please use color and colorSpace args instead") self.setColor(lms, colorSpace='lms', log=False) else: self.setColor(color, log=False) self.ori = float(ori) self.__dict__['angularRes'] = angularRes self.__dict__['radialPhase'] = radialPhase self.__dict__['radialCycles'] = radialCycles self.__dict__['visibleWedge'] = numpy.array(visibleWedge) self.__dict__['angularCycles'] = angularCycles self.__dict__['angularPhase'] = angularPhase self.pos = numpy.array(pos, float) self.depth=depth self.__dict__['sf'] = 1 self.size = val2array(size, False) self.tex = tex self.mask = mask self.contrast = float(contrast) self.opacity = float(opacity) # self._updateEverything() # set autoLog now that params have been initialised self.__dict__['autoLog'] = autoLog or autoLog is None and self.win.autoLog if self.autoLog: logging.exp("Created %s = %s" %(self.name, str(self)))
audioDriver = None for thisLibName in prefs.general['audioLib']: try: if thisLibName == 'pyo': import pyo havePyo = True elif thisLibName == 'pygame': import pygame from pygame import mixer, sndarray else: raise ValueError( "Audio lib options are currently only 'pyo' or 'pygame', not '%s'" % thisLibName) except: logging.warning('%s audio lib was requested but not loaded: %s' % (thisLibName, sys.exc_info()[1])) continue #to try next audio lib #if we got this far we were sucessful in loading the lib audioLib = thisLibName logging.info('sound is using audioLib: %s' % audioLib) break stepsFromA = { 'C': -9, 'Csh': -8, 'Dfl': -8, 'D': -7, 'Dsh': -6, 'Efl': -6, 'E': -5, 'F': -4,
def getProject(filename): """Will try to find (locally synced) pavlovia Project for the filename """ if not haveGit: raise exceptions.DependencyError( "gitpython and a git installation required for getProject()") gitRoot = getGitRoot(filename) if gitRoot in knownProjects: return knownProjects[gitRoot] elif gitRoot: # Existing repo but not in our knownProjects. Investigate logging.info("Investigating repo at {}".format(gitRoot)) localRepo = git.Repo(gitRoot) proj = None for remote in localRepo.remotes: for url in remote.urls: if "gitlab.pavlovia.org" in url: # could be 'https://gitlab.pavlovia.org/NameSpace/Name.git' # or may be '[email protected]:NameSpace/Name.git' namespaceName = url.split('gitlab.pavlovia.org')[1] # remove the first char (: or /) if namespaceName[0] in ['/', ':']: namespaceName = namespaceName[1:] # remove the .git at the end if present namespaceName = namespaceName.replace('.git', '') pavSession = getCurrentSession() if not pavSession.user: nameSpace = namespaceName.split('/')[0] if nameSpace in knownUsers: # Log in if user is known login(nameSpace, rememberMe=True) else: # Check whether project repo is found in any of the known users accounts for user in knownUsers: try: login(user) except requests.exceptions.ConnectionError: break foundProject = False for repo in pavSession.findUserProjects(): if namespaceName in repo['id']: foundProject = True logging.info( "Logging in as {}".format(user)) break if not foundProject: logging.warning( "Could not find {namespace} in your Pavlovia accounts. " "Logging in as {user}.".format( namespace=namespaceName, user=user)) if pavSession.user: proj = pavSession.getProject(namespaceName, repo=localRepo) if proj.pavlovia and proj.pavlovia.get_id() == 0: logging.warning( _translate( "We found a repository pointing to {} " "but no project was found there (deleted?)" ).format(url)) else: logging.warning( _translate( "We found a repository pointing to {} " "but no user is logged in for us to check it". format(url))) return proj if proj == None: logging.warning("We found a repository at {} but it " "doesn't point to gitlab.pavlovia.org. " "You could create that as a remote to " "sync from PsychoPy.".format(gitRoot))
def init(rate=44100, stereo=True, buffer=128): """setup the pyo (sound) server """ global pyoSndServer, Sound, audioDriver, duplex, maxChnls Sound = SoundPyo global pyo try: assert pyo except NameError: # pragma: no cover import pyo # can be needed for microphone.switchOn(), which calls init even # if audioLib is something else # subclass the pyo.Server so that we can insert a __del__ function that # shuts it down skip coverage since the class is never used if we have # a recent version of pyo class _Server(pyo.Server): # pragma: no cover # make libs class variables so they don't get deleted first core = core logging = logging def __del__(self): self.stop() # make sure enough time passes for the server to shutdown self.core.wait(0.5) self.shutdown() # make sure enough time passes for the server to shutdown self.core.wait(0.5) # this may never get printed self.logging.debug('pyo sound server shutdown') if '.'.join(map(str, pyo.getVersion())) < '0.6.4': Server = _Server else: Server = pyo.Server # if we already have a server, just re-initialize it if 'pyoSndServer' in globals() and hasattr(pyoSndServer, 'shutdown'): pyoSndServer.stop() # make sure enough time passes for the server to shutdown core.wait(0.5) pyoSndServer.shutdown() core.wait(0.5) pyoSndServer.reinit(sr=rate, nchnls=maxChnls, buffersize=buffer, audio=audioDriver) pyoSndServer.boot() else: if sys.platform == 'win32': # check for output device/driver #todo: Throwing errors on one users' config https://discourse.psychopy.org/t/error-with-microphone-component-on-psychopy-2020/13168 devNames, devIDs = get_output_devices() audioDriver, outputID = _bestDriver(devNames, devIDs) if outputID is None: # using the default output because we didn't find the one(s) # requested audioDriver = 'Windows Default Output' outputID = pyo.pa_get_default_output() if outputID is not None: logging.info(u'Using sound driver: %s (ID=%i)' % (audioDriver, outputID)) maxOutputChnls = pyo.pa_get_output_max_channels(outputID) else: logging.warning( 'No audio outputs found (no speakers connected?') return -1 # check for valid input (mic) # If no input device is available, devNames and devIDs are empty # lists. devNames, devIDs = get_input_devices() audioInputName, inputID = _bestDriver(devNames, devIDs) # Input devices were found, but requested devices were not found if len(devIDs) > 0 and inputID is None: defaultID = pyo.pa_get_default_input() if defaultID is not None and defaultID != -1: # default input is found # use the default input because we didn't find the one(s) # requested audioInputName = 'Windows Default Input' inputID = defaultID else: # default input is not available inputID = None if inputID is not None: msg = u'Using sound-input driver: %s (ID=%i)' logging.info(msg % (audioInputName, inputID)) maxInputChnls = pyo.pa_get_input_max_channels(inputID) duplex = bool(maxInputChnls > 0) else: maxInputChnls = 0 duplex = False # for other platforms set duplex to True (if microphone is available) else: audioDriver = prefs.hardware['audioDriver'][0] maxInputChnls = pyo.pa_get_input_max_channels( pyo.pa_get_default_input()) maxOutputChnls = pyo.pa_get_output_max_channels( pyo.pa_get_default_output()) duplex = bool(maxInputChnls > 0) maxChnls = min(maxInputChnls, maxOutputChnls) if maxInputChnls < 1: # pragma: no cover msg = (u'%s.init could not find microphone hardware; ' u'recording not available') logging.warning(msg % __name__) maxChnls = maxOutputChnls if maxOutputChnls < 1: # pragma: no cover msg = (u'%s.init could not find speaker hardware; ' u'sound not available') logging.error(msg % __name__) return -1 # create the instance of the server: if sys.platform == 'darwin' or sys.platform.startswith('linux'): # for mac/linux we set the backend using the server audio param pyoSndServer = Server(sr=rate, nchnls=maxChnls, buffersize=buffer, audio=audioDriver) else: # with others we just use portaudio and then set the OutputDevice # below pyoSndServer = Server(sr=rate, nchnls=maxChnls, buffersize=buffer) pyoSndServer.setVerbosity(1) if sys.platform == 'win32': pyoSndServer.setOutputDevice(outputID) if inputID is not None: pyoSndServer.setInputDevice(inputID) # do other config here as needed (setDuplex? setOutputDevice?) pyoSndServer.setDuplex(duplex) pyoSndServer.boot() core.wait(0.5) # wait for server to boot before starting the sound stream pyoSndServer.start() # atexit is filo, will call stop then shutdown upon closing atexit.register(pyoSndServer.shutdown) atexit.register(pyoSndServer.stop) try: Sound() # test creation, no play except pyo.PyoServerStateException: msg = "Failed to start pyo sound Server" if sys.platform == 'darwin' and audioDriver != 'portaudio': msg += "; maybe try prefs.general.audioDriver 'portaudio'?" logging.error(msg) core.quit() logging.debug('pyo sound server started') logging.flush()
def __init__(self, name='', version='', extraInfo=None, runtimeInfo=None, originPath=None, savePickle=True, saveWideText=True, dataFileName='', autoLog=True, appendFiles=False): """ :parameters: name : a string or unicode As a useful identifier later version : usually a string (e.g. '1.1.0') To keep track of which version of the experiment was run extraInfo : a dictionary Containing useful information about this run (e.g. {'participant':'jwp','gender':'m','orientation':90} ) runtimeInfo : :class:`psychopy.info.RunTimeInfo` Containing information about the system as detected at runtime originPath : string or unicode The path and filename of the originating script/experiment If not provided this will be determined as the path of the calling script. dataFileName : string This is defined in advance and the file will be saved at any point that the handler is removed or discarded (unless .abort() had been called in advance). The handler will attempt to populate the file even in the event of a (not too serious) crash! savePickle : True (default) or False saveWideText : True (default) or False autoLog : True (default) or False """ self.loops = [] self.loopsUnfinished = [] self.name = name self.version = version self.runtimeInfo = runtimeInfo if extraInfo is None: self.extraInfo = {} else: self.extraInfo = extraInfo self.originPath = originPath self.savePickle = savePickle self.saveWideText = saveWideText self.dataFileName = dataFileName self.thisEntry = {} self.entries = [] # chronological list of entries self._paramNamesSoFar = [] self.dataNames = [] # names of all the data (eg. resp.keys) self.autoLog = autoLog self.appendFiles = appendFiles if dataFileName in ['', None]: logging.warning('ExperimentHandler created with no dataFileName' ' parameter. No data will be saved in the event ' 'of a crash') else: # fail now if we fail at all! checkValidFilePath(dataFileName, makeValid=True) atexit.register(self.close)
audioDriver = None for thisLibName in prefs.general['audioLib']: try: if thisLibName == 'pyo': import pyo havePyo = True elif thisLibName == 'pygame': import pygame from pygame import mixer, sndarray else: raise ValueError( "Audio lib options are currently only 'pyo' or 'pygame', not '%s'" % thisLibName) except: logging.warning('%s audio lib was requested but not loaded: %s' % (thisLibName, sys.exc_info()[1])) continue #to try next audio lib #if we got this far we were sucessful in loading the lib audioLib = thisLibName break if audioLib == None: logging.warning( 'No audio lib could be loaded. Sounds will not be available.') class _SoundBase: """Create a sound object, from one of many ways. """ def __init__(self, value="C",
def __init__(self, win, tex="sin", mask="none", units=None, anchor="center", pos=(0.0, 0.0), size=None, sf=None, ori=0.0, phase=(0.0, 0.0), texRes=128, rgb=None, dkl=None, lms=None, color=(1.0, 1.0, 1.0), colorSpace='rgb', contrast=1.0, opacity=None, depth=0, rgbPedestal=(0.0, 0.0, 0.0), interpolate=False, blendmode='avg', name=None, autoLog=None, autoDraw=False, maskParams=None): """ """ # Empty docstring. All doc is in attributes # what local vars are defined (these are the init params) for use by # __repr__ self._initParams = dir() for unecess in ['self', 'rgb', 'dkl', 'lms']: self._initParams.remove(unecess) # initialise parent class super(GratingStim, self).__init__(win, units=units, name=name, autoLog=False) # UGLY HACK: Some parameters depend on each other for processing. # They are set "superficially" here. # TO DO: postpone calls to _createTexture, setColor and # _calcCyclesPerStim whin initiating stimulus self.__dict__['contrast'] = 1 self.__dict__['size'] = 1 self.__dict__['sf'] = 1 self.__dict__['tex'] = tex self.__dict__['maskParams'] = maskParams # initialise textures and masks for stimulus self._texID = GL.GLuint() GL.glGenTextures(1, ctypes.byref(self._texID)) self._maskID = GL.GLuint() GL.glGenTextures(1, ctypes.byref(self._maskID)) self.__dict__['texRes'] = texRes # must be power of 2 self.interpolate = interpolate # NB Pedestal isn't currently being used during rendering - this is a # place-holder self.rgbPedestal = val2array(rgbPedestal, False, length=3) # No need to invoke decorator for color updating. It is done just # below. self.colorSpace = colorSpace self.color = color if rgb != None: logging.warning("Use of rgb arguments to stimuli are deprecated." " Please use color and colorSpace args instead") self.color = Color(rgb, 'rgb') elif dkl != None: logging.warning("Use of dkl arguments to stimuli are deprecated." " Please use color and colorSpace args instead") self.color = Color(dkl, 'dkl') elif lms != None: logging.warning("Use of lms arguments to stimuli are deprecated." " Please use color and colorSpace args instead") self.color = Color(lms, 'lms') # set other parameters self.ori = float(ori) self.phase = val2array(phase, False) self._origSize = None # updated if an image texture is loaded self._requestedSize = size self.size = size self.sf = val2array(sf) self.pos = val2array(pos, False, False) self.depth = depth self.anchor = anchor self.tex = tex self.mask = mask self.contrast = float(contrast) self.opacity = opacity self.autoLog = autoLog self.autoDraw = autoDraw self.blendmode = blendmode # fix scaling to window coords self._calcCyclesPerStim() # generate a displaylist ID self._listID = GL.glGenLists(1) # JRG: doing self._updateList() here means MRO issues for RadialStim, # which inherits from GratingStim but has its own _updateList code. # So don't want to do the update here (= ALSO the init of RadialStim). # Could potentially define a BaseGrating class without # updateListShaders code, and have GratingStim and RadialStim # inherit from it and add their own _updateList stuff. # Seems unnecessary. Instead, simply defer the update to the # first .draw(), should be fast: # self._updateList() # ie refresh display list self._needUpdate = True # set autoLog now that params have been initialised wantLog = autoLog is None and self.win.autoLog self.__dict__['autoLog'] = autoLog or wantLog if self.autoLog: logging.exp("Created {} = {}".format(self.name, self))
def getKeys(keyList=None, modifiers=False, timeStamped=False): """Returns a list of keys that were pressed. :Parameters: keyList : **None** or [] Allows the user to specify a set of keys to check for. Only keypresses from this set of keys will be removed from the keyboard buffer. If the keyList is `None`, all keys will be checked and the key buffer will be cleared completely. NB, pygame doesn't return timestamps (they are always 0) modifiers : **False** or True If True will return a list of tuples instead of a list of keynames. Each tuple has (keyname, modifiers). The modifiers are a dict of keyboard modifier flags keyed by the modifier name (eg. 'shift', 'ctrl'). timeStamped : **False**, True, or `Clock` If True will return a list of tuples instead of a list of keynames. Each tuple has (keyname, time). If a `core.Clock` is given then the time will be relative to the `Clock`'s last reset. :Author: - 2003 written by Jon Peirce - 2009 keyList functionality added by Gary Strangman - 2009 timeStamped code provided by Dave Britton - 2016 modifiers code provided by 5AM Solutions """ keys = [] if havePygame and display.get_init(): # see if pygame has anything instead (if it exists) windowSystem = 'pygame' for evts in evt.get(locals.KEYDOWN): # pygame has no keytimes keys.append((pygame.key.name(evts.key), 0)) elif havePyglet: # for each (pyglet) window, dispatch its events before checking event # buffer windowSystem = 'pyglet' for win in _default_display_.get_windows(): try: win.dispatch_events() # pump events on pyglet windows except ValueError as e: # pragma: no cover # Pressing special keys, such as 'volume-up', results in a # ValueError. This appears to be a bug in pyglet, and may be # specific to certain systems and versions of Python. logging.error(u'Failed to handle keypress') global _keyBuffer if len(_keyBuffer) > 0: # then pyglet is running - just use this keys = _keyBuffer # _keyBuffer = [] # DO /NOT/ CLEAR THE KEY BUFFER ENTIRELY elif haveGLFW: windowSystem = 'glfw' # 'poll_events' is called when a window is flipped, all the callbacks # populate the buffer if len(_keyBuffer) > 0: keys = _keyBuffer if keyList is None: _keyBuffer = [] # clear buffer entirely targets = keys # equivalent behavior to getKeys() else: nontargets = [] targets = [] # split keys into keepers and pass-thrus for key in keys: if key[0] in keyList: targets.append(key) else: nontargets.append(key) _keyBuffer = nontargets # save these # now we have a list of tuples called targets # did the user want timestamped tuples or keynames? if modifiers == False and timeStamped == False: keyNames = [k[0] for k in targets] return keyNames elif timeStamped == False: keyNames = [(k[0], modifiers_dict(k[1])) for k in targets] return keyNames elif timeStamped and windowSystem == 'pygame': # provide a warning and set timestamps to be None logging.warning( 'Pygame keyboard events do not support timestamped=True') relTuple = [[ _f for _f in (k[0], modifiers and modifiers_dict(k[1]) or None, None) if _f ] for k in targets] return relTuple elif hasattr(timeStamped, 'getLastResetTime'): # keys were originally time-stamped with # core.monotonicClock._lastResetTime # we need to shift that by the difference between it and # our custom clock _last = timeStamped.getLastResetTime() _clockLast = psychopy.core.monotonicClock.getLastResetTime() timeBaseDiff = _last - _clockLast relTuple = [[ _f for _f in (k[0], modifiers and modifiers_dict(k[1]) or None, k[-1] - timeBaseDiff) if _f ] for k in targets] return relTuple elif timeStamped is True: return [[ _f for _f in (k[0], modifiers and modifiers_dict(k[1]) or None, k[-1]) if _f ] for k in targets] elif isinstance(timeStamped, (float, int, int)): relTuple = [[ _f for _f in (k[0], modifiers and modifiers_dict(k[1]) or None, k[-1] - timeStamped) if _f ] for k in targets] return relTuple else: ## danger - catch anything that gets here because it shouldn't! raise ValueError("We received an unknown combination of params to " "getKeys(): timestamped={}, windowSystem={}, " "modifiers={}".format(timeStamped, windowSystem, modifiers))
def __init__(self, win, contrast=1.0, gamma=None, nEntries=256, mode='bits++', rampType='configFile'): """ :Parameters: contrast : The contrast to be applied to the LUT. See :func:`BitsPlusPlus.setLUT` and :func:`BitsPlusPlus.setContrast` for flexibility on setting just a section of the LUT to a different value gamma : The value used to correct the gamma in the LUT nEntries : 256 [DEPRECATED feature] mode : 'bits++' (or 'mono++' or 'color++') Note that, unlike the Bits#, this only affects the way the window is rendered, it does not switch the state of the Bits++ device itself (because unlike the Bits# have no way to communicate with it). The mono++ and color++ are only supported in PsychoPy 1.82.00 onwards. Even then they suffer from not having gamma correction applied on Bits++ (unlike Bits# which can apply a gamma table in the device hardware). rampType : 'configFile', None or an integer if 'configFile' then we'll look for a valid config in the userPrefs folder if an integer then this will be used during win.setGamma(rampType=rampType): """ self.win = win self.contrast = contrast self.nEntries = nEntries self.mode = mode # used to allow setting via USB which was 'slow': self.method = 'fast' # Bits++ doesn't do its own correction so we need to: self.gammaCorrect = 'software' # import pyglet.GL late so that we can import bits.py without it # initially global GL, visual from psychopy import visual import pyglet.gl as GL if self.gammaCorrect == 'software': if gamma is None: # inherit from window: self.gamma = win.gamma elif len(gamma) > 2: # [Lum,R,G,B] or [R,G,B] self.gamma = gamma[-3:] else: self.gamma = [gamma, gamma, gamma] if init(): setVideoMode(NOGAMMACORRECT | VIDEOENCODEDCOMMS) self.initialised = True logging.debug('Found and initialised Bits++') else: self.initialised = False logging.warning("Couldn't initialise Bits++") # do the processing self._HEADandLUT = np.zeros((524, 1, 3), np.uint8) # R: valsR = (36, 63, 8, 211, 3, 112, 56, 34, 0, 0, 0, 0) self._HEADandLUT[:12, :, 0] = np.asarray(valsR).reshape([12, 1]) # G: valsG = (106, 136, 19, 25, 115, 68, 41, 159, 0, 0, 0, 0) self._HEADandLUT[:12, :, 1] = np.asarray(valsG).reshape([12, 1]) # B: valsB = (133, 163, 138, 46, 164, 9, 49, 208, 0, 0, 0, 0) self._HEADandLUT[:12, :, 2] = np.asarray(valsB).reshape([12, 1]) self.LUT = np.zeros((256, 3), 'd') # just a place holder self.setLUT() # this will set self.LUT and update self._LUTandHEAD self._setupShaders() # replace window methods with our custom ones self.win._prepareFBOrender = self._prepareFBOrender self.win._finishFBOrender = self._finishFBOrender self.win._afterFBOrender = self._afterFBOrender # set gamma of the window to the identity LUT if rampType == 'configFile': # now check that we have a valid configuration of the box self.config = Config(self) # check we matche the prev config for our graphics card etc ok = False # until we find otherwise ok = self.config.quickCheck() if ok: self.win.gammaRamp = self.config.identityLUT else: rampType = None if not rampType == 'configFile': # 'this must NOT be an `else` from the above `if` because can be # overidden possibly we were given a numerical rampType (as in # the :func:`psychopy.gamma.setGamma()`) self.win.winHandle.setGamma(self.win.winHandle, rampType=rampType)
def __init__(self, win, units='', lineWidth=1.5, lineColor=(1.0, 1.0, 1.0), lineColorSpace='rgb', fillColor=None, fillColorSpace='rgb', vertices=((-0.5, 0), (0, +0.5), (+0.5, 0)), closeShape=True, pos=(0, 0), size=1, ori=0.0, opacity=1.0, contrast=1.0, depth=0, interpolate=True, lineRGB=None, fillRGB=None, name=None, autoLog=None, autoDraw=False, color=None, colorSpace=None): """ """ # all doc is in the attributes # what local vars are defined (these are the init params) for use by # __repr__ self._initParams = dir() self._initParams.remove('self') # Initialize inheritance and remove unwanted methods; autoLog is set # later super(BaseShapeStim, self).__init__(win, units=units, name=name, autoLog=False) self.contrast = float(contrast) self.opacity = float(opacity) self.pos = numpy.array(pos, float) self.closeShape = closeShape self.lineWidth = lineWidth self.interpolate = interpolate # Color stuff self.useShaders = False # don't need to combine textures with colors # set color first but then potentially override self.__dict__['colorSpace'] = colorSpace self.__dict__['lineColorSpace'] = lineColorSpace self.__dict__['fillColorSpace'] = fillColorSpace if lineRGB is not None: logging.warning("Use of rgb arguments to stimuli are deprecated." " Please use color and colorSpace args instead") self.setLineColor(lineRGB, colorSpace='rgb', log=None) elif color is not None and lineColor is None: pass # user has set color but not lineColor. Don't override that else: self.setLineColor(lineColor, colorSpace=lineColorSpace, log=None) if fillRGB is not None: logging.warning("Use of rgb arguments to stimuli are deprecated." " Please use color and colorSpace args instead") self.setFillColor(fillRGB, colorSpace='rgb', log=None) elif color is not None and fillColor is None: pass # user has set color but not fillColor. Don't override that else: self.setFillColor(fillColor, colorSpace=fillColorSpace, log=None) # if the fillColor and lineColor are not set but color is # then the user probably wants color applied to both if (lineColor is (1.0, 1.0, 1.0) # check if exactly as the default arg and fillColor is None and color is not None): self.color = color else: self.fillColor = fillColor self.lineColor = lineColor # Other stuff self.depth = depth self.ori = numpy.array(ori, float) self.size = numpy.array([0.0, 0.0]) + size # make sure that it's 2D if vertices != (): # flag for when super-init'ing a ShapeStim self.vertices = vertices # call attributeSetter self.autoDraw = autoDraw # call attributeSetter # set autoLog now that params have been initialised wantLog = autoLog is None and self.win.autoLog self.__dict__['autoLog'] = autoLog or wantLog if self.autoLog: logging.exp("Created %s = %s" % (self.name, str(self)))
def setLUT(self, newLUT=None, gammaCorrect=True, LUTrange=1.0): """Sets the LUT to a specific range of values in 'bits++' mode only Note that, if you leave gammaCorrect=True then any LUT values you supply will automatically be gamma corrected. The LUT will take effect on the next `Window.flip()` **Examples:** ``bitsBox.setLUT()`` builds a LUT using bitsBox.contrast and bitsBox.gamma ``bitsBox.setLUT(newLUT=some256x1array)`` (NB array should be float 0.0:1.0) Builds a luminance LUT using newLUT for each gun (actually array can be 256x1 or 1x256) ``bitsBox.setLUT(newLUT=some256x3array)`` (NB array should be float 0.0:1.0) Allows you to use a different LUT on each gun (NB by using BitsBox.setContr() and BitsBox.setGamma() users may not need this function) """ # choose endpoints LUTrange = np.asarray(LUTrange) if LUTrange.size == 1: startII = int(round((0.5 - LUTrange / 2.0) * 255.0)) # +1 because python ranges exclude last value: endII = int(round((0.5 + LUTrange / 2.0) * 255.0)) + 1 elif LUTrange.size == 2: multiplier = 1.0 if LUTrange[1] <= 1: multiplier = 255.0 startII = int(round(LUTrange[0] * multiplier)) # +1 because python ranges exclude last value: endII = int(round(LUTrange[1] * multiplier)) + 1 stepLength = 2.0 / (endII - startII - 1) if newLUT is None: # create a LUT from scratch (based on contrast and gamma) # rampStep = 2.0/(self.nEntries-1) ramp = np.arange(-1.0, 1.0 + stepLength, stepLength) ramp = (ramp * self.contrast + 1.0) / 2.0 # self.LUT will be stored as 0.0:1.0 (gamma-corrected) self.LUT[startII:endII, 0] = copy(ramp) self.LUT[startII:endII, 1] = copy(ramp) self.LUT[startII:endII, 2] = copy(ramp) elif type(newLUT) in [float, int] or (newLUT.shape == ()): self.LUT[startII:endII, 0] = newLUT self.LUT[startII:endII, 1] = newLUT self.LUT[startII:endII, 2] = newLUT elif len(newLUT.shape) == 1: # one dimensional LUT # replicate LUT to other channels, check range is 0:1 if newLUT > 1.0: logging.warning('newLUT should be float in range 0.0:1.0') self.LUT[startII:endII, 0] = copy(newLUT.flat) self.LUT[startII:endII, 1] = copy(newLUT.flat) self.LUT[startII:endII, 2] = copy(newLUT.flat) elif len(newLUT.shape) == 2: # one dimensional LUT # use LUT as is, check range is 0:1 if max(max(newLUT)) > 1.0: raise AttributeError('newLUT should be float in range 0.0:1.0') self.LUT[startII:endII, :] = newLUT else: logging.warning('newLUT can be None, nx1 or nx3') # do gamma correction if necessary if self.gammaCorrect == 'software': gamma = self.gamma try: lin = self.win.monitor.linearizeLums self.LUT[startII:endII, :] = lin(self.LUT[startII:endII, :], overrideGamma=gamma) except AttributeError: try: lin = self.win.monitor.lineariseLums self.LUT[startII:endII, :] = lin( self.LUT[startII:endII, :], overrideGamma=gamma) except AttributeError: pass # update the bits++ box with new LUT # get bits into correct order, shape and add to header # go from ubyte to uint16 ramp16 = (self.LUT * (2**16 - 1)).astype(np.uint16) ramp16 = np.reshape(ramp16, (256, 1, 3)) # set most significant bits self._HEADandLUT[12::2, :, :] = (ramp16[:, :, :] >> 8).astype(np.uint8) # set least significant bits self._HEADandLUT[13::2, :, :] = (ramp16[:, :, :] & 255).astype( np.uint8) self._HEADandLUTstr = self._HEADandLUT.tostring()
def compareTextFiles(pathToActual, pathToCorrect, delim=None, encoding='utf-8-sig', tolerance=None): """Compare the text of two files, ignoring EOL differences, and save a copy if they differ State a tolerance, or percentage of errors allowed, to account for differences in version numbers, datetime, etc """ if not os.path.isfile(pathToCorrect): logging.warning( 'There was no comparison ("correct") file available, for path "{pathToActual}"\n' '\t\t\tSaving current file as the comparison: {pathToCorrect}'. format(pathToActual=pathToActual, pathToCorrect=pathToCorrect)) shutil.copyfile(pathToActual, pathToCorrect) raise IOError( "File not found" ) # deliberately raise an error to see the warning message, but also to create file allowLines = 0 nLinesMatch = True if delim is None: if pathToCorrect.endswith('.csv'): delim = ',' elif pathToCorrect.endswith(('.dlm', '.tsv')): delim = '\t' try: # we have the necessary file with io.open(pathToActual, 'r', encoding='utf-8-sig', newline=None) as f: txtActual = f.readlines() with io.open(pathToCorrect, 'r', encoding='utf-8-sig', newline=None) as f: txtCorrect = f.readlines() if tolerance is not None: # Set number of lines allowed to fail allowLines = round((tolerance * len(txtCorrect)) / 100, 0) # Check number of lines per document for equality nLinesMatch = len(txtActual) == len(txtCorrect) assert nLinesMatch errLines = [] for lineN in range(len(txtActual)): if delim is None: lineActual = txtActual[lineN] lineCorrect = txtCorrect[lineN] # just compare the entire line if not lineActual == lineCorrect: errLines.append({ 'actual': lineActual, 'correct': lineCorrect }) assert len(errLines) <= allowLines else: # word by word instead lineActual = txtActual[lineN].split(delim) lineCorrect = txtCorrect[lineN].split(delim) for wordN in range(len(lineActual)): wordActual = lineActual[wordN] wordCorrect = lineCorrect[wordN] try: wordActual = float(wordActual.lstrip('"[').strip(']"')) wordCorrect = float( wordCorrect.lstrip('"[').strip(']"')) # its not a whole well-formed list because .split(delim) isFloat = True except Exception: #stick with simple text if not a float value isFloat = False pass if isFloat: #to a default of 8 dp? assert np.allclose(wordActual,wordCorrect), "Numeric values at (%i,%i) differ: %f != %f " \ %(lineN, wordN, wordActual, wordCorrect) else: if wordActual != wordCorrect: print('actual:') print(repr(txtActual[lineN])) print(lineActual) print('expected:') print(repr(txtCorrect[lineN])) print(lineCorrect) assert wordActual==wordCorrect, "Values at (%i,%i) differ: %s != %s " \ %(lineN, wordN, repr(wordActual), repr(wordCorrect)) except AssertionError as err: pathToLocal, ext = os.path.splitext(pathToCorrect) pathToLocal = pathToLocal + '_local' + ext # Set assertion type if not nLinesMatch: # Fail if number of lines not equal msg = "{} has the wrong number of lines".format(pathToActual) elif len(errLines) < allowLines: # Fail if tolerance reached msg = 'Number of differences in {failed} exceeds the {tol}% tolerance'.format( failed=pathToActual, tol=tolerance or 0) else: shutil.copyfile(pathToActual, pathToLocal) msg = "txtActual != txtCorr: Saving local copy to {}".format( pathToLocal) logging.error(msg) raise AssertionError(err)
def __init__(self, win, text="Hello World", font="", pos=(0.0, 0.0), depth=0, rgb=None, color=(1.0, 1.0, 1.0), colorSpace='rgb', opacity=1.0, contrast=1.0, units="", ori=0.0, height=None, antialias=True, bold=False, italic=False, alignHoriz=None, alignVert=None, alignText='center', anchorHoriz='center', anchorVert='center', fontFiles=(), wrapWidth=None, flipHoriz=False, flipVert=False, languageStyle='LTR', name=None, autoLog=None): """ **Performance OBS:** in general, TextStim is slower than many other visual stimuli, i.e. it takes longer to change some attributes. In general, it's the attributes that affect the shapes of the letters: ``text``, ``height``, ``font``, ``bold`` etc. These make the next .draw() slower because that sets the text again. You can make the draw() quick by calling re-setting the text (``myTextStim.text = myTextStim.text``) when you've changed the parameters. In general, other attributes which merely affect the presentation of unchanged shapes are as fast as usual. This includes ``pos``, ``opacity`` etc. The following attribute can only be set at initialization (see further down for a list of attributes which can be changed after initialization): **languageStyle** Apply settings to correctly display content from some languages that are written right-to-left. Currently there are three (case- insensitive) values for this parameter: - ``'LTR'`` is the default, for typical left-to-right, Latin-style languages. - ``'RTL'`` will correctly display text in right-to-left languages such as Hebrew. By applying the bidirectional algorithm, it allows mixing portions of left-to-right content (such as numbers or Latin script) within the string. - ``'Arabic'`` applies the bidirectional algorithm but additionally will _reshape_ Arabic characters so they appear in the cursive, linked form that depends on neighbouring characters, rather than in their isolated form. May also be applied in other scripts, such as Farsi or Urdu, that use Arabic-style alphabets. :Parameters: """ # what local vars are defined (these are the init params) for use by # __repr__ self._initParams = dir() self._initParams.remove('self') """ October 2018: In place to remove the deprecation warning for pyglet.font.Text. Temporary fix until pyglet.text.Label use is identical to pyglet.font.Text. """ warnings.filterwarnings(message='.*text.Label*', action='ignore') super(TextStim, self).__init__(win, units=units, name=name, autoLog=False) if win.blendMode == 'add': logging.warning("Pyglet text does not honor the Window setting " "`blendMode='add'` so 'avg' will be used for the " "text (but objects drawn after can be added)") self._needUpdate = True self._needVertexUpdate = True # use shaders if available by default, this is a good thing self.__dict__['useShaders'] = win._haveShaders self.__dict__['antialias'] = antialias self.__dict__['font'] = font self.__dict__['bold'] = bold self.__dict__['italic'] = italic # NB just a placeholder - real value set below self.__dict__['text'] = '' self.__dict__['depth'] = depth self.__dict__['ori'] = ori self.__dict__['flipHoriz'] = flipHoriz self.__dict__['flipVert'] = flipVert self.__dict__['languageStyle'] = languageStyle self._pygletTextObj = None self.__dict__['pos'] = numpy.array(pos, float) # deprecated attributes if alignVert: self.__dict__['alignVert'] = alignVert logging.warning("TextStim.alignVert is deprecated. Use the " "anchorVert attribute instead") # for compatibility, alignText was historically 'left' anchorVert = alignHoriz if alignHoriz: self.__dict__['alignHoriz'] = alignHoriz logging.warning("TextStim.alignHoriz is deprecated. Use alignText " "and anchorHoriz attributes instead") # for compatibility, alignText was historically 'left' alignText, anchorHoriz = alignHoriz, alignHoriz # alignment and anchors self.alignText = alignText self.anchorHoriz = anchorHoriz self.anchorVert = anchorVert # generate the texture and list holders self._listID = GL.glGenLists(1) # pygame text needs a surface to render to: if not self.win.winType in ["pyglet", "glfw"]: self._texID = GL.GLuint() GL.glGenTextures(1, ctypes.byref(self._texID)) # Color stuff self.colorSpace = colorSpace if rgb != None: msg = ("Use of rgb arguments to stimuli are deprecated. Please " "use color and colorSpace args instead") logging.warning(msg) self.setColor(rgb, colorSpace='rgb', log=False) else: self.setColor(color, log=False) self.__dict__['fontFiles'] = [] self.fontFiles = list(fontFiles) # calls attributeSetter self.setHeight(height, log=False) # calls setFont() at some point # calls attributeSetter without log setAttribute(self, 'wrapWidth', wrapWidth, log=False) self.__dict__['opacity'] = float(opacity) self.__dict__['contrast'] = float(contrast) # self.width and self._fontHeightPix get set with text and # calcSizeRendered is called self.setText(text, log=False) self._needUpdate = True # set autoLog now that params have been initialised wantLog = autoLog is None and self.win.autoLog self.__dict__['autoLog'] = autoLog or wantLog if self.autoLog: logging.exp("Created %s = %s" % (self.name, str(self)))
def __init__(self, win=None, portName=None, mode='', checkConfigLevel=1, gammaCorrect='hardware', gamma=None, noComms=False): """ :Parameters: win : a PsychoPy :class:`~psychopy.visual.Window` object, required portName : the (virtual) serial port to which the device is connected. If None then PsychoPy will search available serial ports and test communication (on OSX, the first match of `/dev/tty.usbmodemfa*` will be used and on linux `/dev/ttyS0` will be used mode : 'bits++', 'color++', 'mono++', 'status' checkConfigLevel : integer Allows you to specify how much checking of the device is done to ensure a valid identity look-up table. If you specify one level and it fails then the check will be escalated to the next level (e.g. if we check level 1 and find that it fails we try to find a new LUT): - 0 don't check at all - 1 check that the graphics driver and OS version haven't changed since last LUT calibration - 2 check that the current LUT calibration still provides identity (requires switch to status mode) - 3 search for a new identity look-up table (requires switch to status mode) gammaCorrect : string governing how gamma correction is performed 'hardware': use the gamma correction file stored on the hardware 'FBO': gamma correct using shaders when rendering the FBO to back buffer 'bitsMode': in bits++ mode there is a user-controlled LUT that we can use for gamma correction noComms : bool If True then don't try to communicate with the device at all (passive mode). This can be useful if you want to debug the system without actually having a Bits# connected. """ # import pyglet.GL late so that we can import bits.py without it # initially global GL, visual from psychopy import visual import pyglet.gl as GL if noComms: self.noComms = True self.OK = True self.sendMessage = self._nullSendMessage self.getResponse = self._nullGetResponse else: self.noComms = False # look for device on valid serial ports # parity="N", # 'N'one, 'E'ven, 'O'dd, 'M'ask, serialdevice.SerialDevice.__init__(self, port=portName, baudrate=19200, byteSize=8, stopBits=1, parity="N", eol='\n', maxAttempts=1, pauseDuration=0.1, checkAwake=True) if not self.OK: return # the following are used by bits++ mode self._HEADandLUT = np.zeros((524, 1, 3), np.uint8) # R valsR = (36, 63, 8, 211, 3, 112, 56, 34, 0, 0, 0, 0) self._HEADandLUT[:12, :, 0] = np.asarray(valsR).reshape([12, 1]) # G valsG = (106, 136, 19, 25, 115, 68, 41, 159, 0, 0, 0, 0) self._HEADandLUT[:12, :, 1] = np.asarray(valsG).reshape([12, 1]) # B valsB = (133, 163, 138, 46, 164, 9, 49, 208, 0, 0, 0, 0) self._HEADandLUT[:12, :, 2] = np.asarray(valsB).reshape([12, 1]) self.LUT = np.zeros((256, 3), 'd') # just a place holder # replace window methods with our custom ones self.win = win self.win._prepareFBOrender = self._prepareFBOrender self.win._finishFBOrender = self._finishFBOrender self.win._afterFBOrender = self._afterFBOrender # Bits++ doesn't do its own correction so we need to self.gammaCorrect = gammaCorrect self.gamma = gamma # we have a confirmed connection. Now check details about device and # system if not hasattr(self, 'info'): self.info = self.getInfo() self.config = None self.mode = mode if self.win is not None: if not hasattr(self.win, '_prepareFBOrender'): logging.error("BitsSharp was given an object as win " "argument but this is not a visual.Window") self.win._prepareFBOrender = self._prepareFBOrender self.win._finishFBOrender = self._finishFBOrender self._setupShaders() # now check that we have a valid configuration of the box if checkConfigLevel: ok = self.checkConfig(level=checkConfigLevel) else: self.win.gammaRamp = self.config.identityLUT else: self.config = None # makes no sense if we have a window? logging.warning("%s was not given any PsychoPy win" % (self))
def font(self, font): """String. Set the font to be used for text rendering. font should be a string specifying the name of the font (in system resources). """ self.__dict__['font'] = None # until we find one if self.win.winType in ["pyglet", "glfw"]: self._font = pyglet.font.load(font, int(self._heightPix), dpi=72, italic=self.italic, bold=self.bold) self.__dict__['font'] = font else: if font is None or len(font) == 0: self.__dict__['font'] = pygame.font.get_default_font() elif font in pygame.font.get_fonts(): self.__dict__['font'] = font elif type(font) == str: # try to find a xxx.ttf file for it # check for possible matching filenames fontFilenames = glob.glob(font + '*') if len(fontFilenames) > 0: for thisFont in fontFilenames: if thisFont[-4:] in ['.TTF', '.ttf']: # take the first match self.__dict__['font'] = thisFont break # stop at the first one we find # trhen check if we were successful if self.font is None and font != "": # we didn't find a ttf filename msg = ("Found %s but it doesn't end .ttf. " "Using default font.") logging.warning(msg % fontFilenames[0]) self.__dict__['font'] = pygame.font.get_default_font() if self.font is not None and os.path.isfile(self.font): self._font = pygame.font.Font(self.font, int(self._heightPix), italic=self.italic, bold=self.bold) else: try: self._font = pygame.font.SysFont(self.font, int(self._heightPix), italic=self.italic, bold=self.bold) self.__dict__['font'] = font logging.info('using sysFont ' + str(font)) except Exception: self.__dict__['font'] = pygame.font.get_default_font() msg = ("Couldn't find font %s on the system. Using %s " "instead! Font names should be written as " "concatenated names all in lower case.\ne.g. " "'arial', 'monotypecorsiva', 'rockwellextra', ...") logging.error(msg % (font, self.font)) self._font = pygame.font.SysFont(self.font, int(self._heightPix), italic=self.italic, bold=self.bold) # re-render text after a font change self._needSetText = True
def _setWindowInfo(self, win, verbose=False, refreshTest='grating', usingTempWin=True): """find and store info about the window: refresh rate, configuration info """ if refreshTest in ['grating', True]: msPFavg, msPFstd, msPFmd6 = visual.getMsPerFrame( win, nFrames=120, showVisual=bool(refreshTest == 'grating')) self['windowRefreshTimeAvg_ms'] = msPFavg self['windowRefreshTimeMedian_ms'] = msPFmd6 self['windowRefreshTimeSD_ms'] = msPFstd if usingTempWin: return # These 'configuration lists' control what attributes are reported. # All desired attributes/properties need a legal internal name, e.g., win.winType. # If an attr is callable, its gets called with no arguments, e.g., win.monitor.getWidth() winAttrList = [ 'winType', '_isFullScr', 'units', 'monitor', 'pos', 'screen', 'rgb', 'size' ] winAttrListVerbose = [ 'allowGUI', 'useNativeGamma', 'recordFrameIntervals', 'waitBlanking', '_haveShaders', '_refreshThreshold' ] if verbose: winAttrList += winAttrListVerbose monAttrList = ['name', 'getDistance', 'getWidth', 'currentCalibName'] monAttrListVerbose = [ 'getGammaGrid', 'getLinearizeMethod', '_gammaInterpolator', '_gammaInterpolator2' ] if verbose: monAttrList += monAttrListVerbose if 'monitor' in winAttrList: # replace 'monitor' with all desired monitor.<attribute> i = winAttrList.index( 'monitor' ) # retain list-position info, put monitor stuff there del winAttrList[i] for monAttr in monAttrList: winAttrList.insert(i, 'monitor.' + monAttr) i += 1 for winAttr in winAttrList: try: attrValue = eval('win.' + winAttr) except AttributeError: logging.warning( 'AttributeError in RuntimeInfo._setWindowInfo(): Window instance has no attribute', winAttr) continue if hasattr(attrValue, '__call__'): try: a = attrValue() attrValue = a except: print('Warning: could not get a value from win.' + winAttr + '() (expects arguments?)') continue while winAttr[0] == '_': winAttr = winAttr[1:] winAttr = winAttr[0].capitalize() + winAttr[1:] winAttr = winAttr.replace('Monitor._', 'Monitor.') if winAttr in ['Pos', 'Size']: winAttr += '_pix' if winAttr in ['Monitor.getWidth', 'Monitor.getDistance']: winAttr += '_cm' if winAttr in ['RefreshThreshold']: winAttr += '_sec' self['window' + winAttr] = attrValue