def stimDisplayMirrorChildFunction(qTo, qFrom, windowSize=[1920 / 2, 1080 / 2], windowPosition=[0, 0]): import sdl2 import sdl2.ext import sys import time from PIL import Image #for image manipulation try: import appnope appnope.nope() except: pass sdl2.SDL_Init(sdl2.SDL_INIT_VIDEO) window = sdl2.ext.Window("mirror", size=windowSize, position=windowPosition, flags=sdl2.SDL_WINDOW_SHOWN) windowID = sdl2.SDL_GetWindowID(window.window) windowSurf = sdl2.SDL_GetWindowSurface(window.window) windowArray = sdl2.ext.pixels3d(windowSurf.contents) sdl2.ext.fill(windowSurf.contents, sdl2.pixels.SDL_Color(r=255, g=255, b=255, a=255)) window.refresh() for i in range(10): sdl2.SDL_PumpEvents() #to show the windows def exitSafely(): sys.exit() while True: if not qTo.empty(): message = qTo.get() if message == 'quit': exitSafely() elif message[0] == 'frame': # print ['q',time.time()-message[3]] #time spent in queue res = message[1] buffer = message[2] image = Image.fromstring(mode="RGB", size=res, data=buffer) image = image.transpose(Image.ROTATE_270) # start = time.time() # image.thumbnail([res[1]/2,res[0]/2],Image.LANCZOS) # print ['resize',time.time()-start] windowArray[:, :, 0:3] = image window.refresh() sdl2.SDL_PumpEvents() for event in sdl2.ext.get_events(): if event.type == sdl2.SDL_WINDOWEVENT: if (event.window.event == sdl2.SDL_WINDOWEVENT_CLOSE): exitSafely()
def writerChildFunction(qTo, qFrom, windowSize=[200, 200], windowPosition=[0, 0]): import sdl2 import sdl2.ext import sys import time try: import appnope appnope.nope() except: pass sdl2.SDL_Init(sdl2.SDL_INIT_VIDEO) window = sdl2.ext.Window("writer", size=windowSize, position=windowPosition, flags=sdl2.SDL_WINDOW_SHOWN) windowID = sdl2.SDL_GetWindowID(window.window) windowSurf = sdl2.SDL_GetWindowSurface(window.window) sdl2.ext.fill(windowSurf.contents, sdl2.pixels.SDL_Color(r=255, g=255, b=255, a=255)) window.refresh() for i in range(10): sdl2.SDL_PumpEvents() #to show the windows files = {} def exitSafely(): try: for index, fileObj in files.items(): fileObj.close() # gpg -r "Michael Lawrence <*****@*****.**>" -e mac.txt except: pass sys.exit() while True: if not qTo.empty(): message = qTo.get() if message == 'quit': exitSafely() elif message[0] == 'newFile': files[message[1]] = open(message[2], 'w') elif message[0] == 'write': files[message[1]].write(message[2] + '\n') else: time.sleep(1) sdl2.SDL_PumpEvents() for event in sdl2.ext.get_events(): if event.type == sdl2.SDL_WINDOWEVENT: if (event.window.event == sdl2.SDL_WINDOWEVENT_CLOSE): exitSafely()
async def run_async(self): self.running = True self.startup() def event_handler(data, event_ptr): if self.running: # SDL2 seems to re-use SDL_Event structures, so we need to make a copy. event = sdl2.SDL_Event() ctypes.pointer(event)[0] = event_ptr.contents self.dispatch(event) return 0 watcher = sdl2.SDL_EventFilter(event_handler) sdl2.SDL_AddEventWatch(watcher, None) loop = asyncio.get_running_loop() fps = 60.0 frame_time = 1.0 / fps last_tick = loop.time() while self.running: sdl2.SDL_PumpEvents() # dt here will be how much time since the last loop minus any event handling. dt = loop.time() - last_tick await asyncio.sleep(max(0, frame_time - dt)) # dt here will be how much time since the last call to tick. dt = loop.time() - last_tick self.tick(dt) last_tick = loop.time() sdl2.SDL_DelEventWatch(watcher, None) self.cleanup() sdl2.SDL_Quit()
def getBrakeVal(): sdl2.SDL_PumpEvents() joy_y = sdl2.SDL_JoystickGetAxis(joystick, 1) joy_y = (joy_y / 32767) if (joy_y < 0.001): joy_y = 0 return joy_y
def __init__(self, stimDisplayRes, stimDisplayPosition): #,stimDisplayMirrorChild): self.stimDisplayRes = stimDisplayRes # self.stimDisplayMirrorChild = stimDisplayMirrorChild sdl2.SDL_Init(sdl2.SDL_INIT_VIDEO) self.stimDisplayRes = stimDisplayRes self.stimDisplayPosition = stimDisplayPosition self.Window = sdl2.video.SDL_CreateWindow( byteify('stimDisplay', "utf-8"), self.stimDisplayPosition[0], self.stimDisplayPosition[1], self.stimDisplayRes[0], self.stimDisplayRes[1], sdl2.SDL_WINDOW_OPENGL | sdl2.SDL_WINDOW_SHOWN | sdl2.SDL_RENDERER_ACCELERATED | sdl2.SDL_RENDERER_PRESENTVSYNC) self.glContext = sdl2.SDL_GL_CreateContext(self.Window) gl.glMatrixMode(gl.GL_PROJECTION) gl.glLoadIdentity() gl.glOrtho(0, stimDisplayRes[0], stimDisplayRes[1], 0, 0, 1) gl.glMatrixMode(gl.GL_MODELVIEW) gl.glDisable(gl.GL_DEPTH_TEST) gl.glReadBuffer(gl.GL_FRONT) gl.glClearColor(0, 0, 0, 1) start = time.time() while time.time() < (start + 2): sdl2.SDL_PumpEvents() self.refresh() self.refresh()
def ss_thread(q, stop_event): """q is a Queue object, stop_event is an Event. stop_event from http://stackoverflow.com/questions/6524459/stopping-a-thread-python """ while (not stop_event.is_set()): if q.empty(): sdl2.SDL_PumpEvents() q.put((getScreenShot(), getJoystickState(globalJoystickInput)))
def do_GET(self): self.send_response(200) self.send_header("Content-type", "text/plain") self.end_headers() sdl2.SDL_PumpEvents() output = globalJoystickInput.getJoystickState() output_json = getJoystickJson(output) if(__name__ == '__main__'): print(output_json) self.wfile.write(str.encode(output_json)) return
def get_gamepad_state(gp): # update joystick info sdl2.SDL_PumpEvents() gp_state = {} for stick in gp['sticks']: gp_state[stick] = scaled_stick_value(gp, gp['sticks'][stick]['id'], gp['sticks'][stick]['invert']) for btn in gp['btns']: gp_state[btn] = sdl2.SDL_JoystickGetButton(gp['gp_object'], gp['btns'][btn]) # This way a pressed button outputs a number equivalent # to a fully bent stick # print(gp_state) return gp_state
def steer(self): angle = self.state.angle dist = self.state.trackPos steer = (angle - dist * 0.5) / self.steer_lock self.control.setSteer(steer) sdl2.SDL_PumpEvents() wheel = sdl2.SDL_JoystickGetAxis(self.joystick, 0) wheel = -wheel / 32767.0 #print steer, wheel, steer-wheel self.generate_force(steer - wheel)
def waitForResponse(): # sdl2.SDL_FlushEvents() done = False while not done: sdl2.SDL_PumpEvents() for event in sdl2.ext.get_events(): if event.type == sdl2.SDL_KEYDOWN: response = sdl2.SDL_GetKeyName( event.key.keysym.sym).lower() if response == 'escape': exitSafely() else: done = True # sdl2.SDL_FlushEvents() return response
def get_gamepad_state(gp): # update joystick info sdl2.SDL_PumpEvents() gp_state = {} for stick in gp['sticks']: gp_state[stick] = scaled_stick_value(gp, gp['sticks'][stick]['id'], gp['sticks'][stick]['invert']) for btn in gp['btns']: gp_state[btn] = sdl2.SDL_JoystickGetButton(gp['gp_object'], gp['btns'][btn]) #this way a pressed button outputs a number equivalent to a fully bent stick if OCULUS_ENABLED: gp_state['look_h'] = int(Y_AXIS * 10) # scaled_stick_value(gp,2,stick_max=300), gp_state['look_v'] = int(X_AXIS * -1.9) # scaled_stick_value(gp,3) * gp['invert_y'], return gp_state
def read(self): """Read all the sticks and switches""" # Force an update of the joystick channels sdl2.SDL_PumpEvents() # Ensure that the Tx is connected if sdl2.SDL_NumJoysticks() == 0: if self.joystick: sdl2.SDL_JoystickClose(self.joystick) self.joystick = None return False elif not self.joystick: # Open the first joystick if necessary self.joystick = sdl2.SDL_JoystickOpen(0) # Read all the channels ret = [] for channel in range(self.channels): val = sdl2.SDL_JoystickGetAxis(self.joystick, channel) ret.append(int(((val / 65536.0) + 0.5) * 800.0 + 1100.0)) # Return the list of values return ret
def do_GET(self): self.send_response(200) self.send_header("Content-type", "text/plain") self.end_headers() sdl2.SDL_PumpEvents() joystick_state = globalJoystickInput.getJoystickState() global globalInputOverride, globalInProcessOfOverride if (joystick_state[12] == 1 and joystick_state[13] == 1 and not globalInProcessOfOverride): globalInputOverride = not globalInputOverride print('inputOverride:', globalInputOverride) globalInProcessOfOverride = True elif (globalInProcessOfOverride): globalInProcessOfOverride = (joystick_state[12] == 1 and joystick_state[13] == 1) if not globalInputOverride: #AI input if (not workerQueue.empty()): global globalOutputJson globalOutputJson = workerQueue.get() print(globalOutputJson) self.wfile.write(str.encode(globalOutputJson)) else: #controller input output_json = getJoystickJson(joystick_state) print(output_json) self.wfile.write(str.encode(output_json)) return
def calibrationChildFunction(qTo, qFrom, viewingDistance=100, stimDisplayWidth=100, stimDisplayRes=(2560, 1440), stimDisplayPosition=(-2560, 0), mirrorDisplayPosition=(0, 0), calibrationDotSizeInDegrees=1, timestampMethod=0, mirrorDownSize=2, manualCalibrationOrder=True): import numpy #for image and display manipulation import scipy.misc #for image and display manipulation import math #for trig and other math stuff import sys #for quitting import sdl2 import sdl2.ext import random #set the getTime function if (timestampMethod == 0) or (timestampMethod == 1): #initialize timer sdl2.SDL_Init(sdl2.SDL_INIT_TIMER) if timestampMethod == 0: #define a function to use the high-precision timer, returning a float in seconds def getTime(): return sdl2.SDL_GetPerformanceCounter( ) * 1.0 / sdl2.SDL_GetPerformanceFrequency() elif timestampMethod == 1: #use the SDL_GetTicks timer def getTime(): return sdl2.SDL_GetTicks() / 1000.0 elif timestampMethod == 2: #use time.time() import time getTime = time.time #initialize video sdl2.SDL_Init(sdl2.SDL_INIT_VIDEO) sdl2.SDL_SetHint("SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS", "0") mirrorDisplay = sdl2.ext.Window("Mirror", size=(stimDisplayRes[0] / mirrorDownSize, stimDisplayRes[1] / mirrorDownSize), position=mirrorDisplayPosition, flags=sdl2.SDL_WINDOW_SHOWN) mirrorDisplaySurf = sdl2.SDL_GetWindowSurface(mirrorDisplay.window) mirrorDisplayArray = sdl2.ext.pixels3d(mirrorDisplaySurf.contents) # stimDisplay = sdl2.ext.Window("Calibration",size=stimDisplayRes,position=stimDisplayPosition,flags=sdl2.SDL_WINDOW_SHOWN|sdl2.SDL_WINDOW_FULLSCREEN_DESKTOP|sdl2.SDL_RENDERER_ACCELERATED | sdl2.SDL_RENDERER_PRESENTVSYNC) stimDisplay = sdl2.ext.Window( "Calibration", size=stimDisplayRes, position=stimDisplayPosition, flags=sdl2.SDL_WINDOW_SHOWN | sdl2.SDL_WINDOW_BORDERLESS | sdl2.SDL_RENDERER_ACCELERATED | sdl2.SDL_RENDERER_PRESENTVSYNC) stimDisplaySurf = sdl2.SDL_GetWindowSurface(stimDisplay.window) stimDisplayArray = sdl2.ext.pixels3d(stimDisplaySurf.contents) sdl2.SDL_PumpEvents() #to show the windows sdl2.SDL_PumpEvents() #to show the windows sdl2.SDL_PumpEvents() #to show the windows sdl2.SDL_PumpEvents() #to show the windows sdl2.SDL_PumpEvents() #to show the windows sdl2.SDL_PumpEvents() #to show the windows ######## #Perform some calculations to convert stimulus measurements in degrees to pixels ######## stimDisplayWidthInDegrees = math.degrees( math.atan((stimDisplayWidth / 2.0) / viewingDistance) * 2) PPD = stimDisplayRes[ 0] / stimDisplayWidthInDegrees #compute the pixels per degree (PPD) calibrationDotSize = int(calibrationDotSizeInDegrees * PPD) #initialize font sdl2.sdlttf.TTF_Init() font = sdl2.sdlttf.TTF_OpenFont('./pytracker/Resources/DejaVuSans.ttf', int(PPD) * 2) ######## # Define some useful colors for SDL2 ######## white = sdl2.pixels.SDL_Color(r=255, g=255, b=255, a=255) black = sdl2.pixels.SDL_Color(r=0, g=0, b=0, a=255) grey = sdl2.pixels.SDL_Color(r=127, g=127, b=127, a=255) lightGrey = sdl2.pixels.SDL_Color(r=200, g=200, b=200, a=255) def drawDot(loc): cy, cx = loc cx = stimDisplayRes[1] / 2 + cx cy = stimDisplayRes[0] / 2 + cy radius = calibrationDotSize / 2 y, x = numpy.ogrid[-radius:radius, -radius:radius] index = numpy.logical_and((x**2 + y**2) <= (radius**2), (x**2 + y**2) >= ((radius / 4)**2)) stimDisplayArray[(cy - radius):(cy + radius), (cx - radius):(cx + radius), ][index] = [ 255, 255, 255, 255 ] calibrationLocations = dict() calibrationLocations['CENTER'] = numpy.array([0, 0]) calibrationLocations['N'] = numpy.array( [0, int(0 - stimDisplayRes[1] / 2.0 + calibrationDotSize)]) calibrationLocations['S'] = numpy.array( [0, int(0 + stimDisplayRes[1] / 2.0 - calibrationDotSize)]) calibrationLocations['E'] = numpy.array( [int(0 - stimDisplayRes[0] / 2.0 + calibrationDotSize), 0]) calibrationLocations['W'] = numpy.array( [int(0 + stimDisplayRes[0] / 2.0 - calibrationDotSize), 0]) calibrationLocations['NE'] = numpy.array([ int(0 + stimDisplayRes[0] / 2.0 - calibrationDotSize), int(0 - stimDisplayRes[1] / 2.0 + calibrationDotSize) ]) calibrationLocations['SE'] = numpy.array([ int(0 + stimDisplayRes[0] / 2.0 - calibrationDotSize), int(0 + stimDisplayRes[1] / 2.0 - calibrationDotSize) ]) calibrationLocations['NW'] = numpy.array([ int(0 - stimDisplayRes[0] / 2.0 + calibrationDotSize), int(0 - stimDisplayRes[1] / 2.0 + calibrationDotSize) ]) calibrationLocations['SW'] = numpy.array([ int(0 - stimDisplayRes[0] / 2.0 + calibrationDotSize), int(0 + stimDisplayRes[1] / 2.0 - calibrationDotSize) ]) calibrationKey = { 'q': 'NW', 'w': 'N', 'e': 'NE', 'a': 'E', 's': 'CENTER', 'd': 'W', 'z': 'SW', 'x': 'S', 'c': 'SE' } # calibrationLocations['N2'] = numpy.array([0,int((0-stimDisplayRes[1]/2.0+calibrationDotSize)/2.0)]) # calibrationLocations['S2'] = numpy.array([0,int((0+stimDisplayRes[1]/2.0-calibrationDotSize)/2.0)]) # calibrationLocations['W2'] = numpy.array([int((0-stimDisplayRes[0]/2.0+calibrationDotSize)/2.0),0]) # calibrationLocations['E2'] = numpy.array([int((0+stimDisplayRes[0]/2.0-calibrationDotSize)/2.0),0]) # calibrationLocations['NE2'] = numpy.array([int((0+stimDisplayRes[0]/2.0-calibrationDotSize)/2.0),int((0-stimDisplayRes[1]/2.0+calibrationDotSize)/2.0)]) # calibrationLocations['SE2'] = numpy.array([int((0+stimDisplayRes[0]/2.0-calibrationDotSize)/2.0),int((0+stimDisplayRes[1]/2.0-calibrationDotSize)/2.0)]) # calibrationLocations['NW2'] = numpy.array([int((0-stimDisplayRes[0]/2.0+calibrationDotSize)/2.0),int((0-stimDisplayRes[1]/2.0+calibrationDotSize)/2.0)]) # calibrationLocations['SW2'] = numpy.array([int((0-stimDisplayRes[0]/2.0+calibrationDotSize)/2.0),int((0+stimDisplayRes[1]/2.0-calibrationDotSize)/2.0)]) #define a function that will kill everything safely def exitSafely(): qFrom.put(['stopQueing', getTime()]) sdl2.ext.quit() sys.exit() #define a function that waits for a given duration to pass def simpleWait(duration): start = getTime() while getTime() < (start + duration): sdl2.SDL_PumpEvents() #define a function to draw a numpy array on surface centered on given coordinates def blitArray(src, dst, xOffset=0, yOffset=0): x1 = dst.shape[0] / 2 + xOffset - src.shape[0] / 2 y1 = dst.shape[1] / 2 + yOffset - src.shape[1] / 2 x2 = x1 + src.shape[0] y2 = y1 + src.shape[1] dst[x1:x2, y1:y2, :] = src def blitSurf(srcSurf, dst, dstSurf, xOffset=0, yOffset=0): x = dst.size[0] / 2 + xOffset - srcSurf.w / 2 y = dst.size[1] / 2 + yOffset - srcSurf.h / 2 sdl2.SDL_BlitSurface(srcSurf, None, dstSurf, sdl2.SDL_Rect(x, y, srcSurf.w, srcSurf.h)) sdl2.SDL_UpdateWindowSurface( dst.window ) #should this really be here? (will it cause immediate update?) # sdl2.SDL_FreeSurface(srcSurf) #define a function that waits for a response def waitForResponse(): # sdl2.SDL_FlushEvents() done = False while not done: sdl2.SDL_PumpEvents() for event in sdl2.ext.get_events(): if event.type == sdl2.SDL_KEYDOWN: response = sdl2.SDL_GetKeyName( event.key.keysym.sym).lower() if response == 'escape': exitSafely() else: done = True # sdl2.SDL_FlushEvents() return response def refreshWindows(): stimDisplay.refresh() image = stimDisplayArray[:, :, 0:3] image = scipy.misc.imresize( image, (stimDisplayRes[0] / 2, stimDisplayRes[1] / 2), interp='nearest') mirrorDisplayArray[:, :, 0:3] = image mirrorDisplay.refresh() return None def clearScreen(color): sdl2.ext.fill(stimDisplaySurf.contents, color) def drawText(myText, myFont, textColor, textWidth=.9): lineHeight = sdl2.sdlttf.TTF_RenderText_Blended( myFont, 'T', textColor).contents.h textWidthMax = int(stimDisplay.size[0]) paragraphs = myText.splitlines() renderList = [] textHeight = 0 for thisParagraph in paragraphs: words = thisParagraph.split(' ') if len(words) == 1: renderList.append(words[0]) if (thisParagraph != paragraphs[len(paragraphs) - 1]): renderList.append(' ') textHeight = textHeight + lineHeight else: thisWordIndex = 0 while thisWordIndex < (len(words) - 1): lineStart = thisWordIndex lineWidth = 0 while (thisWordIndex < (len(words) - 1)) and (lineWidth <= textWidthMax): thisWordIndex = thisWordIndex + 1 lineWidth = sdl2.sdlttf.TTF_RenderText_Blended( myFont, ' '.join(words[lineStart:(thisWordIndex + 1)]), textColor).contents.w if thisWordIndex < (len(words) - 1): #last word went over, paragraph continues renderList.append(' '.join( words[lineStart:(thisWordIndex - 1)])) textHeight = textHeight + lineHeight thisWordIndex = thisWordIndex - 1 else: if lineWidth <= textWidthMax: #short final line renderList.append(' '.join( words[lineStart:(thisWordIndex + 1)])) textHeight = textHeight + lineHeight else: #full line then 1 word final line renderList.append(' '.join( words[lineStart:thisWordIndex])) textHeight = textHeight + lineHeight renderList.append(words[thisWordIndex]) textHeight = textHeight + lineHeight #at end of paragraph, check whether a inter-paragraph space should be added if (thisParagraph != paragraphs[len(paragraphs) - 1]): renderList.append(' ') textHeight = textHeight + lineHeight numLines = len(renderList) * 1.0 for thisLine in range(len(renderList)): thisRender = sdl2.sdlttf.TTF_RenderText_Blended( myFont, renderList[thisLine], textColor).contents x = int(stimDisplay.size[0] / 2.0 - thisRender.w / 2.0) y = int(stimDisplay.size[1] / 2.0 - thisRender.h / 2.0 + 1.0 * thisLine / numLines * textHeight) sdl2.SDL_BlitSurface( thisRender, None, stimDisplaySurf, sdl2.SDL_Rect(x, y, thisRender.w, thisRender.h)) sdl2.SDL_UpdateWindowSurface( stimDisplay.window ) #should this really be here? (will it cause immediate update?) #define a function that prints a message on the stimDisplay while looking for user input to continue. The function returns the total time it waited def showMessage(myText, lockWait=False): messageViewingTimeStart = getTime() clearScreen(black) refreshWindows() clearScreen(black) drawText(myText, font, lightGrey) simpleWait(0.500) refreshWindows() clearScreen(black) if lockWait: response = None while response not in ['return', 'y', 'n']: response = waitForResponse() else: response = waitForResponse() refreshWindows() clearScreen(black) simpleWait(0.500) messageViewingTime = getTime() - messageViewingTimeStart return [response, messageViewingTime] #define a function to show stimuli and collect calibration data def getCalibrationData(): if not manualCalibrationOrder: dotLocationList = ['q', 'w', 'e', 'a', 's', 'd', 'z', 'x', 'c'] random.shuffle(dotLocationList) done = False eyeData = [] coordsList = [] startTimes = [] stopTimes = [] qFrom.put('startQueing') while not done: if manualCalibrationOrder: dotLocation = waitForResponse() else: if len(dotLocationList) == 0: break else: dotLocation = dotLocationList.pop() if dotLocation == '0': phase1Done = True elif not dotLocation in calibrationKey: pass else: displayCoords = calibrationLocations[ calibrationKey[dotLocation]] coordsList.append(displayCoords / PPD) clearScreen(black) drawDot(displayCoords) refreshWindows() junk = waitForResponse() startTimes.append(getTime()) simpleWait(1) stopTimes.append(getTime()) while not qTo.empty(): eyeData.append(qTo.get()) clearScreen(black) refreshWindows() qFrom.put(['stopQueing', getTime()]) simpleWait(1) done = False while not done: if not qTo.empty(): message = qTo.get() if message == 'doneQueing': done = True else: eyeData.append(message) calibrationData = [] for i in range(len(startTimes)): temp = [[ list(coordsList[i])[0], list(coordsList[i])[1], 1.0, e[1], e[2], e[1] * e[2], e[3], e[4], e[3] * e[4] ] for e in eyeData if ((e[0] > startTimes[i]) and (e[0] < stopTimes[i]))] temp = [item for sublist in temp for item in sublist] calibrationData.append(temp) calibrationData = numpy.array( [item for sublist in calibrationData for item in sublist]) calibrationData = calibrationData.reshape( [len(calibrationData) / 9, 9]) return calibrationData #define a function to compute prediciton error def getErrors(calibrationData, xCoefLeft, xCoefRight, yCoefLeft, yCoefRight, leftCols, rightCols): xPredsLeft = xCoefLeft[0] + xCoefLeft[1] * calibrationData[:, leftCols[ 1]] + xCoefLeft[2] * calibrationData[:, leftCols[2]] + xCoefLeft[ 3] * calibrationData[:, leftCols[3]] yPredsLeft = yCoefLeft[0] + yCoefLeft[1] * calibrationData[:, leftCols[ 1]] + yCoefLeft[2] * calibrationData[:, leftCols[2]] + yCoefLeft[ 3] * calibrationData[:, leftCols[3]] xPredsRight = xCoefRight[ 0] + xCoefRight[1] * calibrationData[:, rightCols[1]] + xCoefRight[ 2] * calibrationData[:, rightCols[2]] + xCoefRight[ 3] * calibrationData[:, rightCols[3]] yPredsRight = yCoefRight[ 0] + yCoefRight[1] * calibrationData[:, rightCols[1]] + yCoefRight[ 2] * calibrationData[:, rightCols[2]] + yCoefRight[ 3] * calibrationData[:, rightCols[3]] xPreds = (xPredsLeft + xPredsRight) / 2 yPreds = (yPredsLeft + yPredsRight) / 2 xError = numpy.mean((xPreds - calibrationData[:, 0])**2)**.5 yError = numpy.mean((yPreds - calibrationData[:, 1])**2)**.5 totError = numpy.mean((((xPreds - calibrationData[:, 0])**2) + (yPreds - calibrationData[:, 1])**2)**.5) return [xError, yError, totError] #start calibration done = False while not done: showMessage('When you are ready to begin calibration, press any key.') calibrationData = getCalibrationData() leftCols = [2, 3, 4, 5] rightCols = [2, 6, 7, 8] xCoefLeft = numpy.linalg.lstsq(calibrationData[:, leftCols], calibrationData[:, 0])[0] xCoefRight = numpy.linalg.lstsq(calibrationData[:, rightCols], calibrationData[:, 0])[0] yCoefLeft = numpy.linalg.lstsq(calibrationData[:, leftCols], calibrationData[:, 1])[0] yCoefRight = numpy.linalg.lstsq(calibrationData[:, rightCols], calibrationData[:, 1])[0] xError, yError, totError = getErrors(calibrationData, xCoefLeft, xCoefRight, yCoefLeft, yCoefRight, leftCols, rightCols) showMessage('Calibration results:\nx = ' + str(xError) + '\ny = ' + str(yError) + '\nz = ' + str(totError) + '\nPress any key to validate calibration.') validationData = getCalibrationData() xError, yError, totError = getErrors(validationData, xCoefLeft, xCoefRight, yCoefLeft, yCoefRight, leftCols, rightCols) done2 = False while not done2: response = showMessage( 'Validation results:\nx = ' + str(xError) + '\ny = ' + str(yError) + '\nz = ' + str(totError) + '\nExperimenter: Press "a" to accept calibration, or "r" to repeat calibration.' ) if response[0] == 'a': qFrom.put([ 'calibrationCoefs', [xCoefLeft, xCoefRight, yCoefLeft, yCoefRight] ]) done = True done2 = True elif response[0] == 'r': done2 = True exitSafely()
import math # Connect to the controller (joystick) using SDL sdl2.SDL_Init(sdl2.SDL_INIT_JOYSTICK) joystick = sdl2.SDL_JoystickOpen(0) # Open the NXT Bluetooth connection conn = Connection('/dev/tty.NXT-DevB') # Helper function to limit the range of a value def clamp(n, minn, maxn): return max(min(maxn, n), minn) # Main loop while True: sdl2.SDL_PumpEvents() # Read controller stick Y-inputs and normalize the value # to a range of -100 to 100 joy_1 = -sdl2.SDL_JoystickGetAxis(joystick, 1) / 327.67 joy_3 = -sdl2.SDL_JoystickGetAxis(joystick, 3) / 327.67 m1 = clamp(joy_1,-100,100) m2 = clamp(joy_3,-100,100) # Check left/right triggers (10,11) for 3rd motor control if sdl2.SDL_JoystickGetButton(joystick, 10): m0 = -30 elif sdl2.SDL_JoystickGetButton(joystick, 11): m0 = 30 else: m0 = 0 # Print values to console (mostly for debugging)
def display_init(diagonal_in): """Initializes the display and rendering backend, calculating and assigning the values of runtime KLParams variables related to the screen (e.g. P.screen_c, P.refresh_rate, P.pixels_per_degree). Called by 'klibs run' on launch, for internal use only. Args: diagonal_in (float): The size of the monitor in diagonal inches (e.g. 13 for a 13-inch MacBook Pro). """ if os.name == 'nt': # set video driver explicitly on Windows to avoid misdetection problems os.environ['SDL_VIDEODRIVER'] = 'windows' sdl2.SDL_Init(sdl2.SDL_INIT_VIDEO) sdl2.mouse.SDL_ShowCursor(sdl2.SDL_DISABLE) sdl2.SDL_PumpEvents() display_mode = sdl2.video.SDL_DisplayMode() sdl2.SDL_GetCurrentDisplayMode(0, display_mode) P.screen_x = display_mode.w P.screen_y = display_mode.h P.screen_c = (P.screen_x // 2, P.screen_y // 2) P.screen_x_y = (P.screen_x, P.screen_y) P.refresh_rate = float(display_mode.refresh_rate) if P.refresh_rate == 0: P.refresh_rate = 60.0 print( "\tWarning: Unable to detect your monitor's refresh rate, defaulting to 60Hz." ) elif P.refresh_rate == 59: P.refresh_rate = 59.94 # fix for some Windows monitors P.refresh_time = 1000.0 / P.refresh_rate #TODO: figure out what's actually needed for multi-monitor support for d in P.additional_displays: if d[2]: P.screen_x_y = list(d[1]) P.screen_x = d[1][0] P.screen_y = d[1][1] if P.screen_origin is None: P.screen_origin = (0, 0) # Get conversion factor for pixels to degrees of visual angle based on viewing distance, # screen resolution, and given diagonal screen size P.screen_diagonal_in = diagonal_in P.screen_diagonal_px = sqrt(P.screen_x**2.0 + P.screen_y**2.0) P.ppi = P.screen_diagonal_px / diagonal_in P.monitor_height = P.screen_y / P.ppi P.monitor_width = P.screen_x / P.ppi P.screen_degrees_x = degrees(2 * atan( (2.54 * P.monitor_width / 2.0) / P.view_distance)) P.screen_degrees_y = degrees(2 * atan( (2.54 * P.monitor_height / 2.0) / P.view_distance)) P.pixels_per_degree = P.screen_x / P.screen_degrees_x P.ppd = P.pixels_per_degree # alias for convenience # Create the SDL window object and configure it properly for OpenGL (code from Mike) SCREEN_FLAGS = (sdl2.SDL_WINDOW_SHOWN | sdl2.SDL_WINDOW_FULLSCREEN_DESKTOP | sdl2.SDL_WINDOW_OPENGL | sdl2.SDL_WINDOW_ALLOW_HIGHDPI) window = sdl2.ext.Window(P.project_name, P.screen_x_y, P.screen_origin, SCREEN_FLAGS) sdl2.SDL_GL_CreateContext(window.window) sdl2.SDL_GL_SetSwapInterval(1) # enforce vsync gl.glMatrixMode(gl.GL_PROJECTION) gl.glLoadIdentity() gl.glOrtho(0, P.screen_x, P.screen_y, 0, 0, 1) gl.glMatrixMode(gl.GL_MODELVIEW) gl.glDisable(gl.GL_DEPTH_TEST) # Clear the SDL event queue and open the window, returning the window object sdl2.SDL_PumpEvents() sdl2.mouse.SDL_ShowCursor(sdl2.SDL_DISABLE) window.show() P.display_initialized = True return window
def simpleWait(duration): start = getTime() while getTime() < (start + duration): sdl2.SDL_PumpEvents()
def trackerChildFunction(qTo, qFrom, camIndex=0, camRes=[1920, 1080], previewDownsize=2, previewLoc=[0, 0], faceDetectionScale=10, eyeDetectionScale=5, timestampMethod=0, viewingDistance=100, stimDisplayWidth=100, stimDisplayRes=[1920, 1080], stimDisplayPosition=[0, 0], mirrorDisplayPosition=[0, 0], mirrorDownSize=2, manualCalibrationOrder=True, calibrationDotSizeInDegrees=.5): import fileForker import numpy import cv2 import scipy.ndimage.filters # import scipy.interpolate import sys import sdl2 import sdl2.ext import sdl2.sdlmixer #define a class for a clickable text UI class clickableText: def __init__(self, x, y, text, rightJustified=False, valueText=''): self.x = x self.y = y self.text = text self.rightJustified = rightJustified self.valueText = valueText self.isActive = False self.clicked = False self.updateSurf() def updateSurf(self): if self.isActive: self.surf = sdl2.sdlttf.TTF_RenderText_Blended_Wrapped( font, self.text + self.valueText, sdl2.pixels.SDL_Color(r=0, g=255, b=255, a=255), previewWindow.size[0]).contents else: self.surf = sdl2.sdlttf.TTF_RenderText_Blended_Wrapped( font, self.text + self.valueText, sdl2.pixels.SDL_Color(r=0, g=0, b=255, a=255), previewWindow.size[0]).contents def checkIfActive(self, event): if self.rightJustified: xLeft = self.x - self.surf.w xRight = self.x else: xLeft = self.x xRight = self.x + self.surf.w if (event.button.x > xLeft) & (event.button.x < xRight) & ( event.button.y > self.y) & (event.button.y < (self.y + fontSize)): self.isActive = True else: self.isActive = False self.updateSurf() def draw(self, targetWindowSurf): if self.rightJustified: sdl2.SDL_BlitSurface( self.surf, None, targetWindowSurf, sdl2.SDL_Rect(self.x - self.surf.w, self.y, self.surf.w, self.surf.h)) else: sdl2.SDL_BlitSurface( self.surf, None, targetWindowSurf, sdl2.SDL_Rect(self.x, self.y, self.surf.w, self.surf.h)) #define a class for settings class settingText(clickableText): def __init__(self, value, x, y, text, rightJustified=False): self.value = value self.valueText = str(value) clickableText.__init__(self, x, y, text, rightJustified, self.valueText) def addValue(self, toAdd): self.valueText = self.valueText + toAdd self.updateSurf() def delValue(self): if self.valueText != '': self.valueText = self.valueText[0:(len(self.valueText) - 1)] self.updateSurf() def finalizeValue(self): try: self.value = int(self.valueText) except: print 'Non-numeric value entered!' #define a class for dots class dotObj: def __init__(self, name, isFid, fid, xPixel, yPixel, radiusPixel, blinkCriterion, blurSize, filterSize): self.name = name self.isFid = isFid self.x = xPixel self.y = yPixel self.radius = radiusPixel self.first = True self.last = [self.x, self.y, self.radius] self.lost = False self.blinkHappened = False self.radii = [] self.SDs = [] self.lostCount = 0 self.blinkCriterion = blinkCriterion self.blurSize = blurSize self.filterSize = filterSize self.setPixels() if not self.isFid: self.makeRelativeToFid(fid) def setPixels(self): self.xPixel = int(self.x) self.yPixel = int(self.y) self.radiusPixel = int(self.radius) return None def makeRelativeToFid(self, fid): self.x2 = (self.x - fid.x) / fid.radius self.y2 = (self.y - fid.y) / fid.radius self.radius2 = self.radius / fid.radius return None def getDarkEllipse(self, img): #if not self.isFid: # #cv2.imwrite(self.name + "_" + "%.2d" % imageNum + "_raw.png" , img) try: smoothedImg = cv2.GaussianBlur(img, (self.blurSize, self.blurSize), 0) #if not self.isFid: # #cv2.imwrite(self.name + "_" + "%.2d" % imageNum + "_smoothed.png" , img) except: print 'cv2.GaussianBlur failed' # cv2.imwrite('temp.png',img) return None try: dataMin = scipy.ndimage.filters.minimum_filter( smoothedImg, self.filterSize) except: print 'scipy.ndimage.filters.minimum_filter failed' # cv2.imwrite('temp.png',img) return None if dataMin != None: try: minLocs = numpy.where( dataMin < (numpy.min(dataMin) + numpy.std(dataMin))) except: print 'numpy.where failed' # cv2.imwrite('temp.png',img) return None if len(minLocs[0]) >= 5: try: ellipse = cv2.fitEllipse( numpy.reshape( numpy.column_stack((minLocs[1], minLocs[0])), (len(minLocs[0]), 1, 2))) except: print 'cv2.fitEllipse failed' # cv2.imwrite('temp.png',img) return None return ellipse def cropImage(self, img, cropSize): xLo = self.xPixel - cropSize if xLo < 0: xLo = 0 xHi = self.xPixel + cropSize if xHi > img.shape[1]: xHi = img.shape[1] yLo = self.yPixel - cropSize if yLo < 0: yLo = 0 yHi = self.yPixel + cropSize if yHi > img.shape[0]: yHi = img.shape[0] return [img[yLo:yHi, xLo:xHi], xLo, xHi, yLo, yHi] def search(self, img): if self.first and self.isFid: searchSize = 1 elif self.lost: searchSize = 5 else: searchSize = 3 if self.first: self.first = False img, xLo, xHi, yLo, yHi = self.cropImage(img=img, cropSize=searchSize * self.radiusPixel) self.ellipse = self.getDarkEllipse(img=img) if self.ellipse != None: self.ellipse = ((self.ellipse[0][0] + xLo, self.ellipse[0][1] + yLo), self.ellipse[1], self.ellipse[2]) self.lost = False self.x = self.ellipse[0][0] self.y = self.ellipse[0][1] self.major = self.ellipse[1][0] self.minor = self.ellipse[1][1] self.angle = self.ellipse[2] self.radius = (self.ellipse[1][0] + self.ellipse[1][1]) / 4 self.setPixels() else: self.lost = True def checkSearch(self): self.medianRadius = numpy.median(self.radii) self.critRadius = 10 * ((numpy.median( (self.radii - self.medianRadius)**2))**.5) #print [self.name, self.radius2,(self.radius2<(1/6)) , (self.radius2>2)] if len(self.radii) < 30: self.radii.append(self.radius2) self.lost = False else: #fid diameter is 6mm, so range from 1mm to 12mm #if (self.radius2<(1/6)) or (self.radius2>2) or (self.radius2<(self.medianRadius - self.critRadius)) or (self.radius2>(self.medianRadius + self.critRadius)): if (self.radius2 < (1 / 6)) or (self.radius2 > 2): self.lost = True else: self.lost = False self.radii.append(self.radius2) if len(self.radii) >= 300: self.radii.pop() def checkSD(self, img, fid): self.obsSD = numpy.std( self.cropImage(img=img, cropSize=5 * fid.radiusPixel)[0]) self.medianSD = numpy.median(self.SDs) self.critSD = self.medianSD * self.blinkCriterion #print [self.name,self.obsSD,self.medianSD,self.critSD,self.blinkCriterion] if len(self.SDs) < 30: self.SDs.append(self.obsSD) self.blinkHappened = False else: if (self.obsSD < self.critSD): self.blinkHappened = True else: self.SDs.append(self.obsSD) self.blinkHappened = False if len(self.SDs) >= 300: self.SDs.pop() def update(self, img, fid, blinkCriterion, blurSize, filterSize): self.blinkCriterion = blinkCriterion self.blurSize = blurSize self.filterSize = filterSize self.last = [self.x, self.y, self.radius] if self.isFid: self.search(img=img) else: self.checkSD( img=img, fid=fid ) #alters the value of self.blinkHappened, amongst other things if self.blinkHappened: self.x, self.y, self.radius = self.last self.setPixels() self.makeRelativeToFid(fid) else: self.search( img=img ) #alters the value of self.lost, amongst other things if self.lost: self.x, self.y, self.radius = self.last self.setPixels() self.makeRelativeToFid(fid) else: self.makeRelativeToFid(fid=fid) self.checkSearch( ) #alters the value of self.lost, among other things if self.lost: self.x, self.y, self.radius = self.last self.setPixels() self.makeRelativeToFid(fid) if self.lost and not self.blinkHappened: self.lostCount += 1 else: self.lostCount = 0 ######## # Initialize audio and define a class that handles playing sounds in PySDL2 ######## sdl2.SDL_Init(sdl2.SDL_INIT_AUDIO) sdl2.sdlmixer.Mix_OpenAudio(44100, sdl2.sdlmixer.MIX_DEFAULT_FORMAT, 2, 1024) class Sound: def __init__(self, fileName): self.sample = sdl2.sdlmixer.Mix_LoadWAV( sdl2.ext.compat.byteify(fileName, "utf-8")) self.started = False def play(self): self.channel = sdl2.sdlmixer.Mix_PlayChannel(-1, self.sample, 0) self.started = True def stillPlaying(self): if self.started: if sdl2.sdlmixer.Mix_Playing(self.channel): return True else: self.started = False return False ######## # define some useful functions ######## #define a function to exit safely def exitSafely(): qFrom.put('done') sys.exit() #define a function to rescale def rescaleBiggestHaar(detected, scale, addToX=0, addToY=0): x, y, w, h = detected[numpy.argmax( [numpy.sqrt(w * w + h * h) for x, y, w, h in detected])] return [x * scale + addToX, y * scale + addToY, w * scale, h * scale] ######## # Initialize variables ######## #initialize sounds blinkSound = Sound('./pytracker/Resources/sounds/beep.wav') saccadeSound = Sound('./pytracker/Resources/sounds/stop.wav') #specify the getTime function if (timestampMethod == 0) or (timestampMethod == 1): #initialize timer sdl2.SDL_Init(sdl2.SDL_INIT_TIMER) if timestampMethod == 0: #define a function to use the high-precision timer, returning a float in seconds def getTime(): return sdl2.SDL_GetPerformanceCounter( ) * 1.0 / sdl2.SDL_GetPerformanceFrequency() elif timestampMethod == 1: #use the SDL_GetTicks timer def getTime(): return sdl2.SDL_GetTicks() / 1000.0 elif timestampMethod == 2: #use time.time import time getTime = time.time #initialize font fontSize = camRes[1] / previewDownsize / 10 sdl2.sdlttf.TTF_Init() font = sdl2.sdlttf.TTF_OpenFont('./pytracker/Resources/DejaVuSans.ttf', fontSize) #initialize preview video sdl2.SDL_Init(sdl2.SDL_INIT_VIDEO) previewWindow = sdl2.ext.Window("Preview", size=(camRes[0] / previewDownsize, camRes[1] / previewDownsize), position=previewLoc, flags=sdl2.SDL_WINDOW_SHOWN) previewWindowSurf = sdl2.SDL_GetWindowSurface(previewWindow.window) previewWindowArray = sdl2.ext.pixels3d(previewWindowSurf.contents) sdl2.ext.fill(previewWindowSurf.contents, sdl2.pixels.SDL_Color(r=255, g=255, b=255, a=255)) previewWindow.refresh() lastRefreshTime = getTime() #initialize the settings window settingsWindow = sdl2.ext.Window( "Settings", size=(camRes[0] / previewDownsize, camRes[1] / previewDownsize), position=[ previewLoc[0] + camRes[0] / previewDownsize + 1, previewLoc[1] ]) settingsWindowSurf = sdl2.SDL_GetWindowSurface(settingsWindow.window) settingsWindowArray = sdl2.ext.pixels3d(settingsWindowSurf.contents) sdl2.ext.fill(settingsWindowSurf.contents, sdl2.pixels.SDL_Color(r=0, g=0, b=0, a=255)) settingsWindow.hide() settingsWindow.refresh() #import the haar cascades faceCascade = cv2.CascadeClassifier( './pytracker/Resources/cascades/haarcascade_frontalface_alt2.xml') eyeLeftCascade = cv2.CascadeClassifier( './pytracker/Resources/cascades/LEye18x12.1.xml') eyeRightCascade = cv2.CascadeClassifier( './pytracker/Resources/cascades/REye18x12.1.xml') #create some settings settingsDict = {} settingsDict['blink'] = settingText(value=75, x=fontSize, y=fontSize, text='Blink (0-100) = ') settingsDict['blur'] = settingText(value=3, x=fontSize, y=fontSize * 2, text='Blur (0-; odd only) = ') settingsDict['filter'] = settingText(value=3, x=fontSize, y=fontSize * 3, text='Filter (0-; odd only) = ') settingsDict['saccade0'] = settingText(value=50, x=fontSize, y=fontSize * 4, text='Saccade (0-) = ') settingsDict['saccade'] = settingText(value=1, x=fontSize, y=fontSize * 5, text='Calibrated Saccade (0-) = ') #create some text UIs clickableTextDict = {} clickableTextDict['manual'] = clickableText(x=0, y=0, text='Manual') clickableTextDict['auto'] = clickableText(x=0, y=fontSize, text='Auto') clickableTextDict['calibrate'] = clickableText(x=0, y=previewWindow.size[1] - fontSize, text='Calibrate') clickableTextDict['settings'] = clickableText(x=previewWindow.size[0], y=0, text='Settings', rightJustified=True) clickableTextDict['lag'] = clickableText(x=previewWindow.size[0], y=previewWindow.size[1] - fontSize * 2, text='Lag: ', rightJustified=True) clickableTextDict['f2f'] = clickableText(x=previewWindow.size[0], y=previewWindow.size[1] - fontSize, text='Frame-to-frame: ', rightJustified=True) #initialize variables previewInFocus = True settingsInFocus = False lastTime = 0 dotList = [] lastLocs = [None, None] displayLagList = [] frameToFrameTimeList = [] doHaar = False clickingForDots = False calibrating = False doneCalibration = False doSounds = True queueDataToParent = False #set dummy calibration coefficients (yields untransformed pixel locs) calibrationCoefs = [[0, 1, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 1, 0]] ######## # Initialize camera ######## vc = cv2.VideoCapture(camIndex) vc.set(cv2.cv.CV_CAP_PROP_FRAME_WIDTH, camRes[0]) vc.set(cv2.cv.CV_CAP_PROP_FRAME_HEIGHT, camRes[1]) imageNum = 0 #start the loop while True: #poll the camera t1 = getTime() #time right before requesting the image _, image = vc.read() #request the image t2 = getTime() #time right after requesting the image imageTime = t1 + ( t2 - t1 ) / 2.0 #timestamp the image as halfway between times before and after request image = image[:, :, 2] #grab red channel (image is BGR) imageNum += 1 #iterate the image number #check for messages from the main process if not qTo.empty(): message = qTo.get() if message == 'quit': exitSafely() #process input sdl2.SDL_PumpEvents() for event in sdl2.ext.get_events(): if event.type == sdl2.SDL_WINDOWEVENT: targetWindow = sdl2.SDL_GetWindowFromID(event.window.windowID) title = sdl2.SDL_GetWindowTitle(targetWindow) # if event.window.event==sdl2.SDL_WINDOWEVENT_FOCUS_GAINED: # print title + "focused" # if event.window.event==sdl2.SDL_WINDOWEVENT_ENTER: # print title + " entered" # elif event.window.event==sdl2.SDL_WINDOWEVENT_FOCUS_LOST: # print title + " lost focus" if event.window.event == sdl2.SDL_WINDOWEVENT_LEAVE: if title == 'Preview': previewInFocus = False settingsInFocus = True if (event.window.event == sdl2.SDL_WINDOWEVENT_FOCUS_GAINED ) or (event.window.event == sdl2.SDL_WINDOWEVENT_ENTER): if title == 'Preview': previewInFocus = True settingsInFocus = False elif title == 'Settings': previewInFocus = False settingsInFocus = True elif (event.window.event == sdl2.SDL_WINDOWEVENT_CLOSE): if title == 'Preview': exitSafely() elif title == 'Settings': previewInFocus = True settingsInFocus = False settingsWindow.hide() previewWindow.show() elif settingsInFocus: # if event.type==sdl2.SDL_MOUSEBUTTONUP: # if blinkTextButtonDown: # blinkTextButtonDown = False # if event.type==sdl2.SDL_MOUSEBUTTONDOWN: # if mouseInBlinkText: # blinkTextButtonDown = True if event.type == sdl2.SDL_MOUSEMOTION: alreadyClicked = False for setting in settingsDict: if (settingsDict[setting].isActive) and ( settingsDict[setting].clicked): alreadyClicked = True if not alreadyClicked: for setting in settingsDict: settingsDict[setting].checkIfActive(event) elif event.type == sdl2.SDL_MOUSEBUTTONDOWN: alreadyClicked = False for setting in settingsDict: if (settingsDict[setting].isActive) and ( settingsDict[setting].clicked): alreadyClicked = True if not alreadyClicked: for setting in settingsDict: if settingsDict[setting].isActive: settingsDict[setting].clicked = True elif event.type == sdl2.SDL_KEYDOWN: key = sdl2.SDL_GetKeyName(event.key.keysym.sym).lower() if key == 'backspace': for setting in settingsDict: if (settingsDict[setting].isActive) and ( settingsDict[setting].clicked): settingsDict[setting].delValue() elif key == 'return': for setting in settingsDict: if (settingsDict[setting].isActive) and ( settingsDict[setting].clicked): settingsDict[setting].finalizeValue() settingsDict[setting].clicked = False else: for setting in settingsDict: if (settingsDict[setting].isActive) and ( settingsDict[setting].clicked): settingsDict[setting].addValue(key) elif previewInFocus: if event.type == sdl2.SDL_KEYDOWN: key = sdl2.SDL_GetKeyName(event.key.keysym.sym).lower() if key == 'escape': #exit # exitSafely() clickingForDots = False clickingForFid = False definingFidFinderBox = False dotList = [] if event.type == sdl2.SDL_MOUSEMOTION: if clickingForDots: clickableTextDict[ 'manual'].isActive = True #just making sure if definingFidFinderBox: fidFinderBoxSize = abs(fidFinderBoxX - (previewWindow.size[0] - event.button.x)) else: for clickableText in clickableTextDict: if not (clickableText in ['lag', 'f2f']): clickableTextDict[clickableText].checkIfActive( event) if event.type == sdl2.SDL_MOUSEBUTTONDOWN: if clickingForDots: if clickingForFid: if not definingFidFinderBox: definingFidFinderBox = True fidFinderBoxX = previewWindow.size[ 0] - event.button.x fidFinderBoxY = event.button.y fidFinderBoxSize = 0 else: definingFidFinderBox = False clickingForFid = False fidFinderBoxSize = abs(fidFinderBoxX - (previewWindow.size[0] - event.button.x)) dotList.append( dotObj( name='fid', isFid=True, fid=None, xPixel=fidFinderBoxX * previewDownsize, yPixel=fidFinderBoxY * previewDownsize, radiusPixel=fidFinderBoxSize * previewDownsize, blinkCriterion=settingsDict['blink']. value / 100.0, blurSize=settingsDict['blur'].value, filterSize=settingsDict['filter'].value )) else: clickX = (previewWindow.size[0] - event.button.x) clickY = event.button.y if len(dotList) == 1: dotList.append( dotObj( name='left', isFid=False, fid=dotList[0], xPixel=clickX * previewDownsize, yPixel=clickY * previewDownsize, radiusPixel=dotList[0].radiusPixel, blinkCriterion=settingsDict['blink']. value / 100.0, blurSize=settingsDict['blur'].value, filterSize=settingsDict['filter'].value )) else: dotList.append( dotObj( name='right', isFid=False, fid=dotList[0], xPixel=clickX * previewDownsize, yPixel=clickY * previewDownsize, radiusPixel=dotList[1].radiusPixel, blinkCriterion=settingsDict['blink']. value / 100.0, blurSize=settingsDict['blur'].value, filterSize=settingsDict['filter'].value )) clickingForDots = False manTextSurf = sdl2.sdlttf.TTF_RenderText_Blended_Wrapped( font, 'Manual', sdl2.pixels.SDL_Color(r=0, g=0, b=255, a=255), previewWindow.size[0]).contents else: if clickableTextDict['settings'].isActive: if (sdl2.SDL_GetWindowFlags(settingsWindow.window) & sdl2.SDL_WINDOW_SHOWN): settingsWindow.hide() else: settingsWindow.show() elif clickableTextDict['auto'].isActive: waitingforHaar = False doHaar = True #triggers haar detection for next frame dotList = [] elif clickableTextDict['manual'].isActive: clickingForDots = True clickingForFid = True definingFidFinderBox = False dotList = [] elif clickableTextDict['calibrate'].isActive: doneCalibration = False calibrationChild = fileForker.childClass( childFile='calibrationChild') calibrationChild.initDict[ 'timestampMethod'] = timestampMethod calibrationChild.initDict[ 'viewingDistance'] = viewingDistance calibrationChild.initDict[ 'stimDisplayWidth'] = stimDisplayWidth calibrationChild.initDict[ 'stimDisplayRes'] = stimDisplayRes calibrationChild.initDict[ 'stimDisplayPosition'] = stimDisplayPosition calibrationChild.initDict[ 'mirrorDisplayPosition'] = mirrorDisplayPosition calibrationChild.initDict[ 'mirrorDownSize'] = mirrorDownSize calibrationChild.initDict[ 'calibrationDotSizeInDegrees'] = calibrationDotSizeInDegrees calibrationChild.initDict[ 'manualCalibrationOrder'] = manualCalibrationOrder calibrationChild.start() calibrating = True checkCalibrationStopTime = False queueDataToCalibrationChild = False #do haar detection if requested if doHaar: doHaar = False #only enter this section once faceDetectionImage = cv2.resize( image, dsize=(image.shape[1] / faceDetectionScale, image.shape[0] / faceDetectionScale), interpolation=cv2.INTER_NEAREST) detectedFaces = faceCascade.detectMultiScale( faceDetectionImage ) #,scaleFactor=1.1,minNeighbors=3,minSize=(10,10)) if len(detectedFaces) == 0: #no faces found! print 'no faces found!' #do something here else: faceX, faceY, faceW, faceH = rescaleBiggestHaar( detected=detectedFaces, scale=faceDetectionScale, addToX=0, addToY=0) leftFaceImage = image[faceY:(faceY + faceH), faceX:(faceX + faceW / 2)] eyeLeftDetectionImage = cv2.resize( leftFaceImage, dsize=(leftFaceImage.shape[1] / eyeDetectionScale, leftFaceImage.shape[0] / eyeDetectionScale), interpolation=cv2.INTER_NEAREST) detectedEyeLefts = eyeLeftCascade.detectMultiScale( eyeLeftDetectionImage ) #,minSize=(leftFaceImage.shape[0]/8,leftFaceImage.shape[0]/8)) rightFaceImage = image[faceY:(faceY + faceH), (faceX + faceW / 2):(faceX + faceW)] eyeRightDetectionImage = cv2.resize( rightFaceImage, dsize=(rightFaceImage.shape[1] / eyeDetectionScale, rightFaceImage.shape[0] / eyeDetectionScale), interpolation=cv2.INTER_NEAREST) detectedEyeRights = eyeRightCascade.detectMultiScale( eyeRightDetectionImage ) #,minSize=(rightFaceImage.shape[0]/8,rightFaceImage.shape[0]/8)) if (len(detectedEyeLefts) == 0) | (len(detectedEyeRights) == 0): #at least one eye is missing! if (len(detectedEyeLefts) == 0): print 'left eye missing' #do something here else: print 'right eye missing' #do something here else: eyeLeftX, eyeLeftY, eyeLeftW, eyeLeftH = rescaleBiggestHaar( detected=detectedEyeLefts, scale=eyeDetectionScale, addToX=faceX, addToY=faceY) eyeRightX, eyeRightY, eyeRightW, eyeRightH = rescaleBiggestHaar( detected=detectedEyeRights, scale=eyeDetectionScale, addToX=faceX + faceW / 2, addToY=faceY) #initialize fid dotList.append( dotObj(name='fid', isFid=True, fid=None, xPixel=faceX + faceW / 2, yPixel=(faceY + (eyeLeftY + eyeRightY) / 2) / 2, radiusPixel=(eyeLeftH + eyeRightH) / 4, blinkCriterion=settingsDict['blink'].value / 100.0, blurSize=settingsDict['blur'].value, filterSize=settingsDict['filter'].value)) #initialize left dotList.append( dotObj(name='left', isFid=False, fid=dotList[0], xPixel=eyeLeftX + eyeLeftW / 2, yPixel=eyeLeftY + eyeLeftH / 2, radiusPixel=eyeLeftH / 2, blinkCriterion=settingsDict['blink'].value / 100.0, blurSize=settingsDict['blur'].value, filterSize=settingsDict['filter'].value)) #initialize right dotList.append( dotObj(name='right', isFid=False, fid=dotList[0], xPixel=eyeRightX + eyeRightW / 2, yPixel=eyeRightY + eyeRightH / 2, radiusPixel=eyeRightH / 2, blinkCriterion=settingsDict['blink'].value / 100.0, blurSize=settingsDict['blur'].value, filterSize=settingsDict['filter'].value)) #update the dots given the latest image for i in range(len(dotList)): dotList[i].update(img=image, fid=dotList[0], blinkCriterion=settingsDict['blink'].value / 100.0, blurSize=settingsDict['blur'].value, filterSize=settingsDict['filter'].value) # print 'ok' #some post-processing blinkHappened = False saccadeHappened = False if len(dotList) == 3: if dotList[0].lost: dotList = [] print 'fid lost' elif (dotList[1].lostCount > 30) or (dotList[2].lostCount > 30): print "lost lots" if (not dotList[1].blinkHappened) and ( not dotList[2].blinkHappened ): #only reset if not blinking dotList = [] elif dotList[1].blinkHappened and dotList[2].blinkHappened: blinkHappened = True else: #compute gaze location to check for saccades xCoefLeft, xCoefRight, yCoefLeft, yCoefRight = calibrationCoefs if dotList[1].lost: #left missing, use right xLoc = xCoefRight[0] + xCoefRight[1] * dotList[ 2].x2 + xCoefRight[2] * dotList[2].y2 + xCoefRight[ 3] * dotList[2].y2 * dotList[2].x2 yLoc = yCoefRight[0] + yCoefRight[1] * dotList[ 2].x2 + yCoefRight[2] * dotList[2].y2 + yCoefRight[ 3] * dotList[2].y2 * dotList[2].x2 elif dotList[2].lost: #right missing, use left xLoc = xCoefLeft[0] + xCoefLeft[1] * dotList[ 1].x2 + xCoefLeft[2] * dotList[1].y2 + xCoefLeft[ 3] * dotList[2].y2 * dotList[1].x2 yLoc = yCoefLeft[0] + yCoefLeft[1] * dotList[ 1].x2 + yCoefLeft[2] * dotList[1].y2 + yCoefLeft[ 3] * dotList[2].y2 * dotList[1].x2 elif dotList[1].lost and dotList[ 2].lost: #both missing, use last xLoc = lastLocs[0] yLoc = lastLocs[1] else: #both present, use average xLocLeft = xCoefLeft[0] + xCoefLeft[1] * dotList[ 1].x2 + xCoefLeft[2] * dotList[1].y2 + xCoefLeft[ 3] * dotList[1].y2 * dotList[1].x2 yLocLeft = yCoefLeft[0] + yCoefLeft[1] * dotList[ 1].x2 + yCoefLeft[2] * dotList[1].y2 + yCoefLeft[ 3] * dotList[1].y2 * dotList[1].x2 xLocRight = xCoefRight[0] + xCoefRight[1] * dotList[ 2].x2 + xCoefRight[2] * dotList[2].y2 + xCoefRight[ 3] * dotList[2].y2 * dotList[2].x2 yLocRight = yCoefRight[0] + yCoefRight[1] * dotList[ 2].x2 + yCoefRight[2] * dotList[2].y2 + yCoefRight[ 3] * dotList[2].y2 * dotList[2].x2 xLoc = (xLocLeft + xLocRight) / 2.0 yLoc = (yLocLeft + yLocRight) / 2.0 if None not in lastLocs: locDiff = (((xLoc - lastLocs[0])**2) + ((yLoc - lastLocs[1])**2))**.5 if doneCalibration: saccadeCriterion = settingsDict['saccade'].value else: saccadeCriterion = settingsDict[ 'saccade0'].value / 100.0 if locDiff > saccadeCriterion: saccadeHappened = True lastLocs = [xLoc, yLoc] if queueDataToParent: qFrom.put([ 'eyeData', [ str.format('{0:.3f}', imageTime), xLoc, yLoc, dotlist[1].radius2, dotlist[2].radius2, saccadeHappened, blinkHappened, dotList[1].lost, dotList[2].lost, dotList[1].blinkHappened, dotList[2].blinkHappened ] ]) #play sounds as necessary if doSounds: if (not saccadeSound.stillPlaying()) and ( not blinkSound.stillPlaying()): if blinkHappened: blinkSound.play() elif saccadeHappened: saccadeSound.play() #do drawing if previewDownsize != 1: image = cv2.resize(image, dsize=previewWindow.size, interpolation=cv2.INTER_NEAREST) image = cv2.cvtColor(image, cv2.COLOR_GRAY2BGR) if clickingForDots: if clickingForFid: if definingFidFinderBox: cv2.circle(image, (fidFinderBoxX, fidFinderBoxY), fidFinderBoxSize, color=(255, 0, 0, 255), thickness=1) for dot in dotList: ellipse = ((dot.ellipse[0][0] / previewDownsize, dot.ellipse[0][1] / previewDownsize), (dot.ellipse[1][0] / previewDownsize, dot.ellipse[1][1] / previewDownsize), dot.ellipse[2]) if dot.blinkHappened or dot.lost: dotColor = (0, 0, 255, 255) else: dotColor = (0, 255, 0, 255) cv2.ellipse(image, ellipse, color=dotColor, thickness=1) image = numpy.rot90(image) previewWindowArray[:, :, 0:3] = image frameToFrameTimeList.append(imageTime - lastTime) lastTime = imageTime displayLagList.append(getTime() - imageTime) if len(displayLagList) > 30: displayLagList.pop(0) frameToFrameTimeList.pop(0) clickableTextDict['lag'].valueText = str( int(numpy.median(displayLagList) * 1000)) clickableTextDict['lag'].updateSurf() clickableTextDict['f2f'].valueText = str( int(numpy.median(frameToFrameTimeList) * 1000)) clickableTextDict['f2f'].updateSurf() for clickableText in clickableTextDict: clickableTextDict[clickableText].draw(previewWindowSurf) previewWindow.refresh() thisRefreshTime = getTime() # print (thisRefreshTime - lastRefreshTime)*1000 lastRefreshTime = thisRefreshTime if (sdl2.SDL_GetWindowFlags(settingsWindow.window) & sdl2.SDL_WINDOW_SHOWN): sdl2.ext.fill(settingsWindowSurf.contents, sdl2.pixels.SDL_Color(r=0, g=0, b=0, a=255)) for setting in settingsDict: settingsDict[setting].draw(settingsWindowSurf) settingsWindow.refresh() #calibration stuff if calibrating: if not calibrationChild.qFrom.empty(): message = calibrationChild.qFrom.get() if message == 'startQueing': queueDataToCalibrationChild = True elif message[0] == 'stopQueing': calibrationStopTime = message[1] checkCalibrationStopTime = True elif message[0] == 'calibrationCoefs': calibrationCoefs = message[1] calibrating = False doneCalibration = True calibrationChild.stop() del calibrationChild lastLocs = [] qFrom.put(['calibrationComplete', message]) queueDataToParent = True else: print message if checkCalibrationStopTime: if imageTime > calibrationStopTime: queueDataToCalibrationChild = False calibrationChild.qTo.put('doneQueing') checkCalibrationStopTime = False if queueDataToCalibrationChild: if len(dotList) > 0: calibrationChild.qTo.put([ imageTime, dotList[1].x2, dotList[1].y2, dotList[2].x2, dotList[2].y2 ])
def pump(self): sdl2.SDL_PumpEvents() self._window.refresh()
def labjackChildFunction(qTo, qFrom, windowSize=[200, 200], windowPosition=[0, 0]): import sdl2 import sdl2.ext import sys import time try: import appnope appnope.nope() except: pass sdl2.SDL_Init(sdl2.SDL_INIT_VIDEO) window = sdl2.ext.Window("labjack", size=windowSize, position=windowPosition, flags=sdl2.SDL_WINDOW_SHOWN) windowID = sdl2.SDL_GetWindowID(window.window) windowSurf = sdl2.SDL_GetWindowSurface(window.window) sdl2.ext.fill(windowSurf.contents, sdl2.pixels.SDL_Color(r=255, g=255, b=255, a=255)) window.refresh() for i in range(10): sdl2.SDL_PumpEvents() #to show the windows import u3 d = u3.U3() d.configU3() d.getCalibrationData() d.configAnalog(u3.FIO0) checkForNextZeroTime = False checkForTrialNextZeroTime = False def exitSafely(): d.close() sys.exit() sendTriggers = False while True: if sendTriggers: if not checkForNextZeroTime: temp = d.getAIN(0) # print temp if temp > .5: #photosensor surpasses criterion d.getFeedback(u3.BitStateWrite(IONumber=8, State=1)) nextZeroTime = time.time( ) + .010 #wait 10ms before setting the state back to zero, giving the amp time to pick it up checkForNextZeroTime = True else: if time.time() >= nextZeroTime: #time to turn the bit back off d.getFeedback(u3.BitStateWrite(IONumber=8, State=0)) checkForNextZeroTime = False if checkForTrialNextZeroTime: if time.time() >= trialNextZeroTime: d.getFeedback(u3.BitStateWrite(IONumber=9, State=0)) checkForTrialNextZeroTime = False if not qTo.empty(): message = qTo.get() if message == 'quit': exitSafely() elif message == 'trialDone': sendTriggers = False checkForTrialNextZeroTime = False checkForNextZeroTime = False d.getFeedback(u3.BitStateWrite( IONumber=8, State=0)) #should be zero, but just in case... d.getFeedback(u3.BitStateWrite( IONumber=9, State=0)) #should be zero, but just in case... elif message == 'trialStart': sendTriggers = True d.getFeedback(u3.BitStateWrite(IONumber=9, State=1)) trialNextZeroTime = time.time( ) + .010 #wait 10ms before setting the state back to zero, giving the amp time to pick it up checkForTrialNextZeroTime = True sdl2.SDL_PumpEvents() for event in sdl2.ext.get_events(): if event.type == sdl2.SDL_WINDOWEVENT: if (event.window.event == sdl2.SDL_WINDOWEVENT_CLOSE): exitSafely()
def eyelinkChildFunction(qTo, qFrom, windowSize=[200, 200], windowPosition=[0, 0], stimDisplayRes=[1920, 1080], calibrationDisplaySize=[1920, 1080], calibrationDotSize=10, eyelinkIp='100.1.1.1', edfFileName='temp.edf', edfPath='./_Data/temp.edf', saccadeSoundFile='_Stimuli/stop.wav', blinkSoundFile='_Stimuli/stop.wav'): import sdl2 import sdl2.ext import math import OpenGL.GL as gl import sdl2.sdlmixer import pylink import numpy import sys import shutil import subprocess import time import os import array from PIL import Image from PIL import ImageDraw try: import appnope appnope.nope() except: pass byteify = lambda x, enc: x.encode(enc) sdl2.SDL_Init(sdl2.SDL_INIT_VIDEO) window = sdl2.ext.Window("eyelink", size=windowSize, position=windowPosition, flags=sdl2.SDL_WINDOW_SHOWN) windowID = sdl2.SDL_GetWindowID(window.window) windowSurf = sdl2.SDL_GetWindowSurface(window.window) sdl2.ext.fill(windowSurf.contents, sdl2.pixels.SDL_Color(r=0, g=0, b=0, a=255)) window.refresh() for i in range(10): sdl2.SDL_PumpEvents() #to show the windows sdl2.SDL_Init(sdl2.SDL_INIT_AUDIO) sdl2.sdlmixer.Mix_OpenAudio(44100, sdl2.sdlmixer.MIX_DEFAULT_FORMAT, 2, 1024) class Sound: def __init__(self, fileName): self.sample = sdl2.sdlmixer.Mix_LoadWAV( sdl2.ext.compat.byteify(fileName, "utf-8")) self.started = False def play(self): self.channel = sdl2.sdlmixer.Mix_PlayChannel(-1, self.sample, 0) self.started = True def stillPlaying(self): if self.started: if sdl2.sdlmixer.Mix_Playing(self.channel): return True else: self.started = False return False else: return False saccadeSound = Sound(saccadeSoundFile) blinkSound = Sound(blinkSoundFile) def exitSafely(): if 'eyelink' in locals(): if eyelink.isRecording() == 0: eyelink.stopRecording() eyelink.setOfflineMode() eyelink.closeDataFile() eyelink.receiveDataFile(edfFileName, 'temp.edf') eyelink.close() if os.path.isfile('temp.edf'): shutil.move('temp.edf', edfPath) # if os.path.isfile(edfPath): # subprocess.call('./edf2asc -y ./'+edfPath,shell=True) sys.exit( ) #process gets hung here if called when showing images from eyelink pylink.setDriftCorrectSounds('off', 'off', 'off') pylink.setCalibrationSounds('off', 'off', 'off') edfPath = './_Data/temp.edf' #temporary default location, to be changed later when ID is established done = False while not done: try: # print '\neyelink: Attempting to connect to eyelink (check that wifi is off!)' eyelink = pylink.EyeLink(eyelinkIp) done = True except: while not qTo.empty(): message = qTo.get() if message == 'quit': exitSafely() else: qTo.put(message) # print 'eyelink: connected' eyelink.sendCommand( 'select_parser_configuration 0' ) # 0--> standard (cognitive); 1--> sensitive (psychophysical) # eyelink.sendCommand('sample_rate 500') eyelink.setLinkEventFilter("SACCADE,BLINK,FIXATION,LEFT,RIGHT") eyelink.openDataFile(edfFileName) eyelink.sendCommand( "screen_pixel_coords = %d %d %d %d" % (stimDisplayRes[0] / 2 - calibrationDisplaySize[0] / 2, stimDisplayRes[1] / 2 - calibrationDisplaySize[1] / 2, stimDisplayRes[0] / 2 + calibrationDisplaySize[0] / 2, stimDisplayRes[1] / 2 + calibrationDisplaySize[1] / 2)) eyelink.sendMessage("DISPLAY_COORDS 0 0 %d %d" % (stimDisplayRes[0], stimDisplayRes[1])) eyelink.sendCommand("saccade_velocity_threshold = 60") eyelink.sendCommand("saccade_acceleration_threshold = 19500") class EyeLinkCoreGraphicsPySDL2(pylink.EyeLinkCustomDisplay): def __init__(self): # self.__target_beep__ = Sound('_Stimuli/type.wav') # self.__target_beep__done__ = Sound('qbeep.wav') # self.__target_beep__error__ = Sound('error.wav') if sys.byteorder == 'little': self.byteorder = 1 else: self.byteorder = 0 self.imagebuffer = array.array('I') self.pal = None self.__img__ = None def record_abort_hide(self): pass def play_beep(self, beepid): pass # if beepid == pylink.DC_TARG_BEEP or beepid == pylink.CAL_TARG_BEEP: # self.__target_beep__.play() # elif beepid == pylink.CAL_ERR_BEEP or beepid == pylink.DC_ERR_BEEP: # self.__target_beep__error__.play() # else:# CAL_GOOD_BEEP or DC_GOOD_BEEP # self.__target_beep__done__.play() def clear_cal_display(self): # # print 'clear_cal_display' qFrom.put('clearCalDisplay') def setup_cal_display(self): # # print 'setup_cal_display' qFrom.put('setupCalDisplay') def exit_cal_display(self): # # print 'exit_cal_display' qFrom.put('exitCalDisplay') def erase_cal_target(self): # # print 'erase_cal_target' qFrom.put('eraseCalTarget') def draw_cal_target(self, x, y): # # print 'draw_cal_target' qFrom.put(['drawCalTarget', x, y]) def setup_image_display(self, width, height): # # print 'eyelink: setup_image_display' self.img_size = (width, height) return (0) def exit_image_display(self): # # print 'eyelink: exit_image_display' pass def image_title(self, text): # # print 'eyelink: image_title' pass def set_image_palette(self, r, g, b): # # print 'eyelink: set_image_palette' self.imagebuffer = array.array('I') sz = len(r) i = 0 self.pal = [] while i < sz: rf = int(b[i]) gf = int(g[i]) bf = int(r[i]) if self.byteorder: self.pal.append((rf << 16) | (gf << 8) | (bf)) else: self.pal.append( (bf << 24) | (gf << 16) | (rf << 8)) #for mac i = i + 1 def draw_image_line(self, width, line, totlines, buff): # # print 'eyelink: draw_image_line' i = 0 while i < width: if buff[i] >= len(self.pal): buff[i] = len(self.pal) - 1 self.imagebuffer.append(self.pal[buff[i] & 0x000000FF]) i = i + 1 if line == totlines: img = Image.fromstring('RGBX', (width, totlines), self.imagebuffer.tostring()) img = img.convert('RGBA') self.__img__ = img.copy() self.__draw__ = ImageDraw.Draw(self.__img__) self.draw_cross_hair( ) #inherited method, calls draw_line and draw_losenge qFrom.put([ 'image', numpy.array( self.__img__.resize([ self.__img__.size[0] * 4, self.__img__.size[1] * 4 ], Image.BICUBIC)) ]) self.__img__ = None self.__draw__ = None self.imagebuffer = array.array('I') def getColorFromIndex(self, colorindex): if colorindex == pylink.CR_HAIR_COLOR: return (255, 255, 255, 255) elif colorindex == pylink.PUPIL_HAIR_COLOR: return (255, 255, 255, 255) elif colorindex == pylink.PUPIL_BOX_COLOR: return (0, 255, 0, 255) elif colorindex == pylink.SEARCH_LIMIT_BOX_COLOR: return (255, 0, 0, 255) elif colorindex == pylink.MOUSE_CURSOR_COLOR: return (255, 0, 0, 255) else: return (0, 0, 0, 0) def draw_line(self, x1, y1, x2, y2, colorindex): # # print 'eyelink: draw_line' if x1 < 0: x1 = 0 if x2 < 0: x2 = 0 if y1 < 0: y1 = 0 if y2 < 0: y2 = 0 if x1 > self.img_size[0]: x1 = self.img_size[0] if x2 > self.img_size[0]: x2 = self.img_size[0] if y1 > self.img_size[1]: y1 = self.img_size[1] if y2 > self.img_size[1]: y2 = self.img_size[1] imr = self.__img__.size x1 = int((float(x1) / float(self.img_size[0])) * imr[0]) x2 = int((float(x2) / float(self.img_size[0])) * imr[0]) y1 = int((float(y1) / float(self.img_size[1])) * imr[1]) y2 = int((float(y2) / float(self.img_size[1])) * imr[1]) color = self.getColorFromIndex(colorindex) self.__draw__.line([(x1, y1), (x2, y2)], fill=color) return 0 def draw_lozenge(self, x, y, width, height, colorindex): # # print 'eyelink: draw_lozenge' color = self.getColorFromIndex(colorindex) imr = self.__img__.size x = int((float(x) / float(self.img_size[0])) * imr[0]) width = int((float(width) / float(self.img_size[0])) * imr[0]) y = int((float(y) / float(self.img_size[1])) * imr[1]) height = int((float(height) / float(self.img_size[1])) * imr[1]) if width > height: rad = height / 2 self.__draw__.line([(x + rad, y), (x + width - rad, y)], fill=color) self.__draw__.line([(x + rad, y + height), (x + width - rad, y + height)], fill=color) clip = (x, y, x + height, y + height) self.__draw__.arc(clip, 90, 270, fill=color) clip = ((x + width - height), y, x + width, y + height) self.__draw__.arc(clip, 270, 90, fill=color) else: rad = width / 2 self.__draw__.line([(x, y + rad), (x, y + height - rad)], fill=color) self.__draw__.line([(x + width, y + rad), (x + width, y + height - rad)], fill=color) clip = (x, y, x + width, y + width) self.__draw__.arc(clip, 180, 360, fill=color) clip = (x, y + height - width, x + width, y + height) self.__draw__.arc(clip, 360, 180, fill=color) return 0 def get_mouse_state(self): # pos = pygame.mouse.get_pos() # state = pygame.mouse.get_pressed() # return (pos,state[0]) pass def get_input_key(self): ky = [] while not qTo.empty(): message = qTo.get() # print 'eyelink: ' # print message if message == 'button': ky.append( pylink.KeyInput(32, 0) ) #button translated to space keypress (for drift correct) # if message=='quit': # # print 'received message to exit' # exitSafely() # el elif message[0] == 'keycode': keysym = message[1] keycode = keysym.sym if keycode == sdl2.SDLK_F1: keycode = pylink.F1_KEY elif keycode == sdl2.SDLK_F2: keycode = pylink.F2_KEY elif keycode == sdl2.SDLK_F3: keycode = pylink.F3_KEY elif keycode == sdl2.SDLK_F4: keycode = pylink.F4_KEY elif keycode == sdl2.SDLK_F5: keycode = pylink.F5_KEY elif keycode == sdl2.SDLK_F6: keycode = pylink.F6_KEY elif keycode == sdl2.SDLK_F7: keycode = pylink.F7_KEY elif keycode == sdl2.SDLK_F8: keycode = pylink.F8_KEY elif keycode == sdl2.SDLK_F9: keycode = pylink.F9_KEY elif keycode == sdl2.SDLK_F10: keycode = pylink.F10_KEY elif keycode == sdl2.SDLK_PAGEUP: keycode = pylink.PAGE_UP elif keycode == sdl2.SDLK_PAGEDOWN: keycode = pylink.PAGE_DOWN elif keycode == sdl2.SDLK_UP: keycode = pylink.CURS_UP elif keycode == sdl2.SDLK_DOWN: keycode = pylink.CURS_DOWN elif keycode == sdl2.SDLK_LEFT: keycode = pylink.CURS_LEFT elif keycode == sdl2.SDLK_RIGHT: keycode = pylink.CURS_RIGHT elif keycode == sdl2.SDLK_BACKSPACE: keycode = ord('\b') elif keycode == sdl2.SDLK_RETURN: keycode = pylink.ENTER_KEY elif keycode == sdl2.SDLK_ESCAPE: keycode = pylink.ESC_KEY elif keycode == sdl2.SDLK_TAB: keycode = ord('\t') elif keycode == pylink.JUNK_KEY: keycode = 0 ky.append(pylink.KeyInput(keycode, keysym.mod)) return ky customDisplay = EyeLinkCoreGraphicsPySDL2() pylink.openGraphicsEx(customDisplay) newGazeTarget = False gazeTarget = numpy.array(calibrationDisplaySize) / 2.0 gazeTargetCriterion = calibrationDotSize doSounds = False reportSaccades = False reportBlinks = False lastMessageTime = time.time() lastStartBlinkTime = time.time() while True: sdl2.SDL_PumpEvents() for event in sdl2.ext.get_events(): if event.type == sdl2.SDL_WINDOWEVENT: if (event.window.event == sdl2.SDL_WINDOWEVENT_CLOSE): exitSafely() if not qTo.empty(): message = qTo.get() if message == 'quit': exitSafely() elif message[0] == 'edfPath': edfPath = message[1] elif message[0] == 'doSounds': doSounds = message[1] elif message[0] == 'reportSaccades': reportSaccades = message[1] elif message[0] == 'reportBlinks': reportBlinks = message[1] elif message[0] == 'sendMessage': eyelink.sendMessage(message[1]) elif message[0] == 'doDriftCorrect': # print 'eyelink: drift correct requested' if eyelink.isRecording() == 0: eyelink.stopRecording() try: location = message[1] error = eyelink.doDriftCorrect(location[0], location[1], 0, 1) # print error # print 'eyelink: drift correct attempted' if error != 27: qFrom.put('driftCorrectComplete') else: qFrom.put('doCalibration') except: qFrom.put('doCalibration') elif message == 'startRecording': # print 'eyelink: received message to begin recording' eyelink.startRecording( 1, 1, 1, 1 ) #this retuns immediately takes 10-30ms to actually kick in on the tracker while not (eyelink.isRecording() == 0): pass # print eyelink.isRecording() qFrom.put('recordingStarted') elif message[0] == 'newGazeTarget': # # print message newGazeTarget = True gazeTarget = numpy.array(message[1]) gazeTargetCriterion = numpy.array(message[2]) # # print message # # print 'waiting for gaze confirmation' elif message[0] == 'acceptTrigger': eyelink.accept_trigger() elif message == 'doCalibration': doSounds = False if eyelink.isRecording() == 0: eyelink.stopRecording() eyelink.doTrackerSetup() # # print 'calComplete' qFrom.put('calibrationComplete') if eyelink.isRecording( ) == 0: #stupid, I know, but eyelink.isRecording() returns 0 if it *is* indeed recording! eyeData = eyelink.getNextData() # if eyeData==pylink.SAMPLE_TYPE: # eyeSample = eyelink.getFloatData() # gaze = None # if eyeSample.isRightSample(): # gaze = eyeSample.getRightEye().getGaze() # elif eyeSample.isLeftSample(): # gaze = eyeSample.getLeftEye().getGaze() # if gaze!=None: # if gaze[0]!=-32768.0: # gazeDistFromGazeTarget = numpy.linalg.norm(numpy.array(gaze)-gazeTarget) # if newGazeTarget: # if gazeDistFromGazeTarget<gazeTargetCriterion: # # print ['gazeTargetMet',gaze,gazeTargetCriterion,gazeTarget,gazeDistFromGazeTarget] # qFrom.put(['gazeTargetMet',gazeTarget]) # newGazeTarget = False # else: # qFrom.put(['gazeTargetNotMet',gazeTarget]) # # print ['gazeTargetNotMet',gaze,gazeTarget,gazeDistFromGazeTarget,gazeTargetCriterion] if eyeData == pylink.ENDSACC: eyeSample = eyelink.getFloatData() gazeStartTime = eyeSample.getStartTime() gazeStart = eyeSample.getStartGaze() gazeEnd = eyeSample.getEndGaze() # # print ['eyelink: saccade',gazeStart,gazeEnd] if (gazeStart[0] != -32768.0) & (gazeEnd[0] != -32768.0): gazeDistFromGazeTarget = numpy.linalg.norm( numpy.array(gazeEnd) - gazeTarget) if gazeDistFromGazeTarget < 1000: if newGazeTarget: # # print [gazeDistFromGazeTarget,gazeTargetCriterion,gazeTarget,gazeEnd] if gazeDistFromGazeTarget < gazeTargetCriterion: # # print ['gazeTargetMet',gazeEnd,gazeTargetCriterion,gazeTarget,gazeDistFromGazeTarget] qFrom.put([ 'gazeTargetMet', gazeTarget, gazeStartTime ]) newGazeTarget = False # # print 'gazeTargetMet' elif gazeDistFromGazeTarget > gazeTargetCriterion: if reportSaccades: qFrom.put(['gazeTargetLost', gazeTarget]) # # print ['gazeTargetLost',gazeTarget] if (not saccadeSound.stillPlaying()) and ( not blinkSound.stillPlaying()): if doSounds: saccadeSound.play() elif eyeData == pylink.STARTBLINK: # lastStartBlinkTime = time.time() # elif eyeData==pylink.ENDBLINK: # if (time.time()-lastStartBlinkTime)>.1: if reportBlinks: qFrom.put('blink') # # print 'eyelink: blink' if (not saccadeSound.stillPlaying()) and ( not blinkSound.stillPlaying()): if doSounds: #blinkSound.play() qFrom.put('blink')
def handle_input(self): app = self.app # get and store mouse state # (store everything in parent app object so stuff can access it easily) mx, my = ctypes.c_int(0), ctypes.c_int(0) mouse = sdl2.mouse.SDL_GetMouseState(mx, my) app.left_mouse = bool(mouse & sdl2.SDL_BUTTON(sdl2.SDL_BUTTON_LEFT)) app.middle_mouse = bool(mouse & sdl2.SDL_BUTTON(sdl2.SDL_BUTTON_MIDDLE)) app.right_mouse = bool(mouse & sdl2.SDL_BUTTON(sdl2.SDL_BUTTON_RIGHT)) mx, my = int(mx.value), int(my.value) # if mouse hasn't moved since init, disregard SDL_GetMouseState if self.mouse_has_moved: app.mouse_x, app.mouse_y = mx, my elif mx != 0 or my != 0: self.mouse_has_moved = True # relative mouse move state mdx, mdy = ctypes.c_int(0), ctypes.c_int(0) sdl2.mouse.SDL_GetRelativeMouseState(mdx, mdy) if self.mouse_has_moved: app.mouse_dx, app.mouse_dy = int(mdx.value), int(mdy.value) if app.mouse_dx != 0 or app.mouse_dy != 0: app.keyboard_editing = False # dragging a dialog? if app.left_mouse and self.ui.active_dialog in self.ui.hovered_elements: self.ui.active_dialog.update_drag(app.mouse_dx, app.mouse_dy) # get keyboard state so later we can directly query keys ks = sdl2.SDL_GetKeyboardState(None) # get modifier states self.shift_pressed, self.alt_pressed, self.ctrl_pressed = False, False, False if ks[sdl2.SDL_SCANCODE_LSHIFT] or ks[sdl2.SDL_SCANCODE_RSHIFT]: self.shift_pressed = True if ks[sdl2.SDL_SCANCODE_LALT] or ks[sdl2.SDL_SCANCODE_RALT]: self.alt_pressed = True if ks[sdl2.SDL_SCANCODE_LCTRL] or ks[sdl2.SDL_SCANCODE_RCTRL]: self.ctrl_pressed = True # macOS: treat command as interchangeable with control, is this kosher? if platform.system() == 'Darwin' and (ks[sdl2.SDL_SCANCODE_LGUI] or ks[sdl2.SDL_SCANCODE_RGUI]): self.ctrl_pressed = True if app.capslock_is_ctrl and ks[sdl2.SDL_SCANCODE_CAPSLOCK]: self.ctrl_pressed = True # pack mods into a tuple to save listing em all out repeatedly mods = self.shift_pressed, self.alt_pressed, self.ctrl_pressed # get controller state if self.gamepad: self.gamepad_left_x = sdl2.SDL_JoystickGetAxis( self.gamepad, sdl2.SDL_CONTROLLER_AXIS_LEFTX) / 32768 self.gamepad_left_y = sdl2.SDL_JoystickGetAxis( self.gamepad, sdl2.SDL_CONTROLLER_AXIS_LEFTY) / -32768 for event in sdl2.ext.get_events(): if event.type == sdl2.SDL_QUIT: app.should_quit = True elif event.type == sdl2.SDL_WINDOWEVENT: if event.window.event == sdl2.SDL_WINDOWEVENT_RESIZED: # test window we create on init to detect resolution makes # SDL think we've resized main app window on first tick! if app.updates > 0: app.resize_window(event.window.data1, event.window.data2) elif event.type == sdl2.SDL_JOYBUTTONDOWN: if not app.gw.paused and app.gw.player: app.gw.player.button_pressed(event.jbutton.button) elif event.type == sdl2.SDL_JOYBUTTONUP: if not app.gw.paused and app.gw.player: self.app.gw.player.button_unpressed(event.jbutton.button) elif event.type == sdl2.SDL_KEYDOWN: keysym = self.get_keysym(event) # if console is up, pass input to it if self.ui.console.visible: self.ui.console.handle_input(keysym, *mods) # same with dialog box elif self.ui.active_dialog and self.ui.active_dialog is self.ui.keyboard_focus_element: self.ui.active_dialog.handle_input(keysym, *mods) # bail, process no further input #sdl2.SDL_PumpEvents() #return # handle text input if text tool is active elif self.ui.selected_tool is self.ui.text_tool and self.ui.text_tool.input_active: self.ui.text_tool.handle_keyboard_input(keysym, *mods) # see if there's a function for this bind and run it else: flist = self.get_bind_functions(keysym, *mods) for f in flist: # don't run any command whose menu bar item's dimmed / not allowed (ie wrong mode) if self.is_command_function_allowed(f): f() # if game mode active, pass to world as well as any binds if self.app.game_mode: self.app.gw.handle_input(event, *mods) # for key up events, use the same binds but handle them special case # TODO: once there are enough key up events, figure out a more # elegant way than this elif event.type == sdl2.SDL_KEYUP: keysym = self.get_keysym(event) if self.app.game_mode: self.app.gw.handle_input(event, *mods) # dismiss selector popup flist = self.get_bind_functions(keysym, *mods) if not flist: pass elif self.ui.active_dialog: # keyup shouldn't have any special meaning in a dialog pass elif self.BIND_game_grab in flist: if self.app.game_mode and not self.ui.active_dialog and self.app.gw.player: self.app.gw.player.button_unpressed(0) return elif self.BIND_toggle_picker in flist: # ..but only for default hold-to-show setting if self.ui.popup_hold_to_show: self.ui.popup.hide() elif self.BIND_select_or_paint in flist: app.keyboard_editing = True if not self.ui.selected_tool is self.ui.text_tool and not self.ui.text_tool.input_active: self.app.cursor.finish_paint() # # mouse events aren't handled by bind table for now # elif event.type == sdl2.SDL_MOUSEWHEEL: ui_wheeled = self.ui.wheel_moved(event.wheel.y) if not ui_wheeled: if self.app.can_edit: if event.wheel.y > 0: # only zoom in should track towards cursor app.camera.zoom(-self.wheel_zoom_amount, towards_cursor=True) elif event.wheel.y < 0: app.camera.zoom(self.wheel_zoom_amount) else: self.app.gw.mouse_wheeled(event.wheel.y) elif event.type == sdl2.SDL_MOUSEBUTTONUP: # "consume" input if UI handled it ui_unclicked = self.ui.unclicked(event.button.button) if ui_unclicked: sdl2.SDL_PumpEvents() return if self.app.game_mode: self.app.gw.unclicked(event.button.button) # LMB up: finish paint for most tools, end select drag if event.button.button == sdl2.SDL_BUTTON_LEFT: if self.ui.selected_tool is self.ui.select_tool and self.ui.select_tool.selection_in_progress: self.ui.select_tool.finish_select( self.shift_pressed, self.ctrl_pressed) elif not self.ui.selected_tool is self.ui.text_tool and not self.ui.text_tool.input_active: app.cursor.finish_paint() elif event.type == sdl2.SDL_MOUSEBUTTONDOWN: ui_clicked = self.ui.clicked(event.button.button) # don't register edit commands if a menu is up if ui_clicked or self.ui.menu_bar.active_menu_name or self.ui.active_dialog: sdl2.SDL_PumpEvents() if self.app.game_mode: self.app.gw.last_click_on_ui = True return # pass clicks through to game world if self.app.game_mode: if not ui_clicked: self.app.gw.clicked(event.button.button) # LMB down: start text entry, start select drag, or paint elif event.button.button == sdl2.SDL_BUTTON_LEFT: if not self.ui.active_art: return elif self.ui.selected_tool is self.ui.text_tool: # text tool: only start entry if click is outside popup if not self.ui.text_tool.input_active and \ not self.ui.popup in self.ui.hovered_elements: self.ui.text_tool.start_entry() elif self.ui.selected_tool is self.ui.select_tool: # select tool: accept clicks if they're outside the popup if not self.ui.select_tool.selection_in_progress and \ (not self.ui.keyboard_focus_element or \ (self.ui.keyboard_focus_element is self.ui.popup and \ not self.ui.popup in self.ui.hovered_elements)): self.ui.select_tool.start_select() else: app.cursor.start_paint() elif event.button.button == sdl2.SDL_BUTTON_RIGHT: if self.app.ui.active_art: self.ui.quick_grab() # none of the below applies to cases where a dialog is up if self.ui.active_dialog: sdl2.SDL_PumpEvents() return # directly query keys we don't want affected by OS key repeat delay # TODO: these are hard-coded for the moment, think of a good way # to expose this functionality to the key bind system def pressing_up(ks): return ks[sdl2.SDL_SCANCODE_W] or ks[sdl2.SDL_SCANCODE_UP] or ks[ sdl2.SDL_SCANCODE_KP_8] def pressing_down(ks): return ks[sdl2.SDL_SCANCODE_S] or ks[sdl2.SDL_SCANCODE_DOWN] or ks[ sdl2.SDL_SCANCODE_KP_2] def pressing_left(ks): return ks[sdl2.SDL_SCANCODE_A] or ks[sdl2.SDL_SCANCODE_LEFT] or ks[ sdl2.SDL_SCANCODE_KP_4] def pressing_right(ks): return ks[sdl2.SDL_SCANCODE_D] or ks[ sdl2.SDL_SCANCODE_RIGHT] or ks[sdl2.SDL_SCANCODE_KP_6] # prevent camera move if: console is up, text input is active, editing # is not allowed if self.shift_pressed and not self.alt_pressed and not self.ctrl_pressed and not self.ui.console.visible and not self.ui.text_tool.input_active and self.app.can_edit and self.ui.keyboard_focus_element is None: if pressing_up(ks): app.camera.pan(0, 1, True) if pressing_down(ks): app.camera.pan(0, -1, True) if pressing_left(ks): app.camera.pan(-1, 0, True) if pressing_right(ks): app.camera.pan(1, 0, True) if ks[sdl2.SDL_SCANCODE_X]: app.camera.zoom(-self.keyboard_zoom_amount, keyboard=True, towards_cursor=True) if ks[sdl2.SDL_SCANCODE_Z]: app.camera.zoom(self.keyboard_zoom_amount, keyboard=True) if self.app.can_edit and app.middle_mouse and (app.mouse_dx != 0 or app.mouse_dy != 0): app.camera.mouse_pan(app.mouse_dx, app.mouse_dy) # game mode: arrow keys and left gamepad stick move player if self.app.game_mode and not self.ui.console.visible and not self.ui.active_dialog and self.ui.keyboard_focus_element is None: if pressing_up(ks): # shift = move selected if self.shift_pressed and self.app.can_edit: app.gw.move_selected(0, 1, 0) elif not self.ctrl_pressed and app.gw.player: app.gw.player.move(0, 1) if pressing_down(ks): if self.shift_pressed and self.app.can_edit: app.gw.move_selected(0, -1, 0) elif not self.ctrl_pressed and app.gw.player: app.gw.player.move(0, -1) if pressing_left(ks): if self.shift_pressed and self.app.can_edit: app.gw.move_selected(-1, 0, 0) elif not self.ctrl_pressed and app.gw.player: app.gw.player.move(-1, 0) if pressing_right(ks): if self.shift_pressed and self.app.can_edit: app.gw.move_selected(1, 0, 0) elif not self.ctrl_pressed and app.gw.player: app.gw.player.move(1, 0) if abs(self.gamepad_left_x) > 0.15 and app.gw.player: app.gw.player.move(self.gamepad_left_x, 0) if abs(self.gamepad_left_y) > 0.15 and app.gw.player: app.gw.player.move(0, self.gamepad_left_y) sdl2.SDL_PumpEvents()
def stamperChildFunction(qTo, qFrom, windowSize=[200, 200], windowPosition=[0, 0], windowColor=[255, 255, 255], doBorder=True): import sdl2 import sdl2.ext import sys import time try: import appnope appnope.nope() except: pass sdl2.SDL_Init(sdl2.SDL_INIT_TIMER) timeFreq = 1.0 / sdl2.SDL_GetPerformanceFrequency() sdl2.SDL_Init(sdl2.SDL_INIT_VIDEO) if doBorder: flags = sdl2.SDL_WINDOW_SHOWN else: flags = sdl2.SDL_WINDOW_BORDERLESS | sdl2.SDL_WINDOW_SHOWN window = sdl2.ext.Window("pyStamper", size=windowSize, position=windowPosition, flags=flags) windowID = sdl2.SDL_GetWindowID(window.window) windowSurf = sdl2.SDL_GetWindowSurface(window.window) red = sdl2.pixels.SDL_Color(r=255, g=0, b=0, a=255) green = sdl2.pixels.SDL_Color(r=0, g=255, b=0, a=255) black = sdl2.pixels.SDL_Color(r=0, g=0, b=0, a=255) white = sdl2.pixels.SDL_Color(r=255, g=255, b=255, a=255) if doBorder: sdl2.ext.fill(windowSurf.contents, green) else: sdl2.ext.fill( windowSurf.contents, sdl2.pixels.SDL_Color(r=windowColor[0], g=windowColor[1], b=windowColor[2], a=255)) window.refresh() for i in range(10): sdl2.SDL_PumpEvents() #to show the windows sdl2.SDL_Init( sdl2.SDL_INIT_JOYSTICK) #uncomment if you want joystick input sdl2.SDL_JoystickOpen(0) #uncomment if you want joystick input lostFocus = True lostColors = [red, black, red, white] lastRefreshTime = time.time() while True: if lostFocus and doBorder: if time.time() > (lastRefreshTime + (2.0 / 60)): sdl2.ext.fill(windowSurf.contents, lostColors[0]) window.refresh() lostColors.append(lostColors.pop(0)) lastRefreshTime = time.time() sdl2.SDL_PumpEvents() if not qTo.empty(): message = qTo.get() if message == 'quit': sys.exit() elif message == 'raise': sdl2.SDL_RaiseWindow(window.window) for event in sdl2.ext.get_events(): if event.type == sdl2.SDL_WINDOWEVENT: if event.window.windowID == windowID: if (event.window.event == sdl2.SDL_WINDOWEVENT_CLOSE): qFrom.put({ 'type': 'key', 'time': event.window.timestamp * timeFreq, 'value': 'escape' }) sys.exit() elif event.window.event == sdl2.SDL_WINDOWEVENT_FOCUS_LOST: lostFocus = True elif event.window.event == sdl2.SDL_WINDOWEVENT_FOCUS_GAINED: lostFocus = False if doBorder: sdl2.ext.fill(windowSurf.contents, green) window.refresh() else: message = {} if event.type == sdl2.SDL_KEYDOWN: message['type'] = 'key' message['time'] = event.key.timestamp * timeFreq message['value'] = sdl2.SDL_GetKeyName( event.key.keysym.sym).lower() message['keysym'] = event.key.keysym qFrom.put(message) elif event.type == sdl2.SDL_JOYAXISMOTION: message['type'] = 'axis' message['axis'] = event.jaxis.axis message['time'] = event.jaxis.timestamp * timeFreq message['value'] = event.jaxis.value qFrom.put(message) elif event.type == sdl2.SDL_JOYBUTTONDOWN: message['type'] = 'button' message['time'] = event.jbutton.timestamp * timeFreq message['value'] = event.jbutton.button qFrom.put(message)