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) raise nose.plugins.skip.SkipTest("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 logging.debug('PsychoPyTests: RMS=%.3g at threshold=%3.g' % (rms, crit)) if rms>=crit: filenameLocal = fileName.replace('.png','_local.png') frame.save(filenameLocal, optimize=1) logging.debug('PsychoPyTests: Saving local copy into %s' % filenameLocal) raise AssertionError("RMS=%.3g at threshold=%.3g. Local copy in %s" % (rms, crit, filenameLocal)) assert rms<crit # must never fail here
def addFontFiles(self,font_paths,monospace_only=True): """ Add a list of font files to the FontManger font search space. Each element of the font_paths list must be a valid path including the font file name. Relative paths can be used, with the current working directory being the origin. If monospace_only is True, each font file will only be added if it is a monospace font (as only monospace fonts are currently supported by TextBox). Adding fonts to the FontManager is not persistant across runs of the script, so any extra font paths need to be added each time the script starts. """ fi=None for fp in font_paths: if os.path.isfile(fp) and os.path.exists(fp): try: face=Face(fp) if monospace_only: if face.is_fixed_width: fi=self._createFontInfo(fp,face) else: fi=self._createFontInfo(fp,face) except Exception, e: logging.debug('Error during FontManager.updateFontInfo(): %s\nFont File: %s'%(str(e),fp))
def showAbout(self, event): logging.debug('PsychoPyApp: Showing about dlg') licFile = open(os.path.join(self.prefs.paths['psychopy'],'LICENSE.txt')) license = licFile.read() licFile.close() msg = """For stimulus generation and experimental control in python. PsychoPy depends on your feedback. If something doesn't work then let me/us know at [email protected]""" info = wx.AboutDialogInfo() info.SetName('PsychoPy') info.SetVersion('v'+psychopy.__version__) info.SetDescription(msg) info.SetCopyright('(C) 2002-2012 Jonathan Peirce') info.SetWebSite('http://www.psychopy.org') info.SetLicence(license) info.AddDeveloper('Jonathan Peirce') info.AddDeveloper('Yaroslav Halchenko') info.AddDeveloper('Jeremy Gray') info.AddDocWriter('Jonathan Peirce') info.AddDocWriter('Rebecca Sharman') wx.AboutBox(info)
def show(self): # Initially copied from psychopy.gui (which is copyright # 2011 Jonathan Peirce and available under GPL v3). buttons = wx.BoxSizer(wx.HORIZONTAL) OK = wx.Button(self, wx.ID_OK, " OK ") OK.SetDefault() buttons.Add(OK) self.sizer.Add(buttons,1,flag=wx.ALIGN_RIGHT|wx.ALIGN_BOTTOM,border=5) self.SetSizerAndFit(self.sizer) self.ShowModal() self.data=[] #get data from input fields for n in range(len(self.inputFields)): thisName = self.inputFieldNames[n] thisVal = self.inputFields[n].GetValue() thisType= self.inputFieldTypes[n] #try to handle different types of input from strings debug("%s: %s" %(self.inputFieldNames[n], unicode(thisVal))) if thisType in [tuple,list,float,int]: #probably a tuple or list exec("self.data.append("+thisVal+")")#evaluate it elif thisType==numpy.ndarray: exec("self.data.append(numpy.array("+thisVal+"))") elif thisType in [str,unicode,bool]: self.data.append(thisVal) else: warning('unknown type:'+self.inputFieldNames[n]) self.data.append(thisVal) self.OK=True self.Destroy()
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 __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 showAbout(self, event): logging.debug('PsychoPyApp: Showing about dlg') licFile = open(os.path.join(self.prefs.paths['psychopy'],'LICENSE.txt')) license = licFile.read() licFile.close() msg = """For stimulus generation and experimental control in python. PsychoPy depends on your feedback. If something doesn't work then let me/us know at [email protected]""".replace(' ', '') info = wx.AboutDialogInfo() #info.SetIcon(wx.Icon(os.path.join(self.prefs.paths['resources'], 'psychopy.png'),wx.BITMAP_TYPE_PNG)) info.SetName('PsychoPy') info.SetVersion('v'+psychopy.__version__) info.SetDescription(msg) info.SetCopyright('(C) 2002-2014 Jonathan Peirce') info.SetWebSite('http://www.psychopy.org') info.SetLicence(license) info.AddDeveloper('Jonathan Peirce') info.AddDeveloper('Jeremy Gray') info.AddDeveloper('Sol Simpson') info.AddDeveloper(u'Jonas Lindel\xF8v') info.AddDeveloper('Yaroslav Halchenko') info.AddDeveloper('Erik Kastman') info.AddDeveloper('Michael MacAskill') info.AddDocWriter('Jonathan Peirce') info.AddDocWriter('Jeremy Gray') info.AddDocWriter('Rebecca Sharman') if not self.testMode: wx.AboutBox(info)
def __init__(self): """Class to detect and report `ioLab button box <http://www.iolab.co.uk>`_. The ioLabs library needs to be installed. It is included in the *Standalone* distributions of PsychoPy as of version 1.62.01. Otherwise try "pip install ioLabs" Usage:: from psychopy.hardware import iolab bbox = iolab.ButtonBox() For examples see the demos menu of the PsychoPy Coder or go to the URL above. All times are reported in units of seconds. """ ioLabs.USBBox.__init__(self) logging.debug('init iolabs bbox') self.events = [] self.status = None # helps Builder self._lastReset = 0.0 # time on baseclock at which the bbox clock was reset self._baseclock = core.Clock() # for basetime, not RT time self.resetClock(log=True) # internal clock on the bbox logging.exp('button box resetClock(log=True) took %.4fs' % self._baseclock.getTime()) self.commands.add_callback(REPORT.KEYDN, self._onKeyDown) self.commands.add_callback(REPORT.KEYUP, self._onKeyUp) self.commands.add_callback(REPORT.RTCREP, self._onRtcRep)
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 _checkout(requestedVersion): """Look for a Maj.min.patch requested version, download (fetch) if needed. """ # Check tag of repo if currentTag() == requestedVersion: return requestedVersion # See if the tag already exists in repos if requestedVersion not in _localVersions(forceCheck=True): # Grab new tags msg = _translate("Couldn't find version {} locally. Trying github...") logging.info(msg.format(requestedVersion)) subprocess.check_output('git fetch github'.split()) # is requested here now? forceCheck to refresh cache if requestedVersion not in _localVersions(forceCheck=True): msg = _translate("{} is not currently available.") logging.error(msg.format(requestedVersion)) return '' # Checkout the requested tag cmd = ['git', 'checkout', requestedVersion] out = subprocess.check_output(cmd, stderr=subprocess.STDOUT, cwd=VERSIONSDIR) logging.debug(out) logging.exp('Success: ' + ' '.join(cmd)) return requestedVersion
def doIdleTasks(app=None): global currentTask if currentTask and currentTask['thread'] and \ currentTask['thread'].is_alive(): # is currently tunning in a thread return 0 for taskName in tasks: thisTask = tasks[taskName] thisStatus = tasks[taskName]['status'] if thisStatus == NOT_STARTED: currentTask = thisTask currentTask['tStart'] = time.time() - _t0 currentTask['status'] = STARTED logging.debug('Started {} at {}'.format(taskName, currentTask['tStart'])) _doTask(taskName, app) return 0 # something is in motion elif thisStatus == STARTED: if not currentTask['thread'] \ or not currentTask['thread'].is_alive(): # task finished so take note and pick another currentTask['status'] = FINISHED currentTask['thread'] = None currentTask['tEnd'] = time.time() - _t0 logging.debug('Finished {} at {}'.format(taskName, currentTask['tEnd'])) currentTask = None continue else: return 0 logging.flush() return 1
def sendMessage(self, message, timeout=0.5, DEBUG=False): """Send a command to the photometer and wait an alloted timeout for a response (Timeout should be long for low light measurements) """ if message[-1] != '\n': message += '\n' # append a newline if necess # flush the read buffer first # read as many chars as are in the buffer self.com.read(self.com.inWaiting()) # send the message self.com.write(message) self.com.flush() # time.sleep(0.1) # PR650 gets upset if hurried! # get feedback (within timeout limit) self.com.timeout = timeout logging.debug(message) # send complete message if message in ('d5', 'd5\n'): # we need a spectrum which will have multiple lines return self.com.readlines() else: return self.com.readline()
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 _encrypt_rsa_aes256cbc(datafile, pubkeyPem): """Encrypt a datafile using openssl to do rsa pub-key + aes256cbc. """ logging.debug('_encrypt_rsa_aes256cbc: start') pwd = _onetimePwd(n=65) pwdText = _openTmp() pwdText.write(pwd) try: pwdText.seek(0) # go back to start of tmp file # RSA-PUBKEY-encrypt the password, to file: pwdFileRsa = PWD_EXT +'_file' + RSA_EXT cmdRSA = [OPENSSL, 'rsautl', '-in', pwdText.name, '-out', pwdFileRsa, '-inkey', pubkeyPem, '-keyform', 'PEM', '-pubin', RSA_PADDING, '-encrypt'] so = _shellCall(cmdRSA) # AES-256-CBC encrypt datafile, using the one pwd: dataFileEnc = os.path.abspath(datafile) + AES_EXT pwdFileRsaNew = _uniqFileName(dataFileEnc + pwdFileRsa.replace('_file', '')) os.rename(pwdFileRsa, pwdFileRsaNew) # rename the pwd_file to match the datafile # here gzip the raw datafile? cmdAES = [OPENSSL, 'enc', '-aes-256-cbc', '-a', '-salt', '-in', datafile, '-out', dataFileEnc, '-pass', 'file:' + pwdText.name] so = _shellCall(cmdAES) finally: _wipeClose(pwdText) return os.path.abspath(dataFileEnc), os.path.abspath(pwdFileRsaNew)
def showAbout(self, event): logging.debug("PsychoPyApp: Showing about dlg") licFile = open(os.path.join(self.prefs.paths["psychopy"], "LICENSE.txt")) license = licFile.read() licFile.close() msg = """For stimulus generation and experimental control in python. PsychoPy depends on your feedback. If something doesn't work then let me/us know at [email protected]""" info = wx.AboutDialogInfo() # info.SetIcon(wx.Icon(os.path.join(self.prefs.paths['resources'], 'psychopy.png'),wx.BITMAP_TYPE_PNG)) info.SetName("PsychoPy") info.SetVersion("v" + psychopy.__version__) info.SetDescription(msg) info.SetCopyright("(C) 2002-2012 Jonathan Peirce") info.SetWebSite("http://www.psychopy.org") info.SetLicence(license) info.AddDeveloper("Jonathan Peirce") info.AddDeveloper("Yaroslav Halchenko") info.AddDeveloper("Jeremy Gray") info.AddDeveloper("Erik Kastner") info.AddDeveloper("Michael MacAskill") info.AddDocWriter("Jonathan Peirce") info.AddDocWriter("Jeremy Gray") info.AddDocWriter("Rebecca Sharman") wx.AboutBox(info)
def updateFontInfo(self,monospace_only=True): self._available_font_info.clear() del self.font_family_styles[:] import matplotlib.font_manager as font_manager font_paths=font_manager.findSystemFonts() def createFontInfo(fp,fface): fns=(fface.family_name,fface.style_name) if fns in self.font_family_styles: pass else: self.font_family_styles.append((fface.family_name,fface.style_name)) styles_for_font_dict=self._available_font_info.setdefault(fface.family_name,{}) fonts_for_style=styles_for_font_dict.setdefault(fface.style_name,[]) fi=FontInfo(fp,fface) fonts_for_style.append(fi) for fp in font_paths: if os.path.isfile(fp) and os.path.exists(fp): try: face=Face(fp) if monospace_only: if face.is_fixed_width: createFontInfo(fp,face) else: createFontInfo(fp,face) except Exception, e: logging.debug('Error during FontManager.updateFontInfo(): %s\nFont File: %s'%(str(e),fp))
def _decrypt_rsa_aes256cbc(dataFileEnc, pwdFileRsa, privkeyPem, passphraseFile=None, outFile=''): """Decrypt a file that was encoded by _encrypt_rsa_aes256cbc() """ logging.debug('_decrypt_rsa_aes256cbc: start') pwdText = _openTmp() try: # extract unique password from pwdFileRsa using the private key & passphrase cmdRSA = [OPENSSL, 'rsautl', '-in', pwdFileRsa, '-out', pwdText.name, '-inkey', privkeyPem] if passphraseFile: cmdRSA += ['-passin', 'file:' + passphraseFile] cmdRSA += [RSA_PADDING, '-decrypt'] so, se = _shellCall(cmdRSA, stderr=True) # extract if 'unable to load Private Key' in se: raise PrivateKeyError('unable to load Private Key') elif 'RSA operation error' in se: raise DecryptError('unable to use Private Key, RSA operation error; wrong key?') # decide on name for decrypted file: if outFile: dataDecrypted = outFile else: dataDecrypted = os.path.abspath(dataFileEnc).replace(AES_EXT,'') # decrypt the data using the recovered password: cmdAES = [OPENSSL, 'enc', '-d', '-aes-256-cbc', '-a', '-in', dataFileEnc, '-out', dataDecrypted, '-pass', 'file:'+pwdText.name] so, se = _shellCall(cmdAES, stderr=True) finally: _wipeClose(pwdText) if 'bad decrypt' in se: raise DecryptError('_decrypt_rsa_aes256cbc: openssl bad decrypt') return os.path.abspath(dataDecrypted)
def handleCurrentIndexChanged(new_index): ix = self.inputFields.index(inputBox) self.data[ix] = inputBox.itemData(new_index).toPyObject()[0] logging.debug( "handleCurrentIndexChanged: inputFieldName={0}, " "selected={1}, type: {2}".format( label, self.data[ix], type(self.data[ix])))
def sendMessage(self, message, timeout=0.5, DEBUG=False): """Send a command to the photometer and wait an alloted timeout for a response (Timeout should be long for low light measurements) """ logging.debug("Sending command '%s' to %s" % (message, self.portString)) # send complete message if message[-1] != "\n": message += "\n" # append a newline if necess # flush the read buffer first self.com.read(self.com.inWaiting()) # read as many chars as are in the buffer # send the message for letter in message: self.com.write(letter) # for PR655 have to send individual chars ! :-/ self.com.flush() time.sleep(0.2) # PR655 can get cranky if rushed # get feedback (within timeout) self.com.setTimeout(timeout) if message in ["d5\n", "D5\n"]: # we need a spectrum which will have multiple lines return self.com.readlines() else: return self.com.readline()
def getOriginPathAndFile(self, originPath=None): """Attempts to determine the path of the script that created this data file and returns both the path to that script and its contents. Useful to store the entire experiment with the data. If originPath is provided (e.g. from Builder) then this is used otherwise the calling script is the originPath (fine from a standard python script). """ # self.originPath and self.origin (the contents of the origin file) if originPath == -1: return -1, None # the user wants to avoid storing this elif originPath is None or not os.path.isfile(originPath): try: originPath = inspect.getouterframes( inspect.currentframe())[2][1] if self.autoLog: logging.debug("Using %s as origin file" % originPath) except Exception: if self.autoLog: logging.debug("Failed to find origin file using " "inspect.getouterframes") return '', '' if os.path.isfile(originPath): # do we NOW have a path? origin = codecs.open(originPath, "r", encoding="utf-8").read() else: origin = None return originPath, origin
def _genRsaKeys(pub="pubkey.pem", priv="privkey.pem", pphr=None, bits=2048): """Generate new pub and priv keys, return full path to new files This is intended for testing purposes, not for general use. Definitely need to consider: randomness, bits, file permissions, file location (user home dir?), passphrase creation and usage, passphrase as file generally not so good, can make sense to have no passphrase, .... Well-seeded PRNGs are reasonably good; ideally use hardware RNG gnupg sounds pretty good, cross-platform """ logging.debug("_genRsaKeys: start") # generate priv key: cmdGEN = [OPENSSL, "genrsa", "-out", priv] if pphr: cmdGEN += ["-des3", "-passout", "file:" + pphr] cmdGEN += [str(bits)] so = _shellCall(cmdGEN) # extract pub from priv: cmdGENpub = [OPENSSL, "rsa", "-in", priv, "-pubout", "-out", pub] if pphr: cmdGENpub += ["-passin", "file:" + pphr] so = _shellCall(cmdGENpub) return os.path.abspath(pub), os.path.abspath(priv)
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 _checkoutRequested(requestedVersion): """Look for a tag matching the request, return it if found or return None for the search. """ # Check tag of repo if getCurrentTag() == requestedVersion: # nothing to do! return 1 # See if the tag already exists in repos (no need for internet) cmd = 'git tag'.split() versions = subprocess.check_output(cmd, cwd=VERSIONSDIR) if requestedVersion not in versions: # Grab new tags msg = "Couldn't find version %r locally. Trying github..." logging.info(msg % requestedVersion) cmd = 'git fetch github'.split() out = subprocess.check_output(cmd) # after fetching from github check if it's there now! versions = subprocess.check_output(['git', 'tag'], cwd=VERSIONSDIR) if requestedVersion not in versions: msg = "%r is not a valid version. Please choose one of: %r" logging.error(msg % (requestedVersion, versions.split())) return 0 # Checkout the requested tag cmd = 'git checkout %s' % requestedVersion out = subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT, cwd=VERSIONSDIR) logging.debug(out) return 1
def sendMessage(self, message, timeout=5.0): """Send a command to the photometer and wait an alloted timeout for a response. """ if message[-2:] != '\r\n': message += '\r\n' # append a newline if necess # flush the read buffer first # read as many chars as are in the buffer self.com.read(self.com.inWaiting()) for attemptN in range(self.maxAttempts): # send the message time.sleep(0.1) self.com.write(message) self.com.flush() time.sleep(0.1) # get reply (within timeout limit) self.com.timeout = timeout # send complete message logging.debug('Sent command:' + message[:-2]) retVal = self.com.readline() if len(retVal) > 0: break # we got a reply so can stop trying return retVal
def quit(self, event=None): logging.debug('PsychoPyApp: Quitting...') self.quitting=True #see whether any files need saving for frame in self.allFrames: try:#will fail if the frame has been shut somehow elsewhere ok=frame.checkSave() except: ok=False if not ok: logging.debug('PsychoPyApp: User cancelled shutdown') return#user cancelled quit #save info about current frames for next run if self.coder and len(self.builderFrames)==0: self.prefs.appData['lastFrame']='coder' elif self.coder==None: self.prefs.appData['lastFrame']='builder' else: self.prefs.appData['lastFrame']='both' self.prefs.appData['lastVersion']=self.version #update app data while closing each frame self.prefs.appData['builder']['prevFiles']=[]#start with an empty list to be appended by each frame self.prefs.appData['coder']['prevFiles']=[] for frame in copy.copy(self.allFrames): if frame==None: continue frame.closeFrame(checkSave=False)#should update (but not save) prefs.appData self.prefs.saveAppData()#must do this before destroying the frame? if sys.platform=='darwin': self.menuFrame.Destroy() sys.exit()#really force a quit
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 __del__(self): try: self.endRemoteMode() time.sleep(0.1) self.com.close() logging.debug('Closed PR655 port') except: pass
def sendMessage(self, message): """Send a command to the device (does not wait for a reply or sleep()) """ if not message.endswith(self.eol): message += self.eol #append a newline if necess self.com.write(message) self.com.flush() logging.debug('Sent %s message:' %(self.name) +message.replace(self.eol, ''))#send complete message
def MacOpenFile(self,fileName): logging.debug('PsychoPyApp: Received Mac file dropped event') if fileName.endswith('.py'): if self.coder==None: self.showCoder() self.coder.setCurrentDoc(fileName) elif fileName.endswith('.psyexp'): self.newBuilderFrame(fileName=fileName)
def read(self, timeout=0.1): """Get the current waiting characters from the serial port if there are any """ self._com.setTimeout(timeout) nChars = self._com.inWaiting() raw = self._com.read(nChars) logging.debug("Got BitsSharp reply: %s" %(repr(raw))) return raw
def __init__(self, device=None, sampleRateHz=None, channels=2, streamBufferSecs=2.0, maxRecordingSize=24000, policyWhenFull='warn', audioLatencyMode=None, audioRunMode=0): if not _hasPTB: # fail if PTB is not installed raise ModuleNotFoundError( "Microphone audio capture requires package `psychtoolbox` to " "be installed.") # get information about the selected device devices = Microphone.getDevices() if isinstance(device, AudioDeviceInfo): self._device = device elif isinstance(device, (int, float)): devicesByIndex = {d.deviceIndex: d for d in devices} if device in devicesByIndex: self._device = devicesByIndex[device] else: raise AudioInvalidCaptureDeviceError( 'No suitable audio recording devices found matching index ' '{}.'.format(device)) else: # get default device, first enumerated usually if not devices: raise AudioInvalidCaptureDeviceError( 'No suitable audio recording devices found on this system. ' 'Check connections and try again.') self._device = devices[0] # use first logging.info('Using audio device #{} ({}) for audio capture'.format( self._device.deviceIndex, self._device.deviceName)) # error if specified device is not suitable for capture if not self._device.isCapture: raise AudioInvalidCaptureDeviceError( 'Specified audio device not suitable for audio recording. ' 'Has no input channels.') # get the sample rate self._sampleRateHz = \ self._device.defaultSampleRate if sampleRateHz is None else int( sampleRateHz) logging.debug('Set stream sample rate to {} Hz'.format( self._sampleRateHz)) # set the audio latency mode if audioLatencyMode is None: self._audioLatencyMode = int(prefs.hardware["audioLatencyMode"]) else: self._audioLatencyMode = audioLatencyMode logging.debug('Set audio latency mode to {}'.format( self._audioLatencyMode)) assert 0 <= self._audioLatencyMode <= 4 # sanity check for pref # set the number of recording channels self._channels = \ self._device.inputChannels if channels is None else int(channels) logging.debug('Set recording channels to {} ({})'.format( self._channels, 'stereo' if self._channels > 1 else 'mono')) if self._channels > self._device.inputChannels: raise AudioInvalidDeviceError( 'Invalid number of channels for audio input specified.') # internal recording buffer size in seconds assert isinstance(streamBufferSecs, (float, int)) self._streamBufferSecs = float(streamBufferSecs) # PTB specific stuff self._mode = 2 # open a stream in capture mode # Handle for the recording stream, should only be opened once per # session logging.debug('Opening audio stream for device #{}'.format( self._device.deviceIndex)) self._stream = audio.Stream(device_id=self._device.deviceIndex, latency_class=self._audioLatencyMode, mode=self._mode, freq=self._sampleRateHz, channels=self._channels) logging.debug('Stream opened') assert isinstance(audioRunMode, (float, int)) and \ (audioRunMode == 0 or audioRunMode == 1) self._audioRunMode = int(audioRunMode) self._stream.run_mode = self._audioRunMode logging.debug('Set run mode to `{}`'.format(self._audioRunMode)) # set latency bias self._stream.latency_bias = 0.0 logging.debug('Set stream latency bias to {} ms'.format( self._stream.latency_bias)) # pre-allocate recording buffer, called once self._stream.get_audio_data(self._streamBufferSecs) logging.debug( 'Allocated stream buffer to hold {} seconds of data'.format( self._streamBufferSecs)) # status flag self._statusFlag = NOT_STARTED # setup recording buffer self._recording = RecordingBuffer(sampleRateHz=self._sampleRateHz, channels=self._channels, maxRecordingSize=maxRecordingSize, policyWhenFull=policyWhenFull) # setup clips and transcripts dicts self.clips = {} self.lastClip = None self.scripts = {} self.lastScript = None logging.debug('Audio capture device #{} ready'.format( self._device.deviceIndex))
# Copyright (C) 2018 Jonathan Peirce # Distributed under the terms of the GNU General Public License (GPL). from __future__ import absolute_import, division, print_function from past.utils import old_div import sys import time from psychopy import logging try: import ctypes import ctypes.util importCtypesFailed = False except Exception: importCtypesFailed = True logging.debug("rush() not available because import ctypes " "failed in contrib/darwin.py") # constants KERN_SUCCESS = 0 kCGLCPSwapInterval = ctypes.c_int(222) # these defined in thread_policy.h from apple (googleable) THREAD_STANDARD_POLICY = ctypes.c_int(1) THREAD_STANDARD_POLICY_COUNT = ctypes.c_int(0) THREAD_EXTENDED_POLICY = ctypes.c_int(1) THREAD_EXTENDED_POLICY_COUNT = ctypes.c_int(1) THREAD_TIME_CONSTRAINT_POLICY = ctypes.c_int(2) THREAD_TIME_CONSTRAINT_POLICY_COUNT = ctypes.c_int(4) # these were found in pyglet/window/carbon/constants thanks to Alex Holkner kCFStringEncodingASCII = 0x0600 kCFStringEncodingUnicode = 0x0100 kCFStringEncodingUTF8 = 0x08000100
def __init__(self, port=None, baudrate=9600, byteSize=8, stopBits=1, parity="N", #'N'one, 'E'ven, 'O'dd, 'M'ask, eol="\n", maxAttempts=1, autoPause=0.1, checkAwake=True): if not serial: raise ImportError('The module serial is needed to connect to this device. ' +\ "On most systems this can be installed with\n\t easy_install pyserial") #get a list of port names to try if port is None: ports = self.findPossiblePorts() elif type(port) in [int, float]: ports = ['COM%i' %self.portNumber] else: ports = [port] self.isOpen = False self.com = None self.OK = False self.maxAttempts=maxAttempts self.autoPause = autoPause self.eol = eol self.type = self.name #for backwards compatibility #try to open the port for portString in ports: self.com = serial.Serial(portString, baudrate=baudrate, bytesize=byteSize, # number of data bits parity=parity, # enable parity checking stopbits=stopBits, # number of stop bits timeout=3, # set a timeout value, None for waiting forever xonxoff=0, # enable software flow control rtscts=0, # enable RTS/CTS flow control ) try: self.com = serial.Serial(portString, baudrate=baudrate, bytesize=byteSize, # number of data bits parity=parity, # enable parity checking stopbits=stopBits, # number of stop bits timeout=3, # set a timeout value, None for waiting forever xonxoff=0, # enable software flow control rtscts=0, # enable RTS/CTS flow control ) self.portString = portString except: if port: #the user asked for this port and we couldn't connect to it logging.warn("Couldn't connect to port %s" %portString) else: #we were trying this port on a guess logging.debug("Tried and failed to connect to port %s" %portString) continue #try the next port if not self.com.isOpen(): try: self.com.open() except: logging.info("Couldn't open port %s. Is it being used by another program?" %self.portString) continue if checkAwake and self.com.isOpen():#we have an open com port. try to send a command awake=False #until we confirm otherwise for repN in range(self.maxAttempts): awake = self.isAwake() if awake: self.OK = True time.sleep(self.autoPause) break if not awake: logging.info("Opened port %s but it didn't respond like a %s" %(self.portString, self.type)) self.com.close() self.OK=False if self.OK:# we have successfully sent and read a command logging.info("Successfully opened %s with a %s" %(self.portString, self.name))
def importConditions(fileName, returnFieldNames=False, selection=""): """Imports a list of conditions from an .xlsx, .csv, or .pkl file The output is suitable as an input to :class:`TrialHandler` `trialTypes` or to :class:`MultiStairHandler` as a `conditions` list. If `fileName` ends with: - .csv: import as a comma-separated-value file (header + row x col) - .xlsx: import as Excel 2007 (xlsx) files. No support for older (.xls) is planned. - .pkl: import from a pickle file as list of lists (header + row x col) The file should contain one row per type of trial needed and one column for each parameter that defines the trial type. The first row should give parameter names, which should: - be unique - begin with a letter (upper or lower case) - contain no spaces or other punctuation (underscores are permitted) `selection` is used to select a subset of condition indices to be used It can be a list/array of indices, a python `slice` object or a string to be parsed as either option. e.g.: - "1,2,4" or [1,2,4] or (1,2,4) are the same - "2:5" # 2, 3, 4 (doesn't include last whole value) - "-10:2:" # tenth from last to the last in steps of 2 - slice(-10, 2, None) # the same as above - random(5) * 8 # five random vals 0-8 """ def _assertValidVarNames(fieldNames, fileName): """screens a list of names as candidate variable names. if all names are OK, return silently; else raise with msg """ if not all(fieldNames): msg = ('Conditions file %s: Missing parameter name(s); ' 'empty cell(s) in the first row?') raise ValueError(msg % fileName) for name in fieldNames: OK, msg = isValidVariableName(name) if not OK: # tailor message to importConditions msg = msg.replace('Variables', 'Parameters (column headers)') raise ValueError('Conditions file %s: %s%s"%s"' % (fileName, msg, os.linesep * 2, name)) if fileName in ['None', 'none', None]: if returnFieldNames: return [], [] return [] if not os.path.isfile(fileName): msg = 'Conditions file not found: %s' raise ValueError(msg % os.path.abspath(fileName)) def pandasToDictList(dataframe): """Convert a pandas dataframe to a list of dicts. This helper function is used by csv or excel imports via pandas """ # convert the resulting dataframe to a numpy recarray trialsArr = dataframe.to_records(index=False) if trialsArr.shape == (): # convert 0-D to 1-D with one element: trialsArr = trialsArr[np.newaxis] fieldNames = trialsArr.dtype.names _assertValidVarNames(fieldNames, fileName) # convert the record array into a list of dicts trialList = [] for trialN, trialType in enumerate(trialsArr): thisTrial = OrderedDict() for fieldN, fieldName in enumerate(fieldNames): val = trialsArr[trialN][fieldN] if isinstance(val, basestring): if val.startswith('[') and val.endswith(']'): # val = eval('%s' %unicode(val.decode('utf8'))) val = eval(val) elif type(val) == np.string_: val = str(val.decode('utf-8')) # if it looks like a list, convert it: if val.startswith('[') and val.endswith(']'): # val = eval('%s' %unicode(val.decode('utf8'))) val = eval(val) elif np.isnan(val): val = None thisTrial[fieldName] = val trialList.append(thisTrial) return trialList, fieldNames if fileName.endswith('.csv'): with open(fileName, 'rU') as fileUniv: # use pandas reader, which can handle commas in fields, etc trialsArr = pd.read_csv(fileUniv, encoding='utf-8') logging.debug("Read csv file with pandas: {}".format(fileName)) trialList, fieldNames = pandasToDictList(trialsArr) elif fileName.endswith(('.xlsx', '.xls')) and haveXlrd: trialsArr = pd.read_excel(fileName) logging.debug("Read excel file with pandas: {}".format(fileName)) trialList, fieldNames = pandasToDictList(trialsArr) elif fileName.endswith('.xlsx'): if not haveOpenpyxl: raise ImportError('openpyxl or xlrd is required for loading excel ' 'files, but neither was found.') # data_only was added in 1.8 if StrictVersion(openpyxl.__version__) < StrictVersion('1.8'): wb = load_workbook(filename=fileName) else: wb = load_workbook(filename=fileName, data_only=True) ws = wb.worksheets[0] logging.debug("Read excel file with openpyxl: {}".format(fileName)) try: # in new openpyxl (2.3.4+) get_highest_xx is deprecated nCols = ws.max_column nRows = ws.max_row except Exception: # version openpyxl 1.5.8 (in Standalone 1.80) needs this nCols = ws.get_highest_column() nRows = ws.get_highest_row() # get parameter names from the first row header fieldNames = [] for colN in range(nCols): fieldName = ws.cell(_getExcelCellName(col=colN, row=0)).value fieldNames.append(fieldName) _assertValidVarNames(fieldNames, fileName) # loop trialTypes trialList = [] for rowN in range(1, nRows): # skip header first row thisTrial = {} for colN in range(nCols): val = ws.cell(_getExcelCellName(col=colN, row=rowN)).value # if it looks like a list or tuple, convert it if (isinstance(val, basestring) and (val.startswith('[') and val.endswith(']') or val.startswith('(') and val.endswith(')'))): val = eval(val) fieldName = fieldNames[colN] thisTrial[fieldName] = val trialList.append(thisTrial) elif fileName.endswith('.pkl'): f = open(fileName, 'rU') # is U needed? try: trialsArr = pickle.load(f) except Exception: raise IOError('Could not open %s as conditions' % fileName) f.close() trialList = [] fieldNames = trialsArr[0] # header line first _assertValidVarNames(fieldNames, fileName) for row in trialsArr[1:]: thisTrial = {} for fieldN, fieldName in enumerate(fieldNames): # type is correct, being .pkl thisTrial[fieldName] = row[fieldN] trialList.append(thisTrial) else: raise IOError('Your conditions file should be an ' 'xlsx, csv or pkl file') # if we have a selection then try to parse it if isinstance(selection, basestring) and len(selection) > 0: selection = indicesFromString(selection) if not isinstance(selection, slice): for n in selection: try: assert n == int(n) except AssertionError: raise TypeError("importConditions() was given some " "`indices` but could not parse them") # the selection might now be a slice or a series of indices if isinstance(selection, slice): trialList = trialList[selection] elif len(selection) > 0: allConds = trialList trialList = [] for ii in selection: trialList.append(allConds[int(round(ii))]) logging.exp('Imported %s as conditions, %d conditions, %d params' % (fileName, len(trialList), len(fieldNames))) if returnFieldNames: return (trialList, fieldNames) else: return trialList
def loadPlugin(plugin, *args, **kwargs): """Load a plugin to extend PsychoPy. Plugins are packages which extend upon PsychoPy's existing functionality by dynamically importing code at runtime, without modifying the existing installation files. Plugins create or redefine objects in the namespaces of modules (eg. `psychopy.visual`) and unbound classes, allowing them to be used as if they were part of PsychoPy. In some cases, objects exported by plugins will be registered for a particular function if they define entry points into specific modules. Plugins are simply Python packages,`loadPlugin` will search for them in directories specified in `sys.path`. Only packages which define entry points in their metadata which pertain to PsychoPy can be loaded with this function. This function also permits passing optional arguments to a callable object in the plugin module to run any initialization routines prior to loading entry points. This function is robust, simply returning `True` or `False` whether a plugin has been fully loaded or not. If a plugin fails to load, the reason for it will be written to the log as a warning or error, and the application will continue running. This may be undesirable in some cases, since features the plugin provides may be needed at some point and would lead to undefined behavior if not present. If you want to halt the application if a plugin fails to load, consider using :func:`requirePlugin`. It is advised that you use this function only when using PsychoPy as a library. If using the builder or coder GUI, it is recommended that you use the plugin dialog to enable plugins for PsychoPy sessions spawned by the experiment runner. However, you can still use this function if you want to load additional plugins for a given experiment, having their effects isolated from the main application and other experiments. Parameters ---------- plugin : str Name of the plugin package to load. This usually refers to the package or project name. *args, **kwargs Optional arguments and keyword arguments to pass to the plugin's `__register__` function. Returns ------- bool `True` if the plugin has valid entry points and was loaded successfully. Also returns `True` if the plugin was already loaded by a previous `loadPlugin` call this session, this function will have no effect in this case. `False` is returned if the plugin defines no entry points specific to PsychoPy or crashed (an error is logged). Warnings -------- Make sure that plugins installed on your system are from reputable sources, as they may contain malware! PsychoPy is not responsible for undefined behaviour or bugs associated with the use of 3rd party plugins. See Also -------- listPlugins : Search for and list installed or loaded plugins. requirePlugin : Require a plugin be previously loaded. Examples -------- Load a plugin by specifying its package/project name:: loadPlugin('psychopy-hardware-box') You can give arguments to this function which are passed on to the plugin:: loadPlugin('psychopy-hardware-box', switchOn=True, baudrate=9600) You can use the value returned from `loadPlugin` to determine if the plugin is installed and supported by the platform:: hasPlugin = loadPlugin('psychopy-hardware-box') if hasPlugin: # initialize objects which require the plugin here ... """ global _loaded_plugins_, _failed_plugins_ if isPluginLoaded(plugin): logging.info('Plugin `{}` already loaded. Skipping.'.format(plugin)) return True # already loaded, return True try: entryMap = _installed_plugins_[plugin] except KeyError: logging.warning('Package `{}` does not appear to be a valid plugin. ' 'Skipping.'.format(plugin)) if plugin not in _failed_plugins_: _failed_plugins_.append(plugin) return False if not any([i.startswith('psychopy') for i in entryMap.keys()]): logging.warning( 'Specified package `{}` defines no entry points for PsychoPy. ' 'Skipping.'.format(plugin)) if plugin not in _failed_plugins_.keys(): _failed_plugins_.append(plugin) return False # can't do anything more here, so return # go over entry points, looking for objects explicitly for psychopy validEntryPoints = collections.OrderedDict() # entry points to assign for fqn, attrs in entryMap.items(): if not fqn.startswith('psychopy'): continue # forbid plugins from modifying this module if fqn.startswith('psychopy.plugins') or \ (fqn == 'psychopy' and 'plugins' in attrs): logging.error( "Plugin `{}` declares entry points into the `psychopy.plugins` " "which is forbidden. Skipping.") if plugin not in _failed_plugins_: _failed_plugins_.append(plugin) return False # Get the object the fully-qualified name points to the group which the # plugin wants to modify. targObj = resolveObjectFromName(fqn, error=False) if targObj is None: logging.error( "Plugin `{}` specified entry point group `{}` that does not " "exist or is unreachable.") if plugin not in _failed_plugins_: _failed_plugins_.append(plugin) return False validEntryPoints[fqn] = [] # Import modules assigned to entry points and load those entry points. # We don't assign anything to PsychoPy's namespace until we are sure # that the entry points are valid. This prevents plugins from being # partially loaded which can cause all sorts of undefined behaviour. for attr, ep in attrs.items(): # Load the module the entry point belongs to, this happens # anyways when .load() is called, but we get to access it before # we start binding. If the module has already been loaded, don't # do this again. if ep.module_name not in sys.modules: # Do stuff before loading entry points here, any executable code # in the module will run to configure it. try: imp = importlib.import_module(ep.module_name) except (ModuleNotFoundError, ImportError): logging.error( "Plugin `{}` entry point requires module `{}`, but it" "cannot be imported.".format(plugin, ep.module_name)) if plugin not in _failed_plugins_: _failed_plugins_.append(plugin) return False # call the register function, check if exists and valid if hasattr(imp, '__register__') and imp.__register__ is not None: if isinstance(imp.__register__, str): if hasattr(imp, imp.__register__): # local to module func = getattr(imp, imp.__register__) else: # could be a FQN? func = resolveObjectFromName(imp.__register__, error=False) # check if the reference object is callable if not callable(func): logging.error( "Plugin `{}` module defines `__register__` but " "the specified object is not a callable type. " "Skipping.".format(plugin)) if plugin not in _failed_plugins_: _failed_plugins_.append(plugin) return False elif callable(imp.__register__): # a function was supplied func = imp.__register__ else: logging.error( "Plugin `{}` module defines `__register__` but " "is not `str` or callable type. Skipping.".format( plugin)) if plugin not in _failed_plugins_: _failed_plugins_.append(plugin) return False # call the register function with arguments func(*args, **kwargs) # Ensure that we are not wholesale replacing an existing module. # We want plugins to be explicit about what they are changing. # This makes sure plugins play nice with each other, only # making changes to existing code where needed. However, plugins # are allowed to add new modules to the namespaces of existing # ones. if hasattr(targObj, attr): # handle what to do if an attribute exists already here ... if inspect.ismodule(getattr(targObj, attr)): logging.error( "Plugin `{}` attempted to override module `{}`.". format(plugin, fqn + '.' + attr)) if plugin not in _failed_plugins_: _failed_plugins_.append(plugin) return False try: ep = ep.load() # load the entry point except ImportError: logging.error( "Failed to load entry point `{}` of plugin `{}`. " " Skipping.".format(str(ep), plugin)) if plugin not in _failed_plugins_: _failed_plugins_.append(plugin) return False # If we get here, the entry point is valid and we can safely add it # to PsychoPy's namespace. validEntryPoints[fqn].append((targObj, attr, ep)) # Assign entry points that have been successfully loaded. We defer # assignment until all entry points are deemed valid to prevent plugins # from being partially loaded. for fqn, vals in validEntryPoints.items(): for targObj, attr, ep in vals: # add the object to the module or unbound class setattr(targObj, attr, ep) logging.debug("Assigning to entry point `{}` to `{}`.".format( ep.__name__, fqn + '.' + attr)) # --- handle special cases --- if fqn == 'psychopy.visual.backends': # if window backend _registerWindowBackend(attr, ep) elif fqn == 'psychopy.experiment.components': # if component _registerBuilderComponent(ep) # Retain information about the plugin's entry points, we will use this for # conflict resolution. _loaded_plugins_[plugin] = entryMap # If we made it here on a previously failed plugin, it was likely fixed and # can be removed from the list. if plugin not in _failed_plugins_: try: _failed_plugins_.remove(plugin) except ValueError: pass return True
def handleCheckboxChange(new_state): ix = self.inputFields.index(inputBox) self.data[ix] = inputBox.isChecked() msg = "handleCheckboxChange: inputFieldName={0}, checked={1}" logging.debug(msg.format(label, self.data[ix]))
def encrypt(datafile, pubkeyPem, meta=True, keep=None): """Encrypt a file using openssl, AES-256, and an RSA public-key. Returns: full path to the encrypted file (= .tgz bundle of 3 files). By default the original plaintext is deleted after encryption (see parameter `keep`). The idea is that you can have and share a public key, which anyone can use to encrypt things that only you can decrypt. Generating good keys and managing them is non-trivial, and is entirely up to you. (GPG can help a lot.) For better security, it is good to use signed public keys. No attempt is made here to verify key signatures automatically; you could do so manually using `verify()`. :Parameters: `datafile`: The path (name) of the original plaintext file to be encrypted. `pubkeyPem`: The public key to use, specified as the path to a .pem file. Example file contents (1024 bit pub.pem):: -----BEGIN PUBLIC KEY----- MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBALcw2C2Tyiq514Nc+Oe1TvweyzK92PSm s7KYMziTNcMy50E9KjSb7k8U/6Jaz/foeWFJqID1cmiyj1whZfZ4KycCAwEAAQ== -----END PUBLIC KEY----- `meta`: If `True`, include meta-data as plaintext in the archive:: original file name & sha256 of encrypted platform & date openssl version, padding pubkey info (to aid in key rotation) `keep`: None (default) = remove original (unencrypted) & all intermediate files (more secure) 'orig' = leave original file, delete intermediate (encrypted) files 'all' = leave all intermed files & orig (for testing purposes) """ logging.debug('encrypt (beta): start') if not datafile or not os.path.isfile(datafile): msg = 'encrypt (beta): no data to encrypt; %s not found' % datafile logging.error(msg) raise ValueError(msg) # good file names: if not pubkeyPem or not os.path.isfile(pubkeyPem): msg = 'encrypt (beta): missing public-key.pem file; %s not found' % pubkeyPem logging.error(msg) raise ValueError(msg) # can't / don't proceed without a pub key of sufficient length: pkLen = len(open(pubkeyPem).read()) # proxy for number of bits in key if pkLen < 271: # or 451 chars for 2048 bit raise PublicKeyTooShortError("public key < 1024 bits") if not keep in [None, 'orig', 'all']: raise ValueError("encrypt (beta): bad value for 'keep' parameter") # do encryption via encMethod: data.txt --> (data.aes256, data.aes256.pwd.rsa, data.meta) # the two ideas here are 1) to do the call in a way that ensures that the call is # documentatble (in meta-data), so encMethod is the name that is actually used. # and 2) with more work, this will allow a user to specify some other encryption # tool, eg, gpg or pgp, and that will be able to just drop as and arg (+ kwarg dict), while # retaining the .tgz bundle part encMethod = '_encrypt_rsa_aes256cbc' dataEncFile, pEncFile = eval(encMethod + '(datafile, pubkeyPem)') # bundle as a tarfile: (data.aes256, data.aes256.pwd.rsa) --> data.enc.tgz: savedLocation = os.path.abspath(os.path.curdir) root, _ = os.path.split(os.path.abspath(datafile)) os.chdir(root) # cd into dir containing datafile tgzFile = _uniqFileName(os.path.splitext(datafile)[0] + BUNDLE_EXT) # same name, new ext files = [dataEncFile, pEncFile] # files to be bundled in .tgz # encryption meta-data: if meta: metaDataFile = os.path.split(datafile)[1] + META_EXT f = open(metaDataFile, 'wb') # idea: append new meta after a string, e.g., old meta data in rotate if type(meta) == str: f.write(meta + '\n') f.write(_getMetaData(datafile, dataEncFile, pubkeyPem, encMethod)) f.close() files.append(metaDataFile) tar = tarfile.open(tgzFile, "w:gz") for name in files: # remove leading tmp path info from name tar.add(os.path.split(name)[1]) if keep != 'all': os.remove(name) # remove intermediate encrypted files tar.close() os.chdir(savedLocation) if not keep: # remove unencrypted original os.remove(datafile) return os.path.abspath(tgzFile)
def linearizeLums(self, desiredLums, newInterpolators=False, overrideGamma=None): """lums should be uncalibrated luminance values (e.g. a linear ramp) ranging 0:1 """ linMethod = self.getLinearizeMethod() desiredLums = np.asarray(desiredLums) output = desiredLums * 0.0 # needs same size as input # gamma interpolation if linMethod == 3: lumsPre = copy(self.getLumsPre()) if self._gammaInterpolator is not None and not newInterpolators: pass # we already have an interpolator elif lumsPre is not None: if self.autoLog: logging.info('Creating linear interpolation for gamma') # we can make an interpolator self._gammaInterpolator = [] self._gammaInterpolator2 = [] # each of these interpolators is a function! levelsPre = self.getLevelsPre() / 255.0 for gun in range(4): # scale to 0:1 lumsPre[gun, :] = \ (lumsPre[gun, :] - lumsPre[gun, 0] / (lumsPre[gun, -1] - lumsPre[gun, 0])) self._gammaInterpolator.append( interpolate.interp1d(lumsPre[gun, :], levelsPre, kind='linear')) # interpFunc = Interpolation.InterpolatingFunction( # (lumsPre[gun,:],), levelsPre) # polyFunc = interpFunc.fitPolynomial(3) # self._gammaInterpolator2.append( [polyFunc.coeff]) else: # no way to do this! Calibrate the monitor logging.error("Can't do a gamma interpolation on your " "monitor without calibrating!") return desiredLums # then do the actual interpolations if len(desiredLums.shape) > 1: for gun in range(3): # gun+1 because we don't want luminance interpolator _gammaIntrpGun = self._gammaInterpolator[gun + 1] output[:, gun] = _gammaIntrpGun(desiredLums[:, gun]) else: # just luminance output = self._gammaInterpolator[0](desiredLums) # use a fitted gamma equation (1 or 2) elif linMethod in [1, 2, 4]: # get the min,max lums gammaGrid = self.getGammaGrid() if gammaGrid is not None: # if we have info about min and max luminance then use it minLum = gammaGrid[1, 0] maxLum = gammaGrid[1:4, 1] b = gammaGrid[1:4, 4] if overrideGamma is not None: gamma = overrideGamma else: gamma = gammaGrid[1:4, 2] maxLumWhite = gammaGrid[0, 1] gammaWhite = gammaGrid[0, 2] if self.autoLog: logging.debug('using gamma grid' + str(gammaGrid)) else: # just do the calculation using gamma minLum = 0 maxLumR, maxLumG, maxLumB, maxLumWhite = 1, 1, 1, 1 gamma = self.currentCalib['gamma'] gammaWhite = np.average(gamma) # get the inverse gamma if len(desiredLums.shape) > 1: for gun in range(3): output[:, gun] = gammaInvFun(desiredLums[:, gun], minLum, maxLum[gun], gamma[gun], eq=linMethod, b=b[gun]) else: output = gammaInvFun(desiredLums, minLum, maxLumWhite, gammaWhite, eq=linMethod) else: msg = "Don't know how to linearise with method %i" logging.error(msg % linMethod) output = desiredLums return output
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 fetch(self, charcodes='', face=None): """ Build glyphs corresponding to individual characters in charcodes. Parameters: ----------- charcodes: [str | unicode] Set of characters to be represented """ if face is None: face = ft.Face(str(self.filename)) # doesn't support Pathlib yet # if current glyph is same as last then maybe blank glyph? lastGlyph = None possibleBlank = None nBlanks = 0 for charcode in charcodes: if charcode in self.glyphs: continue face.set_pixel_sizes(int(self.size), int(self.size)) self._dirty = True flags = ft.FT_LOAD_RENDER | ft.FT_LOAD_FORCE_AUTOHINT flags |= ft.FT_LOAD_TARGET_LCD face.load_char(charcode, flags) bitmap = face.glyph.bitmap # check if this looks like a blank (same as a prev glyph) if bitmap.buffer == lastGlyph: possibleBlank = lastGlyph if bitmap.buffer == possibleBlank: # whether newly detected or not nBlanks += 1 continue lastGlyph = bitmap.buffer left = face.glyph.bitmap_left top = face.glyph.bitmap_top width = face.glyph.bitmap.width rows = face.glyph.bitmap.rows pitch = face.glyph.bitmap.pitch if self.format == 'rgb': x, y, w, h = self.atlas.get_region(width / 5, rows + 2) else: x, y, w, h = self.atlas.get_region(width + 2, rows + 2) if x < 0: msg = ("Failed to fit char into font texture ({} at size {}px)" .format(face.family_name, self.size)) raise RuntimeError(msg) x, y = x + 1, y + 1 w, h = w - 2, h - 2 data = np.array(bitmap.buffer).reshape(rows, pitch) data = data[:h, :w] if self.format == 'rgb': Z = (((data / 255.0) ** 1.5) * 255).astype(np.ubyte) self.atlas.set_region((x, y, w, h), data) # Build glyph size = w, h offset = left, top advance = (face.glyph.advance.x / self.scale, face.glyph.advance.y / self.scale) u0 = (x + 0.0) / float(self.atlas.width) v0 = (y + 0.0) / float(self.atlas.height) u1 = (x + w - 0.0) / float(self.atlas.width) v1 = (y + h - 0.0) / float(self.atlas.height) texcoords = (u0, v0, u1, v1) glyph = TextureGlyph(charcode, size, offset, advance, texcoords) self.glyphs[charcode] = glyph # Generate kerning # for g in self.glyphs.values(): # kerning = face.get_kerning(g.charcode, charcode, # mode=ft.FT_KERNING_UNFITTED) # if kerning.x != 0: # glyph.kerning[g.charcode] = kerning.x / self.scale # # kerning = face.get_kerning(charcode, g.charcode, # mode=ft.FT_KERNING_UNFITTED) # if kerning.x != 0: # g.kerning[charcode] = kerning.x / self.scale logging.debug("TextBox2 loaded {} chars with {} blanks and {} valid" .format(len(charcodes), nBlanks, len(charcodes) - nBlanks))
def terminateHubProcess(self): """ Send a UDP message to iohub informing it to exit. Use this when force quitting the experiment script process so iohub knows to exit as well. If message is not sent within 1 second, or the iohub server address in incorrect,the issue is logged. """ sock = None try: logging.debug('PsychoPyApp: terminateHubProcess called.') import socket sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.settimeout(1.0) iohubAddress = '127.0.0.1', 9034 import msgpack txData = msgpack.Packer().pack(('STOP_IOHUB_SERVER',)) return sock.sendto(txData, iohubAddress) except socket.error as e: msg = 'PsychoPyApp: terminateHubProcess socket.error: %s' logging.debug(msg % str(e)) except socket.herror as e: msg = 'PsychoPyApp: terminateHubProcess socket.herror: %s' logging.debug(msg % str(e)) except socket.gaierror as e: msg = 'PsychoPyApp: terminateHubProcess socket.gaierror: %s' logging.debug(msg % str(e)) except socket.timeout as e: msg = 'PsychoPyApp: terminateHubProcess socket.timeout: %s' logging.debug(msg % str(e)) except Exception as e: msg = 'PsychoPyApp: terminateHubProcess exception: %s' logging.debug(msg % str(e)) finally: if sock: sock.close() logging.debug('PsychoPyApp: terminateHubProcess completed.')
def setupProxy(log=True): """Set up the urllib proxy if possible. The function will use the following methods in order to try and determine proxies: #. standard urllib.request.urlopen (which will use any statically-defined http-proxy settings) #. previous stored proxy address (in prefs) #. proxy.pac files if these have been added to system settings #. auto-detect proxy settings (WPAD technology) .. note: This can take time, as each failed attempt to set up a proxy involves trying to load a URL and timing out. Best to do in a separate thread. :Returns: True (success) or False (failure) """ global proxies # try doing nothing proxies = urllib.request.ProxyHandler(urllib.request.getproxies()) if tryProxy(proxies) is True: if log: logging.debug("Using standard urllib (static proxy or " "no proxy required)") # this will now be used globally for ALL urllib opening urllib.request.install_opener(urllib.request.build_opener(proxies)) return 1 # try doing what we did last time if len(prefs.connections['proxy']) > 0: proxConnPref = {'http': prefs.connections['proxy']} proxies = urllib.request.ProxyHandler(proxConnPref) if tryProxy(proxies) is True: if log: msg = 'Using %s (from prefs)' logging.debug(msg % prefs.connections['proxy']) # this will now be used globally for ALL urllib opening opener = urllib.request.build_opener(proxies) urllib.request.install_opener(opener) return 1 else: if log: logging.debug("Found a previous proxy but it didn't work") # try finding/using a proxy.pac file pacURLs = getPacFiles() if log: logging.debug("Found proxy PAC files: %s" % pacURLs) proxies = proxyFromPacFiles(pacURLs) # installs opener, if successful if (proxies and hasattr(proxies, 'proxies') and len(proxies.proxies['http']) > 0): # save that proxy for future prefs.connections['proxy'] = proxies.proxies['http'] prefs.saveUserPrefs() if log: msg = 'Using %s (from proxy PAC file)' logging.debug(msg % prefs.connections['proxy']) return 1 # try finding/using 'auto-detect proxy' pacURLs = getWpadFiles() proxies = proxyFromPacFiles(pacURLs) # installs opener, if successful if (proxies and hasattr(proxies, 'proxies') and len(proxies.proxies['http']) > 0): # save that proxy for future prefs.connections['proxy'] = proxies.proxies['http'] prefs.saveUserPrefs() if log: msg = 'Using %s (from proxy auto-detect)' logging.debug(msg % prefs.connections['proxy']) return 1 proxies = 0 return 0
def __init__(self, win, buffer='back', rect=(-1, 1, 1, -1), sqPower2=False, stim=(), interpolate=True, flipHoriz=False, flipVert=False, mask='None', pos=(0, 0), name=None, autoLog=None): """ :Parameters: buffer : the screen buffer to capture from, default is 'back' (hidden). 'front' is the buffer in view after win.flip() rect : a list of edges [left, top, right, bottom] defining a screen rectangle which is the area to capture from the screen, given in norm units. default is fullscreen: [-1, 1, 1, -1] stim : a list of item(s) to be drawn to the back buffer (in order). The back buffer is first cleared (without the win being flip()ed), then stim items are drawn, and finally the buffer (or part of it) is captured. Each item needs to have its own .draw() method, and have the same window as win. interpolate : whether to use interpolation (default = True, generally good, especially if you change the orientation) sqPower2 : - False (default) = use rect for size if OpenGL = 2.1+ - True = use square, power-of-two image sizes flipHoriz : horizontally flip (mirror) the captured image, default = False flipVert : vertically flip (mirror) the captured image; default = False """ # depends on: window._getRegionOfFrame # what local vars are defined (these are the init params) for use by # __repr__ self._initParams = dir() self._initParams.remove('self') self.autoLog = False # set this False first and change later _clock = core.Clock() if stim: # draw all stim to the back buffer win.clearBuffer() buffer = 'back' if hasattr(stim, '__iter__'): for stimulus in stim: try: if stimulus.win == win: stimulus.draw() else: msg = ('BufferImageStim.__init__: user ' 'requested "%s" drawn in another window') logging.warning(msg % repr(stimulus)) except AttributeError: msg = 'BufferImageStim.__init__: "%s" failed to draw' logging.warning(msg % repr(stimulus)) else: raise ValueError('Stim is not iterable in BufferImageStim. ' 'It should be a list of stimuli.') # take a screenshot of the buffer using win._getRegionOfFrame(): glversion = pyglet.gl.gl_info.get_version() if glversion >= '2.1' and not sqPower2: region = win._getRegionOfFrame(buffer=buffer, rect=rect) else: if not sqPower2: msg = ('BufferImageStim.__init__: defaulting to square ' 'power-of-2 sized image (%s)') logging.debug(msg % glversion) region = win._getRegionOfFrame(buffer=buffer, rect=rect, squarePower2=True) if stim: win.clearBuffer() # turn the RGBA region into an ImageStim() object: if win.units in ['norm']: pos *= win.size / 2. size = region.size / win.size / 2. super(BufferImageStim, self).__init__( win, image=region, units='pix', mask=mask, pos=pos, size=size, interpolate=interpolate, name=name, autoLog=False) self.size = region.size # to improve drawing speed, move these out of draw: self.desiredRGB = self._getDesiredRGB( self.rgb, self.colorSpace, self.contrast) self.thisScale = numpy.array([4, 4]) self.flipHoriz = flipHoriz self.flipVert = flipVert # 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))) msg = 'BufferImageStim %s: took %.1fms to initialize' logging.exp(msg % (name, 1000 * _clock.getTime()))
def _testPubEncDec(secretText): """Tests: Aim for complete coverage of this file, not of openssl """ def _genRsaKeys(pub='pubkey.pem', priv='privkey.pem', pphr=None, bits=2048): """Generate new pub and priv keys, return full path to new files This is intended for testing purposes, not for general use. Definitely need to consider: randomness, bits, file permissions, file location (user home dir?), passphrase creation and usage, passphrase as file generally not so good, can make sense to have no passphrase, .... Well-seeded PRNGs are reasonably good; ideally use hardware RNG gnupg sounds pretty good, cross-platform """ logging.debug('_genRsaKeys: start') # generate priv key: cmdGEN = [OPENSSL, 'genrsa', '-out', priv] if pphr: cmdGEN += ['-des3', '-passout', 'file:' + pphr] cmdGEN += [str(bits)] so = _shellCall(cmdGEN) # extract pub from priv: cmdGENpub = [OPENSSL, 'rsa', '-in', priv, '-pubout', '-out', pub] if pphr: cmdGENpub += ['-passin', 'file:' + pphr] so = _shellCall(cmdGENpub) return os.path.abspath(pub), os.path.abspath(priv) # make a tmp data file holding secrect text, will get encrypted & decrypted: cwd = os.path.split(os.path.abspath(__file__))[0] pathToSelf = os.path.abspath( __file__) # for cmd-line version of decryption call tmp = mkdtemp() logging.debug('tests make tmp dir %s' % tmp) try: # so we can always remove the tmp dir, in finally os.chdir(tmp) # work inside for easy clean-up datafile = os.path.join(tmp, 'data.txt') f = open(datafile, 'wb') f.write(secretText) f.close() dataFileWHITESPACE = os.path.join(tmp, 'data space.txt') shutil.copy(datafile, dataFileWHITESPACE) # set-up to make some key-pairs to test decrypt(encrypt()) privTmp1 = os.path.join(tmp, 'privkey1.pem') pubTmp1 = os.path.join(tmp, 'pubkey1.pem') passphrsTmp1 = os.path.join(tmp, 'passphrs1.txt') f = open(passphrsTmp1, 'w+b') f.write(_onetimePwd(45)) f.close() pub1, priv1 = _genRsaKeys(pubTmp1, privTmp1, pphr=passphrsTmp1, bits=1024) privTmp2 = os.path.join(tmp, 'privkey2.pem') pubTmp2 = os.path.join(tmp, 'pubkey2.pem') passphrsTmp2 = os.path.join(tmp, 'passphrs2.txt') f = open(passphrsTmp2, 'w+b') f.write(_onetimePwd(45)) f.close() #pub2, priv2 = _genRsaKeys(pubTmp2, privTmp2, bits=1024) logging.debug('test: generating 1024 bit key with passphrase') cmd = [ OPENSSL, 'rsa', '-in', priv1, '-passin', 'file:' + passphrsTmp1, '-text' ] out = _shellCall(cmd) good_new_keypair_bits_1024 = (out.startswith('Private-Key: (1024 bit)') ) # test # test decrypt with GOOD passphrase': ----------------- logging.debug('test good_enc_dec_pphr_1024') dataEnc = encrypt(datafile, pub1, keep='all') dataEncDec = decrypt(dataEnc, priv1, passphraseFile=passphrsTmp1) good_enc_dec_pphr_1024 = (open(dataEncDec).read() == secretText ) # test # test decrypt via command line: ----------------- logging.debug('test good_enc_dec_cmdline') os.remove(dataEncDec) # i.e., try to recreate this decrypted file cmdLineCmd = ['python', pathToSelf, dataEnc, priv1, passphrsTmp1] dataEncDec_cmd = _shellCall(cmdLineCmd) good_enc_dec_cmdline = (open(dataEncDec_cmd).read() == secretText ) # test # encrypt with file name with white space: ----------------- logging.debug('test good_whitespace_filename_OK') try: good_whitespace_filename_OK = True # test encrypt(dataFileWHITESPACE, pub1, keep='orig') except: good_whitespace_filename_OK = False # test # a BAD passphrase should fail: ----------------- logging.debug('test bad_pphr_1024') try: bad_pphr_1024 = False # test decrypt(dataEnc, priv1, passphraseFile=passphrsTmp2) except PrivateKeyError: bad_pphr_1024 = True # test # nesting of decrypt(encrypt(file)) should work: ----------------- try: logging.debug('test good_dec_enc_nest') dataDecNest = decrypt(encrypt(datafile, pub1, keep='all'), priv1, passphraseFile=passphrsTmp1) good_dec_enc_nest = (open(dataDecNest).read() == secretText ) # test except OSError: # rare: sometimes seems too fast for file system, so try again logging.debug('test good_dec_enc_nest') dataDecNest = decrypt(encrypt(datafile, pub1, keep='all'), priv1, passphraseFile=passphrsTmp1) good_dec_enc_nest = (open(dataDecNest).read() == secretText ) # test # nested, with white space in file name: ----------------- try: logging.debug('test good_whitespace_nested_decenc_OK') dataDecNestWhitesp = decrypt(encrypt(dataFileWHITESPACE, pub1, keep='all'), priv1, passphraseFile=passphrsTmp1) good_whitespace_nested_decenc_OK = ( open(dataDecNestWhitesp).read() == secretText) # test except OSError: # rare: sometimes seems too fast for file system, so try again logging.debug('test good_whitespace_nested_decenc_OK') dataDecNestWhitesp = decrypt(encrypt(dataFileWHITESPACE, pub1, keep='all'), priv1, passphraseFile=passphrsTmp1) good_whitespace_nested_decenc_OK = ( open(dataDecNestWhitesp).read() == secretText) # test # a correct-format but KNOWN-BAD (NEW) private key should fail: ----------------- logging.debug('test bad_decrypt_BAD') _, priv2_2048 = _genRsaKeys(pubTmp2, os.path.join(tmp, 'privkey_NEW.pem'), bits=2048) try: bad_decrypt_BAD = False # test dataEncDec = decrypt(dataEnc, priv2_2048) # intended to raise except DecryptError: bad_decrypt_BAD = True # test # should refuse-to-encrypt if pub key is too short: ----------------- logging.debug('test bad_pubkey_too_short') pub256, _ = _genRsaKeys('pubkey256.pem', 'privkey256.pem', bits=256) try: bad_pubkey_too_short = False # test dataEnc = encrypt(datafile, pub256) # intended to raise except PublicKeyTooShortError: bad_pubkey_too_short = True # test pub2, priv2 = _genRsaKeys(pubTmp2, privTmp2, bits=1024) # verify a signed file: --------- logging.debug('test bad_verify_bad_key') reps = 10 good_sig = [] for i in xrange(reps): good_sig.append(sign(datafile, priv1, passphraseFile=passphrsTmp1)) if reps > 1: logging.debug('test sign, rep %d' % i) good_sign = all(good_sig) # test sig = good_sig[0] good_ver = [] bad_v = [] bad_ver = [] # stress test it: reps = 10 for i in xrange(reps): good_ver.append(verify(datafile, pub1, sig)) # test bad_v.append(not verify(pub1, pub2, sig)) # test bad_ver.append(not verify(datafile, pub2, sig)) # test logging.debug('test verify, rep %d' % i) good_verify = all(good_ver) bad_verify = all(bad_v) bad_verify_bad_key = all(bad_ver) logging.debug( 'rep-test verify x %d: %d %d %d: '% (reps, \ len([i for i in good_ver if i]),\ len([i for i in bad_v if i]),\ len([i for i in bad_ver if i]))) logging.debug('rep-test sign x %d: %d ' % (reps, len([i for i in good_sig if i]))) # rotate encryption: ----------------- logging.debug('test rotate encryption keys') en = encrypt(dataFileWHITESPACE, pub1, keep='all') ro = rotate(en, priv1, pub2, passphraseFile=passphrsTmp1) de = decrypt(ro, priv2) good_rotate = bool(open(de).read() == secretText) # test # minimal check of the meta-data from key rotation: ----------------- logging.debug('test good_rotate_metadata') # look for two HMAC, check that they differ (do encrypt-then-MAC) meta = open(de + META_EXT).readlines() hashes = [line.split()[-1] for line in meta if line.startswith('HMAC')] good_rotate_metadata = bool( len(hashes) == 2 and not hashes[0] == hashes[1]) # test finally: # remove all tmp stuff before doing any asserts try: shutil.rmtree(tmp, ignore_errors=False) except WindowsError: if len(os.listdir(tmp)): print "test dir left behind was not empty" # win xp in parallels: shutil.rmtree removes everything except tmp # I get "in use by another process", even after a time.sleep(5) # NB: 'bad_' tests pass if an error situation results in the appropriate error: logging.info('test begin reporting') assert good_enc_dec_pphr_1024 # FAILED encrypt -> decrypt using new rsa keys, 1024 bits logging.info('PASS good decrypt with good passphrase') assert good_new_keypair_bits_1024 # new rsa key FAILED to report 1024 bits logging.info('PASS new RSA key reported 1024 bits as requested') assert good_enc_dec_cmdline # FAILED to decrypt using command-line call logging.info('PASS good decrypt via command line') assert good_dec_enc_nest # something FAILED when nesting decrypt(encrypt(file)); just try again? logging.info('PASS nested decrypt(encrypt(file)) okay,') assert good_whitespace_filename_OK # a filename with whitepsace FAILED logging.info('PASS encrypt(file) okay, white space file name') assert good_whitespace_nested_decenc_OK # a filename with whitepsace FAILED logging.info( 'PASS nested decrypt(encrypt(file)) okay, white space file name') assert bad_pphr_1024 # a known-bad passphrase FAILED to result in an error logging.info('PASS a bad passphrase failed') assert bad_decrypt_BAD # known-bad private key FAILED to refuse to decrypt logging.info('PASS a correct-format but known-bad private key failed') assert bad_pubkey_too_short # known-to-be-too-short public key FAILED to refuse to encrypt logging.info('PASS refused-to-encrypt when pub key is too short') assert good_rotate # failed to properly rotate encryption with new key (decrypt failed) logging.info('PASS good decrypt after rotating the encryption key') assert good_rotate_metadata # failed to properly generate meta-data during key rotation logging.info('PASS minimal reasonableness of meta-data after rotation') assert good_verify # failed to verify a signed file logging.info('PASS verified a signed file') assert bad_verify # failed to fail to verify a wrong file (this is an error) logging.info('PASS refused to verify bad signature (wrong file)') assert good_sign # FAILED to get any return value from a sign() call logging.info('PASS got a signature') assert bad_verify_bad_key # failed to refuse to verify using wrong pub key (this is an error) logging.info('PASS refused to verify bad signature (wrong key)') logging.info('test SUCCESS, reporting complete')
def decrypt(dataEnc, privkeyPem, passphraseFile='', outFile=''): """Decrypt a file that was encoded using `encrypt()`. To get the data back, need two files: `data.enc` and `privkey.pem`. If the private key has a passphrase, you'll need that too. """ logging.debug('decrypt (beta): start') privkeyPem = os.path.abspath(privkeyPem) dataEnc = os.path.abspath(dataEnc) if passphraseFile: passphraseFile = os.path.abspath(passphraseFile) # check for bad paths: if not dataEnc or not os.path.isfile(dataEnc): raise ValueError('could not find <file>%s file %s' % (BUNDLE_EXT, str(dataEnc))) if not tarfile.is_tarfile(dataEnc): raise AttributeError('%s lacks expected internal format (.tgz)' % dataEnc) tar = tarfile.open(dataEnc, "r:gz") badNames = [ f for f in tar.getmembers() if f.name[0] in ['.', os.sep] or f.name[1:3] == ':\\' ] if len(badNames): raise InternalFormatError( 'unexpected file name(s) internal file (leading . or %s)' % os.sep) # extract & decrypt: tmp = mkdtemp() try: tar.extractall(path=tmp) # extract from .tgz file tar.close() fileList = os.listdir(tmp) # expect 2 or 3 files, identify them: for f in fileList: if f.endswith(AES_EXT): dataFileEnc = os.path.join(tmp, f) elif f.endswith(PWD_EXT + RSA_EXT): pwdFileRsa = os.path.join(tmp, f) elif f.endswith(META_EXT): metaFile = os.path.join(tmp, f) # decrypt to a file within tmp: dataFileDecr = _decrypt_rsa_aes256cbc(dataFileEnc, pwdFileRsa, privkeyPem, passphraseFile=passphraseFile, outFile=outFile) # move dec and meta files out of tmp: newLocation = _uniqFileName( dataFileDecr.replace(tmp, '').lstrip(os.sep)) os.rename(dataFileDecr, newLocation) newLocationMeta = _uniqFileName( metaFile.replace(tmp, '').lstrip(os.sep)) os.rename(metaFile, newLocationMeta) finally: try: shutil.rmtree(tmp, ignore_errors=False) except WindowsError: assert not len(os.listdir(tmp.name)) # oops, should be empty return os.path.abspath(newLocation)
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.settimeout(1.0) iohub_address = '127.0.0.1', 9034 import msgpack tx_data = msgpack.Packer().pack(('STOP_IOHUB_SERVER', )) return sock.sendto(tx_data, iohub_address) except socket.error, e: logging.debug('PsychoPyApp: terminateHubProcess socket.error: %s' % (str(e))) except socket.herror, e: logging.debug( 'PsychoPyApp: terminateHubProcess socket.herror: %s' % (str(e))) except socket.gaierror, e: logging.debug( 'PsychoPyApp: terminateHubProcess socket.gaierror: %s' % (str(e))) except socket.timeout, e: logging.debug( 'PsychoPyApp: terminateHubProcess socket.timeout: %s' % (str(e))) except Exception, e: logging.debug('PsychoPyApp: terminateHubProcess exception: %s' % (str(e))) finally: if sock: sock.close() logging.debug('PsychoPyApp: terminateHubProcess completed.') def quit(self, event=None): logging.debug('PsychoPyApp: Quitting...')
# Part of the PsychoPy library # Copyright (C) 2002-2018 Jonathan Peirce (C) 2019-2022 Open Science Tools Ltd. # Distributed under the terms of the GNU General Public License (GPL). """Placeholder for adding c (or ctypes) extensions to PsychoPy on linux. """ from psychopy import logging import sys try: import ctypes import ctypes.util c = ctypes.cdll.LoadLibrary(ctypes.util.find_library('c')) importCtypesFailed = False except Exception: importCtypesFailed = True logging.debug("rush() not available because import ctypes, ctypes.util " "failed in psychopy/platform_specific/linux.py") # FIFO and RR(round-robin) allow highest priority for realtime SCHED_NORMAL = 0 SCHED_FIFO = 1 SCHED_RR = 2 SCHED_BATCH = 3 if not importCtypesFailed: class _SchedParams(ctypes.Structure): _fields_ = [('sched_priority', ctypes.c_int)] warnMax = """Could not raise thread priority with sched_setscheduler.
def showPrefs(self, event): from psychopy.app.preferencesDlg import PreferencesDlg logging.debug('PsychoPyApp: Showing prefs dlg') prefsDlg = PreferencesDlg(app=self) prefsDlg.Show()
def __str__(self): if self.valType == 'num': try: # will work if it can be represented as a float return "{}".format(float(self.val)) except Exception: # might be an array return "asarray(%s)" % (self.val) elif self.valType == 'int': try: return "%i" % self.val # int and float -> str(int) except TypeError: return "{}".format(self.val) # try array of float instead? elif self.valType in ['extendedStr','str', 'file', 'table', 'color']: # at least 1 non-escaped '$' anywhere --> code wanted # return str if code wanted # return repr if str wanted; this neatly handles "it's" and 'He # says "hello"' val = self.val if isinstance(self.val, basestring): valid, val = self.dollarSyntax() if self.codeWanted and valid: # If code is wanted, return code (translated to JS if needed) if utils.scriptTarget == 'PsychoJS': valJS = py2js.expression2js(val) if self.val != valJS: logging.debug("Rewriting with py2js: {} -> {}".format(self.val, valJS)) return valJS else: return val else: # If str is wanted, return literal if utils.scriptTarget != 'PsychoPy': if val.startswith("u'") or val.startswith('u"'): # if target is python2.x then unicode will be u'something' # but for other targets that will raise an annoying error val = val[1:] if self.valType in ['file', 'table']: # If param is a file of any kind, escape any \ val = re.sub(r"\\", r"\\\\", val) val=re.sub("\n", "\\n", val) # Replace line breaks with escaped line break character return repr(val) return repr(self.val) elif self.valType in ['code', 'extendedCode']: isStr = isinstance(self.val, basestring) if isStr and self.val.startswith("$"): # a $ in a code parameter is unnecessary so remove it val = "%s" % self.val[1:] elif isStr and self.val.startswith("\$"): # the user actually wanted just the $ val = "%s" % self.val[1:] elif isStr: val = "%s" % self.val else: # if val was a tuple it needs converting to a string first val = "%s" % repr(self.val) if utils.scriptTarget == "PsychoJS": if self.valType == 'code': valJS = py2js.expression2js(val) elif self.valType == 'extendedCode': valJS = py2js.snippet2js(val) if val != valJS: logging.debug("Rewriting with py2js: {} -> {}".format(val, valJS)) return valJS else: return val elif self.valType == 'list': valid, val = self.dollarSyntax() val = toList(val) return "{}".format(val) elif self.valType == 'fixedList': return "{}".format(self.val) elif self.valType == 'fileList': return "{}".format(self.val) elif self.valType == 'bool': if utils.scriptTarget == "PsychoJS": return ("%s" % self.val).lower() # make True -> "true" else: return "%s" % self.val elif self.valType == "table": return "%s" % self.val elif self.valType == "color": if re.match(r"\$", self.val): return self.val.strip('$') else: return f"\"{self.val}\"" else: raise TypeError("Can't represent a Param of type %s" % self.valType)
def __init__( self, port=None, baudrate=9600, byteSize=8, stopBits=1, parity="N", # 'N'one, 'E'ven, 'O'dd, 'M'ask, eol=b"\n", maxAttempts=1, pauseDuration=0.1, checkAwake=True): if not serial: raise ImportError('The module serial is needed to connect to this' ' device. On most systems this can be installed' ' with\n\t easy_install pyserial') # get a list of port names to try if port is None: ports = self._findPossiblePorts() elif type(port) in [int, float]: ports = ['COM%i' % port] else: ports = [port] self.pauseDuration = pauseDuration self.com = None self.OK = False self.maxAttempts = maxAttempts if type(eol) is bytes: self.eol = eol else: self.eol = bytes(eol, 'utf-8') self.type = self.name # for backwards compatibility # try to open the port for portString in ports: try: self.com = serial.Serial( portString, baudrate=baudrate, bytesize=byteSize, # number of data bits parity=parity, # enable parity checking stopbits=stopBits, # number of stop bits timeout=3, # set a timeout value, None for waiting forever xonxoff=0, # enable software flow control rtscts=0, ) # enable RTS/CTS flow control self.portString = portString except Exception: if port: # the user asked for this port and we couldn't connect logging.warn("Couldn't connect to port %s" % portString) else: # we were trying this port on a guess msg = "Tried and failed to connect to port %s" logging.debug(msg % portString) continue # try the next port if not self.com.isOpen(): try: self.com.open() except Exception: msg = ("Couldn't open port %s. Is it being used by " "another program?") logging.info(msg % self.portString) continue if checkAwake and self.com.isOpen(): # we have an open com port. try to send a command self.com.flushInput() awake = False # until we confirm otherwise for repN in range(self.maxAttempts): awake = self.isAwake() if awake: msg = "Opened port %s and looks like a %s" logging.info(msg % (self.portString, self.name)) self.OK = True self.pause() break if not awake: msg = "Opened port %s but it didn't respond like a %s" logging.info(msg % (self.portString, self.name)) self.com.close() self.OK = False else: break if self.OK: # we have successfully sent and read a command msg = "Successfully opened %s with a %s" logging.info(msg % (self.portString, self.name)) # we aren't in a time-critical period so flush messages logging.flush()
def showAbout(self, event): logging.debug('PsychoPyApp: Showing about dlg') with io.open(os.path.join(self.prefs.paths['psychopy'], 'LICENSE.txt'), 'r', encoding='utf-8-sig') as f: license = f.read() msg = _translate( "For stimulus generation and experimental control in Python.\n" "PsychoPy depends on your feedback. If something doesn't work\n" "then let us know at [email protected]") if parse_version(wx.__version__) >= parse_version('4.0a1'): info = wx.adv.AboutDialogInfo() showAbout = wx.adv.AboutBox else: info = wx.AboutDialogInfo() showAbout = wx.AboutBox if wx.version() >= '3.': icon = os.path.join(self.prefs.paths['resources'], 'psychopy.png') info.SetIcon(wx.Icon(icon, wx.BITMAP_TYPE_PNG, 128, 128)) info.SetName('PsychoPy') info.SetVersion('v' + psychopy.__version__) info.SetDescription(msg) info.SetCopyright('(C) 2002-2021 Jonathan Peirce') info.SetWebSite('https://www.psychopy.org') info.SetLicence(license) # developers devNames = [ 'Jonathan Peirce', 'Jeremy Gray', 'Michael MacAskill', 'Sol Simpson', u'Jonas Lindel\xF8v', 'Yaroslav Halchenko', 'Erik Kastman', 'Hiroyuki Sogo', 'David Bridges', 'Matthew Cutone', 'Philipp Wiesemann', u'Richard Höchenberger', 'Andrew Schofield', 'Todd Parsons', 'Dan Fitch', 'Suddha Sourav', 'Philipp Wiesemann', 'Mark Hymers', 'Benjamin T. Vincent', 'Yaroslav Halchenko', 'Jay Borseth', 'chrisgatwin [@github.com]', 'toddrjen [@github.com]' ] docNames = [ 'Jonathan Peirce', 'Jeremy Gray', 'Rebecca Hirst', 'Rebecca Sharman', 'Matthew Cutone' ] devNames.sort() intNames = ['Hiroyuki Sogo'] intNames.sort() for name in devNames: info.AddDeveloper(name) for name in docNames: info.AddDocWriter(name) for name in intNames: info.AddTranslator(name) if not self.testMode: showAbout(info)
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 platform == 'win32': # check for output device/driver 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 platform == 'darwin' or 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 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 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()
# Part of the PsychoPy library # Copyright (C) 2014 Jonathan Peirce # Distributed under the terms of the GNU General Public License (GPL). """ placeholder for adding c (or ctypes) extensions to the linux PsychoPy """ from psychopy import logging import sys try: import ctypes, ctypes.util c = ctypes.cdll.LoadLibrary(ctypes.util.find_library('c')) importCtypesFailed = False except: importCtypesFailed = True logging.debug("rush() not available because import ctypes, ctypes.util failed in ext/linux.py") #FIFO and RR(round-robin) allow highest priority for realtime SCHED_NORMAL=0 SCHED_FIFO =1 SCHED_RR =2 SCHED_BATCH =3 if not importCtypesFailed: class _SchedParams(ctypes.Structure): _fields_ = [('sched_priority', ctypes.c_int)]# 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
def __str__(self): if self.valType == 'num': try: # will work if it can be represented as a float return "{}".format(float(self.val)) except Exception: # might be an array return "asarray(%s)" % (self.val) elif self.valType == 'int': try: return "%i" % self.val # int and float -> str(int) except TypeError: return "{}".format(self.val) # try array of float instead? elif self.valType in ['extendedStr', 'str']: # at least 1 non-escaped '$' anywhere --> code wanted # return str if code wanted # return repr if str wanted; this neatly handles "it's" and 'He # says "hello"' if isinstance(self.val, basestring): codeWanted = utils.unescapedDollarSign_re.search(self.val) if codeWanted: return "%s" % getCodeFromParamStr(self.val) else: # str wanted # remove \ from all \$ s = repr(re.sub(r"[\\]\$", '$', self.val)) # if target is python2.x then unicode will be u'something' # but for other targets that will raise an annoying error if utils.scriptTarget != 'PsychoPy': if s.startswith("u'") or s.startswith('u"'): s = s[1:] return s return repr(self.val) elif self.valType in ['code', 'extendedCode']: isStr = isinstance(self.val, basestring) if isStr and self.val.startswith("$"): # a $ in a code parameter is unnecessary so remove it val = "%s" % self.val[1:] elif isStr and self.val.startswith("\$"): # the user actually wanted just the $ val = "%s" % self.val[1:] elif isStr: val = "%s" % self.val else: # if val was a tuple it needs converting to a string first val = "%s" % repr(self.val) if utils.scriptTarget == "PsychoJS": if self.valType == 'code': valJS = py2js.expression2js(val) elif self.valType == 'extendedCode': valJS = py2js.snippet2js(val) if val != valJS: logging.debug("Rewriting with py2js: {} -> {}".format( val, valJS)) return valJS else: return val elif self.valType == 'list': return "%s" % (toList(self.val)) elif self.valType == 'fixedList': return "{}".format(self.val) elif self.valType == 'bool': return "%s" % self.val else: raise TypeError("Can't represent a Param of type %s" % self.valType)
def clearEvents(self): """Discard all button / voice key events. """ self.events[:] = [] self.commands.clear_received_reports() logging.debug('bbox clear events')
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)
languageID, lang = getID() # use lang like this: # import locale -- the non-wx version of locale # # if sys.platform.startswith('win'): # v = winmap[val] # else: v=val # locale.setlocale(locale.LC_ALL, (v, 'UTF-8')) # ideally rewrite the following using wxlocale only: path = os.path.join(os.path.dirname(__file__), '..', 'app', 'locale', lang, 'LC_MESSAGE') + os.sep mofile = os.path.join(path, 'messages.mo') try: logging.debug("Opening message catalog %s for locale %s" % (mofile, lang)) trans = gettext.GNUTranslations(open(mofile, "rb")) except IOError: logging.debug("Locale for '%s' not found. Using default." % lang) trans = gettext.NullTranslations() if constants.PY3: trans.install() else: trans.install(unicode=True) # PsychoPy app uses a nonstandard name _translate (instead of _) # A dependency overwrites _ somewhere, clobbering use of _ as global: # __builtins__['_translate'] = _ # del(__builtins__['_']) # idea: force psychopy code to use _translate
def handleCurrentIndexChanged(new_index): ix = self.inputFields.index(inputBox) self.data[ix] = inputBox.itemData(new_index).toPyObject()[0] logging.debug("handleCurrentIndexChanged: inputFieldName={0}, " "selected={1}, type: {2}".format( label, self.data[ix], type(self.data[ix])))
class PsychoPyApp(wx.App): def __init__(self, arg=0, **kwargs): wx.App.__init__(self, arg) self.launched_obci = False self.onInit(**kwargs) def onInit(self, showSplash=True, testMode=False): """ :Parameters: testMode: bool If set to True then startup wizard won't appear and stdout/stderr won't be redirected to the Coder """ self.version = psychopy.__version__ self.SetAppName('PsychoPy2') #set default paths and prefs self.prefs = psychopy.prefs if self.prefs.app['debugMode']: logging.console.setLevel(logging.DEBUG) self.testMode = testMode #indicates whether we're running for testing purposes if showSplash: #show splash screen splashFile = os.path.join(self.prefs.paths['resources'], 'psychopySplash.png') splashBitmap = wx.Image(name=splashFile).ConvertToBitmap() splash = AS.AdvancedSplash( None, bitmap=splashBitmap, timeout=3000, style=AS.AS_TIMEOUT | wx.FRAME_SHAPED, shadowcolour=wx.RED ) #could use this in future for transparency splash.SetTextPosition((10, 240)) splash.SetText(" Loading libraries..." + uidRootFlag) else: splash = None #LONG IMPORTS - these need to be imported after splash screen starts (they're slow) #but then that they end up being local so keep track in self if splash: splash.SetText(" Loading PsychoPy2..." + uidRootFlag) from psychopy import compatibility from psychopy.app import coder, builder, dialogs, wxIDs, urls #import coder and builder here but only use them later from psychopy.app import resources self.keys = self.prefs.keys self.prefs.pageCurrent = 0 # track last-viewed page of prefs, to return there self.IDs = wxIDs self.urls = urls.urls # add psychopy-specifi art providers wx.ArtProvider.Push(resources.ArtProvider()) wx.ArtProvider.Push(resources.ComponentArtProvider()) self.quitting = False #check compatibility with last run version (before opening windows) self.firstRun = False if '--firstrun' in sys.argv: del sys.argv[sys.argv.index('--firstrun')] self.firstRun = True if 'lastVersion' not in self.prefs.appData.keys(): last = self.prefs.appData[ 'lastVersion'] = '1.73.04' #must be before 1.74.00 self.firstRun = True else: last = self.prefs.appData['lastVersion'] if self.firstRun and not self.testMode: self.firstrunWizard() #setup links for URLs #on a mac, don't exit when the last frame is deleted, just show a menu if sys.platform == 'darwin': self.menuFrame = MenuFrame(parent=None, app=self) #get preferred view(s) from prefs and previous view if self.prefs.app['defaultView'] == 'last': mainFrame = self.prefs.appData['lastFrame'] else: # configobjValidate should take care of this situation (?), but doesn't: if self.prefs.app['defaultView'] in [ 'last', 'coder', 'builder', 'both' ]: mainFrame = self.prefs.app['defaultView'] else: self.prefs.app['defaultView'] = 'both' mainFrame = 'both' #fetch prev files if that's the preference if self.prefs.coder['reloadPrevFiles']: scripts = self.prefs.appData['coder']['prevFiles'] else: scripts = [] if self.prefs.builder['reloadPrevExp'] and ( 'prevFiles' in self.prefs.appData['builder'].keys()): exps = self.prefs.appData['builder']['prevFiles'] else: exps = [] #then override the prev files by command options and passed files if len(sys.argv) > 1: if sys.argv[1] == __name__: args = sys.argv[ 2:] # program was executed as "python.exe PsychoPyIDE.py %1' else: args = sys.argv[ 1:] # program was executed as "PsychoPyIDE.py %1' #choose which frame to start with if args[0] in ['builder', '--builder', '-b']: mainFrame = 'builder' args = args[1:] #can remove that argument elif args[0] in ['coder', '--coder', '-c']: mainFrame = 'coder' args = args[1:] #can remove that argument #did we get .py or .psyexp files? elif args[0][-7:] == '.psyexp': mainFrame = 'builder' exps = [args[0]] elif args[0][-3:] == '.py': mainFrame = 'coder' scripts = [args[0]] else: args = [] self.dpi = int(wx.GetDisplaySize()[0] / float(wx.GetDisplaySizeMM()[0]) * 25.4) if not (50 < self.dpi < 120): self.dpi = 80 #dpi was unreasonable, make one up #create both frame for coder/builder as necess if splash: splash.SetText(" Creating frames..." + uidRootFlag) self.coder = None self.builderFrames = [] self.copiedRoutine = None self.allFrames = [ ] #these are ordered and the order is updated with self.onNewTopWindow if mainFrame in ['both', 'coder']: self.showCoder(fileList=scripts) if mainFrame in ['both', 'builder']: self.showBuilder(fileList=exps) # TODO: in Windows start OBCI server if sys.platform in ['win32', 'win64']: # detect obci installation if splash: splash.SetText(" Loading OBCI server..." + uidRootFlag) try: import obci obci_path = os.path.abspath(os.path.dirname(obci.__file__)) os.environ['OBCI_INSTALL_DIR'] = obci_path obci_script = os.path.join(obci_path, 'control/launcher/obci_script.py') print 'Launching OBCI' subprocess.Popen( ['python', obci_script, "srv", "--win-silent"]) self.launched_obci = True except Exception: print "OBCI not installed" #send anonymous info to www.psychopy.org/usage.php #please don't disable this - it's important for PsychoPy's development self._latestAvailableVersion = None self.updater = None # connections are disabled for now #if self.prefs.connections['checkForUpdates'] or self.prefs.connections['allowUsageStats']: # connectThread = threading.Thread(target=connections.makeConnections, args=(self,)) # connectThread.start() ok, msg = compatibility.checkCompatibility(last, self.version, self.prefs, fix=True) if not ok and not self.firstRun and not self.testMode: #tell the user what has changed dlg = dialogs.MessageDialog(parent=None, message=msg, type='Info', title="Compatibility information") dlg.ShowModal() if self.prefs.app['showStartupTips'] and not self.testMode: tipIndex = self.prefs.appData['tipIndex'] tp = wx.CreateFileTipProvider( os.path.join(self.prefs.paths['resources'], "tips.txt"), tipIndex) showTip = wx.ShowTip(None, tp) self.prefs.appData['tipIndex'] = tp.GetCurrentTip() self.prefs.saveAppData() self.prefs.app['showStartupTips'] = showTip self.prefs.saveUserPrefs() #if self.prefs.connections['checkForUpdates']: # self.Bind(wx.EVT_IDLE, self.checkUpdates) #else: self.Bind(wx.EVT_IDLE, self.onIdle) return True def _wizard(self, selector, arg=''): from psychopy import core wizard = os.path.join(self.prefs.paths['psychopy'], 'wizard.py') so, se = core.shellCall([sys.executable, wizard, selector, arg], stderr=True) if se and self.prefs.app['debugMode']: print se # stderr contents; sometimes meaningless def firstrunWizard(self): self._wizard('--config', '--firstrun') # wizard typically creates html report file, but user can manually skip reportPath = os.path.join(self.prefs.paths['userPrefsDir'], 'firstrunReport.html') if os.path.exists(reportPath): report = open(reportPath, 'r').read() if 'Configuration problem' in report: # fatal error was encountered (currently only if bad drivers), so # before psychopy shuts down, ensure wizard will be triggered again: del self.prefs.appData['lastVersion'] self.prefs.saveAppData() def benchmarkWizard(self, evt=None): self._wizard('--benchmark') def checkUpdates(self, evt): #if we have internet and haven't yet checked for updates then do so if self._latestAvailableVersion not in [ -1, None ]: #we have a network connection but not yet tried an update #change IDLE routine so we won't come back here self.Unbind(wx.EVT_IDLE) #unbind all EVT_IDLE methods from app self.Bind(wx.EVT_IDLE, self.onIdle) #create updater (which will create dialogs as needed) self.updater = connections.Updater(app=self) self.updater.latest = self._latestAvailableVersion self.updater.suggestUpdate(confirmationDlg=False) evt.Skip() def onIdle(self, evt): evt.Skip() def getPrimaryDisplaySize(self): """Get the size of the primary display (whose coords start (0,0)) """ return list(wx.Display(0).GetGeometry())[2:] def showCoder(self, event=None, fileList=None): from psychopy.app import coder #have to reimport because it is ony local to __init__ so far if self.coder == None: self.coder = coder.CoderFrame(None, -1, title="PsychoPy2 Coder (IDE) (v%s)" % self.version, files=fileList, app=self) self.coder.Show(True) self.SetTopWindow(self.coder) self.coder.Raise() self.coder.setOutputWindow() #takes control of sys.stdout if self.coder not in self.allFrames: self.allFrames.append(self.coder) def newBuilderFrame(self, event=None, fileName=None): from psychopy.app.builder import builder #have to reimport because it is ony local to __init__ so far thisFrame = builder.BuilderFrame( None, -1, title="PsychoPy2 Experiment Builder (v%s)" % self.version, fileName=fileName, app=self) thisFrame.Show(True) thisFrame.Raise() self.SetTopWindow(thisFrame) self.builderFrames.append(thisFrame) self.allFrames.append(thisFrame) def showBuilder(self, event=None, fileList=[]): from psychopy.app import builder #have to reimport because it is ony local to __init__ so far for fileName in fileList: if os.path.isfile(fileName): self.newBuilderFrame(fileName=fileName) #create an empty Builder view if needed if len(self.builderFrames) == 0: self.newBuilderFrame() #loop through all frames, from the back bringing each forward for thisFrame in self.allFrames: if thisFrame.frameType != 'builder': continue thisFrame.Show(True) thisFrame.Raise() self.SetTopWindow(thisFrame) #def showShell(self, event=None): # from psychopy.app import ipythonShell#have to reimport because it is ony local to __init__ so far # if self.shell==None: # self.shell = ipythonShell.ShellFrame(None, -1, # title="IPython in PsychoPy (v%s)" %self.version, app=self) # self.shell.Show() # self.shell.SendSizeEvent() # self.shell.Raise() # self.SetTopWindow(self.shell) # self.shell.SetFocus() def openIPythonNotebook(self, event=None): """Note that right now this is bad because it ceases all activity in the main wx loop and the app has to be quit. We need it to run from a separate process? The necessary depends (zmq and tornado) were included from v1.78 onwards in the standalone """ import IPython.frontend.html.notebook.notebookapp instance = IPython.frontend.html.notebook.notebookapp.launch_new_instance( ) def openUpdater(self, event=None): from psychopy.app import connections dlg = connections.InstallUpdateDialog(parent=None, ID=-1, app=self) def colorPicker(self, event=None): """Opens system color-picker, sets clip-board and parent.new_rgb = string [r,g,b]. Note: units are psychopy -1..+1 rgb units to three decimal places, preserving 24-bit color """ class ColorPicker(wx.Panel): def __init__(self, parent): wx.Panel.__init__(self, parent, wx.ID_ANY) rgb = 'None' dlg = wx.ColourDialog(self) dlg.GetColourData().SetChooseFull(True) if dlg.ShowModal() == wx.ID_OK: data = dlg.GetColourData() rgb = data.GetColour().Get() rgb = map(lambda x: "%.3f" % ((x - 127.5) / 127.5), list(rgb)) rgb = '[' + ','.join(rgb) + ']' if wx.TheClipboard.Open(): #http://wiki.wxpython.org/AnotherTutorial#wx.TheClipboard wx.TheClipboard.Clear() wx.TheClipboard.SetData(wx.TextDataObject(str(rgb))) wx.TheClipboard.Close() dlg.Destroy() parent.new_rgb = rgb frame = wx.Frame(None, wx.ID_ANY, "Color picker", size=(0, 0)) # not shown ColorPicker(frame) new_rgb = frame.new_rgb # string; also on system clipboard, try wx.TheClipboard frame.Destroy() return new_rgb def openMonitorCenter(self, event): from psychopy.monitors import MonitorCenter self.monCenter = MonitorCenter.MainFrame(None, 'PsychoPy2 Monitor Center') self.monCenter.Show(True) def MacOpenFile(self, fileName): logging.debug('PsychoPyApp: Received Mac file dropped event') if fileName.endswith('.py'): if self.coder == None: self.showCoder() self.coder.setCurrentDoc(fileName) elif fileName.endswith('.psyexp'): self.newBuilderFrame(fileName=fileName) def terminateHubProcess(self): """ Send a UPD message to iohub informing it to exit. Use this when force quiting the experiment script process so iohub knows to exit as well. If message is not sent within 1 second, or the iohub server address in incorrect,the issue is logged. """ sock = None try: logging.debug('PsychoPyApp: terminateHubProcess called.') import socket sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.settimeout(1.0) iohub_address = '127.0.0.1', 9034 import msgpack tx_data = msgpack.Packer().pack(('STOP_IOHUB_SERVER', )) return sock.sendto(tx_data, iohub_address) except socket.error, e: logging.debug('PsychoPyApp: terminateHubProcess socket.error: %s' % (str(e))) except socket.herror, e: logging.debug( 'PsychoPyApp: terminateHubProcess socket.herror: %s' % (str(e)))
"Experimenter Initials", "Subject Initials", ] sub_info_order = [ "Subject ID", "Condition", "Start Date", "Experiment", "Testing Location", "Experimenter Initials", "Subject Initials", ] exp_info = get_subject_info(data_dir, experiment_name) logging.debug(exp_info["Subject ID"]) logging.debug(type(exp_info["Subject ID"])) ################################ # * CONDITIONS ################################ # Choose condition and question/instruction text condition = conditions[int(exp_info["Subject ID"]) % len(conditions)] exp_info["Condition"] = condition # save to exp_info question_label = question_labels[condition] # Make data files data_file = make_data_file(data_dir, exp_info, info_order) subject_file = make_subject_file(data_dir, exp_info, sub_info_order) ################################