Example #1
0
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
Example #2
0
    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))
Example #3
0
    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)
Example #4
0
    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()
Example #5
0
    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()
Example #6
0
    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)
Example #7
0
    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)
Example #8
0
    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)
Example #9
0
    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
Example #10
0
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
Example #11
0
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
Example #12
0
    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()
Example #13
0
    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
Example #14
0
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)
Example #15
0
    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)
Example #16
0
    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))
Example #17
0
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)
Example #18
0
 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])))
Example #19
0
    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()
Example #20
0
    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
Example #21
0
    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)
Example #22
0
 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')
Example #23
0
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
Example #24
0
    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
Example #26
0
    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)
Example #27
0
 def __del__(self):
     try:
         self.endRemoteMode()
         time.sleep(0.1)
         self.com.close()
         logging.debug('Closed PR655 port')
     except:
         pass
Example #28
0
 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
Example #29
0
 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)
Example #30
0
 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
Example #31
0
    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))
Example #32
0
# 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
Example #33
0
    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))
Example #34
0
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
Example #35
0
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
Example #36
0
 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]))
Example #37
0
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)
Example #38
0
    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
Example #39
0
    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)
Example #40
0
    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))
Example #41
0
    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.')
Example #42
0
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
Example #43
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()))
Example #44
0
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')
Example #45
0
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)
Example #46
0
            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...')
Example #47
0
# 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.
Example #48
0
 def showPrefs(self, event):
     from psychopy.app.preferencesDlg import PreferencesDlg
     logging.debug('PsychoPyApp: Showing prefs dlg')
     prefsDlg = PreferencesDlg(app=self)
     prefsDlg.Show()
Example #49
0
 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()
Example #51
0
    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)
Example #52
0
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()
Example #53
0
# 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
Example #54
0
 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)
Example #55
0
 def clearEvents(self):
     """Discard all button / voice key events.
     """
     self.events[:] = []
     self.commands.clear_received_reports()
     logging.debug('bbox clear events')
Example #56
0
    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)
Example #57
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
Example #58
0
 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])))
Example #59
0
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)

################################