Exemple #1
0
def RunSequence( sequence ):
    #setup rules
    global rules
    global currentRule
    global ruleList; ruleList = [] #a list of tuples containing the sequence of sorting rules (0, 1, 2, 3) and required n of correct repeats per set(5, 6, 7)
    for item in sequence['blocks']:
        ruleList.append( (int(item['rule']), int(item['reps'])) )
    print 'RULELIST:', ruleList
    global RULE_COUNT; RULE_COUNT = len( ruleList )
    global ruleCount, cardCount, rightAnswers;
    ruleCount = 0;

    ShowInstruction( u'Aloita painamalla jotain näppäintä', -1 )

    while ruleCount < RULE_COUNT: 
        currentRule = rules[ruleList[ruleCount][0]]
        SetupTrial()
        NextTrial( taskType )
        answer = GetResponse()

        if answer == 0: #ESC
            break
        else:
            GiveFeedback( taskType, answer )

        #if enough right answers given, update rule
        if answer > 0:
            if rightAnswers % ruleRepeats == 0: # rightAnswers can't be 0 here since retVal wouldn't be > 0
                ruleCount += 1
                rightAnswers = 0
                logging.flush() #now with every rule change!

        cardCount +=1
Exemple #2
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
Exemple #3
0
    def getEvents(self, timeout=10):
        """Look for a string that matches SDAT;\n.........EDAT;\n
        and process it as events
        """
        foundDataStart=False
        t0=time.time()
        while not foundDataStart and (time.time()-t0)<timeout:
            if self.com.readline().startswith('SDAT'):
                foundDataStart=True
                logging.info("BBTK.getEvents() found data. Processing...")
                logging.flush() #we aren't in a time-critical period so flush messages
                break
        #check if we're processing data
        if not foundDataStart:
            logging.warning("BBTK.getEvents() found no data (SDAT was not found on serial port inputs")
            return []

        #helper function to parse time and event code
        def parseEventsLine(line, lastState=None):
            """Returns a list of dictionaries, one for each change detected in the state
            """
            state = line[:12]
            timeSecs = int(line[-14:-2])/10.0**6
            evts=[]
            evt=''
            if lastState is None:
                evts.append({'evt':'', 'state':state, 'time':timeSecs})
            else:
                for n in evtChannels.keys():
                    if state[n]!=lastState[n]:
                        if state[n]=='1':
                            evt = evtChannels[n]+"_on"
                        else:
                            evt = evtChannels[n]+"_off"
                        evts.append({'evt':evt, 'state':state, 'time':timeSecs})
            return evts

        #we've been sent data so work through it
        events=[]
        eventLines=[]
        lastState=None
        #try to read from port
        self.pause()
        self.com.setTimeout(2.0)
        nEvents = int(self.com.readline()[:-2]) #last two chars are ;\n
        self.com.readline()[:-2] # microseconds recorded (ignore)
        self.com.readline()[:-2] #samples recorded (ignore)
        while True:
            line = self.com.readline()
            if line.startswith('EDAT'): #end of data stream
                break
            events.extend(parseEventsLine(line, lastState))
            lastState = events[-1]['state']
            eventLines.append(line)
        if nEvents != len(eventLines):
            logging.warning("BBTK reported %i events but told us to expect %i events!!" %(len(events), nEvents))
        logging.flush() #we aren't in a time-critical period so flush messages
        return events
Exemple #4
0
 def finish_sync(self):
     """Rebuilds index and saves project file when the sync has finished
     """
     proj = self.proj()
     # when local/remote updates are complete refresh index based on local
     proj.local.rebuild_index()
     proj.index = proj.local.index
     self._set_empty()
     proj.save()
     if hasattr(logging, 'flush'):  # psychopy.logging has control of flush
         logging.flush()
Exemple #5
0
 def _warnTesting(self):
     msg = "We need to run some tests on your graphics card (hopefully just once). " + \
         "The BitsSharp will go into status mode while this is done. " + \
         "It can take a minute or two."
     logging.warn(msg)
     logging.flush()
     msgOnScreen = visual.TextStim(self.win, msg)
     msgOnScreen.draw()
     self.win.flip()
     core.wait(1.0)
     self.win.flip()
Exemple #6
0
def quit():
    """Close everything and exit nicely (ending the experiment)
    """
    #pygame.quit() #safe even if pygame was never initialised
    logging.flush()
    for thisThread in threading.enumerate():
        if hasattr(thisThread,'stop') and hasattr(thisThread,'running'):
            #this is one of our event threads - kill it and wait for success
            thisThread.stop()
            while thisThread.running==0:
                pass#wait until it has properly finished polling
    sys.exit(0)#quits the python session entirely
Exemple #7
0
 def sendMessage(self, message, autoLog=True):
     """Send a command to the device (does not wait for a reply or sleep())
     """
     if self.com.inWaiting():
         inStr = self.com.read(self.com.inWaiting())
         logging.warning("Sending '%s' to %s but found '%s' on the input buffer" %(message, self.name, inStr))
     if not message.endswith(self.eol):
         message += self.eol     #append a newline if necess
     self.com.write(message)
     self.com.flush()
     if autoLog:
         logging.debug('Sent %s message:' %(self.name) +message.replace(self.eol, ''))#send complete message
         logging.flush() #we aren't in a time-critical period so flush messages
Exemple #8
0
def shut_down_cleanly(subdata,win):
    """
    shut down experiment and try to save data
    """
    
    win.close()
    logging.flush()
    try:
        f=open('Output/%s_%s_subdata.pkl'%(subdata['subcode'],subdata['datestamp']),'wb')
        pickle.dump(subdata,f)
        f.close()
    except:
        pass
Exemple #9
0
def throw_ball(fromP, toP):
    global trialCnt, holder, rndCnt
    key = "%ito%i" % (fromP,toP)
    
    logging.log(level=logging.DATA, msg="round %i - trial %i - throw: %s - %s" % (round, trialCnt, key, condition))
    
    for s in throw[key]:
        players.setImage('images/%s/%s' % (key,s))
        players.draw()
        win.flip()
        core.wait(0.15)
    
    trialCnt+=1
    rndCnt+=1
    holder=toP
    logging.flush()
    select_throw()
Exemple #10
0
 def sendMessage(self, message, autoLog=True):
     """Send a command to the device (does not wait for a reply or sleep())
     """
     if self.com.inWaiting():
         inStr = self.com.read(self.com.inWaiting())
         msg = "Sending '%s' to %s but found '%s' on the input buffer"
         logging.warning(msg % (message, self.name, inStr))
     if type(message) is not bytes:
         message = bytes(message, 'utf-8')
     if not message.endswith(self.eol):
         message += self.eol  # append a newline if necess
     self.com.write(message)
     self.com.flush()
     if autoLog:
         msg = b'Sent %s message:' % (self.name)
         logging.debug(msg + message.replace(self.eol, b''))  # complete msg
         # we aren't in a time-critical period so flush msg
         logging.flush()
Exemple #11
0
def quit():
    """Close everything and exit nicely (ending the experiment)
    """
    #pygame.quit() #safe even if pygame was never initialised
    logging.flush()
    for thisThread in threading.enumerate():
        if hasattr(thisThread,'stop') and hasattr(thisThread,'running'):
            #this is one of our event threads - kill it and wait for success
            thisThread.stop()
            while thisThread.running==0:
                pass#wait until it has properly finished polling
    # could check serverCreated() serverBooted() but then need to import pyo
    # checking serverCreated() does not tell you whether it was shutdown or not
    for ps in pyoServers: # should only ever be one Server instance...
        ps.stop()
        wait(.25)
        ps.shutdown()
    sys.exit(0)#quits the python session entirely
Exemple #12
0
 def clearMemory(self):
     """
     """
     self.sendMessage('SPIE')
     self.pause()
     reply = self.getResponse(timeout=10)
     # should return either FRMT or ESEC to indicate it started
     if reply.startswith('FRMT'):
         logging.info("BBTK.clearMemory(): "
                      "Starting full format of BBTK memory")
     elif reply.startswith('ESEC'):
         logging.info("BBTK.clearMemory(): "
                      "Starting quick erase of BBTK memory")
     else:
         logging.error("BBTK.clearMemory(): "
                       "didn't get a reply from %s" % str(self.com))
         return False
     # we aren't in a time-critical period so flush messages
     logging.flush()
     # now wait until we get told 'DONE'
     self.com.timeout = 20
     retVal = self.com.readline()
     if retVal.startswith("DONE"):
         logging.info("BBTK.clearMemory(): completed")
         # we aren't in a time-critical period so flush messages
         logging.flush()
         return True
     else:
         logging.error("BBTK.clearMemory(): "
                       "Stalled waiting for %s" % str(self.com))
         # we aren't in a time-critical period so flush messages
         logging.flush()
         return False
Exemple #13
0
    def _upload(stuff):
        """assumes that SELECTOR_FOR_TEST_UPLOAD is a configured http server
        """
        selector = SELECTOR_FOR_TEST_UPLOAD
        basicAuth = BASIC_AUTH_CREDENTIALS

        # make a tmp dir just for testing:
        tmp = mkdtemp()
        filename = "test.txt"
        tmp_filename = os.path.join(tmp, filename)
        f = open(tmp_filename, "w+")
        f.write(stuff)
        f.close()

        # get local sha256 before cleanup:
        digest = hashlib.sha256()
        digest.update(open(tmp_filename).read())
        dgst = digest.hexdigest()

        # upload:
        status = upload(selector, tmp_filename, basicAuth)
        shutil.rmtree(tmp)  # cleanup; do before asserts

        # test
        good_upload = True
        disgest_match = False
        if not status.startswith("success"):
            good_upload = False
        elif status.find(dgst) > -1:
            logging.exp("digests match")
            digest_match = True
        else:
            logging.error("digest mismatch")

        logging.flush()
        assert good_upload  # remote server FAILED to report success
        assert digest_match  # sha256 mismatch local vs remote file

        return int(status.split()[3])  # bytes
Exemple #14
0
 def recordStimulusData(self, duration):
     """Record data for a given duration (seconds) and return a list of
     events that occured in that period.
     """
     self.sendMessage("DSCM")
     logging.flush() #we aren't in a time-critical period so flush messages
     time.sleep(5.0)
     self.sendMessage("TIML")
     logging.flush() #we aren't in a time-critical period so flush messages
     self.pause()
     self.sendMessage("%i" %(int(duration*1000000)), autoLog=False) #BBTK expects this in microsecs
     self.pause()
     self.sendMessage("RUDS")
     logging.flush() #we aren't in a time-critical period so flush messages
global lastScore
global triggers; triggers=(confInfo[4]=='True') # flag as True when actually recording 

#SETUP CARDS 
cardPrototype = {'G1':0, 'G2':0, 'L1':0, 'L2':0, 'fn':''}

#setup deck of four cards from the whole deck
#deck = SelectCardSubset( 4, N_OF_CARDS )

global currentTgt; currentTgt = (-1, -1, -1, -1)

#TODO: ADD ERROR CHECKING! Here we trust the json files to be correctly formed and valid
confFile = open( '.'+s+'configs'+s+confInfo[3]+'.json' )
config = json.loads( confFile.read() )
confFile.close()
logging.flush()

gameScore = 0
lastScore = 0

triggerAndLog(portCodes['start'], "STR", 0, 0, "START: " + str( startTime ) )

win.setMouseVisible( False )

# - BASELINE VIDEO ------------------------------------------------------------------------------#
movie_filename = "D:/Experiments/video/habit_video_01_wcst.avi"
movie_baseline = visual.MovieStim(win = win, filename = movie_filename, pos = [0,0], size = (1350,1080))
if DEBUGGING_MODE:
    for i in range(25 * 3):
        movie_baseline.draw()
        win.flip()
Exemple #16
0
    def findIdentityLUT(
        self,
        maxIterations=1000,
        errCorrFactor=1.0 /
        5000,  # amount of correction done for each iteration
        nVerifications=50,  #number of repeats (successful) to check dithering has been eradicated
        demoMode=True,  #generate the screen but don't go into status mode
        logFile='',
    ):
        """Search for the identity LUT for this card/operating system.
        This requires that the window being tested is fullscreen on the Bits#
        monitor (or at least occupys the first 256 pixels in the top left corner!)

        :params:

            LUT: The lookup table to be tested (256x3). If None then the LUT will not be altered

        :returns:

            a 256x3 array of error values (integers in range 0:255)
        """
        t0 = time.time()
        #create standard options
        LUTs = {}
        LUTs['intel'] = np.repeat(np.linspace(.05, .95, 256),
                                  3).reshape([-1, 3])
        LUTs['0-255'] = np.repeat(np.linspace(0, 1.0, 256), 3).reshape([-1, 3])
        LUTs['0-65535'] = np.repeat(
            np.linspace(0.0, 65535.0 / 65536.0, num=256), 3).reshape([-1, 3])
        LUTs['1-65536'] = np.repeat(
            np.linspace(0.0, 65535.0 / 65536.0, num=256), 3).reshape([-1, 3])

        if logFile:
            self.logFile = open(logFile, 'w')

        if plotResults:
            pyplot.Figure()
            pyplot.subplot(1, 2, 1)
            pyplot.plot([0, 255], [0, 255], '-k')
            errPlot = pyplot.plot(range(256), range(256), '.r')[0]
            pyplot.subplot(1, 2, 2)
            pyplot.plot(200, 0.01, '.w')
            pyplot.show(block=False)

        lowestErr = 1000000000
        bestLUTname = None
        logging.flush()
        for LUTname, currentLUT in LUTs.items():
            sys.stdout.write('Checking %r LUT:' % (LUTname))
            errs = self.testLUT(currentLUT, demoMode)
            if plotResults:
                errPlot.set_ydata(range(256) + errs[:, 0])
                pyplot.draw()
            print('mean err = %.3f per LUT entry' % (abs(errs).mean()))
            if abs(errs).mean() < abs(lowestErr):
                lowestErr = abs(errs).mean()
                bestLUTname = LUTname
        if lowestErr == 0:
            print("The %r identity LUT produced zero error. We'll use that!" %
                  (LUTname))
            self.identityLUT = LUTs[bestLUTname]
            self.save()  #it worked so save this configuration for future
            return

        print("Best was %r LUT (mean err = %.3f). Optimising that..." %
              (bestLUTname, lowestErr))
        currentLUT = LUTs[bestLUTname]
        errProgression = []
        corrInARow = 0
        for n in range(maxIterations):
            errs = self.testLUT(currentLUT)
            tweaks = errs * errCorrFactor
            currentLUT -= tweaks
            currentLUT[currentLUT > 1] = 1.0
            currentLUT[currentLUT < 0] = 0.0
            meanErr = abs(errs).mean()
            errProgression.append(meanErr)
            if plotResults:
                errPlot.set_ydata(range(256) + errs[:, 0])
                pyplot.subplot(1, 2, 2)
                if meanErr == 0:
                    point = '.k'
                else:
                    point = '.r'
                pyplot.plot(n, meanErr, '.k')
                pyplot.draw()
            if meanErr > 0:
                sys.stdout.write("%.3f " % meanErr)
                corrInARow = 0
            else:
                sys.stdout.write(". ")
                corrInARow += 1
            if corrInARow >= nVerifications:
                print('success in a total of %.1fs' % (time.time() - t0))
                self.identityLUT = currentLUT
                self.save()  #it worked so save this configuration for future
                break
            elif len(errProgression) > 10 and max(errProgression) - min(
                    errProgression) < 0.001:
                print(
                    "Trying to correct the gamma table was having no effect. Make sure the window was fullscreen and on the Bits# screen"
                )
                break

        #did we get here by failure?!
        if n == (maxIterations - 1):
            print(
                "failed to converge on a successful identity LUT. This is BAD!"
            )

        if plotResults:
            pyplot.figure(figsize=[18, 12])
            pyplot.subplot(1, 3, 1)
            pyplot.plot(errProgression)
            pyplot.title('Progression of errors')
            pyplot.ylabel("Mean error per LUT entry (0-1)")
            pyplot.xlabel("Test iteration")
            r256 = np.reshape(range(256), [256, 1])
            pyplot.subplot(1, 3, 2)
            pyplot.plot(r256, r256, 'k-')
            pyplot.plot(r256, currentLUT[:, 0] * 255, 'r.', markersize=2.0)
            pyplot.plot(r256, currentLUT[:, 1] * 255, 'g.', markersize=2.0)
            pyplot.plot(r256, currentLUT[:, 2] * 255, 'b.', markersize=2.0)
            pyplot.title('Final identity LUT')
            pyplot.ylabel("LUT value")
            pyplot.xlabel("LUT entry")

            pyplot.subplot(1, 3, 3)
            deviations = currentLUT - r256 / 255.0
            pyplot.plot(r256, deviations[:, 0], 'r.')
            pyplot.plot(r256, deviations[:, 1], 'g.')
            pyplot.plot(r256, deviations[:, 2], 'b.')
            pyplot.title('LUT deviations from sensible')
            pyplot.ylabel("LUT value")
            pyplot.xlabel("LUT deviation (multiples of 1024)")
            pyplot.savefig("bitsSharpIdentityLUT.pdf")
            pyplot.show()
Exemple #17
0
def main():
    global PART_ID  # PART_ID is used in case of error on @atexit, that's why it must be global
    # === Dialog popup ===
    info = {'IDENTYFIKATOR': '', u'P\u0141EC': ['M', "K"], 'WIEK': '20'}
    dictDlg = gui.DlgFromDict(dictionary=info, title='Czas detekcji wzrokowej')
    if not dictDlg.OK:
        abort_with_error('Info dialog terminated.')

    # === Scene init ===
    win = visual.Window(SCREEN_RES,
                        fullscr=True,
                        monitor='testMonitor',
                        units='pix',
                        screen=0,
                        color='black')
    event.Mouse(visible=False, newPos=None, win=win)  # Make mouse invisible
    PART_ID = info['IDENTYFIKATOR'] + info[u'P\u0141EC'] + info['WIEK']
    logging.LogFile(join(
        '.', 'results',
        f"{PART_ID}_{str(random.choice(range(100, 1000)))}.log"),
                    level=logging.INFO)  # errors logging
    logging.info('FRAME RATE: {}'.format(FRAME_RATE))
    logging.info('SCREEN RES: {}'.format(SCREEN_RES))
    pos_feedb = visual.TextStim(win,
                                text=u'Poprawna odpowied\u017A',
                                color='grey',
                                height=40)
    neg_feedb = visual.TextStim(win,
                                text=u'Niepoprawna odpowied\u017A',
                                color='grey',
                                height=40)
    no_feedb = visual.TextStim(win,
                               text=u'Nie udzieli\u0142e\u015B odpowiedzi',
                               color='grey',
                               height=40)
    show_info(win, join('.', 'messages', 'hello.txt'))

    for proc_version in ['SQUARES', 'CIRCLES']:
        left_stim = visual.ImageStim(win,
                                     image=join('.', 'stims',
                                                f'{proc_version}_LEFT.bmp'))
        right_stim = visual.ImageStim(win,
                                      image=join('.', 'stims',
                                                 f'{proc_version}_RIGHT.bmp'))
        mask_stim = visual.ImageStim(win,
                                     image=join('.', 'stims',
                                                f'{proc_version}_MASK.bmp'))
        # fix_stim = visual.TextStim(win, text='+', height=100, color='grey')
        fix_stim = visual.ImageStim(win,
                                    image=join('.', 'stims',
                                               'PRE_STIMULI.bmp'))
        arrow_label = visual.TextStim(win,
                                      text=u"\u2190       \u2192",
                                      color='grey',
                                      height=30,
                                      pos=(0, -200))
        if proc_version == 'SQUARES':
            question = u'Gdzie pojawi\u0142 si\u0119 OBROCONY kwadrat?'
        elif proc_version == 'CIRCLES':
            question = u'Gdzie pojawi\u0142 si\u0119 WI\u0118KSZY okr\u0119g?'
        else:
            raise NotImplementedError(
                f'Stimulus type: {proc_version} not implemented.')

        question_text = visual.TextStim(win,
                                        text=question,
                                        color='grey',
                                        height=20,
                                        pos=(0, -180))

        # === Load data, configure log ===

        response_clock = core.Clock()
        conf = yaml.load(
            open(join('.', 'configs', f'{proc_version}_config.yaml')))

        show_info(win,
                  join('.', 'messages', f'{proc_version}_before_training.txt'))
        # === Training ===

        training = NUpNDown(start_val=conf['START_SOA'],
                            max_revs=conf['MAX_REVS'])

        old_rev_count_val = -1
        correct_trials = 0
        soas = []
        fix_time = conf['TRAIN_FIX_TIME']
        for idx, soa in enumerate(training):
            corr, rt, rating = run_trial(conf, fix_stim, left_stim, mask_stim,
                                         fix_time, right_stim, soa, win,
                                         arrow_label, question_text,
                                         response_clock)
            training.set_corr(corr)
            level, reversal, revs_count = map(int, training.get_jump_status())
            if reversal:
                soas.append(soa)
            if old_rev_count_val != revs_count:
                old_rev_count_val = revs_count
                rev_count_val = revs_count
            else:
                rev_count_val = '-'

            RESULTS.append([
                PART_ID, idx, proc_version, 1, fix_time, conf['MTIME'],
                int(corr), soa, level, reversal, rev_count_val, rt, rating
            ])

            ### FEEDBACK
            if corr == 1:
                feedb_msg = pos_feedb
                correct_trials += 1
            elif corr == 0:
                feedb_msg = neg_feedb
            else:
                feedb_msg = no_feedb
            for _ in range(30):
                feedb_msg.draw()
                check_exit()
                win.flip()
            # break + jitter
            wait_time_in_secs = 1 + random.choice(range(0, 60)) / 60.0
            core.wait(wait_time_in_secs)

        # === experiment ===
        soa = int(np.mean(soas[:int(0.6 * len(soas))]))
        experiment = [soa] * conf['NO_TRIALS']

        fix_time = conf['EXP_FIX_TIME']
        show_info(win, join('.', 'messages', f'{proc_version}_feedback.txt'))
        for idx in range(idx, conf['NO_TRIALS'] + idx):
            corr, rt, rating = run_trial(conf, fix_stim, left_stim, mask_stim,
                                         fix_time, right_stim, soa, win,
                                         arrow_label, question_text,
                                         response_clock)
            corr = int(corr)
            correct_trials += corr
            RESULTS.append([
                PART_ID, idx, proc_version, 0, fix_time, conf['MTIME'], corr,
                soa, '-', '-', '-', rt, rating
            ])
            # break + jitter
            wait_time_in_secs = 1 + random.choice(range(0, 60)) / 60.0
            core.wait(wait_time_in_secs)

    show_info(win, join('.', 'messages', 'iaf.txt'))
    fix_cross = visual.TextStim(win,
                                text=u"+",
                                color='grey',
                                height=60,
                                pos=(0, 0))
    fix_cross.draw()
    win.callOnFlip(PORT.setData, TriggerTypes.REST_START)
    win.flip()
    core.wait(0.04)
    PORT.setData(TriggerTypes.CLEAR)
    core.wait(conf['RESTTIME'])
    win.flip()
    PORT.setData(TriggerTypes.REST_END)
    core.wait(0.04)
    PORT.setData(TriggerTypes.CLEAR)
    # === Cleaning time ===
    save_beh_results()
    logging.flush()
    show_info(win, join('.', 'messages', 'end.txt'))
    win.close()
def main(info):
    # save log of subjects
    write_subjectlog(subjectlog, info)
    run_nr = int(info['run_nr'])
    subj = info['subject_id']
    fullscr = info['fullscr']
    time = core.Clock()
    subj_dir = pjoin(RESDIR, 'sub-' + subj)
    if not pexists(subj_dir):
        os.makedirs(subj_dir)
    log_fn = config['log_template'].format(
        subj=subj,
        task_name=config['task_name'],
        runnr=run_nr,
        timestamp=ptime.strftime(time_template),
    )
    log_fn = pjoin(subj_dir, log_fn)
    log_responses = logging.LogFile(log_fn, level=logging.INFO)
    # set up global key for quitting; if that happens, log will be moved to
    # {log_fn}__halted.txt
    event.globalKeys.add(key='q',
                         modifiers=['ctrl'],
                         func=move_halted_log,
                         func_args=[log_fn],
                         name='quit experiment gracefully')
    # --- LOAD STIMULI ORDER FOR THIS PARTICIPANT ---
    stim_json = pjoin(PWD, 'cfg',
                      'sub-{0}_task-localizer_4runs.json'.format(subj))
    # create stimulus order if not existing
    if not os.path.exists(stim_json):
        logging.warning("Creating stimulus order for {0}".format(subj))
        MAKESTIMPY = pjoin(HERE, 'make_stim_order.py')
        cmd = "python {cmd} --subid {subj} --output {output} " \
              "--nruns 4".format(cmd=MAKESTIMPY, subj=subj,
                                 output=dirname(stim_json))
        logging.warning("Running '{0}'".format(cmd))
        sp.check_call(cmd.split())
    with open(stim_json, 'rb') as f:
        stimuli = json.load(f)[str(run_nr)]
    # ------------------------
    print "Opening screen"
    tbegin = time.getTime()
    using_scanner = info['scanner?']
    # Setting up visual
    size = [1280, 1024]
    scrwin = visual.Window(size=size,
                           allowGUI=False, units='pix',
                           screen=1, rgb=[-1, -1, -1],
                           fullscr=fullscr)
    # load clips
    print "Loading stimuli"
    loading = visual.TextStim(scrwin,
                              text="Loading stimuli...",
                              height=31)
    loading.draw()
    scrwin.flip()
    stimuli_clip = dict()
    for stim in stimuli:
        if stim['stim_type'] != 'fixation':
            stim_fn = stim['stim_fn']
            print("Loading {0}".format(stim_fn))
            stimuli_clip[stim_fn] = \
                visual.MovieStim3(scrwin, pjoin(PWD, stim_fn),
                                  size=(1280, 940),
                                  name=stim_fn,
                                  noAudio=True, loop=True)
    scrwin.flip()
    cross_hair = visual.TextStim(scrwin, text='+', height=31,
                                 pos=(0, 0), color='#FFFFFF')
    if using_scanner:
        intro_msg = "Waiting for trigger..."
    else:
        intro_msg = "Press Enter to start"
    intro_msg = instructions + '\n' + intro_msg
    intro = visual.TextStim(scrwin, text=intro_msg, height=31, wrapWidth=900)
    # Start of experiment
    intro.draw()
    scrwin.flip()
    # open up serial port and wait for first trigger
    if using_scanner:
        ser_port = '/dev/ttyUSB0'
        ser = serial.Serial(ser_port, 115200, timeout=.0001)
        ser.flushInput()
        trigger = ''
        while trigger != '5':
            trigger = ser.read()
    else:
        from psychopy.hardware.emulator import launchScan
        event.waitKeys(keyList=['return'])
        # XXX: set up TR here
        MR_settings = {
            'TR': 1,
            'volumes': 280,
            'sync': '5',
            'skip': 3,
            'sound': False,
        }
        vol = launchScan(scrwin, MR_settings, globalClock=time, mode='Test')

        class FakeSerial(object):
            @staticmethod
            def read():
                k = event.getKeys(['1', '2', '5'])
                return k[-1] if k else ''

        ser = FakeSerial()

    # set up timer for experiment starting from first trigger
    timer_exp = core.Clock()
    trunbegin = timer_exp.getTime()
    # setup bids log
    logbids("onset\tduration\tstim_type\trepetition")
    # duration will be filled later
    template_bids = '{onset:.3f}\t{duration:.3f}\t{stim_type}\t{stim_fn}\t' \
                    '{repetition}'
    # and now we just loop through the trials
    for trial in stimuli:
        stim_type = trial['stim_type']
        stim_fn = trial['stim_fn']
        duration = trial['duration']
        logbids(template_bids.format(
            onset=timer_exp.getTime(),
            duration=duration,
            stim_type=stim_type,
            stim_fn=stim_fn,
            repetition=trial.get('repetition', 0)),
        )
        trial_counter = core.CountdownTimer(duration)
        if stim_type == 'fixation':
            cross_hair.draw()
            scrwin.flip()
            logging.flush()
            while trial_counter.getTime() > 0:
                pass
        else:
            movie = stimuli_clip[stim_fn]
            while trial_counter.getTime() > 0:
                key = ser.read()
                if key in ['1', '2']:
                    logbids(template_bids.format(
                        onset=timer_exp.getTime(),
                        duration=0.,
                        stim_type='button_press',
                        stim_fn=None,
                        repetition=0)
                    )
                if movie.status != visual.FINISHED:
                    movie.draw()
                    scrwin.flip()
                else:
                    cross_hair.draw()
                    scrwin.flip()
    logging.exp("Done in {0:.2f}s".format(timer_exp.getTime()))
    logging.flush()
    scrwin.close()
    core.quit()
Exemple #19
0
    # check if all components have finished
    if not continueRoutine:  # a component has requested a forced-end of Routine
        break
    continueRoutine = False  # will revert to True if at least one component still running
    for thisComponent in trialComponents:
        if hasattr(thisComponent,
                   "status") and thisComponent.status != FINISHED:
            continueRoutine = True
            break  # at least one component has not yet finished

    # check for quit (the Esc key)
    if endExpNow or event.getKeys(keyList=["escape"]):
        core.quit()

    # refresh the screen
    if continueRoutine:  # don't flip if this routine is over or we'll get a blank screen
        win.flip()

# -------Ending Routine "trial"-------
for thisComponent in trialComponents:
    if hasattr(thisComponent, "setAutoDraw"):
        thisComponent.setAutoDraw(False)
# these shouldn't be strictly necessary (should auto-save)
thisExp.saveAsWideText(filename + '.csv')
thisExp.saveAsPickle(filename)
logging.flush()
# make sure everything is closed down
thisExp.abort()  # or data files will save again on exit
win.close()
core.quit()
def handle_audio(G):
    '''
    this should handle the audio stimuli, using the async programming style.
    it starts a new clock and depending on timings, will start some audio
    samples, L or R, 40 or 55 Hz.
    '''

    audio_stim_list = G['A']['audio_stim_list']
    astims = G['astims']
    eh = G['eh']

    audioClock = clock.Clock()
    playing = False
    withinAudioBlock = False
    prevWithinAudioBlock = False
    RunAudio = True

    currentTime = audioClock.getTime()

    logging.data('aud_BEGIN')
    eh.send_message('aud_BEGIN')
    # just before going in here -- LOG it.
    # log the beginning...

    while currentTime < 340.:  #currentTime < 340.:

        # print('hello')
        # print(currentTime)

        if not playing:  # I can safely use this since only one audio is playing at a time.

            withinAudioBlock = False

            for item in audio_stim_list:
                b, e, stim = item
                if b < currentTime < e:
                    currentStim = stim
                    withinAudioBlock = True
                    astims[stim].play()
                    playDuration = astims[stim].getDuration()
                    playing = True
                    playClock = clock.Clock()

                    # print(stim)
                    logging.data(stim)
                    eh.send_message(stim)
                    logging.flush()

        else:
            if playClock.getTime(
            ) > playDuration:  # figure out if something is playing
                playing = False

        # try dealing with begin and ending markers:
        if withinAudioBlock and not prevWithinAudioBlock:
            messg = currentStim.replace('_', '_b')
            # print(messg)
            logging.data(messg)
            eh.send_message(messg)
            prevWithinAudioBlock = True

        elif prevWithinAudioBlock and not withinAudioBlock:
            messg = currentStim.replace('_', '_e')
            # print(messg)
            logging.data(messg)
            eh.send_message(messg)
            prevWithinAudioBlock = False

        # this will stop this loop, probably:
        currentTime = audioClock.getTime()
        #if currentTime > 340.:
        #    print('Stopping!')
        #    RunAudio=False

        yield From(asyncio.sleep(
            0))  # pass control to someone else, while this guy sleeps a bit.

    logging.data('aud_END')
    eh.send_message('aud_END')
def run1_func(expInfo):
    # main function
    # Ensure that relative paths start from the same directory as this script
    _thisDir = os.path.dirname(os.path.abspath(__file__)).decode(
        sys.getfilesystemencoding())
    os.chdir(_thisDir)

    # Store info about the experiment session
    expName = u'tnac_exp'  # from the Builder filename that created this script
    expInfo['expName'] = expName

    # Data file name stem = absolute path + name; later add .psyexp, .csv, .log, etc
    filename = _thisDir + os.sep + u'data/%s_%s_%s' % (
        expInfo['participant'], expName, expInfo['date'])

    trg_dict = {
        "music": 1,
        "voice": 2,
        "song": 3,
        "sound_off": 100,
        "pause_block": 200,
        "stop_run": 300,
        "start_run": 400,
        "start_block": 500,
    }

    #define path to csvs
    run_var = _thisDir + '/run1.csv'
    # An ExperimentHandler isn't essential but helps with data saving
    thisExp = data.ExperimentHandler(name=expName,
                                     version='',
                                     extraInfo=expInfo,
                                     runtimeInfo=None,
                                     originPath=None,
                                     savePickle=True,
                                     saveWideText=True,
                                     dataFileName=filename)
    # save a log file for detail verbose info
    logFile = logging.LogFile(filename + '.log', level=logging.EXP)
    logging.console.setLevel(
        logging.WARNING)  # this outputs to the screen, not a file

    endExpNow = False  # flag for 'escape' or other condition => quit the exp

    # Start Code - component code to be run before the window creation

    # Setup the Window
    ## TODO: set window to fullscreen
    win = visual.Window(size=[1366, 768],
                        fullscr=True,
                        screen=0,
                        allowGUI=True,
                        allowStencil=False,
                        monitor='testMonitor',
                        color=[0, 0, 0],
                        colorSpace='rgb',
                        blendMode='avg',
                        useFBO=True)
    # store frame rate of monitor if we can measure it
    expInfo['frameRate'] = win.getActualFrameRate()
    if expInfo['frameRate'] != None:
        frameDur = 1.0 / round(expInfo['frameRate'])
    else:
        frameDur = 1.0 / 60.0  # could not measure, so guess

    # Initialize components for Routine "trial"
    trialClock = core.Clock()
    stim_1 = sound.Sound('A', secs=-1)
    stim_1.setVolume(1)
    fix_1 = visual.TextStim(win=win,
                            name='fix_1',
                            text='+',
                            font='Arial',
                            pos=(0, 0),
                            height=0.1,
                            wrapWidth=None,
                            ori=0,
                            color='white',
                            colorSpace='rgb',
                            opacity=1,
                            depth=-1.0)

    # Initialize components for Routine "run_start"
    run_startClock = core.Clock()
    run_start_msg_screen = visual.TextStim(win=win,
                                           name='run_start_msg_screen',
                                           text=u'Kurze Pause.',
                                           font='Arial',
                                           units='norm',
                                           pos=[0, 0],
                                           height=0.12,
                                           wrapWidth=2,
                                           ori=0,
                                           color='white',
                                           colorSpace='rgb',
                                           opacity=1,
                                           depth=0.0)

    # Initialize components for Routine "run_trigger_sync"
    StartClock = core.Clock()
    run_trigger_syncClock = core.Clock()
    run_start_msg = visual.TextStim(win=win,
                                    name='run_start_msg',
                                    text='Durchgang beginnt!',
                                    font='Arial',
                                    units='norm',
                                    pos=[0, 0],
                                    height=0.15,
                                    wrapWidth=2,
                                    ori=0,
                                    color='white',
                                    colorSpace='rgb',
                                    opacity=1,
                                    depth=-1.0)
    movie = visual.MovieStim3(
        win=win,
        name='movie',
        units='pix',
        noAudio=True,
        # rename path
        filename='C:\Paradigmen\AG_Brain\Peer\TNAC\movies\mov1.mkv',
        ori=0,
        pos=(0, 0),
        opacity=1,
        depth=0.0,
    )

    # Create some handy timers
    globalClock = core.Clock()  # to track the time since experiment started
    routineTimer = core.CountdownTimer(
    )  # to track time remaining of each (non-slip) routine

    block_delay = [4, 5, 6] * 12
    random.shuffle(block_delay)
    #print(block_delay)
    # ------Prepare to start Routine "run_start"-------
    t = 0
    run_startClock.reset()  # clock
    frameN = -1
    continueRoutine = True
    # update component parameters for each repeat
    run_start_trigger_key = event.BuilderKeyResponse()
    # keep track of which components have finished
    run_startComponents = [run_start_msg_screen, run_start_trigger_key]
    for thisComponent in run_startComponents:
        if hasattr(thisComponent, 'status'):
            thisComponent.status = NOT_STARTED

    # -------Start Routine "run_start"-------
    while continueRoutine:
        # get current time
        t = run_startClock.getTime()
        thisExp.addData('start_run', globalClock.getTime())
        frameN = frameN + 1  # number of completed frames (so 0 is the first frame)
        # update/draw components on each frame

        # *run_start_msg_screen* updates
        if t >= 0.0 and run_start_msg_screen.status == NOT_STARTED:
            # keep track of start time/frame for later
            run_start_msg_screen.tStart = t
            run_start_msg_screen.frameNStart = frameN  # exact frame index
            run_start_msg_screen.setAutoDraw(True)

        # *run_start_trigger_key* updates
        if t >= 0.0 and run_start_trigger_key.status == NOT_STARTED:
            # keep track of start time/frame for later
            run_start_trigger_key.tStart = t
            run_start_trigger_key.frameNStart = frameN  # exact frame index
            run_start_trigger_key.status = STARTED
            # keyboard checking is just starting
            event.clearEvents(eventType='keyboard')
        if run_start_trigger_key.status == STARTED:
            theseKeys = event.getKeys(keyList=['s'])

            # check for quit:
            if "escape" in theseKeys:
                endExpNow = True
            if len(theseKeys) > 0:  # at least one key was pressed
                # a response ends the routine
                continueRoutine = False

        # check if all components have finished
        if not continueRoutine:  # a component has requested a forced-end of Routine
            break
        continueRoutine = False  # will revert to True if at least one component still running
        for thisComponent in run_startComponents:
            if hasattr(thisComponent,
                       "status") and thisComponent.status != FINISHED:
                continueRoutine = True
                break  # at least one component has not yet finished

        # check for quit (the Esc key)
        if endExpNow or event.getKeys(keyList=["escape"]):
            core.quit()

        # refresh the screen
        if continueRoutine:  # don't flip if this routine is over or we'll get a blank screen
            win.flip()

    # -------Ending Routine "run_start"-------
    for thisComponent in run_startComponents:
        if hasattr(thisComponent, "setAutoDraw"):
            thisComponent.setAutoDraw(False)
    # the Routine "run_start" was not non-slip safe, so reset the non-slip timer
    routineTimer.reset()

    # ------Prepare to start Routine "run_trigger_sync"-------
    t = 0
    run_trigger_syncClock.reset()  # clock
    frameN = -1
    continueRoutine = True
    # update component parameters for each repeat
    run_trigger_sync_ = event.BuilderKeyResponse()

    # keep track of which components have finished
    run_trigger_syncComponents = [run_trigger_sync_, run_start_msg]
    for thisComponent in run_trigger_syncComponents:
        if hasattr(thisComponent, 'status'):
            thisComponent.status = NOT_STARTED

    # -------Start Routine "run_trigger_sync"-------
    while continueRoutine:
        # get current time
        #print('waiting for scanner trigger....')
        t = run_trigger_syncClock.getTime()
        frameN = frameN + 1  # number of completed frames (so 0 is the first frame)
        # update/draw components on each frame

        # *run_trigger_sync_* updates
        if t >= 0.0 and run_trigger_sync_.status == NOT_STARTED:
            # keep track of start time/frame for later
            run_trigger_sync_.tStart = t
            run_trigger_sync_.frameNStart = frameN  # exact frame index
            run_trigger_sync_.status = STARTED
            # keyboard checking is just starting
            win.callOnFlip(
                run_trigger_sync_.clock.reset)  # t=0 on next screen flip
            event.clearEvents(eventType='keyboard')
        if run_trigger_sync_.status == STARTED:
            theseKeys = event.getKeys(keyList=['t'])

            # check for quit:
            if "escape" in theseKeys:
                endExpNow = True
            if len(theseKeys) > 0:  # at least one key was pressed
                run_trigger_sync_.keys = theseKeys[
                    -1]  # just the last key pressed
                run_trigger_sync_.rt = run_trigger_sync_.clock.getTime()
                # a response ends the routine
                continueRoutine = False

        # *run_start_msg* updates
        if t >= 0.0 and run_start_msg.status == NOT_STARTED:
            # keep track of start time/frame for later
            run_start_msg.tStart = t
            run_start_msg.frameNStart = frameN  # exact frame index
            run_start_msg.setAutoDraw(True)
        frameRemains = 0.0 + 5 - win.monitorFramePeriod * 0.75  # most of one frame period left
        if run_start_msg.status == STARTED and t >= frameRemains:
            run_start_msg.setAutoDraw(False)

        # check if all components have finished
        if not continueRoutine:  # a component has requested a forced-end of Routine
            break
        continueRoutine = False  # will revert to True if at least one component still running
        for thisComponent in run_trigger_syncComponents:
            if hasattr(thisComponent,
                       "status") and thisComponent.status != FINISHED:
                continueRoutine = True
                break  # at least one component has not yet finished

        # check for quit (the Esc key)
        if endExpNow or event.getKeys(keyList=["escape"]):
            core.quit()

        # refresh the screen
        if continueRoutine:  # don't flip if this routine is over or we'll get a blank screen
            win.flip()

    # -------Ending Routine "run_trigger_sync"-------
    for thisComponent in run_trigger_syncComponents:
        if hasattr(thisComponent, "setAutoDraw"):
            thisComponent.setAutoDraw(False)
    # check responses
    if run_trigger_sync_.keys in ['', [], None]:  # No response was made
        run_trigger_sync_.keys = None
    thisExp.addData('run_trigger_sync_.keys', run_trigger_sync_.keys)
    if run_trigger_sync_.keys != None:  # we had a response
        thisExp.addData('run_trigger_sync_.rt', run_trigger_sync_.rt)
    run_start_timestamp = StartClock.getTime()

    send_trigger(400)
    # the Routine "run_trigger_sync" was not non-slip safe, so reset the non-slip timer
    routineTimer.reset()

    start_delay = False
    delay_counter = 0
    #print(block_delay)
    print(delay_counter)
    # start movie for whole run (loop over trials)
    mov = 'movies/mov1.mkv'
    #print(mov)
    movie.setMovie(mov)
    if t >= 0.0 and movie.status == NOT_STARTED:
        # keep track of start time/frame for later
        movie.tStart = t
        movie.frameNStart = frameN  # exact frame index
        movie.setAutoDraw(True)
        frameRemains = 0.0 + 2 - win.monitorFramePeriod * 0.75  # most of one frame period left

        # set up handler to look after randomisation of conditions etc
        trials = data.TrialHandler(nReps=1,
                                   method='sequential',
                                   extraInfo=expInfo,
                                   originPath=-1,
                                   trialList=data.importConditions(run_var),
                                   seed=None,
                                   name='trials')
        thisExp.addLoop(trials)  # add the loop to the experiment
        thisTrial = trials.trialList[
            0]  # so we can initialise stimuli with some values
        # abbreviate parameter names if possible (e.g. rgb = thisTrial.rgb)
        if thisTrial != None:
            for paramName in thisTrial.keys():
                exec(paramName + '= thisTrial.' + paramName)
        stimuli_played = 0

        for thisTrial in trials:
            currentLoop = trials
            # abbreviate parameter names if possible (e.g. rgb = thisTrial.rgb)
            if thisTrial != None:
                for paramName in thisTrial.keys():
                    exec(paramName + '= thisTrial.' + paramName)

            # ------Prepare to start Routine "trial"-------
            t = 0
            trialClock.reset()  # clock
            frameN = -1
            continueRoutine = True

            routineTimer.add(2.000000)
            # update component parameters for each repeat
            stim_1.setSound(stimuli, secs=2)
            #read stimuli into dict and set port value
            abc = stimuli.split('/')[0]
            trg = trg_dict.get(abc, 100)
            # keep track of which components have finished
            trialComponents = [stim_1, fix_1]
            for thisComponent in trialComponents:
                if hasattr(thisComponent, 'status'):
                    thisComponent.status = NOT_STARTED

            # -------Start Routine "trial"-------
            while continueRoutine and routineTimer.getTime() > 0:

                # get current time
                t = trialClock.getTime()
                frameN = frameN + 1  # number of completed frames (so 0 is the first frame)
                # update/draw components on each frame
                # start/stop stim_1
                if t >= 0.0 and stim_1.status == NOT_STARTED:
                    # keep track of start time/frame for later
                    stim_1.tStart = t
                    stim_1.frameNStart = frameN  # exact frame index

                    ## TODO reinstate: send_trigger(abc)
                    #print(abc)
                    stim_1.play(
                    )  # start the sound (it finishes automatically)
                    send_trigger(trg)  # send block specific trigger
                    # get time for stimuls start
                    thisExp.addData('stimulus_start_global',
                                    globalClock.getTime())
                    thisExp.addData('stimulus_start_routineTimer',
                                    routineTimer.getTime())
                    thisExp.addData('stimulus_start_', frameN)
                    #print(stim_1)
                    stimuli_played += 1
                    if stimuli_played % 5 == 0:
                        start_delay = True
                    print('stimuli_nr:' + str(stimuli_played))

                frameRemains = 0.0 + 1.5 - win.monitorFramePeriod * 0.75  # most of one frame period left
                #frameRemainsdelay = 0.0 + 1.5- win.monitorFramePeriod * 0.75  # most of one frame period left
                if stim_1.status == STARTED and t >= frameRemains:
                    stim_1.stop()  # stop the sound (if longer than duration)
                    send_trigger(100)  # send sound off trigger
                    # get info on stim end
                thisExp.addData('stimulus_end_global', globalClock.getTime())
                thisExp.addData('stimulus_end_routineTimer',
                                routineTimer.getTime())

                # add delay intervall after 5 stimuli
                if stimuli_played % 5 == 0 and start_delay and delay_counter != 35:
                    send_trigger(200)
                    delay = block_delay[delay_counter]
                    routineTimer.add(block_delay[delay_counter])
                    #frameRemainsdelay = 0.0 + 1.5 + delay - win.monitorFramePeriod * 0.75  # most of one frame period left
                    #print('delay='+str(delay_counter))
                    delay_counter += 1
                    thisExp.addData('delay_counter',
                                    block_delay[delay_counter])
                    thisExp.addData('block_end_global', globalClock.getTime())
                    start_delay = False

                if stim_1.status == STARTED and t >= frameRemains:
                    stim_1.stop()  # stop the sound (if longer than duration)
                    send_trigger(100)  # send sound off trigger
                    # get info on stim end
                thisExp.addData('stimulus_end_global', globalClock.getTime())
                thisExp.addData('stimulus_end_routineTimer',
                                routineTimer.getTime())

                # check if all components have finished
                if not continueRoutine:  # a component has requested a forced-end of Routine
                    break
                continueRoutine = False  # will revert to True if at least one component still running
                for thisComponent in trialComponents:
                    if hasattr(thisComponent,
                               "status") and thisComponent.status != FINISHED:
                        continueRoutine = True
                        break  # at least one component has not yet finished

                # check for quit (the Esc key)
                if endExpNow or event.getKeys(keyList=["escape"]):
                    core.quit()

                # refresh the screen
                if continueRoutine:  # don't flip if this routine is over or we'll get a blank screen
                    win.flip()

            stim_1.stop()  # ensure sound has stopped at end of routine
            thisExp.nextEntry()

            # completed 1 repeats of 'trials'

        thisExp.nextEntry()

        # completed 1 repeats of 'block'

    if stimuli_played == 180:
        movie.setAutoDraw(False)
        send_trigger(300)  # END RUN
        thisExp.saveAsWideText(filename + 'run1' + '.csv')
        thisExp.saveAsPickle(filename + 'run1')
        logging.flush()
        # make sure everything is closed down
        thisExp.abort()  # or data files will save again on exit
        win.close()
Exemple #22
0
def main_loop(
    all_tasks,
    subject,
    session,
    output_ds,
    enable_eyetracker=False,
    use_fmri=False,
    use_meg=False,
    show_ctl_win=False,
    allow_run_on_battery=False,
    enable_ptt=False,
    record_movie=False,
):

    # force screen resolution to solve issues with video splitter at scanner
    """xrandr = Popen([
        'xrandr',
        '--output', 'eDP-1',
        '--mode', '%dx%d'%config.EXP_WINDOW['size'],
        '--rate', str(config.FRAME_RATE)])
    time.sleep(5)"""

    if not utils.check_power_plugged():
        print("*" * 25 + "WARNING: the power cord is not connected" + "*" * 25)
        if not allow_run_on_battery:
            return

    bids_sub_ses = ("sub-%s" % subject, "ses-%s" % session)
    log_path = os.path.abspath(
        os.path.join(output_ds, "sourcedata", *bids_sub_ses))
    if not os.path.exists(log_path):
        os.makedirs(log_path, exist_ok=True)
    log_name_prefix = "sub-%s_ses-%s_%s" % (
        subject,
        session,
        datetime.datetime.now().strftime("%Y%m%d-%H%M%S"),
    )
    logfile_path = os.path.join(log_path, log_name_prefix + ".log")
    log_file = logging.LogFile(logfile_path, level=logging.INFO, filemode="w")

    exp_win = visual.Window(**config.EXP_WINDOW, monitor=config.EXP_MONITOR)
    exp_win.mouseVisible = False

    if show_ctl_win:
        ctl_win = visual.Window(**config.CTL_WINDOW)
        ctl_win.name = "Stimuli"
    else:
        ctl_win = None

    ptt = None
    if enable_ptt:
        from .ptt import PushToTalk

        ptt = PushToTalk()

    eyetracker_client = None
    gaze_drawer = None
    if enable_eyetracker:
        print("creating et client")
        eyetracker_client = eyetracking.EyeTrackerClient(
            output_path=log_path,
            output_fname_base=log_name_prefix,
            profile=False,
            debug=False,
        )
        print("starting et client")
        eyetracker_client.start()
        print("done")
        all_tasks = sum(([
            eyetracking.EyetrackerCalibration(eyetracker_client,
                                              name="EyeTracker-Calibration"), t
        ] for t in all_tasks), [])

        if show_ctl_win:
            gaze_drawer = eyetracking.GazeDrawer(ctl_win)
    if use_fmri:
        all_tasks = itertools.chain(
            [
                task_base.Pause(
                    """We are completing the setup and initializing the scanner.
We will start the tasks in a few minutes.
Please remain still.""")
            ],
            all_tasks,
            [
                task_base.Pause("""We are done for today.
The scanner might run for a few seconds to acquire reference images.
Please remain still.
We are coming to get you out of the scanner shortly.""")
            ],
        )

        if not skip_soundcheck:
            setup_video_path = utils.get_subject_soundcheck_video(subject)
            all_tasks = itertools.chain(
                [
                    video.VideoAudioCheckLoop(
                        setup_video_path,
                        name="setup_soundcheck_video",
                    )
                ],
                all_tasks,
            )

    else:
        all_tasks = itertools.chain(
            all_tasks,
            [
                task_base.Pause("""We are done with the tasks for today.
Thanks for your participation!""")
            ],
        )

    if not isinstance(all_tasks, Iterator):

        # list of tasks to be ran in a session

        print("Here are the stimuli planned for today\n" + "_" * 50)
        for task in all_tasks:
            print(f"- {task.name} {getattr(task,'duration','')}")
        print("_" * 50)

    try:
        for task in all_tasks:

            # clear events buffer in case the user pressed a lot of buttoons
            event.clearEvents()

            use_eyetracking = False
            if enable_eyetracker and task.use_eyetracking:
                use_eyetracking = True

            # setup task files (eg. video)
            task.setup(
                exp_win,
                log_path,
                log_name_prefix,
                use_fmri=use_fmri,
                use_eyetracking=use_eyetracking,
                use_meg=use_meg,
            )
            print("READY")

            while True:
                # force focus on the task window to ensure getting keys, TTL, ...
                exp_win.winHandle.activate()
                # record frame intervals for debug

                shortcut_evt = run_task(
                    task,
                    exp_win,
                    ctl_win,
                    eyetracker_client,
                    gaze_drawer,
                    record_movie=record_movie,
                )

                if shortcut_evt == "n":
                    # restart the task
                    logging.exp(msg="task - %s: restart" % str(task))
                    task.restart()
                    continue
                elif shortcut_evt:
                    # abort/skip or quit
                    logging.exp(msg="task - %s: abort" % str(task))
                    break
                else:  # task completed
                    logging.exp(msg="task - %s: complete" % str(task))
                    # send stop trigger/marker to MEG + Biopac (or anything else on parallel port)
                    break

                logging.flush()
            if record_movie:
                out_fname = os.path.join(
                    task.output_path,
                    "%s_%s.mp4" % (task.output_fname_base, task.name))
                print(f"saving movie as {out_fname}")
                exp_win.saveMovieFrames(out_fname, fps=10)
            task.unload()

            if shortcut_evt == "q":
                print("quit")
                break
            elif shortcut_evt is None:
                # add a delay between tasks to avoid remaining TTL to start next task
                # do that only if the task was not aborted to save time
                # there is anyway the duration of the instruction before listening to TTL
                for i in range(DELAY_BETWEEN_TASK * config.FRAME_RATE):
                    exp_win.flip(clearBuffer=False)

        exp_win.saveFrameIntervals("exp_win_frame_intervals.txt")
        if ctl_win:
            ctl_win.saveFrameIntervals("ctl_win_frame_intervals.txt")

    except KeyboardInterrupt as ki:
        print(traceback.format_exc())
        logging.exp(msg="user killing the program")
        print("you killing me!")
    finally:
        if enable_eyetracker:
            eyetracker_client.join(TIMEOUT)
Exemple #23
0
    def loadFromXML(self, filename):
        """Loads an xml file and parses the builder Experiment from it
        """
        self._doc.parse(filename)
        root = self._doc.getroot()

        # some error checking on the version (and report that this isn't valid
        # .psyexp)?
        filenameBase = os.path.basename(filename)

        if root.tag != "PsychoPy2experiment":
            logging.error('%s is not a valid .psyexp file, "%s"' %
                          (filenameBase, root.tag))
            # the current exp is already vaporized at this point, oops
            return
        self.psychopyVersion = root.get('version')

        # Parse document nodes
        # first make sure we're empty
        self.flow = Flow(exp=self)  # every exp has exactly one flow
        self.routines = {}
        self.namespace = NameSpace(self)  # start fresh
        modifiedNames = []
        duplicateNames = []

        # fetch exp settings
        settingsNode = root.find('Settings')
        for child in settingsNode:
            self._getXMLparam(params=self.settings.params,
                              paramNode=child,
                              componentNode=settingsNode)
        # name should be saved as a settings parameter (only from 1.74.00)
        if self.settings.params['expName'].val in ['', None, 'None']:
            shortName = os.path.splitext(filenameBase)[0]
            self.setExpName(shortName)
        # fetch routines
        routinesNode = root.find('Routines')
        allCompons = getAllComponents(self.prefsBuilder['componentsFolders'],
                                      fetchIcons=False)
        # get each routine node from the list of routines
        for routineNode in routinesNode:
            routineGoodName = self.namespace.makeValid(routineNode.get('name'))
            if routineGoodName != routineNode.get('name'):
                modifiedNames.append(routineNode.get('name'))
            self.namespace.user.append(routineGoodName)
            routine = Routine(name=routineGoodName, exp=self)
            # self._getXMLparam(params=routine.params, paramNode=routineNode)
            self.routines[routineNode.get('name')] = routine
            for componentNode in routineNode:

                componentType = componentNode.tag
                if componentType in allCompons:
                    # create an actual component of that type
                    component = allCompons[componentType](
                        name=componentNode.get('name'),
                        parentName=routineNode.get('name'),
                        exp=self)
                else:
                    # create UnknownComponent instead
                    component = allCompons['UnknownComponent'](
                        name=componentNode.get('name'),
                        parentName=routineNode.get('name'),
                        exp=self)
                # check for components that were absent in older versions of
                # the builder and change the default behavior
                # (currently only the new behavior of choices for RatingScale,
                # HS, November 2012)
                # HS's modification superceded Jan 2014, removing several
                # RatingScale options
                if componentType == 'RatingScaleComponent':
                    if (componentNode.get('choiceLabelsAboveLine')
                            or componentNode.get('lowAnchorText')
                            or componentNode.get('highAnchorText')):
                        pass
                    # if not componentNode.get('choiceLabelsAboveLine'):
                    #    # this rating scale was created using older version
                    #    component.params['choiceLabelsAboveLine'].val=True
                # populate the component with its various params
                for paramNode in componentNode:
                    self._getXMLparam(params=component.params,
                                      paramNode=paramNode,
                                      componentNode=componentNode)
                compGoodName = self.namespace.makeValid(
                    componentNode.get('name'))
                if compGoodName != componentNode.get('name'):
                    modifiedNames.append(componentNode.get('name'))
                self.namespace.add(compGoodName)
                component.params['name'].val = compGoodName
                routine.append(component)
        # for each component that uses a Static for updates, we need to set
        # that
        for thisRoutine in list(self.routines.values()):
            for thisComp in thisRoutine:
                for thisParamName in thisComp.params:
                    thisParam = thisComp.params[thisParamName]
                    if thisParamName == 'advancedParams':
                        continue  # advanced isn't a normal param
                    elif thisParam.updates and "during:" in thisParam.updates:
                        # remove the part that says 'during'
                        updates = thisParam.updates.split(': ')[1]
                        routine, static = updates.split('.')
                        if routine not in self.routines:
                            msg = ("%s was set to update during %s Static "
                                   "Component, but that component no longer "
                                   "exists")
                            logging.warning(msg % (thisParamName, static))
                        else:
                            self.routines[routine].getComponentFromName(
                                static).addComponentUpdate(
                                    thisRoutine.params['name'],
                                    thisComp.params['name'], thisParamName)
        # fetch flow settings
        flowNode = root.find('Flow')
        loops = {}
        for elementNode in flowNode:
            if elementNode.tag == "LoopInitiator":
                loopType = elementNode.get('loopType')
                loopName = self.namespace.makeValid(elementNode.get('name'))
                if loopName != elementNode.get('name'):
                    modifiedNames.append(elementNode.get('name'))
                self.namespace.add(loopName)
                loop = eval('%s(exp=self,name="%s")' % (loopType, loopName))
                loops[loopName] = loop
                for paramNode in elementNode:
                    self._getXMLparam(paramNode=paramNode, params=loop.params)
                    # for conditions convert string rep to list of dicts
                    if paramNode.get('name') == 'conditions':
                        param = loop.params['conditions']
                        # e.g. param.val=[{'ori':0},{'ori':3}]
                        try:
                            param.val = eval('%s' % (param.val))
                        except SyntaxError:
                            # This can occur if Python2.7 conditions string
                            # contained long ints (e.g. 8L) and these can't be
                            # parsed by Py3. But allow the file to carry on
                            # loading and the conditions will still be loaded
                            # from the xlsx file
                            pass
                # get condition names from within conditionsFile, if any:
                try:
                    # psychophysicsstaircase demo has no such param
                    conditionsFile = loop.params['conditionsFile'].val
                except Exception:
                    conditionsFile = None
                if conditionsFile in ['None', '']:
                    conditionsFile = None
                if conditionsFile:
                    try:
                        trialList, fieldNames = data.importConditions(
                            conditionsFile, returnFieldNames=True)
                        for fname in fieldNames:
                            if fname != self.namespace.makeValid(fname):
                                duplicateNames.append(fname)
                            else:
                                self.namespace.add(fname)
                    except Exception:
                        pass  # couldn't load the conditions file for now
                self.flow.append(LoopInitiator(loop=loops[loopName]))
            elif elementNode.tag == "LoopTerminator":
                self.flow.append(
                    LoopTerminator(loop=loops[elementNode.get('name')]))
            elif elementNode.tag == "Routine":
                if elementNode.get('name') in self.routines:
                    self.flow.append(self.routines[elementNode.get('name')])
                else:
                    logging.error("A Routine called '{}' was on the Flow but "
                                  "could not be found (failed rename?). You "
                                  "may need to re-insert it".format(
                                      elementNode.get('name')))
                    logging.flush()

        if modifiedNames:
            msg = 'duplicate variable name(s) changed in loadFromXML: %s\n'
            logging.warning(msg % ', '.join(list(set(modifiedNames))))
        if duplicateNames:
            msg = 'duplicate variable names: %s'
            logging.warning(msg % ', '.join(list(set(duplicateNames))))
        # if we succeeded then save current filename to self
        self.filename = filename
Exemple #24
0
    def _getXMLparam(self, params, paramNode, componentNode=None):
        """params is the dict of params of the builder component
        (e.g. stimulus) into which the parameters will be inserted
        (so the object to store the params should be created first)
        paramNode is the parameter node fetched from the xml file
        """
        name = paramNode.get('name')
        valType = paramNode.get('valType')
        val = paramNode.get('val')
        # many components need web char newline replacement
        if not name == 'advancedParams':
            val = val.replace("&#10;", "\n")

        # custom settings (to be used when
        if valType == 'fixedList':  # convert the string to a list
            params[name].val = eval('list({})'.format(val))
        elif name == 'storeResponseTime':
            return  # deprecated in v1.70.00 because it was redundant
        elif name == 'nVertices':  # up to 1.85 there was no shape param
            # if no shape param then use "n vertices" only
            if _findParam('shape', componentNode) is None:
                if val == '2':
                    params['shape'].val = "line"
                elif val == '3':
                    params['shape'].val = "triangle"
                elif val == '4':
                    params['shape'].val = "rectangle"
                else:
                    params['shape'].val = "regular polygon..."
            params['nVertices'].val = val
        elif name == 'startTime':  # deprecated in v1.70.00
            params['startType'].val = "{}".format('time (s)')
            params['startVal'].val = "{}".format(val)
            return  # times doesn't need to update its type or 'updates' rule
        elif name == 'forceEndTrial':  # deprecated in v1.70.00
            params['forceEndRoutine'].val = bool(val)
            return  # forceEndTrial doesn't need to update type or 'updates'
        elif name == 'forceEndTrialOnPress':  # deprecated in v1.70.00
            params['forceEndRoutineOnPress'].val = bool(val)
            return  # forceEndTrial doesn't need to update  type or 'updates'
        elif name == 'forceEndRoutineOnPress':
            if val == 'True':
                val = "any click"
            elif val == 'False':
                val = "never"
            params['forceEndRoutineOnPress'].val = val
            return
        elif name == 'trialList':  # deprecated in v1.70.00
            params['conditions'].val = eval(val)
            return  # forceEndTrial doesn't need to update  type or 'updates'
        elif name == 'trialListFile':  # deprecated in v1.70.00
            params['conditionsFile'].val = "{}".format(val)
            return  # forceEndTrial doesn't need to update  type or 'updates'
        elif name == 'duration':  # deprecated in v1.70.00
            params['stopType'].val = u'duration (s)'
            params['stopVal'].val = "{}".format(val)
            return  # times doesn't need to update its type or 'updates' rule
        elif name == 'allowedKeys' and valType == 'str':  # changed v1.70.00
            # ynq used to be allowed, now should be 'y','n','q' or
            # ['y','n','q']
            if len(val) == 0:
                newVal = val
            elif val[0] == '$':
                newVal = val[1:]  # they were using code (which we can reuse)
            elif val.startswith('[') and val.endswith(']'):
                # they were using code (slightly incorectly!)
                newVal = val[1:-1]
            elif val in ['return', 'space', 'left', 'right', 'escape']:
                newVal = val  # they were using code
            else:
                # convert string to list of keys then represent again as a
                # string!
                newVal = repr(list(val))
            params['allowedKeys'].val = newVal
            params['allowedKeys'].valType = 'code'
        elif name == 'correctIf':  # deprecated in v1.60.00
            corrIf = val
            corrAns = corrIf.replace('resp.keys==unicode(',
                                     '').replace(')', '')
            params['correctAns'].val = corrAns
            name = 'correctAns'  # then we can fetch other aspects below
        elif 'olour' in name:  # colour parameter was Americanised v1.61.00
            name = name.replace('olour', 'olor')
            params[name].val = val
        elif name == 'times':  # deprecated in v1.60.00
            times = eval('%s' % val)
            params['startType'].val = "{}".format('time (s)')
            params['startVal'].val = "{}".format(times[0])
            params['stopType'].val = "{}".format('time (s)')
            params['stopVal'].val = "{}".format(times[1])
            return  # times doesn't need to update its type or 'updates' rule
        elif name in ('Begin Experiment', 'Begin Routine', 'Each Frame',
                      'End Routine', 'End Experiment'):
            params[name].val = val
            params[name].valType = 'extendedCode'  # changed in 1.78.00
            return  # so that we don't update valTyp again below
        elif name == 'Saved data folder':
            # deprecated in 1.80 for more complete data filename control
            params[name] = Param(val,
                                 valType='code',
                                 allowedTypes=[],
                                 hint=_translate(
                                     "Name of the folder in which to save data"
                                     " and log files (blank defaults to the "
                                     "builder pref)"),
                                 categ='Data')
        elif name == 'channel':  # was incorrectly set to be valType='str' until 3.1.2
            params[name].val = val
            params[name].valType = 'code'  # override
        elif 'val' in list(paramNode.keys()):
            if val == 'window units':  # changed this value in 1.70.00
                params[name].val = 'from exp settings'
            # in v1.80.00, some RatingScale API and Param fields were changed
            # Try to avoid a KeyError in these cases so can load the expt
            elif name in ('choiceLabelsAboveLine', 'lowAnchorText',
                          'highAnchorText'):
                # not handled, just ignored; want labels=[lowAnchor,
                # highAnchor]
                return
            elif name == 'customize_everything':
                # Try to auto-update the code:
                v = val  # python code, not XML
                v = v.replace('markerStyle',
                              'marker').replace('customMarker', 'marker')
                v = v.replace('stretchHoriz',
                              'stretch').replace('displaySizeFactor', 'size')
                v = v.replace('textSizeFactor', 'textSize')
                v = v.replace('ticksAboveLine=False', 'tickHeight=-1')
                v = v.replace('showScale=False',
                              'scale=None').replace('allowSkip=False',
                                                    'skipKeys=None')
                v = v.replace('showAnchors=False', 'labels=None')
                # lowAnchorText highAnchorText will trigger obsolete error
                # when run the script
                params[name].val = v
            elif name == 'storeResponseTime':
                return  # deprecated in v1.70.00 because it was redundant
            elif name == 'Resources':
                # if the xml import hasn't automatically converted from string?
                if type(val) == str:
                    resources = data.utils.listFromString(val)
                if self.psychopyVersion == '2020.2.5':
                    # in 2020.2.5 only, problems were:
                    #   a) resources list was saved as a string and
                    #   b) with wrong root folder
                    resList = []
                    for resourcePath in resources:
                        # doing this the blunt way but should we check for existence?
                        resourcePath = resourcePath.replace(
                            "../", "")  # it was created using wrong root
                        resourcePath = resourcePath.replace(
                            "\\", "/")  # created using windows \\
                        resList.append(resourcePath)
                    resources = resList  # push our new list back to resources
                params[name].val = resources
            else:
                if name in params:
                    params[name].val = val
                else:
                    # we found an unknown parameter (probably from the future)
                    params[name] = Param(
                        val,
                        valType=paramNode.get('valType'),
                        allowedTypes=[],
                        hint=_translate(
                            "This parameter is not known by this version "
                            "of PsychoPy. It might be worth upgrading"))
                    params[name].allowedTypes = paramNode.get('allowedTypes')
                    if params[name].allowedTypes is None:
                        params[name].allowedTypes = []
                    params[name].readOnly = True
                    if name not in ['JS libs', 'OSF Project ID']:
                        # don't warn people if we know it's OK (e.g. for params
                        # that have been removed
                        msg = _translate(
                            "Parameter %r is not known to this version of "
                            "PsychoPy but has come from your experiment file "
                            "(saved by a future version of PsychoPy?). This "
                            "experiment may not run correctly in the current "
                            "version.")
                        logging.warn(msg % name)
                        logging.flush()

        # get the value type and update rate
        if 'valType' in list(paramNode.keys()):
            params[name].valType = paramNode.get('valType')
            # compatibility checks:
            if name in ['allowedKeys'] and paramNode.get('valType') == 'str':
                # these components were changed in v1.70.00
                params[name].valType = 'code'
            elif name == 'Selected rows':
                # changed in 1.81.00 from 'code' to 'str': allow string or var
                params[name].valType = 'str'
            # conversions based on valType
            if params[name].valType == 'bool':
                params[name].val = eval("%s" % params[name].val)
        if 'updates' in list(paramNode.keys()):
            params[name].updates = paramNode.get('updates')
Exemple #25
0
def launch():
    """Launch a test battery.

    The test battery is stored in testlist.py.
    This is to be called from the RT_test folder, otherwise some tests
    may not be loaded when they contain a reference to other resources
    (like the stimuli list in the VerbCRT).
    """
    setup_console()

    # This part aims to fix the problem with multitasking when making
    # executable using pyinstaller
    if getattr(sys, 'frozen', False):
        # we are running in a bundle
        HOME_FOLDER = os.path.dirname(sys.executable)
    else:
        # we are running in a normal Python environment
        HOME_FOLDER = os.path.dirname(os.path.abspath(__file__))

    os.chdir(HOME_FOLDER)

    # Make folders and attach output table
    if not os.access('data', os.F_OK):
        os.mkdir('data')

    if os.path.isfile('data/participants.csv'):
        try:
            out_file = open('data/participants.csv', mode='a')
        except:
            print('The directory is not writable, cannot write the data')
    else:
        out_file = open('data/participants.csv', mode='w')
        out_file.write('test_mode;id;name;age;sex;status;start_time;end_time;')
        for i in test_battery:
            out_file.write(i + '_status;' + \
                           i + '_start_time;' + \
                           i + '_end_time;')
        out_file.write('\n')

    # Showing information dialog
    intro = u'''
Спасибо, что согласились поучаствовать в исследовании времени реакции!

Предлагаю Вам пройти набор тестов, в которых нужно как можно быстрее и точнее
реагировать на стимулы (изображения или слова).

Время выполнения заданий составляет ~20 минут в демо-версии и ~50 минут в полной версии. 
В тесте установлено общее ограничение времени 90 минут. Тест можно прервать во время демонстрации
или между тренировочной и основной сериями, нажав кнопку Y.\

Перед началом тестов программа соберет информацию о характеристиках Вашего компьютера:
    - Название операционной системы
    - Тип процессора
    - Количество оперативной памяти
    - Размер и частота обновления экрана
    - Используемую версию Питона

Данные и логи записываются в папке data. \
В данный момент батарея находится на стадии тестирования, \
поэтому я буду очень благодарен любой обратной связи. \
Отправляйте обратную связь и результаты из папки data по адресу:
[email protected]
Страница проекта на гитхабе: 
https://github.com/IvanVoronin/RT_tests

С уважением,
Иван Воронин'''

    app = wx.App()
    intro_dlg = wx.MessageDialog(
        None, intro, u'Добро пожаловать',
        wx.OK_DEFAULT | wx.CANCEL | wx.OK | wx.ALIGN_LEFT
        | wx.ICON_INFORMATION)

    resp = intro_dlg.ShowModal()
    if resp != wx.ID_OK:
        core.wait(1)
        core.quit()
        sys.exit(0)

    # Show the ID questionnaire
    info_dlg = gui.Dlg(title=u'Начать эксперимент')

    info_dlg.addField(u'ID:', u'0001')
    info_dlg.addField(u'Имя:', u'')
    info_dlg.addField(u'Дата рождения:', u'01.01.1990')
    info_dlg.addField(u'Пол:', choices=[u'Мужской', u'Женский'])

    info_dlg.addField(u'Режим теста:', choices=[u'Демо', u'Полный'])

    info_dlg.addField(u'Я согласен/согласна участвовать', True)

    info_dlg.addText(u'\nВыберите тесты для выполнения')
    for i in test_battery.keys():
        info_dlg.addField(i, True)

    # TODO: Make a separate window
    info_dlg.addText(
        u'\nНе забудьте включить английскую расскладку клавиатуры!\n')
    info_dlg.show()

    if not info_dlg.OK:
        core.wait(1)
        core.quit()
        sys.exit(0)

    START_TIME = datetime.now()
    (ID, name, age, sex, test_mode, agr), run_tests = \
        info_dlg.data[0:6], info_dlg.data[6:]

    if not agr:
        core.wait(1)
        core.quit()
        sys.exit(0)

    out_file.write(test_mode + ';' + ID + ';' + name + ';' + str(age) + ';' +
                   sex + ';')
    out_dir = START_TIME.strftime('%Y-%m-%d_%H%M__') + str(ID)
    os.mkdir('data/' + out_dir)

    for test, run in zip(test_battery.values(), run_tests):
        if not run:
            test.status = 'skipped'

    # Log the warnings
    # TODO: Log in-test warnings, log interruptions, pauses and demonstrations
    logging.console.setLevel(logging.WARNING)
    log = logging.LogFile('data/' + out_dir + '/log.log',
                          level=logging.INFO,
                          filemode='w')

    # The same window can be shared by tests
    # Here you can put window specifications
    test_screen = psychopy.visual.Window(size=(1024, 768),
                                         fullscr=True,
                                         units='pix',
                                         monitor=0,
                                         winType='pyglet')
    test_screen.winHandle.activate()
    test_screen.mouseVisible = False

    try:
        screen_size = test_screen.size
        fps = test_screen.getActualFrameRate()
        frame_duration = test_screen.getMsPerFrame()
        test_screen.flip()

        # Gather system information
        with open('data/' + out_dir + '/specs.txt', mode='w') as specs:
            specs.write('Platform: %s\n' % platform.platform())
            specs.write('Machine: %s\n' % platform.machine())
            specs.write('Processor: %s\n' % platform.processor())
            specs.write('Number of CPUs: %d\n' %
                        psutil.cpu_count(logical=False))
            specs.write('Available CPUs: %d\n' %
                        len(psutil.Process().cpu_affinity()))
            specs.write('Current CPU load: %0.1f%%\n' % psutil.cpu_percent())
            specs.write('Total RAM: %dMb\n' %
                        int(psutil.virtual_memory().total / (1024 * 1024)))
            specs.write('Available RAM: %dMb\n' %
                        int(psutil.virtual_memory().available / (1024 * 1024)))
            specs.write('Screen size: %dx%d\n' % tuple(screen_size))
            specs.write('FPS: %0.1f\n' % fps)
            specs.write(
                'Frame duration: mean=%0.1fms, SD=%0.1fms, median=%0.1fms\n' %
                frame_duration)
            specs.write('Python version: %s\n' % platform.python_version())
            specs.write('Python implementation: %s\n' %
                        platform.python_implementation())
            specs.write('PsychoPy version: %s\n' % psychopy.__version__)
            specs.write('Battery version: %s\n' % VERSION)
            specs.write('Battery ID: %s\n' % BATTERY_ID)
    except Exception as e:
        log.write('EXCEPTION: %s\n' % e)

    logging.flush()

    # Run the tests, collect stats
    skip_the_rest = False
    for test in test_battery.itervalues():
        if skip_the_rest:
            test.status = 'skipped'
        if test.status != 'skipped':
            log.write(
                '\n======================================================================\n'
                + 'STARTING ' + test.name + '\n')
            exc_info = sys.exc_info()
            try:
                test.start(out_dir, mode=test_mode, test_screen=test_screen)
            except Exception as e:
                test.status = 'failed'
                test.end_time = datetime.now()
                log.write('SOMETHING HAPPENED!\n')
                log.write('EXCEPTION: %s\n' % e)
                logging.flush()
                # If something went wrong we open the test screen again
                # FIXME: Probably not working as expected
                if test_screen not in locals() or test_screen._closed:
                    log.write('OPENING A NEW WINDOW\n')
                    test_screen = psychopy.visual.Window(size=(1024, 768),
                                                         fullscr=True,
                                                         units='pix',
                                                         monitor=0,
                                                         winType='pyglet')
                    test_screen.winHandle.activate()
                    test_screen.mouseVisible = False
                    logging.flush()
            finally:
                log.write(traceback.format_exc(exc_info))
                del exc_info
            log.write(
                'FINISHING ' + test.name +
                '\n======================================================================\n'
            )
        else:
            test.start_time = datetime.now()
            test.end_time = datetime.now()
            log.write(test.name + ' SKIPPED\n')
        current_dur = test.end_time - START_TIME
        if current_dur.seconds / 60.0 > TIME_LIMIT:
            skip_the_rest = True
            log.write('TIME LIMIT REACHED\n')

    END_TIME = datetime.now()

    BAT_STATUS = all(
        [test.status == 'complete' for test in test_battery.values()])
    # TODO: Ввести секретную комбинацию клавиш для определения набора тестов

    out_file.write(('complete' if BAT_STATUS else 'incomplete') + ';' +
                   START_TIME.strftime('%Y-%m-%d %H:%M:%S') + ';' +
                   END_TIME.strftime('%Y-%m-%d %H:%M:%S') + ';')
    for test in test_battery.itervalues():
        out_file.write(test.status + ';' +
                       test.start_time.strftime('%Y-%m-%d %H:%M:%S') + ';' +
                       test.end_time.strftime('%Y-%m-%d %H:%M:%S') + ';')
    out_file.write('\n')
    out_file.close()

    debriefing = psychopy.visual.TextStim(test_screen,
                                          text=u'\
Благодарим за участие в эксперименте!\n\n\
Окно закроется автоматически')
    debriefing.draw()
    test_screen.flip()
    core.wait(3)
    test_screen.close()
    core.quit()
Exemple #26
0
def endExp():
    win.flip()
    logging.flush()
    win.close()
    core.quit()
Exemple #27
0
    def tick(self):

        # getting in the variables:
        G = self.G
        # from efl.efl_v6 import *
        # put everything here, which is in from __name__ == "__main__"
        # so this is one 'tick', but that's OK - one tick is all we need from pyff.

        try:

            wait_for_key(G)

            measure_artifact_program(G)

            test_buttons(G)
            instr_screen0(G)
            eo_stim(G)
            ec_stim(G)
            logging.flush()

            test_buttons(G)
            instr_screen(G)
            logging.flush()

            # print(G['eh'].is_alive())
            # print('----><----')
            # G['eh'].send_message('boe!')
            # print('----><----')
            run_main_loop(G)
            logging.flush()

            eo_stim(G)
            ec_stim(G)
            end_task(G)
            logging.flush()

            # close window here.
            G['win'].close()
            logging.flush()

        except KeyboardInterrupt:
            G['eh'].shutdown()
            G['eh'].join()
            G['win'].close()
            logging.flush()

        # once done, stop the FB with this:
        # so we can re-start it, right?
        self.on_stop()
Exemple #28
0
def initPyo(rate=44100, stereo=True, buffer=128):
    """setup the pyo (sound) server
    """
    global pyoSndServer, Sound, audioDriver, duplex, maxChnls
    Sound = SoundPyo
    if not "pyo" in locals():
        import pyo  # microphone.switchOn() calls initPyo even if audioLib is something else
    # subclass the pyo.Server so that we can insert a __del__ function that shuts it down
    class _Server(pyo.Server):
        core = core  # make libs class variables so they don't get deleted first
        logging = logging

        def __del__(self):
            self.stop()
            self.core.wait(0.5)  # make sure enough time passes for the server to shutdown
            self.shutdown()
            self.core.wait(0.5)  # make sure enough time passes for the server to shutdown
            self.logging.debug("pyo sound server shutdown")  # this may never get printed

    if ".".join(map(str, pyo.getVersion())) < "0.6.4":
        Server = _Server
    else:
        Server = pyo.Server

    # if we already have a server, just re-initialize it
    if globals().has_key("pyoSndServer") and hasattr(pyoSndServer, "shutdown"):
        pyoSndServer.stop()
        core.wait(0.5)  # make sure enough time passes for the server to shutdown
        pyoSndServer.shutdown()
        core.wait(0.5)
        pyoSndServer.reinit(sr=rate, nchnls=maxChnls, buffersize=buffer, audio=audioDriver)
        pyoSndServer.boot()
    else:
        if platform == "win32":
            # check for output device/driver
            devNames, devIDs = pyo.pa_get_output_devices()
            audioDriver, outputID = _bestDriver(devNames, devIDs)
            if outputID:
                logging.info("Using sound driver: %s (ID=%i)" % (audioDriver, outputID))
                maxOutputChnls = pyo.pa_get_output_max_channels(outputID)
            else:
                logging.warning("No audio outputs found (no speakers connected?")
                return -1
            # check for valid input (mic)
            devNames, devIDs = pyo.pa_get_input_devices()
            audioInputName, inputID = _bestDriver(devNames, devIDs)
            if inputID is not None:
                logging.info("Using sound-input driver: %s (ID=%i)" % (audioInputName, inputID))
                maxInputChnls = pyo.pa_get_input_max_channels(inputID)
                duplex = bool(maxInputChnls > 0)
            else:
                duplex = False
        else:  # for other platforms set duplex to True (if microphone is available)
            audioDriver = prefs.general["audioDriver"][0]
            maxInputChnls = pyo.pa_get_input_max_channels(pyo.pa_get_default_input())
            maxOutputChnls = pyo.pa_get_output_max_channels(pyo.pa_get_default_output())
            duplex = bool(maxInputChnls > 0)

        maxChnls = min(maxInputChnls, maxOutputChnls)
        if maxInputChnls < 1:
            logging.warning("%s.initPyo could not find microphone hardware; recording not available" % __name__)
            maxChnls = maxOutputChnls
        if maxOutputChnls < 1:
            logging.error("%s.initPyo could not find speaker hardware; sound not available" % __name__)
            return -1

        # create the instance of the server:
        if platform in ["darwin", "linux2"]:
            # for mac/linux we set the backend using the server audio param
            pyoSndServer = Server(sr=rate, nchnls=maxChnls, buffersize=buffer, audio=audioDriver)
        else:
            # with others we just use portaudio and then set the OutputDevice below
            pyoSndServer = Server(sr=rate, nchnls=maxChnls, buffersize=buffer)

        pyoSndServer.setVerbosity(1)
        if platform == "win32":
            pyoSndServer.setOutputDevice(outputID)
            if inputID:
                pyoSndServer.setInputDevice(inputID)
        # do other config here as needed (setDuplex? setOutputDevice?)
        pyoSndServer.setDuplex(duplex)
        pyoSndServer.boot()
    core.wait(0.5)  # wait for server to boot before starting te sound stream
    pyoSndServer.start()
    try:
        Sound()  # test creation, no play
    except pyo.PyoServerStateException:
        msg = "Failed to start pyo sound Server"
        if platform == "darwin" and audioDriver != "portaudio":
            msg += "; maybe try prefs.general.audioDriver 'portaudio'?"
        logging.error(msg)
        core.quit()
    logging.debug("pyo sound server started")
    logging.flush()
    def onInit(self, showSplash=True, testMode=False):
        """This is launched immediately *after* the app initialises with wx
        :Parameters:

          testMode: bool
        """
        self.SetAppName('PsychoPy3')

        if showSplash:  #showSplash:
            # show splash screen
            splashFile = os.path.join(self.prefs.paths['resources'],
                                      'psychopySplash.png')
            splashImage = wx.Image(name=splashFile)
            splashImage.ConvertAlphaToMask()
            splash = AS.AdvancedSplash(
                None,
                bitmap=splashImage.ConvertToBitmap(),
                timeout=3000,
                agwStyle=AS.AS_TIMEOUT | AS.AS_CENTER_ON_SCREEN,
            )  # transparency?
            w, h = splashImage.GetSize()
            splash.SetTextPosition((int(340), h - 30))
            splash.SetText(
                _translate("Copyright (C) 2020 OpenScienceTools.org"))
        else:
            splash = None

        # SLOW IMPORTS - these need to be imported after splash screen starts
        # but then that they end up being local so keep track in self

        from psychopy.compatibility import checkCompatibility
        # import coder and builder here but only use them later
        from psychopy.app import coder, builder, runner, dialogs

        if '--firstrun' in sys.argv:
            del sys.argv[sys.argv.index('--firstrun')]
            self.firstRun = True
        if 'lastVersion' not in self.prefs.appData:
            # must be before 1.74.00
            last = self.prefs.appData['lastVersion'] = '1.73.04'
            self.firstRun = True
        else:
            last = self.prefs.appData['lastVersion']

        if self.firstRun and not self.testMode:
            pass

        # setup links for URLs
        # on a mac, don't exit when the last frame is deleted, just show menu
        if sys.platform == 'darwin':
            self.menuFrame = MenuFrame(parent=None, app=self)
        # fetch prev files if that's the preference
        if self.prefs.coder['reloadPrevFiles']:
            scripts = self.prefs.appData['coder']['prevFiles']
        else:
            scripts = []
        appKeys = list(self.prefs.appData['builder'].keys())
        if self.prefs.builder['reloadPrevExp'] and ('prevFiles' in appKeys):
            exps = self.prefs.appData['builder']['prevFiles']
        else:
            exps = []
        runlist = []

        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

        if sys.platform == 'win32':
            # wx.SYS_DEFAULT_GUI_FONT is default GUI font in Win32
            self._mainFont = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT)
        else:
            self._mainFont = wx.SystemSettings.GetFont(wx.SYS_ANSI_FIXED_FONT)

        try:
            self._codeFont = wx.SystemSettings.GetFont(wx.SYS_ANSI_FIXED_FONT)
        except wx._core.wxAssertionError:
            # if no SYS_ANSI_FIXED_FONT then try generic FONTFAMILY_MODERN
            self._codeFont = wx.Font(self._mainFont.GetPointSize(),
                                     wx.FONTFAMILY_MODERN, wx.FONTSTYLE_NORMAL,
                                     wx.FONTWEIGHT_NORMAL)
        # that gets most of the properties of _codeFont but the FaceName
        # FaceName is set in the setting of the theme:
        self.theme = self.prefs.app['theme']

        # removed Aug 2017: on newer versions of wx (at least on mac)
        # this looks too big
        # if hasattr(self._mainFont, 'Larger'):
        #     # Font.Larger is available since wyPython version 2.9.1
        #     # PsychoPy still supports 2.8 (see ensureMinimal above)
        #     self._mainFont = self._mainFont.Larger()
        #     self._codeFont.SetPointSize(
        #         self._mainFont.GetPointSize())  # unify font size

        # create both frame for coder/builder as necess
        if splash:
            splash.SetText(_translate("  Creating frames..."))

        # Parse incoming call
        parser = argparse.ArgumentParser(prog=self)
        parser.add_argument('--builder', dest='builder', action="store_true")
        parser.add_argument('-b', dest='builder', action="store_true")
        parser.add_argument('--coder', dest='coder', action="store_true")
        parser.add_argument('-c', dest='coder', action="store_true")
        parser.add_argument('--runner', dest='runner', action="store_true")
        parser.add_argument('-r', dest='runner', action="store_true")
        view, args = parser.parse_known_args(sys.argv)
        print(args)
        # Check from filetype if any windows need to be open
        if any(arg.endswith('.psyexp') for arg in args):
            view.builder = True
            exps = [file for file in args if file.endswith('.psyexp')]
        if any(arg.endswith('.psyrun') for arg in args):
            view.runner = True
            runlist = [file for file in args if file.endswith('.psyrun')]
        # If still no window specified, use default from prefs
        if not any(
                getattr(view, key) for key in ['builder', 'coder', 'runner']):
            if self.prefs.app['defaultView'] in view:
                setattr(view, self.prefs.app['defaultView'], True)
            elif self.prefs.app['defaultView'] == 'all':
                view.builder = True
                view.coder = True
                view.runner = True

        # Create windows
        if view.runner:
            self.showRunner(fileList=runlist)
        if view.coder:
            self.showCoder(fileList=scripts)
        if view.builder:
            self.showBuilder(fileList=exps)

        # 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
        self.news = None
        self.tasks = None

        prefsConn = self.prefs.connections

        ok, msg = checkCompatibility(last, self.version, self.prefs, fix=True)
        # tell the user what has changed
        if not ok and not self.firstRun and not self.testMode:
            title = _translate("Compatibility information")
            dlg = dialogs.MessageDialog(parent=None,
                                        message=msg,
                                        type='Info',
                                        title=title)
            dlg.ShowModal()

        if (self.prefs.app['showStartupTips'] and not self.testMode
                and not blockTips):
            tipFile = os.path.join(self.prefs.paths['resources'],
                                   _translate("tips.txt"))
            tipIndex = self.prefs.appData['tipIndex']
            if parse_version(wx.__version__) >= parse_version('4.0.0a1'):
                tp = wx.adv.CreateFileTipProvider(tipFile, tipIndex)
                showTip = wx.adv.ShowTip(None, tp)
            else:
                tp = wx.CreateFileTipProvider(tipFile, tipIndex)
                showTip = wx.ShowTip(None, tp)

            self.prefs.appData['tipIndex'] = tp.GetCurrentTip()
            self.prefs.saveAppData()
            self.prefs.app['showStartupTips'] = showTip
            self.prefs.saveUserPrefs()

        self.Bind(wx.EVT_IDLE, self.onIdle)

        # doing this once subsequently enables the app to open & switch among
        # wx-windows on some platforms (Mac 10.9.4) with wx-3.0:
        v = parse_version
        if sys.platform == 'darwin':
            if v('3.0') <= v(wx.version()) < v('4.0'):
                _Showgui_Hack()  # returns ~immediately, no display
                # focus stays in never-land, so bring back to the app:
                if prefs.app['defaultView'] in [
                        'all', 'builder', 'coder', 'runner'
                ]:
                    self.showBuilder()
                else:
                    self.showCoder()
        # after all windows are created (so errors flushed) create output
        self._appLoaded = True
        if self.coder:
            self.coder.setOutputWindow()  # takes control of sys.stdout

        # flush any errors to the last run log file
        logging.flush()
        sys.stdout.flush()
        # we wanted debug mode while loading but safe to go back to info mode
        if not self.prefs.app['debugMode']:
            logging.console.setLevel(logging.INFO)

        return True
Exemple #30
0
    def getEvents(self, timeout=10):
        """Look for a string that matches SDAT;\n.........EDAT;\n
        and process it as events
        """
        foundDataStart = False
        t0 = time.time()
        while not foundDataStart and time.time() - t0 < timeout:
            startLine = self.com.readline()
            if startLine == b'\n':
                startLine = self.com.readline()
            if startLine.startswith(b'SDAT'):
                foundDataStart = True
                logging.info("BBTK.getEvents() found data. Processing...")
                logging.flush()  # we aren't in a time-critical period
                break
        # check if we're processing data
        if not foundDataStart:
            logging.warning("BBTK.getEvents() found no data "
                            "(SDAT was not found on serial port inputs")
            return []

        # helper function to parse time and event code
        def parseEventsLine(line, lastState=None):
            """Returns a list of dictionaries, one for each change
            detected in the state
            """
            state = line[:12]
            timeSecs = int(line[-14:-2]) / 10.0**6
            evts = []
            evt = ''
            if lastState is None:
                evts.append({'evt': '',
                             'state': state,
                             'time': timeSecs})
            else:
                for n in evtChannels:
                    if state[n] != lastState[n]:
                        if chr(state[n]) =='1':
                            evt = evtChannels[n] + "_on"
                        else:
                            evt = evtChannels[n] + "_off"
                        evts.append({'evt': evt,
                                     'state': state,
                                     'time': timeSecs})
            return evts

        # we've been sent data so work through it
        events = []
        eventLines = []
        lastState = None
        # try to read from port
        self.pause()
        self.com.timeout = 5.0
        nEvents = int(self.com.readline()[:-2])  # last two chars are ;\n
        self.com.readline()[:-2]  # microseconds recorded (ignore)
        self.com.readline()[:-2]  # samples recorded (ignore)
        while True:
            line = self.com.readline()
            if line.startswith(b'EDAT'):  # end of data stream
                break
            events.extend(parseEventsLine(line, lastState))
            lastState = events[-1]['state']
            eventLines.append(line)
        if nEvents != len(eventLines):
            msg = "BBTK reported %i events but told us to expect %i events!!"
            logging.warning(msg % (len(events), nEvents))
        logging.flush()  # we aren't in a time-critical period
        return events
def run_block(fix):

    # Await scan trigger
    while True:
        scan_trigger_text.draw()
        win.flip()
        if 'o' in event.waitKeys():
            logging.log(logging.DATA, "start key press")
            break
        event.clearEvents()
        
    clock=core.Clock()
    t = clock.getTime()
    
    #set up the fixation
    ratings_and_onsets.append(['fixation',t])
    logging.log(logging.DATA, "fixation %f"%t)
    show_stim(fixation_text, fix)  # 8 sec blank screen with fixation cross
    
    #log fixation
    logging.log(logging.DATA, "fixation end %f"%t)
    t = clock.getTime()
    
    #reset the clock so the onsets are correct (if onsets have the 8 sec in them then you dont need this)
    clock.reset()
    ratings_and_onsets.append(['start',t])
    logging.log(logging.DATA, "START")
    
    
    #start the taste loop
    for trial in range(ntrials):
        #check for quit
        if check_for_quit(subdata,win):
            exptutils.shut_down_cleanly(subdata,win)
            sys.exit()
        #empty trial data 
        trialdata={}
        trialdata['onset']=onsets[trial]
        
        
        #shuffle the positions
        shuffle(pos_ind)
        visual_stim1=visual.ImageStim(win, image=N.zeros((300,300)),pos=positions[pos_ind[0]], size=(0.25,0.25),units='height')
        visual_stim2=visual.ImageStim(win, image=N.zeros((300,300)),pos=positions[pos_ind[1]], size=(0.25,0.25),units='height')
        
        #set which image is which
        x=int(N.random.choice(prob_index, 1, p=[0.34, 0.33,0.33]))
        print(x)
        
        stim_images=stim_list[x]
        trial_prob=prob_list[x]
        trial_inv_prob=inv_prob_list[x]
        visual_stim1.setImage(stim_images[indices[0]])#set which image appears
        visual_stim2.setImage(stim_images[indices[1]])#set which image appears
        
        master_prob_list=[trial_prob,trial_inv_prob]
        
        shuffle(indices)
        #creating a dictory which will store the postion with the image and pump, the image and pump need to match
        mydict={}
#        mydict[positions_eng[pos_ind[1]]] = [stim_images[indices[1]], master_prob_list[indices[1]]]
#        mydict[positions_eng[pos_ind[0]]] = [stim_images[indices[0]], master_prob_list[indices[0]]]
        mydict[positions_scan[pos_ind[1]]] = [stim_images[indices[1]], master_prob_list[indices[1]]]
        mydict[positions_scan[pos_ind[0]]] = [stim_images[indices[0]], master_prob_list[indices[0]]]

        print(mydict)
        
        #which is sweet?
        message=visual.TextStim(win, text='Which is Correct?',pos=(0,5))
        print trial
        t = clock.getTime()
        
        #get the time of the image and log, this log is appending it to the csv file 
        visual_stim1.draw()#making image of the logo appear
        visual_stim2.draw()#making image of the logo appear
        message.draw()
        RT = core.Clock()
        
        #this is logging when the message is shown
        logging.log(logging.DATA, "%s at position=%s and %s at position=%s"%(stim_images[indices[0]],positions_eng[pos_ind[0]],stim_images[indices[1]],positions_eng[pos_ind[1]]))
        logging.flush()
        
        while clock.getTime()<trialdata['onset']:
            pass
        win.flip()
        
        RT.reset() # reaction time starts immediately after flip 
        
        while clock.getTime()<(trialdata['onset']+cue_time):#show the image, while clock is less than onset and cue, show cue
            pass
        
        keys = event.getKeys(keyList=['1','2'],timeStamped=RT)
        message=visual.TextStim(win, text='')#blank screen while the taste is delivered
        message.draw()
        win.flip()
        
        
        # get the key press logged, and time stamped 
        
        if len(keys)>0:
            logging.log(logging.DATA, "keypress=%s at time= %f"%(keys[0][0],keys[0][1]))
            print("here are the keys:")
            print(keys)
            t = clock.getTime()
            logging.flush()
            
            #back up of the key press
            tempArray = [t, keys[0]]
            key_responses.append(tempArray)
            ratings_and_onsets.append(["keypress=%s"%keys[0][0],t])
            if keys[0][0] == '1':
                # draw the image for keypress 2
                visual_stim1.draw()
                #from the dictionary find the image code associated with the key press
                #taste=int(mydict['left'][1])
                image=(mydict['1'][0])
                trial_prob=(mydict['1'][1])
                taste=int(N.random.choice(pump_responses, 1, p=trial_prob))
                #if image=='sweet.jpg':
                    #taste=int(N.random.choice(pump_responses, 1, p=[0.5, 0.5]))
                #elif image=='unsweet.jpg':
                   #taste=int(N.random.choice(pump_responses, 1, p=[0.5, 0.5]))
                print(image)
                print(taste)
                #log the pump used, time, and key press
                print 'injecting via pump at address %s'%taste
                logging.log(logging.DATA,"injecting via pump at address %d and a keypress of %s and image of %s"%(taste,keys[0][0], image))
                t = clock.getTime()
                ratings_and_onsets.append(["injecting via pump at address %d"%taste, t, keys[0][0]])
                #trigger pump with the numeral from the dictonary above 
                ser.write('%dRUN\r'%taste)
                logging.flush()
            elif keys[0][0] == '2':
                # draw the image for keypress 2
                visual_stim2.draw()
                
                #from the dictonary get the image associated with the right key press
                image=(mydict['2'][0])
                trial_prob=(mydict['2'][1])
                taste=int(N.random.choice(pump_responses, 1, p=trial_prob))
                print(image)
                print(taste)
                #log the time, keypress, and pump 
                print 'injecting via pump at address %s'%taste
                logging.log(logging.DATA,"injecting via pump at address %d and a keypress of %s and image of %s"%(taste,keys[0][0], image))
                t = clock.getTime()
                ratings_and_onsets.append(["injecting via pump at address %d"%taste, t])
                #trigger the pump with the numeral from the dictionary
                ser.write('%dRUN\r'%taste)
                logging.flush()
        else:
            taste=0
            t = clock.getTime()
            logging.log(logging.DATA,"Key Press Missed!")
            keys=keys.append(['MISS',t])
            logging.flush()
            message=visual.TextStim(win, text='Please answer quicker', pos=(0, 0), height=2)#this lasts throught the taste
            message.draw()
            win.flip()
            

        while clock.getTime()<(trialdata['onset']+cue_time+delivery_time):
            pass
        
        message=visual.TextStim(win, text='+', pos=(0, 0), height=2) #this lasts throught the wait
        message.draw()
        win.flip()
        t = clock.getTime()
        ratings_and_onsets.append(["wait", t])
        
        
        
        trialdata['dis']=[ser.write('0DIS\r'),ser.write('1DIS\r')]
        print(trialdata['dis'])
        
        while clock.getTime()<(trialdata['onset']+cue_time+delivery_time+wait_time):
            pass
       
        message=visual.TextStim(win, text='', pos=(0, 0), height=2)#this lasts throught the rinse 
        message.draw()
        win.flip()
                
        print 'injecting rinse via pump at address %d'%0
        t = clock.getTime()
        ratings_and_onsets.append(['injecting rinse via pump at address %d'%0, t])
        ser.write('%dRUN\r'%0)
        logging.log(logging.DATA, "RINSE")
        logging.flush()
        
      
        
        while clock.getTime()<(trialdata['onset']+cue_time+delivery_time+wait_time+rinse_time):
            pass

        message=visual.TextStim(win, text='+', pos=(0, 0), height=2)#lasts through the jitter 
        message.draw()
        win.flip()
        t = clock.getTime()
        ratings_and_onsets.append(["jitter", t])

        while clock.getTime()<(trialdata['onset']+cue_time+delivery_time+wait_time+rinse_time+jitter[trial]):
            pass
        
        t = clock.getTime()
        ratings_and_onsets.append(['end time', t])
        logging.log(logging.DATA,"finished")
        logging.flush()
        subdata['trialdata'][trial]=trialdata
        
      
        
        print(key_responses)
        
    win.close()
Exemple #32
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 = pyo.pa_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('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 = pyo.pa_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 = '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.general['audioDriver'][0]
            maxInputChnls = pyo.pa_get_input_max_channels(
                pyo.pa_get_default_input())
            maxOutputChnls = pyo.pa_get_output_max_channels(
                pyo.pa_get_default_output())
            duplex = bool(maxInputChnls > 0)

        maxChnls = min(maxInputChnls, maxOutputChnls)
        if maxInputChnls < 1:  # pragma: no cover
            msg = ('%s.init could not find microphone hardware; '
                   'recording not available')
            logging.warning(msg % __name__)
            maxChnls = maxOutputChnls
        if maxOutputChnls < 1:  # pragma: no cover
            msg = ('%s.init could not find speaker hardware; '
                   'sound not available')
            logging.error(msg % __name__)
            return -1

        # create the instance of the server:
        if platform in ['darwin', 'linux2']:
            # for mac/linux we set the backend using the server audio param
            pyoSndServer = Server(sr=rate,
                                  nchnls=maxChnls,
                                  buffersize=buffer,
                                  audio=audioDriver)
        else:
            # with others we just use portaudio and then set the OutputDevice
            # below
            pyoSndServer = Server(sr=rate, nchnls=maxChnls, buffersize=buffer)

        pyoSndServer.setVerbosity(1)
        if platform == 'win32':
            pyoSndServer.setOutputDevice(outputID)
            if inputID is not None:
                pyoSndServer.setInputDevice(inputID)
        # do other config here as needed (setDuplex? setOutputDevice?)
        pyoSndServer.setDuplex(duplex)
        pyoSndServer.boot()
    core.wait(0.5)  # wait for server to boot before starting te sound stream
    pyoSndServer.start()
    try:
        Sound()  # test creation, no play
    except pyo.PyoServerStateException:
        msg = "Failed to start pyo sound Server"
        if platform == 'darwin' and audioDriver != 'portaudio':
            msg += "; maybe try prefs.general.audioDriver 'portaudio'?"
        logging.error(msg)
        core.quit()
    logging.debug('pyo sound server started')
    logging.flush()
Exemple #33
0
def initPyo(rate=44100, stereo=True, buffer=128):
    """setup the pyo (sound) server
    """
    global pyoSndServer, Sound, audioDriver, duplex
    Sound = SoundPyo
    if not 'pyo' in locals():
        import pyo  # microphone.switchOn() calls initPyo even if audioLib is something else
    #subclass the pyo.Server so that we can insert a __del__ function that shuts it down
    class Server(pyo.Server):
        core=core #make libs class variables so they don't get deleted first
        logging=logging
        def __del__(self):
            self.stop()
            self.core.wait(0.5)#make sure enough time passes for the server to shutdown
            self.shutdown()
            self.core.wait(0.5)#make sure enough time passes for the server to shutdown
            self.logging.debug('pyo sound server shutdown')#this may never get printed

    maxInputChnls = pyo.pa_get_input_max_channels(pyo.pa_get_default_input())
    maxOutputChnls = pyo.pa_get_output_max_channels(pyo.pa_get_default_output())
    maxChnls = min(maxInputChnls, maxOutputChnls)
    if maxInputChnls < 1:
        logging.warning('%s.initPyo could not find microphone hardware; recording not available' % __name__)
        maxChnls = maxOutputChnls
    if maxOutputChnls < 1:
        logging.error('%s.initPyo could not find speaker hardware; sound not available' % __name__)
        core.quit()
    #check if we already have a server and kill it
    if globals().has_key('pyoSndServer') and hasattr(pyoSndServer,'shutdown'): #if it exists and isn't None!
        #this doesn't appear to work!
        pyoSndServer.stop()
        core.wait(0.5)#make sure enough time passes for the server to shutdown
        pyoSndServer.shutdown()
        core.wait(0.5)
        pyoSndServer.reinit(sr=rate, nchnls=maxChnls, buffersize=buffer, audio=audioDriver)
        pyoSndServer.boot()
    else:
        if platform=='win32':
            #check for output device/driver
            devNames, devIDs=pyo.pa_get_output_devices()
            audioDriver,outputID=_bestDriver(devNames, devIDs)
            if outputID:
                logging.info('Using sound driver: %s (ID=%i)' %(audioDriver, outputID))
            else:
                logging.warning('No audio outputs found (no speakers connected?')
                return -1
            #check for valid input (mic)
            devNames, devIDs = pyo.pa_get_input_devices()
            junk, inputID=_bestDriver(devNames, devIDs)
            if inputID:
                duplex = bool(maxInputChnls > 0)
            else:
                duplex=False
        else:#for other platforms set duplex to True (if microphone is available)
            audioDriver = prefs.general['audioDriver'][0]
            duplex = bool(maxInputChnls > 0)
        # create the instance of the server:
        if platform=='darwin':
            #for mac 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:
                pyoSndServer.setInputDevice(inputID)
        #do other config here as needed (setDuplex? setOutputDevice?)
        pyoSndServer.setDuplex(duplex)
        pyoSndServer.boot()
    core.wait(0.5)#wait for server to boot before starting te sound stream
    pyoSndServer.start()
    try:
        Sound()  # test creation, no play
    except pyo.PyoServerStateException:
        msg = "Failed to start pyo sound Server"
        if platform == 'darwin' and audioDriver != 'portaudio':
            msg += "; maybe try prefs.general.audioDriver 'portaudio'?"
        logging.error(msg)
        core.quit()
    logging.debug('pyo sound server started')
    logging.flush()
Exemple #34
0
    def loadMovie(self, filename, log=True):
        """Load a movie from file

        :Parameters:

            filename: string
                The name of the file, including path if necessary


        After the file is loaded MovieStim.duration is updated with the movie
        duration (in seconds).
        """
        self._unload()
        self._reset()
        if self._no_audio is False:
            self._createAudioStream()

        # Create Video Stream stuff
        self._video_stream = cv2.VideoCapture()
        self._video_stream.open(filename)
        if not self._video_stream.isOpened():
          raise RuntimeError( "Error when reading image file")

        self._total_frame_count = self._video_stream.get(cv2.cv.CV_CAP_PROP_FRAME_COUNT)
        self._video_width = self._video_stream.get(cv2.cv.CV_CAP_PROP_FRAME_WIDTH)
        self._video_height = self._video_stream.get(cv2.cv.CV_CAP_PROP_FRAME_HEIGHT)
        self._format = self._video_stream.get(cv2.cv.CV_CAP_PROP_FORMAT)
        # TODO: Read depth from video source
        self._video_frame_depth = 3

        cv_fps = self._video_stream.get(cv2.cv.CV_CAP_PROP_FPS)
        if self._requested_fps:
            if self._no_audio is False and cv_fps != self._requested_fps:
                self._no_audio = True
                logging.error("MovieStim2 video fps != requested fps. Disabling Audio Stream.")
                logging.flush()

        if self._no_audio and self._requested_fps:
            self._video_frame_rate = self._requested_fps
        else:
            self._video_frame_rate = self._video_stream.get(cv2.cv.CV_CAP_PROP_FPS)

        self._inter_frame_interval = 1.0/self._video_frame_rate

        # Create a numpy array that can hold one video frame, as returned by cv2.
        self._numpy_frame = numpy.zeros((self._video_height,
                                          self._video_width,
                                          self._video_frame_depth),
                                         dtype=numpy.uint8)

        # Uses a preallocated numpy array as the pyglet ImageData data
        self._frame_data_interface = ArrayInterfaceImage(self._numpy_frame,
                                                         allow_copy=False,
                                                         rectangle=True,
                                                         force_rectangle=True)
        #frame texture; transformed so it looks right in psychopy
        self._frame_texture = self._frame_data_interface.texture.get_transform(flip_x=not self.flipHoriz,
                                                    flip_y=not self.flipVert)

        self.duration = self._total_frame_count * self._inter_frame_interval
        self.status = NOT_STARTED

        self.filename = filename
        logAttrib(self, log, 'movie', filename)
Exemple #35
0
def baseline():
    # Initialize components for Routine "fixation_cross"
    fixation_crossClock = core.Clock()
    cross = visual.TextStim(win=win, name='cross',
        text='+',
        font='Arial',
        pos=(0, 0), height=0.1, wrapWidth=None, ori=0,
        color='white', colorSpace='rgb', opacity=1,
        languageStyle='LTR',
        depth=0.0);

    # ------Prepare to start Routine "fixation_cross"-------
    continueRoutine = True
    routineTimer.add(125.000000)
    # update component parameters for each repeat
    # keep track of which components have finished
    fixation_crossComponents = [cross]
    for thisComponent in fixation_crossComponents:
        thisComponent.tStart = None
        thisComponent.tStop = None
        thisComponent.tStartRefresh = None
        thisComponent.tStopRefresh = None
        if hasattr(thisComponent, 'status'):
            thisComponent.status = NOT_STARTED
    # reset timers
    t = 0
    _timeToFirstFrame = win.getFutureFlipTime(clock="now")
    fixation_crossClock.reset(-_timeToFirstFrame)  # t0 is time of first possible flip
    frameN = -1

    # -------Run Routine "fixation_cross"-------
    while continueRoutine and routineTimer.getTime() > 0:
        # get current time
        t = fixation_crossClock.getTime()
        tThisFlip = win.getFutureFlipTime(clock=fixation_crossClock)
        tThisFlipGlobal = win.getFutureFlipTime(clock=None)
        frameN = frameN + 1  # number of completed frames (so 0 is the first frame)
        # update/draw components on each frame

        # *cross* updates
        if cross.status == NOT_STARTED and tThisFlip >= 0.0-frameTolerance:
            # keep track of start time/frame for later
            cross.frameNStart = frameN  # exact frame index
            cross.tStart = t  # local t and not account for scr refresh
            cross.tStartRefresh = tThisFlipGlobal  # on global time
            win.timeOnFlip(cross, 'tStartRefresh')  # time at next scr refresh
            cross.setAutoDraw(True)
        if cross.status == STARTED:
            # is it time to stop? (based on global clock, using actual start)
            if tThisFlipGlobal > cross.tStartRefresh + 120-frameTolerance:
                # keep track of stop time/frame for later
                cross.tStop = t  # not accounting for scr refresh
                cross.frameNStop = frameN  # exact frame index
                win.timeOnFlip(cross, 'tStopRefresh')  # time at next scr refresh
                cross.setAutoDraw(False)

        # check for quit (typically the Esc key)
        if endExpNow or defaultKeyboard.getKeys(keyList=["escape"]):
            core.quit()

        # check if all components have finished
        if not continueRoutine:  # a component has requested a forced-end of Routine
            break
        continueRoutine = False  # will revert to True if at least one component still running
        for thisComponent in fixation_crossComponents:
            if hasattr(thisComponent, "status") and thisComponent.status != FINISHED:
                continueRoutine = True
                break  # at least one component has not yet finished

        # refresh the screen
        if continueRoutine:  # don't flip if this routine is over or we'll get a blank screen
            win.flip()

    # -------Ending Routine "fixation_cross"-------
    for thisComponent in fixation_crossComponents:
        if hasattr(thisComponent, "setAutoDraw"):
            thisComponent.setAutoDraw(False)

    # Flip one final time so any remaining win.callOnFlip()
    # and win.timeOnFlip() tasks get executed before quitting
    win.flip()
    logging.flush()
    # make sure everything is closed down
    win.close()
    core.quit()
"""
sentencecomp.py - code to run the sentence comprehension task for the reading remediation study
"""

from psychopy import visual, core, event, logging,gui
import numpy as N
import pickle
import datetime
import sys
import os
import inspect
import hashlib
from socket import gethostname

from exptutils import *

# study-specific routines

def load_sentences(filename):
    f=open(filename)
    ll=f.readlines()
    f.close()
    s={}
    s['sentence']=[]
    s['cond']=[]
    s['onset']=[]
    for l in ll:
        l_s=l.split('\t')
        #print l_s
        s['sentence'].append(' '.join(l_s[2:]).strip('\n'))
Exemple #37
0
    def findIdentityLUT(
        self, maxIterations=1000, errCorrFactor=1.0 / 5000, nVerifications=50, demoMode=True, logFile=""
    ):
        """Search for the identity LUT for this card/operating system.
        This requires that the window being tested is fullscreen on the Bits#
        monitor (or at least occupies the first 256 pixels in the top left
        corner!)

        :params:

            LUT: The lookup table to be tested (256 x 3).
            If None then the LUT will not be altered

            errCorrFactor: amount of correction done for each iteration
                number of repeats (successful) to check dithering
                has been eradicated

            demoMode: generate the screen but don't go into status mode

        :returns:

            a 256x3 array of error values (integers in range 0:255)
        """
        t0 = time.time()
        # create standard options
        intel = np.linspace(0.05, 0.95, 256)
        one = np.linspace(0, 1.0, 256)
        fraction = np.linspace(0.0, 65535.0 / 65536.0, num=256)
        LUTs = {
            "intel": np.repeat(intel, 3).reshape([-1, 3]),
            "0-255": np.repeat(one, 3).reshape([-1, 3]),
            "0-65535": np.repeat(fraction, 3).reshape([-1, 3]),
            "1-65536": np.repeat(fraction, 3).reshape([-1, 3]),
        }

        if logFile:
            self.logFile = open(logFile, "w")

        if plotResults:
            pyplot.Figure()
            pyplot.subplot(1, 2, 1)
            pyplot.plot([0, 255], [0, 255], "-k")
            errPlot = pyplot.plot(range(256), range(256), ".r")[0]
            pyplot.subplot(1, 2, 2)
            pyplot.plot(200, 0.01, ".w")
            pyplot.show(block=False)

        lowestErr = 1000000000
        bestLUTname = None
        logging.flush()
        for LUTname, currentLUT in LUTs.items():
            sys.stdout.write("Checking %r LUT:" % LUTname)
            errs = self.testLUT(currentLUT, demoMode)
            if plotResults:
                errPlot.set_ydata(range(256) + errs[:, 0])
                pyplot.draw()
            print("mean err = %.3f per LUT entry" % abs(errs).mean())
            if abs(errs).mean() < abs(lowestErr):
                lowestErr = abs(errs).mean()
                bestLUTname = LUTname
        if lowestErr == 0:
            msg = "The %r identity LUT produced zero error. We'll use that!"
            print(msg % LUTname)
            self.identityLUT = LUTs[bestLUTname]
            # it worked so save this configuration for future
            self.save()
            return

        msg = "Best was %r LUT (mean err = %.3f). Optimising that..."
        print(msg % (bestLUTname, lowestErr))
        currentLUT = LUTs[bestLUTname]
        errProgression = []
        corrInARow = 0
        for n in range(maxIterations):
            errs = self.testLUT(currentLUT)
            tweaks = errs * errCorrFactor
            currentLUT -= tweaks
            currentLUT[currentLUT > 1] = 1.0
            currentLUT[currentLUT < 0] = 0.0
            meanErr = abs(errs).mean()
            errProgression.append(meanErr)
            if plotResults:
                errPlot.set_ydata(range(256) + errs[:, 0])
                pyplot.subplot(1, 2, 2)
                if meanErr == 0:
                    point = ".k"
                else:
                    point = ".r"
                pyplot.plot(n, meanErr, ".k")
                pyplot.draw()
            if meanErr > 0:
                sys.stdout.write("%.3f " % meanErr)
                corrInARow = 0
            else:
                sys.stdout.write(". ")
                corrInARow += 1
            if corrInARow >= nVerifications:
                print("success in a total of %.1fs" % (time.time() - t0))
                self.identityLUT = currentLUT
                # it worked so save this configuration for future
                self.save()
                break
            elif len(errProgression) > 10 and max(errProgression) - min(errProgression) < 0.001:
                print(
                    "Trying to correct the gamma table was having no "
                    "effect. Make sure the window was fullscreen and "
                    "on the Bits# screen"
                )
                break

        # did we get here by failure?!
        if n == maxIterations - 1:
            print("failed to converge on a successful identity LUT. " "This is BAD!")

        if plotResults:
            pyplot.figure(figsize=[18, 12])
            pyplot.subplot(1, 3, 1)
            pyplot.plot(errProgression)
            pyplot.title("Progression of errors")
            pyplot.ylabel("Mean error per LUT entry (0-1)")
            pyplot.xlabel("Test iteration")
            r256 = np.reshape(range(256), [256, 1])
            pyplot.subplot(1, 3, 2)
            pyplot.plot(r256, r256, "k-")
            pyplot.plot(r256, currentLUT[:, 0] * 255, "r.", markersize=2.0)
            pyplot.plot(r256, currentLUT[:, 1] * 255, "g.", markersize=2.0)
            pyplot.plot(r256, currentLUT[:, 2] * 255, "b.", markersize=2.0)
            pyplot.title("Final identity LUT")
            pyplot.ylabel("LUT value")
            pyplot.xlabel("LUT entry")

            pyplot.subplot(1, 3, 3)
            deviations = currentLUT - r256 / 255.0
            pyplot.plot(r256, deviations[:, 0], "r.")
            pyplot.plot(r256, deviations[:, 1], "g.")
            pyplot.plot(r256, deviations[:, 2], "b.")
            pyplot.title("LUT deviations from sensible")
            pyplot.ylabel("LUT value")
            pyplot.xlabel("LUT deviation (multiples of 1024)")
            pyplot.savefig("bitsSharpIdentityLUT.pdf")
            pyplot.show()
Exemple #38
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, 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.isOpen = False
        self.com = None
        self.OK = False
        self.maxAttempts = maxAttempts
        self.eol = eol
        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()
Exemple #39
0
def save_beh():
    logging.flush()
    with open(join('results', 'behavioral_data', 'beh_{}_{}.csv'.format(NAME, RAND)), 'w') as csvfile:
        beh_writer = csv.writer(csvfile)
        beh_writer.writerows(RESULTS)
Exemple #40
0
def createTexture(tex, id, pixFormat, stim, res=128, maskParams=None,
                  forcePOW2=True, dataType=None):
    """
    :params:

        id:
            is the texture ID

        pixFormat:
            GL.GL_ALPHA, GL.GL_RGB

        useShaders:
            bool

        interpolate:
            bool (determines whether texture will use GL_LINEAR or GL_NEAREST

        res:
            the resolution of the texture (unless a bitmap image is used)

        dataType:
            None, GL.GL_UNSIGNED_BYTE, GL_FLOAT. Only affects image files (numpy arrays will be float)

    For grating stimuli (anything that needs multiple cycles) forcePOW2 should
    be set to be True. Otherwise the wrapping of the texture will not work.

    """

    """
    Create an intensity texture, ranging -1:1.0
    """
    global _nImageResizes
    notSqr=False #most of the options will be creating a sqr texture
    wasImage=False #change this if image loading works
    useShaders = stim.useShaders
    interpolate = stim.interpolate
    if dataType==None:
        if useShaders and pixFormat==GL.GL_RGB:
            dataType = GL.GL_FLOAT
        else:
            dataType = GL.GL_UNSIGNED_BYTE

    if type(tex) == numpy.ndarray:
        #handle a numpy array
        #for now this needs to be an NxN intensity array
        intensity = tex.astype(numpy.float32)
        if intensity.max()>1 or intensity.min()<-1:
            logging.error('numpy arrays used as textures should be in the range -1(black):1(white)')
        if len(tex.shape)==3:
            wasLum=False
        else: wasLum = True
        ##is it 1D?
        if tex.shape[0]==1:
            stim._tex1D=True
            res=tex.shape[1]
        elif len(tex.shape)==1 or tex.shape[1]==1:
            stim._tex1D=True
            res=tex.shape[0]
        else:
            stim._tex1D=False
            #check if it's a square power of two
            maxDim = max(tex.shape)
            powerOf2 = 2**numpy.ceil(numpy.log2(maxDim))
            if forcePOW2 and (tex.shape[0]!=powerOf2 or tex.shape[1]!=powerOf2):
                logging.error("Requiring a square power of two (e.g. 16x16, 256x256) texture but didn't receive one")
                core.quit()
            res=tex.shape[0]
    elif tex in [None,"none", "None"]:
        res=1 #4x4 (2x2 is SUPPOSED to be fine but generates wierd colors!)
        intensity = numpy.ones([res,res],numpy.float32)
        wasLum = True
    elif tex == "sin":
        onePeriodX, onePeriodY = numpy.mgrid[0:res, 0:2*pi:1j*res]# NB 1j*res is a special mgrid notation
        intensity = numpy.sin(onePeriodY-pi/2)
        wasLum = True
    elif tex == "sqr":#square wave (symmetric duty cycle)
        onePeriodX, onePeriodY = numpy.mgrid[0:res, 0:2*pi:1j*res]# NB 1j*res is a special mgrid notation
        sinusoid = numpy.sin(onePeriodY-pi/2)
        intensity = numpy.where(sinusoid>0, 1, -1)
        wasLum = True
    elif tex == "saw":
        intensity = numpy.linspace(-1.0,1.0,res,endpoint=True)*numpy.ones([res,1])
        wasLum = True
    elif tex == "tri":
        intensity = numpy.linspace(-1.0,3.0,res,endpoint=True)#-1:3 means the middle is at +1
        intensity[int(res/2.0+1):] = 2.0-intensity[int(res/2.0+1):]#remove from 3 to get back down to -1
        intensity = intensity*numpy.ones([res,1])#make 2D
        wasLum = True
    elif tex == "sinXsin":
        onePeriodX, onePeriodY = numpy.mgrid[0:2*pi:1j*res, 0:2*pi:1j*res]# NB 1j*res is a special mgrid notation
        intensity = numpy.sin(onePeriodX-pi/2)*numpy.sin(onePeriodY-pi/2)
        wasLum = True
    elif tex == "sqrXsqr":
        onePeriodX, onePeriodY = numpy.mgrid[0:2*pi:1j*res, 0:2*pi:1j*res]# NB 1j*res is a special mgrid notation
        sinusoid = numpy.sin(onePeriodX-pi/2)*numpy.sin(onePeriodY-pi/2)
        intensity = numpy.where(sinusoid>0, 1, -1)
        wasLum = True
    elif tex == "circle":
        rad=makeRadialMatrix(res)
        intensity = (rad<=1)*2-1
        fromFile=0
        wasLum=True
    elif tex == "gauss":
        rad=makeRadialMatrix(res)
        sigma = 1/3.0;
        intensity = numpy.exp( -rad**2.0 / (2.0*sigma**2.0) )*2-1 #3sd.s by the edge of the stimulus
        fromFile=0
        wasLum=True
    elif tex == "radRamp":#a radial ramp
        rad=makeRadialMatrix(res)
        intensity = 1-2*rad
        intensity = numpy.where(rad<-1, intensity, -1)#clip off the corners (circular)
        fromFile=0
        wasLum=True
    elif tex == "raisedCos": # A raised cosine
        wasLum=True
        hamming_len = 1000 # This affects the 'granularity' of the raised cos

        # If no user input was provided:
        if maskParams is None:
            fringe_proportion = 0.2 # This one affects the proportion of the
                                # stimulus diameter that is devoted to the
                                # raised cosine.

        # Users can provide the fringe proportion through a dict, maskParams
        # input:
        else:
            fringe_proportion = maskParams['fringeWidth']

        rad = makeRadialMatrix(res)
        intensity = numpy.zeros_like(rad)
        intensity[numpy.where(rad < 1)] = 1
        raised_cos_idx = numpy.where(
            [numpy.logical_and(rad <= 1, rad >= 1-fringe_proportion)])[1:]

        # Make a raised_cos (half a hamming window):
        raised_cos = numpy.hamming(hamming_len)[:hamming_len/2]
        raised_cos -= numpy.min(raised_cos)
        raised_cos /= numpy.max(raised_cos)

        # Measure the distance from the edge - this is your index into the hamming window:
        d_from_edge = numpy.abs((1 - fringe_proportion)- rad[raised_cos_idx])
        d_from_edge /= numpy.max(d_from_edge)
        d_from_edge *= numpy.round(hamming_len/2)

        # This is the indices into the hamming (larger for small distances from the edge!):
        portion_idx = (-1 * d_from_edge).astype(int)

        # Apply the raised cos to this portion:
        intensity[raised_cos_idx] = raised_cos[portion_idx]

        # Scale it into the interval -1:1:
        intensity = intensity - 0.5
        intensity = intensity / numpy.max(intensity)

        #Sometimes there are some remaining artifacts from this process, get rid of them:
        artifact_idx = numpy.where(numpy.logical_and(intensity == -1,
                                                     rad < 0.99))
        intensity[artifact_idx] = 1
        artifact_idx = numpy.where(numpy.logical_and(intensity == 1, rad >
                                                     0.99))
        intensity[artifact_idx] = 0

    else:
        if type(tex) in [str, unicode, numpy.string_]:
            # maybe tex is the name of a file:
            if not os.path.isfile(tex):
                logging.error("Couldn't find image file '%s'; check path?" %(tex)); logging.flush()
                raise OSError, "Couldn't find image file '%s'; check path? (tried: %s)" \
                    % (tex, os.path.abspath(tex))#ensure we quit
            try:
                im = Image.open(tex)
                im = im.transpose(Image.FLIP_TOP_BOTTOM)
            except IOError:
                logging.error("Found file '%s' but failed to load as an image" %(tex)); logging.flush()
                raise IOError, "Found file '%s' [= %s] but it failed to load as an image" \
                    % (tex, os.path.abspath(tex))#ensure we quit
        else:
            # can't be a file; maybe its an image already in memory?
            try:
                im = tex.copy().transpose(Image.FLIP_TOP_BOTTOM) # ? need to flip if in mem?
            except AttributeError: # nope, not an image in memory
                logging.error("Couldn't make sense of requested image."); logging.flush()
                raise AttributeError, "Couldn't make sense of requested image."#ensure we quit
        # at this point we have a valid im
        stim._origSize=im.size
        wasImage=True
        #is it 1D?
        if im.size[0]==1 or im.size[1]==1:
            logging.error("Only 2D textures are supported at the moment")
        else:
            maxDim = max(im.size)
            powerOf2 = int(2**numpy.ceil(numpy.log2(maxDim)))
            if im.size[0]!=powerOf2 or im.size[1]!=powerOf2:
                if not forcePOW2:
                    notSqr=True
                elif _nImageResizes<reportNImageResizes:
                    logging.warning("Image '%s' was not a square power-of-two image. Linearly interpolating to be %ix%i" %(tex, powerOf2, powerOf2))
                    _nImageResizes+=1
                    im=im.resize([powerOf2,powerOf2],Image.BILINEAR)
                elif _nImageResizes==reportNImageResizes:
                    logging.warning("Multiple images have needed resizing - I'll stop bothering you!")
                    im=im.resize([powerOf2,powerOf2],Image.BILINEAR)

        #is it Luminance or RGB?
        if im.mode=='L' and pixFormat==GL.GL_ALPHA:
            wasLum = True
        elif pixFormat==GL.GL_ALPHA:#we have RGB and need Lum
            wasLum = True
            im = im.convert("L")#force to intensity (in case it was rgb)
        elif pixFormat==GL.GL_RGB:#we have RGB and keep it that way
            #texture = im.tostring("raw", "RGB", 0, -1)
            im = im.convert("RGBA")#force to rgb (in case it was CMYK or L)
            wasLum=False
        if dataType==GL.GL_FLOAT:
            #convert from ubyte to float
            intensity = numpy.array(im).astype(numpy.float32)*0.0078431372549019607-1.0 # much faster to avoid division 2/255
        else:
            intensity = numpy.array(im)
        if wasLum and intensity.shape!=im.size:
            intensity.shape=im.size

    if pixFormat==GL.GL_RGB and wasLum and dataType==GL.GL_FLOAT: #grating stim on good machine
        #keep as float32 -1:1
        if sys.platform!='darwin' and stim.win.glVendor.startswith('nvidia'):
            #nvidia under win/linux might not support 32bit float
            internalFormat = GL.GL_RGB16F_ARB #could use GL_LUMINANCE32F_ARB here but check shader code?
        else:#we've got a mac or an ATI card and can handle 32bit float textures
            internalFormat = GL.GL_RGB32F_ARB #could use GL_LUMINANCE32F_ARB here but check shader code?
        data = numpy.ones((intensity.shape[0],intensity.shape[1],3),numpy.float32)#initialise data array as a float
        data[:,:,0] = intensity#R
        data[:,:,1] = intensity#G
        data[:,:,2] = intensity#B
    elif pixFormat==GL.GL_RGB and wasLum: #Grating on legacy hardware, or ImageStim with wasLum=True
        #scale by rgb and convert to ubyte
        internalFormat = GL.GL_RGB
        if stim.colorSpace in ['rgb', 'dkl', 'lms','hsv']:
            rgb=stim.rgb
        else:
            rgb=stim.rgb/127.5-1.0#colour is not a float - convert to float to do the scaling
        #scale by rgb
        data = numpy.ones((intensity.shape[0],intensity.shape[1],3),numpy.float32)#initialise data array as a float
        data[:,:,0] = intensity*rgb[0]  + stim.rgbPedestal[0]#R
        data[:,:,1] = intensity*rgb[1]  + stim.rgbPedestal[1]#G
        data[:,:,2] = intensity*rgb[2]  + stim.rgbPedestal[2]#B
        #convert to ubyte
        data = float_uint8(stim.contrast*data)
    elif pixFormat==GL.GL_RGB and dataType==GL.GL_FLOAT: #probably a custom rgb array or rgb image
        internalFormat = GL.GL_RGB32F_ARB
        data = intensity
    elif pixFormat==GL.GL_RGB:# not wasLum, not useShaders  - an RGB bitmap with no shader options
        internalFormat = GL.GL_RGB
        data = intensity #float_uint8(intensity)
    elif pixFormat==GL.GL_ALPHA:
        internalFormat = GL.GL_ALPHA
        if wasImage:
            data = intensity
        else:
            data = float_uint8(intensity)
    #check for RGBA textures
    if len(intensity.shape)>2 and intensity.shape[2] == 4:
        if pixFormat==GL.GL_RGB: pixFormat=GL.GL_RGBA
        if internalFormat==GL.GL_RGB: internalFormat=GL.GL_RGBA
        elif internalFormat==GL.GL_RGB32F_ARB: internalFormat=GL.GL_RGBA32F_ARB

    texture = data.ctypes#serialise
    #bind the texture in openGL
    GL.glEnable(GL.GL_TEXTURE_2D)
    GL.glBindTexture(GL.GL_TEXTURE_2D, id)#bind that name to the target
    GL.glTexParameteri(GL.GL_TEXTURE_2D,GL.GL_TEXTURE_WRAP_S,GL.GL_REPEAT) #makes the texture map wrap (this is actually default anyway)
    #important if using bits++ because GL_LINEAR
    #sometimes extrapolates to pixel vals outside range
    if interpolate:
        GL.glTexParameteri(GL.GL_TEXTURE_2D,GL.GL_TEXTURE_MAG_FILTER,GL.GL_LINEAR)
        if useShaders:#GL_GENERATE_MIPMAP was only available from OpenGL 1.4
            GL.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR)
            GL.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_GENERATE_MIPMAP, GL.GL_TRUE)
            GL.glTexImage2D(GL.GL_TEXTURE_2D, 0, internalFormat,
                data.shape[1],data.shape[0], 0, # [JRG] for non-square, want data.shape[1], data.shape[0]
                pixFormat, dataType, texture)
        else:#use glu
            GL.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR_MIPMAP_NEAREST)
            GL.gluBuild2DMipmaps(GL.GL_TEXTURE_2D, internalFormat,
                data.shape[1],data.shape[0], pixFormat, dataType, texture)    # [JRG] for non-square, want data.shape[1], data.shape[0]
    else:
        GL.glTexParameteri(GL.GL_TEXTURE_2D,GL.GL_TEXTURE_MAG_FILTER,GL.GL_NEAREST)
        GL.glTexParameteri(GL.GL_TEXTURE_2D,GL.GL_TEXTURE_MIN_FILTER,GL.GL_NEAREST)
        GL.glTexImage2D(GL.GL_TEXTURE_2D, 0, internalFormat,
                        data.shape[1],data.shape[0], 0, # [JRG] for non-square, want data.shape[1], data.shape[0]
                        pixFormat, dataType, texture)
    GL.glTexEnvi(GL.GL_TEXTURE_ENV, GL.GL_TEXTURE_ENV_MODE, GL.GL_MODULATE)#?? do we need this - think not!
    return wasLum
def move_halted_log(fn):
    # flush log
    logging.flush()
    shutil.move(fn, fn.replace('.txt', '__halted.txt'))
    # quit
    core.quit()
Exemple #42
0
    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()
Exemple #43
0
    def _createTexture(self, tex, id, pixFormat, stim, res=128, maskParams=None,
                      forcePOW2=True, dataType=None):
        """
        :params:
            id:
                is the texture ID
            pixFormat:
                GL.GL_ALPHA, GL.GL_RGB
            useShaders:
                bool
            interpolate:
                bool (determines whether texture will use GL_LINEAR or GL_NEAREST
            res:
                the resolution of the texture (unless a bitmap image is used)
            dataType:
                None, GL.GL_UNSIGNED_BYTE, GL_FLOAT. Only affects image files (numpy arrays will be float)

        For grating stimuli (anything that needs multiple cycles) forcePOW2 should
        be set to be True. Otherwise the wrapping of the texture will not work.
        """

        """
        Create an intensity texture, ranging -1:1.0
        """
        notSqr=False #most of the options will be creating a sqr texture
        wasImage=False #change this if image loading works
        useShaders = stim.useShaders
        interpolate = stim.interpolate
        if dataType==None:
            if useShaders and pixFormat==GL.GL_RGB:
                dataType = GL.GL_FLOAT
            else:
                dataType = GL.GL_UNSIGNED_BYTE

        if type(tex) == numpy.ndarray:
            #handle a numpy array
            #for now this needs to be an NxN intensity array
            intensity = tex.astype(numpy.float32)
            if intensity.max()>1 or intensity.min()<-1:
                logging.error('numpy arrays used as textures should be in the range -1(black):1(white)')
            if len(tex.shape)==3:
                wasLum=False
            else: wasLum = True
            ##is it 1D?
            if tex.shape[0]==1:
                stim._tex1D=True
                res=tex.shape[1]
            elif len(tex.shape)==1 or tex.shape[1]==1:
                stim._tex1D=True
                res=tex.shape[0]
            else:
                stim._tex1D=False
                #check if it's a square power of two
                maxDim = max(tex.shape)
                powerOf2 = 2**numpy.ceil(numpy.log2(maxDim))
                if forcePOW2 and (tex.shape[0]!=powerOf2 or tex.shape[1]!=powerOf2):
                    logging.error("Requiring a square power of two (e.g. 16x16, 256x256) texture but didn't receive one")
                    core.quit()
                res=tex.shape[0]
        elif tex in [None,"none", "None"]:
            res=1 #4x4 (2x2 is SUPPOSED to be fine but generates wierd colors!)
            intensity = numpy.ones([res,res],numpy.float32)
            wasLum = True
        elif tex == "sin":
            onePeriodX, onePeriodY = numpy.mgrid[0:res, 0:2*pi:1j*res]# NB 1j*res is a special mgrid notation
            intensity = numpy.sin(onePeriodY-pi/2)
            wasLum = True
        elif tex == "sqr":#square wave (symmetric duty cycle)
            onePeriodX, onePeriodY = numpy.mgrid[0:res, 0:2*pi:1j*res]# NB 1j*res is a special mgrid notation
            sinusoid = numpy.sin(onePeriodY-pi/2)
            intensity = numpy.where(sinusoid>0, 1, -1)
            wasLum = True
        elif tex == "saw":
            intensity = numpy.linspace(-1.0,1.0,res,endpoint=True)*numpy.ones([res,1])
            wasLum = True
        elif tex == "tri":
            intensity = numpy.linspace(-1.0,3.0,res,endpoint=True)#-1:3 means the middle is at +1
            intensity[int(res/2.0+1):] = 2.0-intensity[int(res/2.0+1):]#remove from 3 to get back down to -1
            intensity = intensity*numpy.ones([res,1])#make 2D
            wasLum = True
        elif tex == "sinXsin":
            onePeriodX, onePeriodY = numpy.mgrid[0:2*pi:1j*res, 0:2*pi:1j*res]# NB 1j*res is a special mgrid notation
            intensity = numpy.sin(onePeriodX-pi/2)*numpy.sin(onePeriodY-pi/2)
            wasLum = True
        elif tex == "sqrXsqr":
            onePeriodX, onePeriodY = numpy.mgrid[0:2*pi:1j*res, 0:2*pi:1j*res]# NB 1j*res is a special mgrid notation
            sinusoid = numpy.sin(onePeriodX-pi/2)*numpy.sin(onePeriodY-pi/2)
            intensity = numpy.where(sinusoid>0, 1, -1)
            wasLum = True
        elif tex == "circle":
            rad=makeRadialMatrix(res)
            intensity = (rad<=1)*2-1
            wasLum=True
        elif tex == "gauss":
            rad=makeRadialMatrix(res)
            # Set SD if specified
            if maskParams == None:    
                sigma = 1.0 / 3
            else:
                sigma = 1.0 / maskParams['sd']
            intensity = numpy.exp( -rad**2.0 / (2.0*sigma**2.0) )*2-1 #3sd.s by the edge of the stimulus
            wasLum=True
        elif tex == "cross":
            X, Y = numpy.mgrid[-1:1:1j*res, -1:1:1j*res]
            tf_neg_cross = ((X < -0.2) & (Y < -0.2)) | ((X < -0.2) & (Y > 0.2)) | ((X > 0.2) & (Y < -0.2)) | ((X > 0.2) & (Y > 0.2))
            #tf_neg_cross == True at places where the cross is transparent, i.e. the four corners
            intensity = numpy.where(tf_neg_cross, -1, 1)
            wasLum = True
        elif tex == "radRamp":#a radial ramp
            rad=makeRadialMatrix(res)
            intensity = 1-2*rad
            intensity = numpy.where(rad<-1, intensity, -1)#clip off the corners (circular)
            wasLum=True
        elif tex == "raisedCos": # A raised cosine
            wasLum=True
            hamming_len = 1000 # This affects the 'granularity' of the raised cos

            # If no user input was provided:
            if maskParams is None:
                fringe_proportion = 0.2 # This one affects the proportion of the
                                    # stimulus diameter that is devoted to the
                                    # raised cosine.

            # Users can provide the fringe proportion through a dict, maskParams
            # input:
            else:
                fringe_proportion = maskParams['fringeWidth']

            rad = makeRadialMatrix(res)
            intensity = numpy.zeros_like(rad)
            intensity[numpy.where(rad < 1)] = 1
            raised_cos_idx = numpy.where(
                [numpy.logical_and(rad <= 1, rad >= 1-fringe_proportion)])[1:]

            # Make a raised_cos (half a hamming window):
            raised_cos = numpy.hamming(hamming_len)[:hamming_len/2]
            raised_cos -= numpy.min(raised_cos)
            raised_cos /= numpy.max(raised_cos)

            # Measure the distance from the edge - this is your index into the hamming window:
            d_from_edge = numpy.abs((1 - fringe_proportion)- rad[raised_cos_idx])
            d_from_edge /= numpy.max(d_from_edge)
            d_from_edge *= numpy.round(hamming_len/2)

            # This is the indices into the hamming (larger for small distances from the edge!):
            portion_idx = (-1 * d_from_edge).astype(int)

            # Apply the raised cos to this portion:
            intensity[raised_cos_idx] = raised_cos[portion_idx]

            # Scale it into the interval -1:1:
            intensity = intensity - 0.5
            intensity = intensity / numpy.max(intensity)

            #Sometimes there are some remaining artifacts from this process, get rid of them:
            artifact_idx = numpy.where(numpy.logical_and(intensity == -1,
                                                         rad < 0.99))
            intensity[artifact_idx] = 1
            artifact_idx = numpy.where(numpy.logical_and(intensity == 1, rad >
                                                         0.99))
            intensity[artifact_idx] = 0

        else:
            if type(tex) in [str, unicode, numpy.string_]:
                # maybe tex is the name of a file:
                if not os.path.isfile(tex):
                    logging.error("Couldn't find image file '%s'; check path?" %(tex)); logging.flush()
                    raise OSError, "Couldn't find image file '%s'; check path? (tried: %s)" \
                        % (tex, os.path.abspath(tex))#ensure we quit
                try:
                    im = Image.open(tex)
                    im = im.transpose(Image.FLIP_TOP_BOTTOM)
                except IOError:
                    logging.error("Found file '%s' but failed to load as an image" %(tex)); logging.flush()
                    raise IOError, "Found file '%s' [= %s] but it failed to load as an image" \
                        % (tex, os.path.abspath(tex))#ensure we quit
            else:
                # can't be a file; maybe its an image already in memory?
                try:
                    im = tex.copy().transpose(Image.FLIP_TOP_BOTTOM) # ? need to flip if in mem?
                except AttributeError: # nope, not an image in memory
                    logging.error("Couldn't make sense of requested image."); logging.flush()
                    raise AttributeError, "Couldn't make sense of requested image."#ensure we quit
            # at this point we have a valid im
            stim._origSize=im.size
            wasImage=True
            #is it 1D?
            if im.size[0]==1 or im.size[1]==1:
                logging.error("Only 2D textures are supported at the moment")
            else:
                maxDim = max(im.size)
                powerOf2 = int(2**numpy.ceil(numpy.log2(maxDim)))
                if im.size[0]!=powerOf2 or im.size[1]!=powerOf2:
                    if not forcePOW2:
                        notSqr=True
                    elif glob_vars.nImageResizes<reportNImageResizes:
                        logging.warning("Image '%s' was not a square power-of-two image. Linearly interpolating to be %ix%i" %(tex, powerOf2, powerOf2))
                        glob_vars.nImageResizes+=1
                        im=im.resize([powerOf2,powerOf2],Image.BILINEAR)
                    elif glob_vars.nImageResizes==reportNImageResizes:
                        logging.warning("Multiple images have needed resizing - I'll stop bothering you!")
                        im=im.resize([powerOf2,powerOf2],Image.BILINEAR)

            #is it Luminance or RGB?
            if pixFormat==GL.GL_ALPHA and im.mode!='L':#we have RGB and need Lum
                wasLum = True
                im = im.convert("L")#force to intensity (in case it was rgb)
            elif im.mode=='L': #we have lum and no need to change
                wasLum = True
            elif pixFormat==GL.GL_RGB: #we want RGB and might need to convert from CMYK or Lm
                #texture = im.tostring("raw", "RGB", 0, -1)
                im = im.convert("RGBA")
                wasLum=False
            if dataType==GL.GL_FLOAT:
                #convert from ubyte to float
                intensity = numpy.array(im).astype(numpy.float32)*0.0078431372549019607-1.0 # much faster to avoid division 2/255
            else:
                intensity = numpy.array(im)

        if pixFormat==GL.GL_RGB and wasLum and dataType==GL.GL_FLOAT: #grating stim on good machine
            #keep as float32 -1:1
            if sys.platform!='darwin' and stim.win.glVendor.startswith('nvidia'):
                #nvidia under win/linux might not support 32bit float
                internalFormat = GL.GL_RGB16F_ARB #could use GL_LUMINANCE32F_ARB here but check shader code?
            else:#we've got a mac or an ATI card and can handle 32bit float textures
                internalFormat = GL.GL_RGB32F_ARB #could use GL_LUMINANCE32F_ARB here but check shader code?
            data = numpy.ones((intensity.shape[0],intensity.shape[1],3),numpy.float32)#initialise data array as a float
            data[:,:,0] = intensity#R
            data[:,:,1] = intensity#G
            data[:,:,2] = intensity#B
        elif pixFormat==GL.GL_RGB and wasLum and dataType!=GL.GL_FLOAT and stim.useShaders:
            #was a lum image: stick with ubyte for speed
            internalFormat = GL.GL_RGB
            data = numpy.ones((intensity.shape[0],intensity.shape[1],3),numpy.ubyte)#initialise data array as a float
            data[:,:,0] = intensity#R
            data[:,:,1] = intensity#G
            data[:,:,2] = intensity#B
        elif pixFormat==GL.GL_RGB and wasLum and not stim.useShaders: #Grating on legacy hardware, or ImageStim with wasLum=True
            #scale by rgb and convert to ubyte
            internalFormat = GL.GL_RGB
            if stim.colorSpace in ['rgb', 'dkl', 'lms','hsv']:
                rgb=stim.rgb
            else:
                rgb=stim.rgb/127.5-1.0#colour is not a float - convert to float to do the scaling
            # if wasImage it will also have ubyte values for the intensity
            if wasImage:
                intensity = intensity/127.5-1.0
            #scale by rgb
            data = numpy.ones((intensity.shape[0],intensity.shape[1],3),numpy.float32)#initialise data array as a float
            data[:,:,0] = intensity*rgb[0]  + stim.rgbPedestal[0]#R
            data[:,:,1] = intensity*rgb[1]  + stim.rgbPedestal[1]#G
            data[:,:,2] = intensity*rgb[2]  + stim.rgbPedestal[2]#B
            #convert to ubyte
            data = float_uint8(stim.contrast*data)
        elif pixFormat==GL.GL_RGB and dataType==GL.GL_FLOAT: #probably a custom rgb array or rgb image
            internalFormat = GL.GL_RGB32F_ARB
            data = intensity
        elif pixFormat==GL.GL_RGB:# not wasLum, not useShaders  - an RGB bitmap with no shader options
            internalFormat = GL.GL_RGB
            data = intensity #float_uint8(intensity)
        elif pixFormat==GL.GL_ALPHA:
            internalFormat = GL.GL_ALPHA
            if wasImage:
                data = intensity
            else:
                data = float_uint8(intensity)
        #check for RGBA textures
        if len(intensity.shape)>2 and intensity.shape[2] == 4:
            if pixFormat==GL.GL_RGB: pixFormat=GL.GL_RGBA
            if internalFormat==GL.GL_RGB: internalFormat=GL.GL_RGBA
            elif internalFormat==GL.GL_RGB32F_ARB: internalFormat=GL.GL_RGBA32F_ARB
        texture = data.ctypes#serialise
        #bind the texture in openGL
        GL.glEnable(GL.GL_TEXTURE_2D)
        GL.glBindTexture(GL.GL_TEXTURE_2D, id)#bind that name to the target
        GL.glTexParameteri(GL.GL_TEXTURE_2D,GL.GL_TEXTURE_WRAP_S,GL.GL_REPEAT) #makes the texture map wrap (this is actually default anyway)
        #important if using bits++ because GL_LINEAR
        #sometimes extrapolates to pixel vals outside range
        if interpolate:
            GL.glTexParameteri(GL.GL_TEXTURE_2D,GL.GL_TEXTURE_MAG_FILTER,GL.GL_LINEAR)
            if useShaders:#GL_GENERATE_MIPMAP was only available from OpenGL 1.4
                GL.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR)
                GL.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_GENERATE_MIPMAP, GL.GL_TRUE)
                GL.glTexImage2D(GL.GL_TEXTURE_2D, 0, internalFormat,
                    data.shape[1],data.shape[0], 0, # [JRG] for non-square, want data.shape[1], data.shape[0]
                    pixFormat, dataType, texture)
            else:#use glu
                GL.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR_MIPMAP_NEAREST)
                GL.gluBuild2DMipmaps(GL.GL_TEXTURE_2D, internalFormat,
                    data.shape[1],data.shape[0], pixFormat, dataType, texture)    # [JRG] for non-square, want data.shape[1], data.shape[0]
        else:
            GL.glTexParameteri(GL.GL_TEXTURE_2D,GL.GL_TEXTURE_MAG_FILTER,GL.GL_NEAREST)
            GL.glTexParameteri(GL.GL_TEXTURE_2D,GL.GL_TEXTURE_MIN_FILTER,GL.GL_NEAREST)
            GL.glTexImage2D(GL.GL_TEXTURE_2D, 0, internalFormat,
                            data.shape[1],data.shape[0], 0, # [JRG] for non-square, want data.shape[1], data.shape[0]
                            pixFormat, dataType, texture)
        GL.glTexEnvi(GL.GL_TEXTURE_ENV, GL.GL_TEXTURE_ENV_MODE, GL.GL_MODULATE)#?? do we need this - think not!

        return wasLum
Exemple #44
0
    def _vlc_start(self):
        """
        Create the vlc stream player for the video using python-vlc.
        """
        if not os.access(self.filename, os.R_OK):
            raise RuntimeError('Error: %s file not readable' % self.filename)
        if self.no_audio:
            instance = vlc.Instance("--no-audio")
        else:
            instance = vlc.Instance()
        try:
            stream = instance.media_new(self.filename)
        except NameError:
            msg = 'NameError: %s vs LibVLC %s'
            raise ImportError(msg %
                              (vlc.__version__, vlc.libvlc_get_version()))

        player = instance.media_player_new()
        player.set_media(stream)

        # Load up the file
        stream.parse()
        size = player.video_get_size()
        self.video_width = size[0]
        self.video_height = size[1]
        self.frame_rate = player.get_fps()
        self.frame_counter = 0

        # TODO: Why is duration -1 still even after parsing? Newer vlc docs seem to hint this won't work until playback starts
        duration = player.get_length()
        logging.warning(
            "Video is %ix%i, duration %s, fps %s" %
            (self.video_width, self.video_height, duration, self.frame_rate))
        logging.flush()

        # We assume we can use the RGBA format here
        player.video_set_format("RGBA", self.video_width, self.video_height,
                                self.video_width << 2)

        # Configure a lock and a buffer for the pixels coming from VLC
        self.pixel_lock = threading.Lock()
        self.pixel_buffer = (ctypes.c_ubyte * self.video_width *
                             self.video_height * 4)()

        # Once you set these callbacks, you are in complete control of what to do with the video buffer
        selfref = ctypes.cast(ctypes.pointer(ctypes.py_object(self)),
                              ctypes.c_void_p)
        player.video_set_callbacks(vlcLockCallback, vlcUnlockCallback,
                                   vlcDisplayCallback, selfref)

        manager = player.event_manager()
        manager.event_attach(vlc.EventType.MediaPlayerTimeChanged,
                             vlcTimeCallback, weakref.ref(self), player)
        manager.event_attach(vlc.EventType.MediaPlayerEndReached,
                             vlcEndReached, weakref.ref(self), player)

        # Keep references
        self._self_ref = selfref
        self._instance = instance
        self._player = player
        self._stream = stream
        self._manager = manager

        logging.info("Initialized VLC...")
        self._vlc_initialized = True
Exemple #45
0
       if len(thisInfo[dlgLabelsOrdered.index('staircaseTrials')]) >0:
           staircaseTrials = int( thisInfo[ dlgLabelsOrdered.index('staircaseTrials') ] ) #convert string to integer
           print('staircaseTrials entered by user='******'staircaseTrials entered by user='******'easyTrials')]) >0:
           prefaceStaircaseTrialsN = int( thisInfo[ dlgLabelsOrdered.index('easyTrials') ] ) #convert string to integer
           print('prefaceStaircaseTrialsN entered by user='******'easyTrials')])
           logging.info('prefaceStaircaseTrialsN entered by user='******'trialsPerCondition') ] ) #convert string to integer
       print('trialsPerCondition=',trialsPerCondition)
       logging.info('trialsPerCondition =',trialsPerCondition)
       defaultNoiseLevel = int (thisInfo[ dlgLabelsOrdered.index('defaultNoiseLevel') ])
else: 
   print('User cancelled from dialog box.')
   logging.flush()
   core.quit()
if not demo: 
    allowGUI = False

myWin = openMyStimWindow()
#set up output data file, log file,  copy of program code, and logging
infix = ''
if doStaircase:
    infix = 'staircase_'
fileName = os.path.join(dataDir, subject + '_' + infix+ timeAndDateStr)
if not demo and not exportImages:
    dataFile = open(fileName+'.txt', 'w')
    saveCodeCmd = 'cp \'' + sys.argv[0] + '\' '+ fileName + '.py'
    os.system(saveCodeCmd)  #save a copy of the code as it was when that subject was run
    logFname = fileName+'.log'
Exemple #46
0
    def findIdentityLUT(self, maxIterations = 1000, errCorrFactor = 1.0/2048, # amount of correction done for each iteration
        nVerifications = 50, #number of repeats (successful) to check dithering has been eradicated
        demoMode = True, #generate the screen but don't go into status mode
        logFile = '',
        ):
        """Search for the identity LUT for this card/operating system.
        This requires that the window being tested is fullscreen on the Bits#
        monitor (or at least occupys the first 256 pixels in the top left corner!)

        :params:

            LUT: The lookup table to be tested (256x3). If None then the LUT will not be altered

        :returns:

            a 256x3 array of error values (integers in range 0:255)
        """
        t0 = time.time()
        #create standard options
        LUTs = {}
        LUTs['intel'] = np.repeat(np.linspace(.05,.95,256),3).reshape([-1,3])
        LUTs['0-255'] = np.repeat(np.linspace(0,1.0,256),3).reshape([-1,3])
        LUTs['0-65535'] = np.repeat(np.linspace(0.0, 65535.0/65536.0, num=256),3).reshape([-1,3])
        LUTs['1-65536'] = np.repeat(np.linspace(0.0, 65535.0/65536.0, num=256),3).reshape([-1,3])

        if logFile:
            self.logFile = open(logFile,'w')

        if plotResults:
            pyplot.Figure()
            pyplot.plot([0,255],[0,255], '-k')
            errPlot = pyplot.plot(range(256), range(256), '.r')[0]
            pyplot.show(block=False)

        lowestErr = 1000000000
        bestLUTname = None
        logging.flush()
        for LUTname, currentLUT in LUTs.items():
            print 'Checking %r LUT:' %(LUTname),
            errs = self.testLUT(currentLUT, demoMode)
            if plotResults:
                errPlot.set_ydata(range(256)+errs[:,0])
                pyplot.draw()
            print 'mean err = %.3f per LUT entry' %(abs(errs).mean())
            if abs(errs).mean()< abs(lowestErr):
                lowestErr = abs(errs).mean()
                bestLUTname = LUTname
        if lowestErr==0:
            print "The %r identity LUT produced zero error. We'll use that!" %(LUTname)
            return

        print "Best was %r LUT (mean err = %.3f). Optimising that..." %(bestLUTname, lowestErr)
        currentLUT = LUTs[bestLUTname]
        errProgression=[]
        corrInARow=0
        for n in range(maxIterations):
            errs = self.testLUT(currentLUT)
            tweaks = errs*errCorrFactor
            currentLUT -= tweaks
            currentLUT[currentLUT>1] = 1.0
            currentLUT[currentLUT<0] = 0.0
            meanErr = abs(errs).mean()
            errProgression.append(meanErr)
            if plotResults:
                errPlot.set_ydata(range(256)+errs[:,0])
                pyplot.draw()
            if meanErr>0:
                print "%.3f" %meanErr,
                corrInARow=0
            else:
                print ".",
                corrInARow+=1
            if corrInARow>=nVerifications:
                print 'success in a total of %.1fs' %(time.time()-t0)
                self.identityLUT = currentLUT
                self.save() #it worked so save this configuration for future
                break
            elif len(errProgression)>10 and max(errProgression)-min(errProgression)<0.001:
                print "Trying to correct the gamma table was having no effect. Make sure the window was fullscreen and on the Bits# screen"
                break

        #did we get here by failure?!
        if n==(maxIterations-1):
            print "failed to converge on a successful identity LUT. This is BAD!"

        if plotResults:
            pyplot.figure(figsize=[18,12])
            pyplot.subplot(1,3,1)
            pyplot.plot(errProgression)
            pyplot.title('Progression of errors')
            pyplot.ylabel("Mean error per LUT entry (0-1)")
            pyplot.xlabel("Test iteration")
            r256 = np.reshape(range(256),[256,1])
            pyplot.subplot(1,3,2)
            pyplot.plot(r256, r256, 'k-')
            pyplot.plot(r256, currentLUT[:,0]*255, 'r.', markersize=2.0)
            pyplot.plot(r256, currentLUT[:,1]*255, 'g.', markersize=2.0)
            pyplot.plot(r256, currentLUT[:,2]*255, 'b.', markersize=2.0)
            pyplot.title('Final identity LUT')
            pyplot.ylabel("LUT value")
            pyplot.xlabel("LUT entry")

            pyplot.subplot(1,3,3)
            deviations = currentLUT-r256/255.0
            pyplot.plot(r256, deviations[:,0], 'r.')
            pyplot.plot(r256, deviations[:,1], 'g.')
            pyplot.plot(r256, deviations[:,2], 'b.')
            pyplot.title('LUT deviations from sensible')
            pyplot.ylabel("LUT value")
            pyplot.xlabel("LUT deviation (multiples of 1024)")
            pyplot.savefig("bitsSharpIdentityLUT.pdf")
            pyplot.show()
Exemple #47
0
def initPyo(rate=44100, stereo=True, buffer=128):
    """setup the pyo (sound) server
    """
    global pyoSndServer, Sound, audioDriver, duplex
    Sound = SoundPyo
    #subclass the pyo.Server so that we can insert a __del__ function that shuts it down
    class Server(pyo.Server):
        core=core #make libs class variables so they don't get deleted first
        logging=logging
        def __del__(self):
            self.stop()
            self.core.wait(0.5)#make sure enough time passes for the server to shutdown
            self.shutdown()
            self.core.wait(0.5)#make sure enough time passes for the server to shutdown
            self.logging.debug('pyo sound server shutdown')#this may never get printed

    #check if we already have a server and kill it
    if hasattr(pyoSndServer,'shutdown'):
        #this doesn't appear to work!
        pyoSndServer.stop()
        core.wait(0.5)#make sure enough time passes for the server to shutdown
        pyoSndServer.shutdown()
        pyoSndServer.reinit(sr=rate, nchnls=2, buffersize=buffer, duplex=1, audio=audioDriver)
        pyoSndServer.boot()
    else:
        #create the instance of the server
        if platform=='win32':
            #check for output device/driver
            devNames, devIDs=pyo.pa_get_output_devices()
            audioDriver,outputID=_bestDriver(devNames, devIDs)
            if outputID:
                logging.info('Using sound driver: %s (ID=%i)' %(audioDriver, outputID))
            else:
                logging.warning('No audio outputs found (no speakers connected?')
                return -1
            #check for valid input (mic)
            devNames, devIDs = pyo.pa_get_input_devices()
            junk, inputID=_bestDriver(devNames, devIDs)
            if inputID:
                duplex=True
            else:
                duplex=False
        else:#for other platforms set duplex to True
            audioDriver = prefs.general['audioDriver'][0]
            duplex=True
        if platform=='darwin':
            #for mac we set the backend using the server audio param
            pyoSndServer = Server(sr=rate, nchnls=2, buffersize=buffer, audio=audioDriver, duplex=duplex)
        else:
            #with others we just use portaudio and then set the OutputDevice below
            pyoSndServer = Server(sr=rate, nchnls=2, buffersize=buffer, duplex=duplex)
        pyoSndServer.setVerbosity(1)
        if platform=='win32':
            pyoSndServer.setOutputDevice(outputID)
            if inputID:
                pyoSndServer.setInputDevice(inputID)
        #do other config here as needed (setDuplex? setOutputDevice?)
        pyoSndServer.boot()
    core.wait(0.5)#wait for server to boot before starting te sound stream
    pyoSndServer.start()
    logging.debug('pyo sound server started')
    logging.flush()
Exemple #48
0
    def loadFromXML(self, filename):
        """Loads an xml file and parses the builder Experiment from it
        """
        self._doc.parse(filename)
        root = self._doc.getroot()

        # some error checking on the version (and report that this isn't valid
        # .psyexp)?
        filenameBase = os.path.basename(filename)
        if root.tag != "PsychoPy2experiment":
            logging.error('%s is not a valid .psyexp file, "%s"' %
                          (filenameBase, root.tag))
            # the current exp is already vaporized at this point, oops
            return
        self.psychopyVersion = root.get('version')
        versionf = float(self.psychopyVersion.rsplit('.', 1)[0])
        if versionf < 1.63:
            msg = 'note: v%s was used to create %s ("%s")'
            vals = (self.psychopyVersion, filenameBase, root.tag)
            logging.warning(msg % vals)

        # Parse document nodes
        # first make sure we're empty
        self.flow = Flow(exp=self)  # every exp has exactly one flow
        self.routines = {}
        self.namespace = NameSpace(self)  # start fresh
        modifiedNames = []
        duplicateNames = []

        # fetch exp settings
        settingsNode = root.find('Settings')
        for child in settingsNode:
            self._getXMLparam(params=self.settings.params, paramNode=child,
                              componentNode=settingsNode)
        # name should be saved as a settings parameter (only from 1.74.00)
        if self.settings.params['expName'].val in ['', None, 'None']:
            shortName = os.path.splitext(filenameBase)[0]
            self.setExpName(shortName)
        # fetch routines
        routinesNode = root.find('Routines')
        allCompons = getAllComponents(
            self.prefsBuilder['componentsFolders'], fetchIcons=False)
        # get each routine node from the list of routines
        for routineNode in routinesNode:
            routineGoodName = self.namespace.makeValid(
                routineNode.get('name'))
            if routineGoodName != routineNode.get('name'):
                modifiedNames.append(routineNode.get('name'))
            self.namespace.user.append(routineGoodName)
            routine = Routine(name=routineGoodName, exp=self)
            # self._getXMLparam(params=routine.params, paramNode=routineNode)
            self.routines[routineNode.get('name')] = routine
            for componentNode in routineNode:

                componentType = componentNode.tag
                if componentType in allCompons:
                    # create an actual component of that type
                    component = allCompons[componentType](
                        name=componentNode.get('name'),
                        parentName=routineNode.get('name'), exp=self)
                else:
                    # create UnknownComponent instead
                    component = allCompons['UnknownComponent'](
                        name=componentNode.get('name'),
                        parentName=routineNode.get('name'), exp=self)
                # check for components that were absent in older versions of
                # the builder and change the default behavior
                # (currently only the new behavior of choices for RatingScale,
                # HS, November 2012)
                # HS's modification superceded Jan 2014, removing several
                # RatingScale options
                if componentType == 'RatingScaleComponent':
                    if (componentNode.get('choiceLabelsAboveLine') or
                            componentNode.get('lowAnchorText') or
                            componentNode.get('highAnchorText')):
                        pass
                    # if not componentNode.get('choiceLabelsAboveLine'):
                    #    # this rating scale was created using older version
                    #    component.params['choiceLabelsAboveLine'].val=True
                # populate the component with its various params
                for paramNode in componentNode:
                    self._getXMLparam(params=component.params,
                                      paramNode=paramNode,
                                      componentNode=componentNode)
                compGoodName = self.namespace.makeValid(
                    componentNode.get('name'))
                if compGoodName != componentNode.get('name'):
                    modifiedNames.append(componentNode.get('name'))
                self.namespace.add(compGoodName)
                component.params['name'].val = compGoodName
                routine.append(component)
        # for each component that uses a Static for updates, we need to set
        # that
        for thisRoutine in list(self.routines.values()):
            for thisComp in thisRoutine:
                for thisParamName in thisComp.params:
                    thisParam = thisComp.params[thisParamName]
                    if thisParamName == 'advancedParams':
                        continue  # advanced isn't a normal param
                    elif thisParam.updates and "during:" in thisParam.updates:
                        # remove the part that says 'during'
                        updates = thisParam.updates.split(': ')[1]
                        routine, static = updates.split('.')
                        if routine not in self.routines:
                            msg = ("%s was set to update during %s Static "
                                   "Component, but that component no longer "
                                   "exists")
                            logging.warning(msg % (thisParamName, static))
                        else:
                            self.routines[routine].getComponentFromName(
                                static).addComponentUpdate(
                                thisRoutine.params['name'],
                                thisComp.params['name'], thisParamName)
        # fetch flow settings
        flowNode = root.find('Flow')
        loops = {}
        for elementNode in flowNode:
            if elementNode.tag == "LoopInitiator":
                loopType = elementNode.get('loopType')
                loopName = self.namespace.makeValid(elementNode.get('name'))
                if loopName != elementNode.get('name'):
                    modifiedNames.append(elementNode.get('name'))
                self.namespace.add(loopName)
                loop = eval('%s(exp=self,name="%s")' % (loopType, loopName))
                loops[loopName] = loop
                for paramNode in elementNode:
                    self._getXMLparam(paramNode=paramNode, params=loop.params)
                    # for conditions convert string rep to list of dicts
                    if paramNode.get('name') == 'conditions':
                        param = loop.params['conditions']
                        # e.g. param.val=[{'ori':0},{'ori':3}]
                        try:
                            param.val = eval('%s' % (param.val))
                        except SyntaxError:
                            # This can occur if Python2.7 conditions string
                            # contained long ints (e.g. 8L) and these can't be
                            # parsed by Py3. But allow the file to carry on
                            # loading and the conditions will still be loaded
                            # from the xlsx file
                            pass
                # get condition names from within conditionsFile, if any:
                try:
                    # psychophysicsstaircase demo has no such param
                    conditionsFile = loop.params['conditionsFile'].val
                except Exception:
                    conditionsFile = None
                if conditionsFile in ['None', '']:
                    conditionsFile = None
                if conditionsFile:
                    try:
                        trialList, fieldNames = data.importConditions(
                            conditionsFile, returnFieldNames=True)
                        for fname in fieldNames:
                            if fname != self.namespace.makeValid(fname):
                                duplicateNames.append(fname)
                            else:
                                self.namespace.add(fname)
                    except Exception:
                        pass  # couldn't load the conditions file for now
                self.flow.append(LoopInitiator(loop=loops[loopName]))
            elif elementNode.tag == "LoopTerminator":
                self.flow.append(LoopTerminator(
                    loop=loops[elementNode.get('name')]))
            elif elementNode.tag == "Routine":
                if elementNode.get('name') in self.routines:
                    self.flow.append(self.routines[elementNode.get('name')])
                else:
                    logging.error("A Routine called '{}' was on the Flow but "
                                  "could not be found (failed rename?). You "
                                  "may need to re-insert it".format(
                        elementNode.get('name')))
                    logging.flush()

        if modifiedNames:
            msg = 'duplicate variable name(s) changed in loadFromXML: %s\n'
            logging.warning(msg % ', '.join(list(set(modifiedNames))))
        if duplicateNames:
            msg = 'duplicate variable names: %s'
            logging.warning(msg % ', '.join(list(set(duplicateNames))))
        # if we succeeded then save current filename to self
        self.filename = filename
def handle_gonogo(G):
    '''
    This contains the experimenal logic of the Stop Task. A lot of work
    went into constructing the stimuli. Stimuli parameters are loaded
    into variables in the code above. Runs 136 trials of Go-Nogo.
    This function is to be run within the asyncio loop.
    '''

    # we just need it here...
    STOP = 1
    GO = 0

    tooSoonTime = G['S']['tooSoonTime']

    myMultiStairs = G['S']['myMultiStairs']

    # if the time it took tov respond is smaller than this time --> invalid.
    numberOfResponses = 0

    G['S']['nextFLipTasks'] = []  # stuff winflupper needs to do later on..

    # set the visual contents here...
    # INITIAL SETTING
    G['S']['goNogoStim'] = G['vstims']['S']['fix']

    # yeah, do all kinds of init here.
    for trialNumber in range(len(G['S']['SSstopgo'])):

        thisDirection = random.choice(
            ('al', 'ar'))  # obtain this from the file!!
        LorR = thisDirection[1].upper()

        thisTrialType_num = G['S']['SSstopgo'][
            trialNumber]  # this is a 0 (GO) or 1 (STOP)
        thisTrialType = [GO, STOP][int(
            thisTrialType_num
        )]  # shady practices indeed -- so later on I cany say 'if this TrialType is GO:, etc'
        GorNG = ['Go', 'Stop'][int(thisTrialType)]

        thisISIWaitTime = G['S']['ISIwaitTime'][trialNumber]

        correctResponseSide = G['S']['correctResponseSides'][trialNumber]
        wrongResponseSide = G['S']['wrongResponseSides'][trialNumber]

        allResponses = []
        responded = False  # subj responded?
        tooManyResponses = False
        trialHandled = False

        if thisTrialType is STOP:
            # this should be called only 40 times, since there are 40 stop trials...
            thisSSD, thisCondition = myMultiStairs.next(
            )  # I defined the myMultiStairs above.

        # this code tells the loop to only continue when continueTroutine is not False
        # otherwise it'll just keep yielding.
        # let winflipper make new clock
        G['S']['continueRoutine'] = False
        G['S']['nextFlipTasks'].append(
            [G['S']['resetClock'],
             G])  # the makeNewClock automatically makes things continue
        while G['S']['continueRoutine'] is False:
            yield From(asyncio.sleep(0))
        cl = G['S']['clock']  # obtain the clock that was just made.

        # ok, we can proceed -- the clock has been set.
        G['S']['goNogoStim'] = G['vstims']['S']['pre']
        while cl.getTime() < 0.5:
            yield From(asyncio.sleep(0))

        # obtain our next clock...
        # this code tells the loop to only continue when continueTroutine is not False
        # otherwise it'll just keep yielding.
        # let winflipper make new clock
        G['S']['continueRoutine'] = False

        # make sure upon next window flow, we have a new clock set, and also - that marker is sent signalling the start of the new go/stop trial.
        G['S']['nextFlipTasks'].append(
            [G['S']['resetClock'],
             G])  # the makeNewClock automatically makes things continue
        # send the trigger regarding the arrow, as soon as the windows flips
        G['S']['nextFlipTasks'].append(
            [G['eh'].send_message, ['Begin' + GorNG + LorR]])
        while G['S']['continueRoutine'] is False:
            yield From(asyncio.sleep(0))
        cl = G['S']['clock']  # obtain the clock that was just made.

        # this is where we show the arrow + find out whether a key is pressed:
        G['S']['goNogoStim'] = G['vstims']['S'][thisDirection]
        currentTime = 0.0
        while currentTime < 1.0:
            currentTime = cl.getTime()

            # set the stimulus to the proper direction (it's a choice, for now... -- but it's much much better to hard-code it)
            # make the arrow (+ circle)

            evs = event.getKeys(timeStamped=cl)
            if len(evs) > 0:
                buttonsPressed, timesPressed = zip(*evs)
                # it's highly unlikely that two buttons are pressed in a signle
                # frame, but control for that anyway.
                allResponses.append((buttonsPressed[0], timesPressed[0]))
                numberOfResponses += 1
                # LOG this event... (i.e. send trigger)

                # handle event:

            # once a button is pressed -- display fixation point again.
            if len(allResponses) > 0 and not responded:
                # 'clear' the visual window --> fixation cross, again:
                G['S']['goNogoStim'] = G['vstims']['S']['fix']
                responded = True

                buttonPressed, RTime = allResponses[0]

                if RTime < tooSoonTime:
                    G['eh'].send_message('PressedTooSoon')
                else:
                    if buttonsPressed[0] == BUTTONS[0]:
                        G['eh'].send_message('RespL')
                    elif buttonsPressed[0] == BUTTONS[1]:
                        G['eh'].send_message('RespR')

            # if it's a stop trial, then make arrow red after X time
            if thisTrialType is STOP and not responded:
                if currentTime > thisSSD:
                    G['S']['goNogoStim'] = G['vstims']['S'][thisDirection +
                                                            'r']

            # here we let the screen flip, for example...
            yield From(asyncio.sleep(0))

        #            # Stop / Inhibit Response Codes
        #            'BeginGoL':1,
        #            'BeginGoR':2,
        #            'BeginStopL':3,
        #            'BeginStopR':4,
        #
        #            'RespL':5,
        #            'RespR':6,
        #
        #            'CorrectGoL':11,
        #            'CorrectGoR':12,
        #            'CorrectStopL':13,
        #            'CorrectStopR':14,
        #            'ErrorCommission':15,
        #
        #            # don't expect too many of these:
        #            'ErrorOmission':21,
        #            'PressedTooSoon':22,
        #            'TooManyResponses':23,
        #            'WrongSideErrorCommission':24,
        #            'WrongSideGo':25,

        # so the loop is done -- let's figure out what kind of trial this was.
        # taking care of the button press itself, as soon as button is pressed:
        if not trialHandled and responded:

            trialHandled = True

            if len(allResponses) > 1:
                trialOutcome = 'TooManyResponses'
                if thisTrialType is STOP:
                    myMultiStairs.addResponse(0)

            else:
                if RTime < tooSoonTime:
                    trialOutcome = 'PressedTooSoon'
                    if thisTrialType is STOP:
                        myMultiStairs.addResponse(0)
                else:
                    if thisTrialType is STOP:

                        if buttonPressed == correctResponseSide:
                            trialOutcome = 'ErrorCommission'
                            myMultiStairs.addResponse(0)

                        elif buttonPressed == wrongResponseSide:
                            trialOutcome = 'WrongSideErrorCommission'
                            myMultiStairs.addResponse(0)

                    elif thisTrialType is GO:
                        if buttonPressed == correctResponseSide:
                            trialOutcome = 'CorrectGo' + correctResponseSide

                            # not yet...
                        elif buttonPressed == wrongResponseSide:
                            trialOutcome = 'WrongSideGo'

        # handle the 'response' if the button was NOT pressed:
        if not trialHandled and not responded:
            trialHandled = True

            if thisTrialType is GO:
                trialOutcome = 'ErrorOmission'

            if thisTrialType is STOP:
                trialOutcome = 'CorrectStop' + LorR
                myMultiStairs.addResponse(1)

        # so we send it out:
        G['eh'].send_message(trialOutcome)

        # this code tells the loop to only continue when continueTroutine is not False
        # otherwise it'll just keep yielding.
        # let winflipper make new clock

        # this code tells the loop to only continue when continueTroutine is not False
        # otherwise it'll just keep yielding.
        # let winflipper make new clock
        G['S']['continueRoutine'] = False
        G['S']['nextFlipTasks'].append(
            [G['S']['resetClock'],
             G])  # the makeNewClock automatically makes things continue
        while G['S']['continueRoutine'] is False:
            yield From(asyncio.sleep(0))
        cl = G['S']['clock']  # obtain the clock that was just made.

        # ok, we can proceed -- the clock has been set.
        flushed = False
        G['S']['goNogoStim'] = G['vstims']['S']['fix']
        while cl.getTime() < thisISIWaitTime:
            if not flushed:
                # this is a nice place to save it to logfile: before the
                # send a report about the STOP trial, write a nice line:
                # logging.data('messa')
                logging.flush()
                flused = True
            yield From(asyncio.sleep(0))
    def pre_mainloop(self):

        MostBasicPsychopyFeedback.pre_mainloop(self)

        # do the trick -- SAVE all of those things! --> and put it in settings.pkl.
        v = dict()

        v['caption'] = self.caption
        v['color'] = self.color
        v['fontheight'] = self.fontheight

        v['STARTKEYS'] = self.STARTKEYS

        v['EX_THRLINEWIDTH'] = self.EX_THRLINEWIDTH
        v['EX_COLORGAP'] = self.EX_COLORGAP
        v['EX_TVSP'] = self.EX_TVSP
        v['EX_TPAUSE'] = self.EX_TPAUSE
        v['EX_NREGULATE'] = self.EX_NREGULATE
        v['EX_NTRANSFER'] = self.EX_NTRANSFER
        v['EX_SHOWCHECKORCROSSTRANSFER'] = self.EX_SHOWCHECKORCROSSTRANSFER
        v['EX_SQUARESIZE'] = self.EX_SQUARESIZE
        v['EX_UPREGTEXT'] = self.EX_UPREGTEXT
        v['EX_TESTSIGNALUPDATEINTERVAL'] = self.EX_TESTSIGNALUPDATEINTERVAL
        v['EX_NREST'] = self.EX_NREST
        v['EX_SCALING'] = self.EX_SCALING
        v['EX_INTERACTIONMODE'] = self.EX_INTERACTIONMODE
        v['EX_NOBSERVE'] = self.EX_NOBSERVE
        v['EX_NOREGTEXT'] = self.EX_NOREGTEXT
        v['EX_TINSTR'] = self.EX_TINSTR
        v['EX_THERMOCLIMS'] = self.EX_THERMOCLIMS
        v['EX_GRAPHICSMODE'] = self.EX_GRAPHICSMODE
        v['EX_SHOWCHECKORCROSS'] = self.EX_SHOWCHECKORCROSS
        v['EX_STAIRCASEMANIPULATION'] = self.EX_STAIRCASEMANIPULATION
        v['EX_POINTS_PENALTY'] = self.EX_POINTS_PENALTY
        v['EX_TESTSIGNALPERIOD'] = self.EX_TESTSIGNALPERIOD
        v['EX_TMARK'] = self.EX_TMARK
        v['EX_TESTNFNOISE'] = self.EX_TESTNFNOISE
        v['EX_PATCHCOLOR'] = self.EX_PATCHCOLOR
        v['EX_TJITT'] = self.EX_TJITT
        v['EX_TFB'] = self.EX_TFB
        v['EX_POINTS_REWARD'] = self.EX_POINTS_REWARD
        v['EX_PR_SLEEPTIME'] = self.EX_PR_SLEEPTIME
        v['EX_TESTSIGNALTYPE'] = self.EX_TESTSIGNALTYPE
        v['EX_BUTTONS'] = self.EX_BUTTONS
        v['EX_INSTR'] = self.EX_INSTR
        v['EX_RUNS'] = self.EX_RUNS

        v['MONITOR_PIXWIDTH'] = self.MONITOR_PIXWIDTH
        v['MONITOR_PIXHEIGHT'] = self.MONITOR_PIXHEIGHT
        v['MONITOR_WIDTH'] = self.MONITOR_WIDTH
        v['MONITOR_HEIGHT'] = self.MONITOR_HEIGHT
        v['MONITOR_DISTANCE'] = self.MONITOR_DISTANCE
        v['MONITOR_GAMMA'] = self.MONITOR_GAMMA
        v['MONITOR_FPS'] = self.MONITOR_FPS
        v['MONITOR_USEDEGS'] = self.MONITOR_USEDEGS
        v['MONITOR_DEGS_WIDTHBASE'] = self.MONITOR_DEGS_WIDTHBASE
        v['MONITOR_DEGS_HEIGHTBASE'] = self.MONITOR_DEGS_HEIGHTBASE
        v['MONITOR_FLIPHORIZONTAL'] = self.MONITOR_FLIPHORIZONTAL
        v['MONITOR_FLIPVERTICAL'] = self.MONITOR_FLIPVERTICAL
        v['MONITOR_RECORDFRAMEINTERVALS'] = self.MONITOR_RECORDFRAMEINTERVALS
        v['MONITOR_NSCREENS'] = self.MONITOR_NSCREENS
        v['MONITOR_DISPLAYONSCREEN'] = self.MONITOR_DISPLAYONSCREEN
        v['MONITOR_FULLSCR'] = self.MONITOR_FULLSCR
        v['MONITOR_ALLOWGUI'] = self.MONITOR_ALLOWGUI

        v['LOG_PATHFILE'] = self.LOG_PATHFILE
        v['LOG_PATHFILE_EVENT'] = self.LOG_PATHFILE_EVENT

        v['EVENT_LPT_TRIGGER_WAIT'] = self.EVENT_LPT_TRIGGER_WAIT
        v['EVENT_destip'] = self.EVENT_destip
        v['EVENT_destport'] = self.EVENT_destport
        v['EVENT_LPTAddress'] = self.EVENT_LPTAddress
        v['EVENT_LPTTrigWaitTime'] = self.EVENT_LPTTrigWaitTime
        v['EVENT_TRIGLOG'] = self.EVENT_TRIGLOG
        v['EVENT_sendParallel'] = self.EVENT_sendParallel
        v['EVENT_sendTcpIp'] = self.EVENT_sendTcpIp
        v['EVENT_sendLogFile'] = self.EVENT_sendLogFile
        v['EVENT_printToTerminal'] = self.EVENT_printToTerminal
        v['EVENT_printToTerminalAllowed'] = self.EVENT_printToTerminalAllowed

        # use the Cpntrol Parameters:
        CP = self.CP  # control parameters...

        # create G, put it into self too..
        G = dict()
        G['v'] = v
        self.G = G

        # we need this in order to continue working as if we're doing it using the normal (test) script...
        for key in G['v']:
            G[key] = G['v'][
                key]  # this is actually superfluous. But removing it might possibly break things.

        # the main clock
        mainClock = clock.Clock()
        G['mainClock'] = mainClock

        # screen/monitor...
        G = init_screen(G)  # we need to do this

        # logging...
        logging.setDefaultClock(G['mainClock'])
        newLogFile = create_incremental_filename(G['v']['LOG_PATHFILE'])
        expLogger = logging.LogFile(
            newLogFile, logging.EXP)  # the correct loglevel should be EXP!
        print(expLogger)
        logging.LogFile(newLogFile,
                        logging.EXP)  # the correct loglevel should be EXP!
        print('made new logfile: ' + newLogFile)
        for key in G['v'].keys():
            logging.data("{key}: {value}".format(key=key, value=G['v'][key]))
        logging.flush()
        G['logging'] = logging  # put into the G, which is in self

        # event handler...
        G = init_eventcodes(G)  # and this??
        G = start_eh(G)

        st = make_stimuli(G, CP)
        pr = init_programs(G, st, CP)

        ex = define_experiment(
            G, st, pr,
            CP)  # pr is passed to define_experiment, but then we won't need...

        self.st = st
        self.ex = ex

        # take care of the randomization(s)...
        trialopts = []

        trialopts.append([1, 1, 2, 1, 3, 4])
        trialopts.append([1, 1, 2, 1, 4, 3])
        trialopts.append([1, 1, 2, 3, 1, 4])
        trialopts.append([1, 1, 2, 4, 1, 3])
        trialopts.append([1, 1, 3, 1, 2, 4])
        trialopts.append([1, 1, 3, 1, 4, 2])
        trialopts.append([1, 1, 3, 2, 1, 4])
        trialopts.append([1, 1, 3, 4, 1, 2])
        trialopts.append([1, 1, 4, 1, 3, 2])
        trialopts.append([1, 1, 4, 1, 2, 3])
        trialopts.append([1, 1, 4, 3, 1, 2])
        trialopts.append([1, 1, 4, 2, 1, 3])
        trialopts.append([1, 2, 1, 4, 1, 3])
        trialopts.append([1, 2, 1, 3, 1, 4])
        trialopts.append([1, 3, 1, 4, 1, 2])
        trialopts.append([1, 3, 1, 2, 1, 4])
        trialopts.append([1, 4, 1, 2, 1, 3])
        trialopts.append([1, 4, 1, 3, 1, 2])

        random.shuffle(trialopts)
        random.shuffle(trialopts)
        random.shuffle(trialopts)  # 3 time shuffle, for good luck :-)
        # computational anathema and heretic!

        my_trial_sequence = flatten(
            trialopts[0:G['v']['EX_RUNS']])  # we do 5 of them.
        my_trial_definitions = {
            1: 'train',
            2: 'transfer',
            3: 'observe',
            4: 'rest'
        }

        # so to debug, just run tasks_dbg instead of tasks.
        for t_i in my_trial_sequence:
            self.runlist = iter(
                [my_trial_definitions[i] for i in my_trial_sequence])

        # the ev loop we're going to be using..
        loop = asyncio.new_event_loop()
        asyncio.set_event_loop(loop)
        self.loop = loop

        if G['EX_TESTNFNOISE'] is True:
            self.loop.create_task(pr['GenTestSignal'](G, st, CP))

        logging.flush()
        G['logging'] = logging

        G['cl'] = clock.Clock(
        )  # init the trial-by-trial clock here and put into G...
Exemple #51
0
def initPyo(rate=44100, stereo=True, buffer=128):
    """setup the pyo (sound) server
    """
    global pyoSndServer, Sound, audioDriver, duplex, maxChnls
    Sound = SoundPyo
    global pyo
    try:
        assert pyo
    except NameError:  # pragma: no cover
        import pyo  # microphone.switchOn() calls initPyo even if audioLib is something else
    #subclass the pyo.Server so that we can insert a __del__ function that shuts it down
    # skip coverage since the class is never used if we have a recent version of pyo
    class _Server(pyo.Server):  # pragma: no cover
        core=core #make libs class variables so they don't get deleted first
        logging=logging
        def __del__(self):
            self.stop()
            self.core.wait(0.5)#make sure enough time passes for the server to shutdown
            self.shutdown()
            self.core.wait(0.5)#make sure enough time passes for the server to shutdown
            self.logging.debug('pyo sound server shutdown')#this may never get printed
    if '.'.join(map(str, pyo.getVersion())) < '0.6.4':
        Server = _Server
    else:
        Server = pyo.Server

    # if we already have a server, just re-initialize it
    if 'pyoSndServer' in globals() and hasattr(pyoSndServer,'shutdown'):
        pyoSndServer.stop()
        core.wait(0.5)#make sure enough time passes for the server to shutdown
        pyoSndServer.shutdown()
        core.wait(0.5)
        pyoSndServer.reinit(sr=rate, nchnls=maxChnls, buffersize=buffer, audio=audioDriver)
        pyoSndServer.boot()
    else:
        if platform=='win32':
            #check for output device/driver
            devNames, devIDs=pyo.pa_get_output_devices()
            audioDriver,outputID=_bestDriver(devNames, devIDs)
            if outputID is None:
                audioDriver = 'Windows Default Output' #using the default output because we didn't find the one(s) requested
                outputID = pyo.pa_get_default_output()
            if outputID is not None:
                logging.info('Using sound driver: %s (ID=%i)' %(audioDriver, outputID))
                maxOutputChnls = pyo.pa_get_output_max_channels(outputID)
            else:
                logging.warning('No audio outputs found (no speakers connected?')
                return -1
            #check for valid input (mic)
            devNames, devIDs = pyo.pa_get_input_devices()
            audioInputName, inputID = _bestDriver(devNames, devIDs)
            if inputID is None:
                audioInputName = 'Windows Default Input' #using the default input because we didn't find the one(s) requested
                inputID = pyo.pa_get_default_input()
            if inputID is not None:
                logging.info('Using sound-input driver: %s (ID=%i)' %(audioInputName, inputID))
                maxInputChnls = pyo.pa_get_input_max_channels(inputID)
                duplex = bool(maxInputChnls > 0)
            else:
                maxInputChnls = 0
                duplex=False
        else:#for other platforms set duplex to True (if microphone is available)
            audioDriver = prefs.general['audioDriver'][0]
            maxInputChnls = pyo.pa_get_input_max_channels(pyo.pa_get_default_input())
            maxOutputChnls = pyo.pa_get_output_max_channels(pyo.pa_get_default_output())
            duplex = bool(maxInputChnls > 0)

        maxChnls = min(maxInputChnls, maxOutputChnls)
        if maxInputChnls < 1:  # pragma: no cover
            logging.warning('%s.initPyo could not find microphone hardware; recording not available' % __name__)
            maxChnls = maxOutputChnls
        if maxOutputChnls < 1:  # pragma: no cover
            logging.error('%s.initPyo could not find speaker hardware; sound not available' % __name__)
            return -1

        # create the instance of the server:
        if platform in ['darwin', 'linux2']:
            #for mac/linux we set the backend using the server audio param
            pyoSndServer = Server(sr=rate, nchnls=maxChnls, buffersize=buffer, audio=audioDriver)
        else:
            #with others we just use portaudio and then set the OutputDevice below
            pyoSndServer = Server(sr=rate, nchnls=maxChnls, buffersize=buffer)

        pyoSndServer.setVerbosity(1)
        if platform=='win32':
            pyoSndServer.setOutputDevice(outputID)
            if inputID is not None:
                pyoSndServer.setInputDevice(inputID)
        #do other config here as needed (setDuplex? setOutputDevice?)
        pyoSndServer.setDuplex(duplex)
        pyoSndServer.boot()
    core.wait(0.5)#wait for server to boot before starting te sound stream
    pyoSndServer.start()
    try:
        Sound()  # test creation, no play
    except pyo.PyoServerStateException:
        msg = "Failed to start pyo sound Server"
        if platform == 'darwin' and audioDriver != 'portaudio':
            msg += "; maybe try prefs.general.audioDriver 'portaudio'?"
        logging.error(msg)
        core.quit()
    logging.debug('pyo sound server started')
    logging.flush()
    def runPsychopy(self):
        # make the marker stream. Must do before the window setup to be able to start Lab Recorder
        self.__marker_outlet = self.__createMarkerStream()

        # Get experiement details and filename
        filename, thisExp, expInfo = self.__getDatafilenameAndSetupWindow()

        # save a log file for detail verbose info
        # logFile = logging.LogFile(filename+'.log', level=logging.EXP)
        logging.console.setLevel(
            logging.WARNING)  # this outputs to the screen, not a file

        ### ESC flag ###
        self.__endExpNow = False  # flag for 'escape' or other condition => quit the exp

        # Create some handy timers
        globalClock = core.Clock(
        )  # to track the time since experiment started
        self.__routineTimer = core.CountdownTimer(
        )  # to track time remaining of each (non-slip) routine
        self.__kb = keyboard.Keyboard()

        # Flag the start of the Psychopy experiment
        self.__marker_outlet.push_sample([RECORDING_START_MARKER])
        self.__showTextWithSpaceExit(instructions_text)
        self.__marker_outlet.push_sample([CALIBRATION_START_MARKER])
        self.__marker_outlet.push_sample([BLINK_START_MARKER])
        self.__showTextWithSpaceExit(blink_text)
        self.__marker_outlet.push_sample([BLINK_END_MARKER])
        self.__marker_outlet.push_sample([CLOSE_EYE_START_MARKER])
        self.__showTextWithSpaceExit(close_eye_text)
        self.__marker_outlet.push_sample([CLOSE_EYE_END_MARKER])

        self.__marker_outlet.push_sample([CALIBRATION_END_MARKER])

        for i in range(NUM_IMAGES_TO_SHOW):
            if i % 140 == 0:
                print("shuffling!")
                rd.shuffle(faces_filenames)
                rd.shuffle(landscape_filenames)

            # Reset the timers
            self.__routineTimer.reset()
            self.__kb.clock.reset()
            time_shown = 0
            self.__marker_outlet.push_sample([CROSS_START_MARKER])
            self.__showTimedText(
                "+",
                rd.randrange((NUM_SECONDS_TO_SHOW_CROSS) * 10,
                             (NUM_SECONDS_TO_SHOW_CROSS + 0.2) * 10) / 10)
            self.__marker_outlet.push_sample([CROSS_END_MARKER])

            self.__marker_outlet.push_sample([NEW_IMAGE_START_MARKER])
            self.__marker_outlet.push_sample(
                [FACE_IMAGE_MARKER] if self.__current_type ==
                FACE else [LANDSCAPE_IMAGE_MARKER])

            self.__getNextImage()
            self.__startRoutine([self.__image_stim])
            self.__marker_outlet.push_sample([self.__image_filename])
            print(self.__image_filename)

            self.__setDrawOn([self.__image_stim])
            self.__showTimedText(
                "",
                rd.randrange((NUM_SECONDS_TO_SHOW_IMAGE) * 10,
                             (NUM_SECONDS_TO_SHOW_IMAGE + 0.2) * 10) / 10)
            self.__endRoutine([self.__image_stim])

            self.__marker_outlet.push_sample([NEW_IMAGE_END_MARKER])

        # Flag the end of the Psychopy experiment

        self.__marker_outlet.push_sample([RECORDING_END_MARKER])

        logging.flush()
        # make sure everything is closed down
        thisExp.abort()  # or data files will save again on exit
        self.__win.close()
Exemple #53
0
def findPhotometer(ports=None, device=None):
    """Try to find a connected photometer/photospectrometer!

    PsychoPy will sweep a series of serial ports trying to open them.
    If a port successfully opens then it will try to issue a command to
    the device. If it responds with one of the expected values then it
    is assumed to be the appropriate device.

    :parameters:

        ports : a list of ports to search
            Each port can be a string (e.g. 'COM1', ''/dev/tty.Keyspan1.1')
            or a number (for win32 comports only). If none are provided
            then PsychoPy will sweep COM0-10 on win32 and search known
            likely port names on macOS and Linux.

        device : string giving expected device (e.g. 'PR650', 'PR655',
            'LS100', 'LS110'). If this is not given then an attempt will be made
            to find a device of any type, but this often fails

    :returns:

        * An object representing the first photometer found
        * None if the ports didn't yield a valid response
        * None if there were not even any valid ports (suggesting a driver
          not being installed)

    e.g.::

        # sweeps ports 0 to 10 searching for a PR655
        photom = findPhotometer(device='PR655')
        print(photom.getLum())
        if hasattr(photom, 'getSpectrum'):
            # can retrieve spectrum (e.g. a PR650)
            print(photom.getSpectrum())

    """
    if isinstance(device, basestring):
        photometers = [getPhotometerByName(device)]
    elif isinstance(device, Iterable):
        # if we find a string assume it is a name, otherwise treat it like a
        # photometer
        photometers = [getPhotometerByName(d)
                       if isinstance(d, basestring) else d
                       for d in device]
    else:
        photometers = getAllPhotometers()

    # determine candidate ports
    if ports is None:
        ports = getSerialPorts()
    elif type(ports) in (int, float) or isinstance(ports, basestring):
        ports = [ports]  # so that we can iterate

    # go through each port in turn
    photom = None
    logging.info('scanning serial ports...')
    logging.flush()
    for thisPort in ports:
        logging.info('...{}'.format(thisPort))
        logging.flush()
        for Photometer in photometers:
            # Looks like we got an invalid photometer, carry on
            if Photometer is None:
                continue
            try:
                photom = Photometer(port=thisPort)
            except Exception as ex:
                msg = "Couldn't initialize photometer {0}: {1}"
                logging.error(msg.format(Photometer.__name__, ex))
                # We threw an exception so we should just skip ahead
                continue
            if photom.OK:
                logging.info(' ...found a %s\n' % (photom.type))
                logging.flush()
                # we're now sure that this is the correct device and that
                # it's configured now increase the number of attempts made
                # to communicate for temperamental devices!
                if hasattr(photom, 'setMaxAttempts'):
                    photom.setMaxAttempts(10)
                # we found one so stop looking
                return photom
            else:
                if photom.com and photom.com.isOpen:
                    logging.info('closing port')
                    photom.com.close()

        # If we got here we didn't find one
        logging.info('...nope!\n\t')
        logging.flush()

    return None
Exemple #54
0
    def __init__(self, arg=0, testMode=False, **kwargs):
        """With a wx.App some things get done here, before App.__init__
        then some further code is launched in OnInit() which occurs after
        """
        if profiling:
            import cProfile, time
            profile = cProfile.Profile()
            profile.enable()
            t0 = time.time()

        self._appLoaded = False  # set to true when all frames are created
        self.coder = None
        self.runner = None
        self.version = psychopy.__version__
        # set default paths and prefs
        self.prefs = psychopy.prefs
        self._currentThemeSpec = None

        self.keys = self.prefs.keys
        self.prefs.pageCurrent = 0  # track last-viewed page, can return there
        self.IDs = IDStore()
        self.urls = urls.urls
        self.quitting = False
        # check compatibility with last run version (before opening windows)
        self.firstRun = False
        self.testMode = testMode
        self._stdout = sys.stdout
        self._stderr = sys.stderr
        self._stdoutFrame = None
        self.iconCache = themes.IconCache()

        if not self.testMode:
            self._lastRunLog = open(os.path.join(
                    self.prefs.paths['userPrefsDir'], 'last_app_load.log'),
                    'w')
            sys.stderr = sys.stdout = lastLoadErrs = self._lastRunLog
            logging.console.setLevel(logging.DEBUG)

        # indicates whether we're running for testing purposes
        self.osfSession = None
        self.pavloviaSession = None

        self.copiedRoutine = None
        self.copiedCompon = None
        self._allFrames = frametracker.openFrames  # ordered; order updated with self.onNewTopWindow

        wx.App.__init__(self, arg)

        # import localization after wx:
        from psychopy import localization  # needed by splash screen
        self.localization = localization
        self.locale = localization.setLocaleWX()
        self.locale.AddCatalog(self.GetAppName())

        logging.flush()
        self.onInit(testMode=testMode, **kwargs)
        if profiling:
            profile.disable()
            print("time to load app = {:.2f}".format(time.time()-t0))
            profile.dump_stats('profileLaunchApp.profile')
        logging.flush()

        # set the exception hook to present unhandled errors in a dialog
        if not travisCI:
            from psychopy.app.errorDlg import exceptionCallback
            sys.excepthook = exceptionCallback
Exemple #55
0
    def _getXMLparam(self, params, paramNode, componentNode=None):
        """params is the dict of params of the builder component
        (e.g. stimulus) into which the parameters will be inserted
        (so the object to store the params should be created first)
        paramNode is the parameter node fetched from the xml file
        """
        name = paramNode.get('name')
        valType = paramNode.get('valType')
        val = paramNode.get('val')
        # many components need web char newline replacement
        if not name == 'advancedParams':
            val = val.replace("&#10;", "\n")

        # custom settings (to be used when
        if valType == 'fixedList':  # convert the string to a list
            params[name].val = eval('list({})'.format(val))
        elif name == 'storeResponseTime':
            return  # deprecated in v1.70.00 because it was redundant
        elif name == 'nVertices':  # up to 1.85 there was no shape param
            # if no shape param then use "n vertices" only
            if _findParam('shape', componentNode) is None:
                if val == '2':
                    params['shape'].val = "line"
                elif val == '3':
                    params['shape'].val = "triangle"
                elif val == '4':
                    params['shape'].val = "rectangle"
                else:
                    params['shape'].val = "regular polygon..."
            params['nVertices'].val = val
        elif name == 'startTime':  # deprecated in v1.70.00
            params['startType'].val = "{}".format('time (s)')
            params['startVal'].val = "{}".format(val)
            return  # times doesn't need to update its type or 'updates' rule
        elif name == 'forceEndTrial':  # deprecated in v1.70.00
            params['forceEndRoutine'].val = bool(val)
            return  # forceEndTrial doesn't need to update type or 'updates'
        elif name == 'forceEndTrialOnPress':  # deprecated in v1.70.00
            params['forceEndRoutineOnPress'].val = bool(val)
            return  # forceEndTrial doesn't need to update  type or 'updates'
        elif name == 'forceEndRoutineOnPress':
            if val == 'True':
                val = "any click"
            elif val == 'False':
                val = "never"
            params['forceEndRoutineOnPress'].val = val
            return
        elif name == 'trialList':  # deprecated in v1.70.00
            params['conditions'].val = eval(val)
            return  # forceEndTrial doesn't need to update  type or 'updates'
        elif name == 'trialListFile':  # deprecated in v1.70.00
            params['conditionsFile'].val = "{}".format(val)
            return  # forceEndTrial doesn't need to update  type or 'updates'
        elif name == 'duration':  # deprecated in v1.70.00
            params['stopType'].val = u'duration (s)'
            params['stopVal'].val = "{}".format(val)
            return  # times doesn't need to update its type or 'updates' rule
        elif name == 'allowedKeys' and valType == 'str':  # changed v1.70.00
            # ynq used to be allowed, now should be 'y','n','q' or
            # ['y','n','q']
            if len(val) == 0:
                newVal = val
            elif val[0] == '$':
                newVal = val[1:]  # they were using code (which we can reuse)
            elif val.startswith('[') and val.endswith(']'):
                # they were using code (slightly incorectly!)
                newVal = val[1:-1]
            elif val in ['return', 'space', 'left', 'right', 'escape']:
                newVal = val  # they were using code
            else:
                # convert string to list of keys then represent again as a
                # string!
                newVal = repr(list(val))
            params['allowedKeys'].val = newVal
            params['allowedKeys'].valType = 'code'
        elif name == 'correctIf':  # deprecated in v1.60.00
            corrIf = val
            corrAns = corrIf.replace(
                'resp.keys==unicode(', '').replace(')', '')
            params['correctAns'].val = corrAns
            name = 'correctAns'  # then we can fetch other aspects below
        elif 'olour' in name:  # colour parameter was Americanised v1.61.00
            name = name.replace('olour', 'olor')
            params[name].val = val
        elif name == 'times':  # deprecated in v1.60.00
            times = eval('%s' % val)
            params['startType'].val = "{}".format('time (s)')
            params['startVal'].val = "{}".format(times[0])
            params['stopType'].val = "{}".format('time (s)')
            params['stopVal'].val = "{}".format(times[1])
            return  # times doesn't need to update its type or 'updates' rule
        elif name in ('Begin Experiment', 'Begin Routine', 'Each Frame',
                      'End Routine', 'End Experiment'):
            params[name].val = val
            params[name].valType = 'extendedCode'  # changed in 1.78.00
            return  # so that we don't update valTyp again below
        elif name == 'Saved data folder':
            # deprecated in 1.80 for more complete data filename control
            params[name] = Param(
                val, valType='code', allowedTypes=[],
                hint=_translate("Name of the folder in which to save data"
                                " and log files (blank defaults to the "
                                "builder pref)"),
                categ='Data')
        elif 'val' in list(paramNode.keys()):
            if val == 'window units':  # changed this value in 1.70.00
                params[name].val = 'from exp settings'
            # in v1.80.00, some RatingScale API and Param fields were changed
            # Try to avoid a KeyError in these cases so can load the expt
            elif name in ('choiceLabelsAboveLine', 'lowAnchorText',
                          'highAnchorText'):
                # not handled, just ignored; want labels=[lowAnchor,
                # highAnchor]
                return
            elif name == 'customize_everything':
                # Try to auto-update the code:
                v = val  # python code, not XML
                v = v.replace('markerStyle', 'marker').replace(
                    'customMarker', 'marker')
                v = v.replace('stretchHoriz', 'stretch').replace(
                    'displaySizeFactor', 'size')
                v = v.replace('textSizeFactor', 'textSize')
                v = v.replace('ticksAboveLine=False', 'tickHeight=-1')
                v = v.replace('showScale=False', 'scale=None').replace(
                    'allowSkip=False', 'skipKeys=None')
                v = v.replace('showAnchors=False', 'labels=None')
                # lowAnchorText highAnchorText will trigger obsolete error
                # when run the script
                params[name].val = v
            else:
                if name in params:
                    params[name].val = val
                else:
                    # we found an unknown parameter (probably from the future)
                    params[name] = Param(
                        val, valType=paramNode.get('valType'),
                        allowedTypes=[],
                        hint=_translate(
                            "This parameter is not known by this version "
                            "of PsychoPy. It might be worth upgrading"))
                    params[name].allowedTypes = paramNode.get('allowedTypes')
                    if params[name].allowedTypes is None:
                        params[name].allowedTypes = []
                    params[name].readOnly = True
                    msg = _translate(
                        "Parameter %r is not known to this version of "
                        "PsychoPy but has come from your experiment file "
                        "(saved by a future version of PsychoPy?). This "
                        "experiment may not run correctly in the current "
                        "version.")
                    logging.warn(msg % name)
                    logging.flush()

        # get the value type and update rate
        if 'valType' in list(paramNode.keys()):
            params[name].valType = paramNode.get('valType')
            # compatibility checks:
            if name in ['allowedKeys'] and paramNode.get('valType') == 'str':
                # these components were changed in v1.70.00
                params[name].valType = 'code'
            elif name == 'Selected rows':
                # changed in 1.81.00 from 'code' to 'str': allow string or var
                params[name].valType = 'str'
            # conversions based on valType
            if params[name].valType == 'bool':
                params[name].val = eval("%s" % params[name].val)
        if 'updates' in list(paramNode.keys()):
            params[name].updates = paramNode.get('updates')
Exemple #56
0
    def __init__(self, arg=0, testMode=False, **kwargs):
        """With a wx.App some things get done here, before App.__init__
        then some further code is launched in OnInit() which occurs after
        """
        if profiling:
            import cProfile
            import time
            profile = cProfile.Profile()
            profile.enable()
            t0 = time.time()

        self._appLoaded = False  # set to true when all frames are created
        self.coder = None
        self.runner = None
        self.version = psychopy.__version__
        # set default paths and prefs
        self.prefs = psychopy.prefs
        self._currentThemeSpec = None

        self.keys = self.prefs.keys
        self.prefs.pageCurrent = 0  # track last-viewed page, can return there
        self.IDs = IDStore()
        self.urls = urls.urls
        self.quitting = False
        # check compatibility with last run version (before opening windows)
        self.firstRun = False
        self.testMode = testMode
        self._stdout = sys.stdout
        self._stderr = sys.stderr
        self._stdoutFrame = None

        # Shared memory used for messaging between app instances, this gets
        # allocated when `OnInit` is called.
        self._sharedMemory = None
        self._singleInstanceChecker = None  # checker for instances
        self._timer = None
        # Size of the memory map buffer, needs to be large enough to hold UTF-8
        # encoded long file paths.
        self.mmap_sz = 2048

        # mdc - removed the following and put it in `app.startApp()` to have
        #       error logging occur sooner.
        #
        # if not self.testMode:
        #     self._lastRunLog = open(os.path.join(
        #             self.prefs.paths['userPrefsDir'], 'last_app_load.log'),
        #             'w')
        #     sys.stderr = sys.stdout = lastLoadErrs = self._lastRunLog
        #     logging.console.setLevel(logging.DEBUG)

        # indicates whether we're running for testing purposes
        self.osfSession = None
        self.pavloviaSession = None

        self.copiedRoutine = None
        self.copiedCompon = None
        self._allFrames = frametracker.openFrames  # ordered; order updated with self.onNewTopWindow

        wx.App.__init__(self, arg)

        # import localization after wx:
        from psychopy import localization  # needed by splash screen
        self.localization = localization
        self.locale = localization.setLocaleWX()
        self.locale.AddCatalog(self.GetAppName())

        logging.flush()
        self.onInit(testMode=testMode, **kwargs)
        if profiling:
            profile.disable()
            print("time to load app = {:.2f}".format(time.time() - t0))
            profile.dump_stats('profileLaunchApp.profile')
        logging.flush()

        # if we're on linux, check if we have the permissions file setup
        from psychopy.app.linuxconfig import (LinuxConfigDialog,
                                              linuxConfigFileExists)

        if not linuxConfigFileExists():
            linuxConfDlg = LinuxConfigDialog(
                None, timeout=1000 if self.testMode else None)
            linuxConfDlg.ShowModal()
            linuxConfDlg.Destroy()
Exemple #57
0
def findPhotometer(ports=None, device=None):
    """Try to find a connected photometer/photospectrometer!
    PsychoPy will sweep a series of serial ports trying to open them. If a port
    successfully opens then it will try to issue a command to the device. If it
    responds with one of the expected values then it is assumed to be the
    appropriate device.

    :parameters:

        ports : a list of ports to search
            Each port can be a string (e.g. 'COM1', ''/dev/tty.Keyspan1.1') or a
            number (for win32 comports only). If none are provided then PsychoPy
            will sweep COM0-10 on win32 and search known likely port names on OS X
            and linux.

        device : string giving expected device (e.g. 'PR650', 'PR655', 'LS110').
            If this is not given then an attempt will be made to find a device of
            any type, but this often fails

    :returns:

        * An object representing the first photometer found
        * None if the ports didn't yield a valid response
        * None if there were not even any valid ports (suggesting a driver not being installed)

    e.g.::

        photom = findPhotometer(device='PR655') #sweeps ports 0 to 10 searching for a PR655
        print photom.getLum()
        if hasattr(photom, 'getSpectrum'):#can retrieve spectrum (e.g. a PR650)
            print photom.getSpectrum()

    """
    if isinstance(device,basestring):
        photometers = [getPhotometerByName(device)]
    elif isinstance(device,collections.Iterable):
        # if we find a string assume it is a name, otherwise treat it like a photometer
        photometers = [getPhotometerByName(d) if isinstance(d,basestring) else d for d in device]
    else:
        photometers = getAllPhotometers()


    #determine candidate ports
    if ports is None:
        ports = getSerialPorts()
    elif type(ports) in [int,float] or isinstance(ports,basestring):
        ports=[ports] #so that we can iterate

    #go through each port in turn
    photom=None
    logging.info('scanning serial ports...')
    logging.flush()
    for thisPort in ports:
        logging.info('...'+str(thisPort)); logging.flush()
        for Photometer in photometers:
            # Looks like we got an invalid photometer, carry on
            if Photometer is None:
                continue
            try:
                photom = Photometer(port=thisPort)
            except Exception as ex:
                logging.error("Couldn't initialize photometer {0}: {1}".format(Photometer.__name__,ex))
                continue # We threw an exception so we should just skip ahead
            if photom.OK:
                logging.info(' ...found a %s\n' %(photom.type)); logging.flush()
                #we're now sure that this is the correct device and that it's configured
                #now increase the number of attempts made to communicate for temperamental devices!
                if hasattr(photom,'setMaxAttempts'):photom.setMaxAttempts(10)
                return photom#we found one so stop looking
            else:
                if photom.com and photom.com.isOpen:
                    logging.info('closing port')
                    photom.com.close()

        #If we got here we didn't find one
        logging.info('...nope!\n\t'); logging.flush()

    return None
Exemple #58
0
    def onInit(self, showSplash=True, testMode=False):
        """This is launched immediately *after* the app initialises with wx
        :Parameters:

          testMode: bool
        """
        self.SetAppName('PsychoPy3')

        # Single instance check is done here prior to loading any GUI stuff.
        # This permits one instance of PsychoPy from running at any time.
        # Clicking on files will open them in the extant instance rather than
        # loading up a new one.
        #
        # Inter-process messaging is done via a memory-mapped file created by
        # the first instance. Successive instances will write their args to
        # this file and promptly close. The main instance will read this file
        # periodically for data and open and file names stored to this buffer.
        #
        # This uses similar logic to this example:
        # https://github.com/wxWidgets/wxPython-Classic/blob/master/wx/lib/pydocview.py

        # Create the memory-mapped file if not present, this is handled
        # differently between Windows and UNIX-likes.
        if wx.Platform == '__WXMSW__':
            tfile = tempfile.TemporaryFile(prefix="ag", suffix="tmp")
            fno = tfile.fileno()
            self._sharedMemory = mmap.mmap(fno, self.mmap_sz, "shared_memory")
        else:
            tfile = open(
                os.path.join(
                    tempfile.gettempdir(),
                    tempfile.gettempprefix() + self.GetAppName() + '-' +
                    wx.GetUserId() + "AGSharedMemory"), 'w+b')

            # insert markers into the buffer
            tfile.write(b"*")
            tfile.seek(self.mmap_sz)
            tfile.write(b" ")
            tfile.flush()
            fno = tfile.fileno()
            self._sharedMemory = mmap.mmap(fno, self.mmap_sz)

        # use wx to determine if another instance is running
        self._singleInstanceChecker = wx.SingleInstanceChecker(
            self.GetAppName() + '-' + wx.GetUserId(), tempfile.gettempdir())

        # If another instance is running, message our args to it by writing the
        # path the the buffer.
        if self._singleInstanceChecker.IsAnotherRunning():
            # Message the extant running instance the arguments we want to
            # process.
            args = sys.argv[1:]

            # if there are no args, tell the user another instance is running
            if not args:
                errMsg = "Another instance of PsychoPy is already running."
                errDlg = wx.MessageDialog(None,
                                          errMsg,
                                          caption="PsychoPy Error",
                                          style=wx.OK | wx.ICON_ERROR,
                                          pos=wx.DefaultPosition)
                errDlg.ShowModal()
                errDlg.Destroy()

                self.quit(None)

            # serialize the data
            data = pickle.dumps(args)

            # Keep alive until the buffer is free for writing, this allows
            # multiple files to be opened in succession. Times out after 5
            # seconds.
            attempts = 0
            while attempts < 5:
                # try to write to the buffer
                self._sharedMemory.seek(0)
                marker = self._sharedMemory.read(1)
                if marker == b'\0' or marker == b'*':
                    self._sharedMemory.seek(0)
                    self._sharedMemory.write(b'-')
                    self._sharedMemory.write(data)
                    self._sharedMemory.seek(0)
                    self._sharedMemory.write(b'+')
                    self._sharedMemory.flush()
                    break
                else:
                    # wait a bit for the buffer to become free
                    time.sleep(1)
                    attempts += 1
            else:
                if not self.testMode:
                    # error that we could not access the memory-mapped file
                    errMsg = \
                        "Cannot communicate with running PsychoPy instance!"
                    errDlg = wx.MessageDialog(None,
                                              errMsg,
                                              caption="PsychoPy Error",
                                              style=wx.OK | wx.ICON_ERROR,
                                              pos=wx.DefaultPosition)
                    errDlg.ShowModal()
                    errDlg.Destroy()

            # since were not the main instance, exit ...
            self.quit(None)

        # ----

        if showSplash:
            # show splash screen
            splashFile = os.path.join(self.prefs.paths['resources'],
                                      'psychopySplash.png')
            splashImage = wx.Image(name=splashFile)
            splashImage.ConvertAlphaToMask()
            splash = AS.AdvancedSplash(None,
                                       bitmap=splashImage.ConvertToBitmap(),
                                       timeout=3000,
                                       agwStyle=AS.AS_TIMEOUT
                                       | AS.AS_CENTER_ON_SCREEN)
            w, h = splashImage.GetSize()
            splash.SetTextPosition((340, h - 30))
            splash.SetText(
                _translate("Copyright (C) 2022 OpenScienceTools.org"))
        else:
            splash = None

        # SLOW IMPORTS - these need to be imported after splash screen starts
        # but then that they end up being local so keep track in self

        from psychopy.compatibility import checkCompatibility
        # import coder and builder here but only use them later
        from psychopy.app import coder, builder, runner, dialogs

        if '--firstrun' in sys.argv:
            del sys.argv[sys.argv.index('--firstrun')]
            self.firstRun = True
        if 'lastVersion' not in self.prefs.appData:
            # must be before 1.74.00
            last = self.prefs.appData['lastVersion'] = '1.73.04'
            self.firstRun = True
        else:
            last = self.prefs.appData['lastVersion']

        if self.firstRun and not self.testMode:
            pass

        # setup links for URLs
        # on a mac, don't exit when the last frame is deleted, just show menu
        if sys.platform == 'darwin':
            self.menuFrame = MenuFrame(parent=None, app=self)
        # fetch prev files if that's the preference
        if self.prefs.coder['reloadPrevFiles']:
            scripts = self.prefs.appData['coder']['prevFiles']
        else:
            scripts = []
        appKeys = list(self.prefs.appData['builder'].keys())
        if self.prefs.builder['reloadPrevExp'] and ('prevFiles' in appKeys):
            exps = self.prefs.appData['builder']['prevFiles']
        else:
            exps = []
        runlist = []

        self.dpi = int(wx.GetDisplaySize()[0] /
                       float(wx.GetDisplaySizeMM()[0]) * 25.4)
        # detect retina displays
        self.isRetina = self.dpi > 80 and wx.Platform == '__WXMAC__'
        if self.isRetina:
            fontScale = 1.2  # fonts are looking tiny on macos (only retina?) right now
            # mark icons as being retina
            icons.retStr = "@2x"
        else:
            fontScale = 1
        # adjust dpi to something reasonable
        if not (50 < self.dpi < 120):
            self.dpi = 80  # dpi was unreasonable, make one up

        # Manage fonts
        if sys.platform == 'win32':
            # wx.SYS_DEFAULT_GUI_FONT is default GUI font in Win32
            self._mainFont = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT)
        else:
            self._mainFont = wx.SystemSettings.GetFont(wx.SYS_ANSI_FIXED_FONT)
            # rescale for tiny retina fonts

        if hasattr(wx.Font, "AddPrivateFont") and sys.platform != "darwin":
            # Load packaged fonts if possible
            for fontFile in (Path(__file__).parent / "Resources" /
                             "fonts").glob("*"):
                if fontFile.suffix in ['.ttf', '.truetype']:
                    wx.Font.AddPrivateFont(str(fontFile))
            # Set fonts as those loaded
            self._codeFont = wx.Font(
                wx.FontInfo(
                    self._mainFont.GetPointSize()).FaceName("JetBrains Mono"))
        else:
            # Get system defaults if can't load fonts
            try:
                self._codeFont = wx.SystemSettings.GetFont(
                    wx.SYS_ANSI_FIXED_FONT)
            except wx._core.wxAssertionError:
                # if no SYS_ANSI_FIXED_FONT then try generic FONTFAMILY_MODERN
                self._codeFont = wx.Font(self._mainFont.GetPointSize(),
                                         wx.FONTFAMILY_TELETYPE,
                                         wx.FONTSTYLE_NORMAL,
                                         wx.FONTWEIGHT_NORMAL)

        if self.isRetina:
            self._codeFont.SetPointSize(
                int(self._codeFont.GetPointSize() * fontScale))
            self._mainFont.SetPointSize(
                int(self._mainFont.GetPointSize() * fontScale))

        # that gets most of the properties of _codeFont but the FaceName
        # FaceName is set in the setting of the theme:
        self.theme = prefs.app['theme']

        # removed Aug 2017: on newer versions of wx (at least on mac)
        # this looks too big
        # if hasattr(self._mainFont, 'Larger'):
        #     # Font.Larger is available since wyPython version 2.9.1
        #     # PsychoPy still supports 2.8 (see ensureMinimal above)
        #     self._mainFont = self._mainFont.Larger()
        #     self._codeFont.SetPointSize(
        #         self._mainFont.GetPointSize())  # unify font size

        # create both frame for coder/builder as necess
        if splash:
            splash.SetText(_translate("  Creating frames..."))

        # Parse incoming call
        parser = argparse.ArgumentParser(prog=self)
        parser.add_argument('--builder', dest='builder', action="store_true")
        parser.add_argument('-b', dest='builder', action="store_true")
        parser.add_argument('--coder', dest='coder', action="store_true")
        parser.add_argument('-c', dest='coder', action="store_true")
        parser.add_argument('--runner', dest='runner', action="store_true")
        parser.add_argument('-r', dest='runner', action="store_true")
        parser.add_argument('-x', dest='direct', action='store_true')
        view, args = parser.parse_known_args(sys.argv)
        # Check from filetype if any windows need to be open
        if any(arg.endswith('.psyexp') for arg in args):
            view.builder = True
            exps = [file for file in args if file.endswith('.psyexp')]
        if any(arg.endswith('.psyrun') for arg in args):
            view.runner = True
            runlist = [file for file in args if file.endswith('.psyrun')]
        # If still no window specified, use default from prefs
        if not any(
                getattr(view, key) for key in ['builder', 'coder', 'runner']):
            if self.prefs.app['defaultView'] in view:
                setattr(view, self.prefs.app['defaultView'], True)
            elif self.prefs.app['defaultView'] == 'all':
                view.builder = True
                view.coder = True
                view.runner = True

        # set the dispatcher for standard output
        # self.stdStreamDispatcher = console.StdStreamDispatcher(self)
        # self.stdStreamDispatcher.redirect()

        # Create windows
        if view.runner:
            self.showRunner(fileList=runlist)
        if view.coder:
            self.showCoder(fileList=scripts)
        if view.builder:
            self.showBuilder(fileList=exps)
        if view.direct:
            self.showRunner()
            for exp in [
                    file for file in args
                    if file.endswith('.psyexp') or file.endswith('.py')
            ]:
                self.runner.panel.runFile(exp)

        # 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
        self.news = None
        self.tasks = None

        prefsConn = self.prefs.connections

        ok, msg = checkCompatibility(last, self.version, self.prefs, fix=True)
        # tell the user what has changed
        if not ok and not self.firstRun and not self.testMode:
            title = _translate("Compatibility information")
            dlg = dialogs.MessageDialog(parent=None,
                                        message=msg,
                                        type='Info',
                                        title=title)
            dlg.ShowModal()

        if self.prefs.app['showStartupTips'] and not self.testMode:
            tipFile = os.path.join(self.prefs.paths['resources'],
                                   _translate("tips.txt"))
            tipIndex = self.prefs.appData['tipIndex']
            if parse_version(wx.__version__) >= parse_version('4.0.0a1'):
                tp = wx.adv.CreateFileTipProvider(tipFile, tipIndex)
                showTip = wx.adv.ShowTip(None, tp)
            else:
                tp = wx.CreateFileTipProvider(tipFile, tipIndex)
                showTip = wx.ShowTip(None, tp)

            self.prefs.appData['tipIndex'] = tp.GetCurrentTip()
            self.prefs.saveAppData()
            self.prefs.app['showStartupTips'] = showTip
            self.prefs.saveUserPrefs()

        self.Bind(wx.EVT_IDLE, self.onIdle)

        # doing this once subsequently enables the app to open & switch among
        # wx-windows on some platforms (Mac 10.9.4) with wx-3.0:
        v = parse_version
        if sys.platform == 'darwin':
            if v('3.0') <= v(wx.version()) < v('4.0'):
                _Showgui_Hack()  # returns ~immediately, no display
                # focus stays in never-land, so bring back to the app:
                if prefs.app['defaultView'] in [
                        'all', 'builder', 'coder', 'runner'
                ]:
                    self.showBuilder()
                else:
                    self.showCoder()
        # after all windows are created (so errors flushed) create output
        self._appLoaded = True
        if self.coder:
            self.coder.setOutputWindow()  # takes control of sys.stdout

        # flush any errors to the last run log file
        logging.flush()
        sys.stdout.flush()
        # we wanted debug mode while loading but safe to go back to info mode
        if not self.prefs.app['debugMode']:
            logging.console.setLevel(logging.INFO)

        # if the program gets here, there are no other instances running
        self._timer = wx.PyTimer(self._bgCheckAndLoad)
        self._timer.Start(250)

        return True
        break
    continueRoutine = False  # will revert to True if at least one component still running
    for thisComponent in trialComponents:
        if hasattr(thisComponent, "status") and thisComponent.status != FINISHED:
            continueRoutine = True
            break  # at least one component has not yet finished
    
    # refresh the screen
    if continueRoutine:  # don't flip if this routine is over or we'll get a blank screen
        win.flip()

# -------Ending Routine "trial"-------
for thisComponent in trialComponents:
    if hasattr(thisComponent, "setAutoDraw"):
        thisComponent.setAutoDraw(False)
thisExp.addData('polygon.started', polygon.tStartRefresh)
thisExp.addData('polygon.stopped', polygon.tStopRefresh)

# Flip one final time so any remaining win.callOnFlip() 
# and win.timeOnFlip() tasks get executed before quitting
win.flip()

# these shouldn't be strictly necessary (should auto-save)
thisExp.saveAsWideText(filename+'.csv')
thisExp.saveAsPickle(filename)
logging.flush()
# make sure everything is closed down
thisExp.abort()  # or data files will save again on exit
win.close()
core.quit()
Exemple #60
0
    def onInit(self, showSplash=True, testMode=False):
        """This is launched immediately *after* the app initialises with wx
        :Parameters:

          testMode: bool
        """
        self.SetAppName('PsychoPy3')

        if showSplash:
            # show splash screen
            splashFile = os.path.join(self.prefs.paths['resources'],
                                      'psychopySplash.png')
            splashImage = wx.Image(name=splashFile)
            splashImage.ConvertAlphaToMask()
            splash = AS.AdvancedSplash(
                None,
                bitmap=splashImage.ConvertToBitmap(),
                timeout=3000,
                agwStyle=AS.AS_TIMEOUT | AS.AS_CENTER_ON_SCREEN,
            )  # transparency?
            w, h = splashImage.GetSize()
            splash.SetTextPosition((int(200), h - 20))
            splash.SetText(
                _translate("Copyright (C) 2020 OpenScienceTools.org"))
        else:
            splash = None

        # SLOW IMPORTS - these need to be imported after splash screen starts
        # but then that they end up being local so keep track in self

        from psychopy.compatibility import checkCompatibility
        # import coder and builder here but only use them later
        from psychopy.app import coder, builder, runner, dialogs

        if '--firstrun' in sys.argv:
            del sys.argv[sys.argv.index('--firstrun')]
            self.firstRun = True
        if 'lastVersion' not in self.prefs.appData:
            # must be before 1.74.00
            last = self.prefs.appData['lastVersion'] = '1.73.04'
            self.firstRun = True
        else:
            last = self.prefs.appData['lastVersion']

        if self.firstRun and not self.testMode:
            pass

        # setup links for URLs
        # on a mac, don't exit when the last frame is deleted, just show 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
            allowed = ['last', 'coder', 'builder', 'both']
            if self.prefs.app['defaultView'] in allowed:
                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 = []
        appKeys = list(self.prefs.appData['builder'].keys())
        if self.prefs.builder['reloadPrevExp'] and ('prevFiles' in appKeys):
            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__:
                # program was executed as "python.exe psychopyApp.py %1'
                args = sys.argv[2:]
            else:
                # program was executed as "psychopyApp.py %1'
                args = sys.argv[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

        if sys.platform == 'win32':
            # wx.SYS_DEFAULT_GUI_FONT is default GUI font in Win32
            self._mainFont = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT)
        else:
            self._mainFont = wx.SystemSettings.GetFont(wx.SYS_ANSI_FIXED_FONT)

        try:
            self._codeFont = wx.SystemSettings.GetFont(wx.SYS_ANSI_FIXED_FONT)
        except wx._core.wxAssertionError:
            # if no SYS_ANSI_FIXED_FONT then try generic FONTFAMILY_MODERN
            self._codeFont = wx.Font(self._mainFont.GetPointSize(),
                                     wx.FONTFAMILY_MODERN, wx.FONTSTYLE_NORMAL,
                                     wx.FONTWEIGHT_NORMAL)
        self._codeFont.SetFaceName(self.prefs.coder['codeFont'])

        # removed Aug 2017: on newer versions of wx (at least on mac)
        # this looks too big
        # if hasattr(self._mainFont, 'Larger'):
        #     # Font.Larger is available since wyPython version 2.9.1
        #     # PsychoPy still supports 2.8 (see ensureMinimal above)
        #     self._mainFont = self._mainFont.Larger()
        #     self._codeFont.SetPointSize(
        #         self._mainFont.GetPointSize())  # unify font size

        # create both frame for coder/builder as necess
        if splash:
            splash.SetText(_translate("  Creating frames..."))
        # Always show runner
        self.showRunner()
        if mainFrame in ['both', 'coder']:
            self.showCoder(fileList=scripts)
        if mainFrame in ['both', 'builder']:
            self.showBuilder(fileList=exps)

        # if darwin, check for inaccessible keyboard
        if sys.platform == 'darwin':
            from psychopy.hardware import keyboard
            if keyboard.macPrefsBad:
                title = _translate("Mac keyboard security")
                if platform.mac_ver()[0] < '10.15':
                    settingName = 'Accessibility'
                    setting = 'Privacy_Accessibility'
                else:
                    setting = 'Privacy_ListenEvent'
                    settingName = 'Input Monitoring'
                msg = _translate(
                    "To use high-precision keyboard timing you should "
                    "enable {} for PsychoPy in System Preferences. "
                    "Shall we go there (and you can drag PsychoPy app into "
                    "the box)?").format(settingName)
                dlg = dialogs.MessageDialog(title=title,
                                            message=msg,
                                            type='Query')
                resp = dlg.ShowModal()
                if resp == wx.ID_YES:
                    from AppKit import NSWorkspace
                    from Foundation import NSURL

                    sys_pref_link = ('x-apple.systempreferences:'
                                     'com.apple.preference.security?'
                                     '{}'.format(setting))

                    # create workspace object
                    workspace = NSWorkspace.sharedWorkspace()

                    # Open System Preference
                    workspace.openURL_(NSURL.URLWithString_(sys_pref_link))

        # 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
        self.news = None
        self.tasks = None

        prefsConn = self.prefs.connections

        ok, msg = checkCompatibility(last, self.version, self.prefs, fix=True)
        # tell the user what has changed
        if not ok and not self.firstRun and not self.testMode:
            title = _translate("Compatibility information")
            dlg = dialogs.MessageDialog(parent=None,
                                        message=msg,
                                        type='Info',
                                        title=title)
            dlg.ShowModal()

        if (self.prefs.app['showStartupTips'] and not self.testMode
                and not blockTips):
            tipFile = os.path.join(self.prefs.paths['resources'],
                                   _translate("tips.txt"))
            tipIndex = self.prefs.appData['tipIndex']
            if parse_version(wx.__version__) >= parse_version('4.0.0a1'):
                tp = wx.adv.CreateFileTipProvider(tipFile, tipIndex)
                showTip = wx.adv.ShowTip(None, tp)
            else:
                tp = wx.CreateFileTipProvider(tipFile, tipIndex)
                showTip = wx.ShowTip(None, tp)

            self.prefs.appData['tipIndex'] = tp.GetCurrentTip()
            self.prefs.saveAppData()
            self.prefs.app['showStartupTips'] = showTip
            self.prefs.saveUserPrefs()

        self.Bind(wx.EVT_IDLE, self.onIdle)

        # doing this once subsequently enables the app to open & switch among
        # wx-windows on some platforms (Mac 10.9.4) with wx-3.0:
        v = parse_version
        if sys.platform == 'darwin':
            if v('3.0') <= v(wx.version()) < v('4.0'):
                _Showgui_Hack()  # returns ~immediately, no display
                # focus stays in never-land, so bring back to the app:
                if mainFrame in ['both', 'builder']:
                    self.showBuilder()
                else:
                    self.showCoder()
        # after all windows are created (so errors flushed) create output
        self._appLoaded = True
        if self.coder:
            self.coder.setOutputWindow()  # takes control of sys.stdout

        # flush any errors to the last run log file
        logging.flush()
        sys.stdout.flush()
        # we wanted debug mode while loading but safe to go back to info mode
        if not self.prefs.app['debugMode']:
            logging.console.setLevel(logging.INFO)
        # Runner captures standard streams until program closed
        if not self.testMode:
            sys.stdout = self.runner.stdOut
            sys.stderr = self.runner.stdOut

        return True