def insert_bind_button(self, param, section, name, double=False): button = Gtk.Button() button.set_size_request(105 * self.scale_factor, -1) raw_value = self.parent.m64p_wrapper.ConfigGetParameter(param) #self.map_controls[param] = raw_value if raw_value != '': if self.parent.m64p_wrapper.ConfigGetParameter( 'name') == "Keyboard": raw_value = raw_value.split(',') if len(raw_value) == 2: first_value = sdl.SDL_GetKeyName( self.filter_number(raw_value[0])) second_value = sdl.SDL_GetKeyName( self.filter_number(raw_value[1])) keyname = b"(" + self.purify( first_value) + b", " + self.purify(second_value) + b")" else: keyname = self.purify( sdl.SDL_GetKeyName(self.filter_number(raw_value[0]))) button.set_label(keyname.decode('utf-8')) else: button.set_label(raw_value) else: button.set_label("(empty)") button.connect("clicked", self.on_bind_key, param, section, name, double) return button
def handle_input(self, key, shift_pressed, alt_pressed, ctrl_pressed): keystr = sdl2.SDL_GetKeyName(key).decode() if keystr == 'Left': self.other_pressed() elif keystr == 'Right': self.confirm_pressed() elif keystr == 'Escape': self.cancel_pressed()
def handle_input(self, event, shift_pressed, alt_pressed, ctrl_pressed): # pass event's key to any objects that want to handle it if not event.type in [sdl2.SDL_KEYDOWN, sdl2.SDL_KEYUP]: return key = sdl2.SDL_GetKeyName(event.key.keysym.sym).decode() key = key.lower() args = (key, shift_pressed, alt_pressed, ctrl_pressed) for obj in self.objects.values(): if obj.handle_key_events: if event.type == sdl2.SDL_KEYDOWN: self.try_object_method(obj, obj.handle_key_down, args) elif event.type == sdl2.SDL_KEYUP: self.try_object_method(obj, obj.handle_key_up, args)
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 handle_input(self, key, shift_pressed, alt_pressed, ctrl_pressed): keystr = sdl2.SDL_GetKeyName(key).decode() # up/down keys navigate list new_index = self.selected_item_index navigated = False if keystr == 'Return': # if handle_enter returns True, bail before rest of input handling - # make sure any changes to handle_enter are safe for this! if self.handle_enter(shift_pressed, alt_pressed, ctrl_pressed): return elif keystr == 'Up': navigated = True if self.selected_item_index > 0: new_index -= 1 elif keystr == 'Down': navigated = True if self.selected_item_index < len(self.items) - 1: new_index += 1 elif keystr == 'PageUp': navigated = True page_size = int(self.items_in_view / 2) new_index -= page_size new_index = max(0, new_index) # scroll follows selection jumps self.scroll_index -= page_size self.scroll_index = max(0, self.scroll_index) elif keystr == 'PageDown': navigated = True page_size = int(self.items_in_view / 2) new_index += page_size new_index = min(new_index, len(self.items) - 1) self.scroll_index += page_size self.scroll_index = min(self.scroll_index, self.get_max_scroll()) # home/end: beginning/end of list, respectively elif keystr == 'Home': navigated = True new_index = 0 self.scroll_index = 0 elif keystr == 'End': navigated = True new_index = len(self.items) - 1 self.scroll_index = len(self.items) - self.items_in_view self.set_selected_item_index(new_index, set_field_text=navigated) # handle alphanumeric input etc UIDialog.handle_input(self, key, shift_pressed, alt_pressed, ctrl_pressed) # if we didn't navigate, seek based on new alphanumeric input if not navigated: self.text_input_seek()
def run2(): sdl2.SDL_Init(sdl2.SDL_INIT_VIDEO) sdl2.SDL_Init(sdl2.SDL_INIT_JOYSTICK) joystick = sdl2.SDL_JoystickOpen(0) sdl2.ext.init() window = sdl2.ext.Window("test", size=(800, 600), position=(0, 0), flags=sdl2.SDL_WINDOW_SHOWN) window.refresh() while True: for event in sdl2.ext.get_events(): if event.type == sdl2.SDL_KEYDOWN: print sdl2.SDL_GetKeyName(event.key.keysym.sym).lower() elif event.type == sdl2.SDL_JOYAXISMOTION: print[event.jaxis.axis, event.jaxis.value] return 0
def binding(self, widget, param, device, name, controller, execute): dialog = BindDialog(self, widget, device, name, controller) if dialog.key_pressed != None: if dialog.key_pressed.value == 42: store = "" if execute == True: widget.set_label("(empty)") self.parent.m64p_wrapper.ConfigSetParameter(param, store) return [store, "empty"] else: value = dialog.key_pressed.value keycode = sdl.SDL_GetKeyFromScancode(value) store = "key(" + str(keycode) + ")" if execute == True: widget.set_label( sdl.SDL_GetKeyName(keycode).decode("utf-8")) self.parent.m64p_wrapper.ConfigSetParameter(param, store) else: return [str(keycode), "key"] elif dialog.gamepad_pressed != None: value = dialog.gamepad_pressed if dialog.gamepad_type == "button": store = "button(" + str(value) + ")" else: if dialog.gamepad_type == "Naxis": store = "axis(" + str(value) + "-)" elif dialog.gamepad_type == "Paxis": store = "axis(" + str(value) + "+)" if execute == True: widget.set_label(store) self.parent.m64p_wrapper.ConfigSetParameter(param, store) else: return [str(value), dialog.gamepad_type] else: return ["", None]
def handle_input(self, key, shift_pressed, alt_pressed, ctrl_pressed): keystr = sdl2.SDL_GetKeyName(key).decode() field = None field_text = '' if self.active_field < len(self.fields): field = self.fields[self.active_field] field_text = self.field_texts[self.active_field] # special case: shortcut 'D' for 3rd button if no field input if len(self.fields) == 0 and keystr.lower() == 'd': self.other_pressed() return if keystr == '`' and not shift_pressed: self.ui.console.toggle() return # if list panel is up don't let user tab away lp = self.ui.edit_list_panel # only allow tab to focus shift IF list panel accepts it if keystr == 'Tab' and lp.is_visible() and \ lp.list_operation in lp.list_operations_allow_kb_focus: self.ui.keyboard_focus_element = self.ui.edit_list_panel return elif keystr == 'Return': self.confirm_pressed() elif keystr == 'Escape': self.cancel_pressed() # cycle through fields with up/down elif keystr == 'Up' or (keystr == 'Tab' and shift_pressed): if len(self.fields) > 1: self.active_field -= 1 self.active_field %= len(self.fields) # skip over None-type fields aka dead labels while self.fields[self.active_field].type is None: self.active_field -= 1 self.active_field %= len(self.fields) return elif keystr == 'Down' or keystr == 'Tab': if len(self.fields) > 1: self.active_field += 1 self.active_field %= len(self.fields) while self.fields[self.active_field].type is None: self.active_field += 1 self.active_field %= len(self.fields) return elif keystr == 'Backspace': if len(field_text) == 0: pass # don't let user clear a bool value # TODO: allow for checkboxes but not radio buttons elif field and field.type is bool: pass elif alt_pressed: # for file dialogs, delete back to last slash last_slash = field_text[:-1].rfind('/') # on windows, recognize backslash as well if platform.system() == 'Windows': last_backslash = field_text[:-1].rfind('\\') if last_backslash != -1 and last_slash != -1: last_slash = min(last_backslash, last_slash) if last_slash == -1: field_text = '' else: field_text = field_text[:last_slash + 1] else: field_text = field_text[:-1] elif keystr == 'Space': # if field.type is bool, toggle value if field.type is bool: field_text = self.get_toggled_bool_field(self.active_field) else: field_text += ' ' elif len(keystr) > 1: return # alphanumeric text input elif field and not field.type is bool: if field.type is str: if not shift_pressed: keystr = keystr.lower() if not keystr.isalpha() and shift_pressed: keystr = SHIFT_MAP.get(keystr, '') elif field.type is int and not keystr.isdigit() and keystr != '-': return # this doesn't guard against things like 0.00.001 elif field.type is float and not keystr.isdigit( ) and keystr != '.' and keystr != '-': return field_text += keystr # apply new field text and redraw if field and (len(field_text) < field.width or field.type is bool): self.field_texts[self.active_field] = field_text self.draw_fields(self.always_redraw_labels)
#!/usr/bin/python import sdl2 import sdl2.ext sdl2.SDL_Init(sdl2.SDL_INIT_VIDEO) sdl2.SDL_Init(sdl2.SDL_INIT_JOYSTICK) joystick = sdl2.SDL_JoystickOpen(0) #sdl2.ext.Window("test", size=(800,600),position=(0,0),flags=sdl2.SDL_WINDOW_SHOWN) #window.refresh() while True: for event in sdl2.ext.get_events(): if event.type == sdl2.SDL_KEYDOWN: print(sdl2.SDL_GetKeyName(event.key.keysym.sym).lower()) elif event.type == sdl2.SDL_JOYAXISMOTION: print('Axis: ', [event.jaxis.axis, event.jaxis.value]) if event.jaxis.axis == 0: print('detectado eje 0') if event.jaxis.axis == 1: print('detectado eje 1') # Eje Throttle if event.jaxis.axis == 3: print('detectado eje throttle') elif event.type == sdl2.SDL_JOYBUTTONDOWN: print('Button: ', [event.jaxis.axis, event.jaxis.value]) if event.jaxis.axis == 0: print('detectado boton 1') if event.jaxis.axis == 1: print('detectado boton 2') if event.jaxis.axis == 2: print('detectado boton 3') if event.jaxis.axis == 3:
def main(): parser = argparse.ArgumentParser() parser.add_argument('-l', '--list-controllers', action='store_true', help='Display a list of controllers attached to the system.') parser.add_argument('-c', '--controller', type=str, default='0', help='Controller to use. Default: 0.') parser.add_argument('-m', '--macro-controller', metavar='CONTROLLER:RECORD_BUTTON:PLAY_BUTTON', type=str, default=None, help='Controller and buttons to use for macro control. Default: None.') parser.add_argument('-p', '--port', type=str, default='/dev/ttyUSB0', help='Serial port or "functionfs" for direct USB mode. Default: /dev/ttyUSB0.') parser.add_argument('-b', '--baud-rate', type=int, default=115200, help='Baud rate. Default: 115200.') parser.add_argument('-u', '--udc', type=str, default='dummy_udc.0', help='UDC for direct USB mode. Default: dummy_udc.0 (loopback mode).') parser.add_argument('-R', '--record', type=str, default=None, help='Record events to file.') parser.add_argument('-P', '--playback', type=str, default=None, help='Play back events from file.') parser.add_argument('-d', '--dontexit', action='store_true', help='Switch to live input when playback finishes, instead of exiting. Default: False.') parser.add_argument('-q', '--quiet', action='store_true', help='Disable speed meter. Default: False.') parser.add_argument('-M', '--macros-dir', type=str, default='.', help='Directory to save macros. Default: current directory.') parser.add_argument('-f', '--function', type=str, nargs='*', default=[], help='Map a macro function to a button.') parser.add_argument('-D', '--log-level', type=str, default='INFO', help='Debugging level. CRITICAL, ERROR, WARNING, INFO, DEBUG. Default=INFO') args = parser.parse_args() numeric_level = getattr(logging, args.log_level.upper(), None) if not isinstance(numeric_level, int): raise ValueError('Invalid log level: %s' % args.log_level) root_logger.setLevel(numeric_level) if args.list_controllers: Controller.enumerate() exit(0) states = [] if args.playback is None or args.dontexit: if args.controller == 'fake': states = fakeinput() else: states = Controller(args.controller) if args.playback is not None: states = itertools.chain(replay_states(args.playback), states) macro_controller = None macro_record = None macro_play = None if args.macro_controller is not None: try: macro_controller, macro_record, macro_play = args.macro_controller.rsplit(':', maxsplit=3) macro_record = int(macro_record, 10) macro_play = int(macro_play, 10) except ValueError: logger.critical('Macro controller must be <controller number or name>:<record button>:<play button>') exit(-1) try: n = int(macro_controller, 10) if n < sdl2.SDL_NumJoysticks(): sdl2.SDL_JoystickOpen(n) macro_controller = n except ValueError: for n in range(sdl2.SDL_NumJoysticks()): name = sdl2.SDL_JoystickNameForIndex(n) if name is not None: name = name.decode('utf8') if name == macro_controller: sdl2.SDL_JoystickOpen(n) macro_controller = n window = None try: window = Window() except sdl2.ext.common.SDLError: logger.warning('Could not create a window with SDL. Keyboard input will not be available.') pass function_macros = {} for arg in args.function: try: b,f = arg.split(':') function_macros[int(b, 10)] = macros_dict[f] except ValueError: logger.error('Invalid function macro ignored.') except KeyError: logger.error('Invalid function macro ignored.') with MacroManager(states, macros_dir=args.macros_dir, record_button=macro_record, play_button=macro_play, function_macros=function_macros) as mm: with Recorder(args.record) as record: with HAL(args.port, args.baud_rate, args.udc) as hal: with tqdm(unit=' updates', disable=args.quiet, dynamic_ncols=True) as pbar: try: while True: for event in sdl2.ext.get_events(): # we have to fetch the events from SDL in order for the controller # state to be updated. if event.type == sdl2.SDL_WINDOWEVENT: if event.window.event == sdl2.SDL_WINDOWEVENT_CLOSE: raise WindowClosed else: if event.type == sdl2.SDL_KEYDOWN and event.key.repeat == 0: logger.debug('Key down: {:s}'.format(sdl2.SDL_GetKeyName(event.key.keysym.sym).decode('utf8'))) mm.key_event(event.key.keysym.sym, True) elif event.type == sdl2.SDL_KEYUP: logger.debug('Key up: {:s}'.format(sdl2.SDL_GetKeyName(event.key.keysym.sym).decode('utf8'))) mm.key_event(event.key.keysym.sym, False) elif event.jdevice.which == macro_controller: if event.type == sdl2.SDL_JOYBUTTONDOWN: logger.debug('Macro controller button down: {:d}'.format(event.jbutton.button)) mm.button_event(event.jbutton.button, True) elif event.type == sdl2.SDL_JOYBUTTONUP: mm.button_event(event.jbutton.button, False) # wait for the arduino to request another state. if hal.poll(): state = next(mm) hal.write(state) record.write(state) pbar.set_description('Sent {:s}'.format(state.hexstr)) pbar.update() if window is not None: window.update(state) except StopIteration: logger.info('Exiting because replay finished.') except KeyboardInterrupt: logger.info('Exiting due to keyboard interrupt.') except WindowClosed: logger.info('Exiting because input window was closed.')
def handle_keyboard_input(self, key, shift_pressed, ctrl_pressed, alt_pressed): # for now, do nothing on ctrl/alt if ctrl_pressed or alt_pressed: return # popup should get input if it's up if self.ui.popup.visible: return keystr = sdl2.SDL_GetKeyName(key).decode() art = self.ui.active_art frame, layer = art.active_frame, art.active_layer x, y = int(self.cursor.x), int(-self.cursor.y) char_w, char_h = art.quad_width, art.quad_height # TODO: if cursor isn't inside selection, bail early if keystr == 'Return': if self.cursor.y < art.width: self.cursor.x = self.start_x self.cursor.y -= 1 elif keystr == 'Backspace': if self.cursor.x > self.start_x: self.cursor.x -= char_w # undo command on previous tile self.cursor.current_command.undo_commands_for_tile( frame, layer, x - 1, y) elif keystr == 'Space': keystr = ' ' elif keystr == 'Up': if -self.cursor.y > 0: self.cursor.y += 1 elif keystr == 'Down': if -self.cursor.y < art.height - 1: self.cursor.y -= 1 elif keystr == 'Left': if self.cursor.x > 0: self.cursor.x -= char_w elif keystr == 'Right': if self.cursor.x < art.width - 1: self.cursor.x += char_w elif keystr == 'Escape': self.finish_entry() return # ignore any other non-character keys if len(keystr) > 1: return if keystr.isalpha() and not shift_pressed: keystr = keystr.lower() elif not keystr.isalpha() and shift_pressed: keystr = SHIFT_MAP.get(keystr, ' ') # if cursor got out of bounds, don't input if 0 > x or x >= art.width or 0 > y or y >= art.height: return # create tile command new_tc = EditCommandTile(art) new_tc.set_tile(frame, layer, x, y) b_char, b_fg, b_bg, b_xform = art.get_tile_at(frame, layer, x, y) new_tc.set_before(b_char, b_fg, b_bg, b_xform) a_char = art.charset.get_char_index(keystr) a_fg = self.ui.selected_fg_color if self.affects_fg_color else None a_bg = self.ui.selected_bg_color if self.affects_bg_color else None a_xform = self.ui.selected_xform if self.affects_xform else None new_tc.set_after(a_char, a_fg, a_bg, a_xform) # add command, apply immediately, and move cursor if self.cursor.current_command: self.cursor.current_command.add_command_tiles([new_tc]) else: self.ui.app.log('DEV WARNING: Cursor current command was expected') new_tc.apply() self.cursor.x += char_w if self.cursor.x >= self.ui.active_art.width: self.cursor.x = self.start_x self.cursor.y -= char_h if -self.cursor.y >= self.ui.active_art.height: self.finish_entry()
def main(): args = argparse.ArgumentParser(description="Run " + TITLE) args.add_argument('-i', '--midi-input', help="connect to specified MIDI input port") args.add_argument('-o', '--midi-output', help="connect to specified MIDI output port") args.add_argument('-c', '--debug-camera', action='store_true', help="use a controllable camera") args.add_argument('-s', '--vsync', action='store_true', help="use vsync") args.add_argument('-v', '--verbose', action='store_true', help="increase verbosity") args.add_argument('-w', '--windowed', action='store_true', help="run in a window") args.add_argument('-3', '--stereoscopy', choices=[scene.STEREOSCOPY_OFF, scene.STEREOSCOPY_ANAGLYPH], help="stereoscopy mode") args.add_argument('-e', '--eye-separation', type=float, help="stereoscopic eye separation") opts = args.parse_args(sys.argv[1:]) if opts.verbose: logging.basicConfig(level=logging.DEBUG) else: logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) logger.info("Initializing") sdl2.SDL_Init(sdl2.SDL_INIT_VIDEO) dm = sdl2.SDL_DisplayMode() sdl2.SDL_GetDesktopDisplayMode(0, dm) if not opts.windowed: width = dm.w height = dm.h else: width = round(dm.w * .8) height = round(dm.h * .8) window_flags = sdl2.SDL_WINDOW_OPENGL | (sdl2.SDL_WINDOW_FULLSCREEN if not opts.windowed else 0) window = sdl2.SDL_CreateWindow(TITLE.encode('utf-8'), sdl2.SDL_WINDOWPOS_UNDEFINED, sdl2.SDL_WINDOWPOS_UNDEFINED, width, height, window_flags) context = sdl2.SDL_GL_CreateContext(window) fbo = create_multisampled_fbo(width, height, 0) if opts.vsync: if sdl2.SDL_GL_SetSwapInterval(-1) == -1: logger.warning("Adaptive vsync not available") sdl2.SDL_GL_SetSwapInterval(1) else: sdl2.SDL_GL_SetSwapInterval(0) midi_handler = midi.MidiHandler(opts.midi_input, opts.midi_output) main_scene = scene.Scene((width, height), midi_handler, debug_camera=opts.debug_camera) if opts.stereoscopy is not None: main_scene.set_stereoscopy(opts.stereoscopy) if opts.eye_separation is not None: main_scene.stereoscopy_eye_separation = opts.eye_separation frames = 0 frame_count_time = time.monotonic() ev = sdl2.SDL_Event() running = True while running: while True: if (sdl2.SDL_PollEvent(ev) == 0): break if ev.type == sdl2.SDL_QUIT: running = False elif ev.type == sdl2.SDL_KEYUP and ev.key.keysym.sym == sdl2.SDLK_ESCAPE: running = False elif ev.type == sdl2.SDL_KEYDOWN and ev.key.repeat == 0: main_scene.key_down(sdl2.SDL_GetKeyName(ev.key.keysym.sym).decode('ascii').lower()) elif ev.type == sdl2.SDL_KEYUP and ev.key.repeat == 0: main_scene.key_up(sdl2.SDL_GetKeyName(ev.key.keysym.sym).decode('ascii').lower()) elif ev.type == sdl2.SDL_MOUSEBUTTONDOWN: main_scene.mouse_down(ev.button.button, (ev.button.x / width, ev.button.y / height)) elif ev.type == sdl2.SDL_MOUSEBUTTONUP: main_scene.mouse_up(ev.button.button, (ev.button.x / width, ev.button.y / height)) main_scene.update() main_scene.render() blit_multisampled_fbo(width, height, fbo) sdl2.SDL_GL_SwapWindow(window) frames += 1 now = time.monotonic() if now - frame_count_time > FPS_PRINT_TIME: fps = frames / (now - frame_count_time) frames = 0 frame_count_time = now logger.debug("%.3f FPS", fps) main_scene.shutdown() sdl2.SDL_GL_DeleteContext(context) sdl2.SDL_DestroyWindow(window) sdl2.SDL_Quit()
def get_bind_functions(self, keysym, shift, alt, ctrl): "returns a list of methods for the given key + mods if one exists" keystr = sdl2.SDL_GetKeyName(keysym).decode().lower() key_data = (keystr, shift, alt, ctrl) return self.edit_binds.get(key_data, [])
def handle_input(self, key, shift_pressed, alt_pressed, ctrl_pressed): "handles a key from Application.input" keystr = sdl2.SDL_GetKeyName(key).decode() # TODO: get console bound key from InputLord, detect that instead of # hard-coded backquote if keystr == '`': self.toggle() return elif keystr == 'Return': line = '%s %s' % (self.prompt, self.current_line) self.ui.app.log(line) # if command is same as last, don't repeat it if len(self.command_history) == 0 or ( len(self.command_history) > 0 and self.current_line != self.command_history[-1]): self.command_history.append(self.current_line) self.history_file.write(self.current_line + '\n') self.parse(self.current_line) self.current_line = '' self.history_index = 0 elif keystr == 'Tab': # TODO: autocomplete (commands, filenames) pass elif keystr == 'Up': # page back through command history self.visit_command_history(self.history_index - 1) elif keystr == 'Down': # page forward through command history self.visit_command_history(self.history_index + 1) elif keystr == 'Backspace' and len(self.current_line) > 0: # alt-backspace: delete to last delimiter, eg periods if alt_pressed: # "index to delete to" delete_index = -1 for delimiter in delimiters: this_delimiter_index = self.current_line.rfind(delimiter) if this_delimiter_index > delete_index: delete_index = this_delimiter_index if delete_index > -1: self.current_line = self.current_line[:delete_index] else: self.current_line = '' # user is bailing on whatever they were typing, # reset position in cmd history self.history_index = 0 else: self.current_line = self.current_line[:-1] if len(self.current_line) == 0: # same as above: reset position in cmd history self.history_index = 0 elif keystr == 'Space': keystr = ' ' # ignore any other non-character keys if len(keystr) > 1: return if keystr.isalpha() and not shift_pressed: keystr = keystr.lower() elif not keystr.isalpha() and shift_pressed: keystr = SHIFT_MAP.get(keystr, '') if len(self.current_line) < self.max_line_length: self.current_line += keystr
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 on_bind_key(self, widget, param, section, name, double): controller = None if self.parent.m64p_wrapper.ConfigGetParameter('name') == "Keyboard": device = "keyboard" else: device = "gamepad" stored_name = self.parent.m64p_wrapper.ConfigGetParameter('name') for joy_id, instance in self.active_gamepads.items(): this_name = sdl.SDL_JoystickName(instance).decode("utf-8") if this_name == stored_name: controller = self.active_gamepads[joy_id] # Now we start preparations for the dialog if double == True: # In case we have to bind twice in a single GUI button, e.g. for an axis of the controller if name == "X Axis": first_name = "X Axis (Left)" second_name = "X Axis (Right)" elif name == "Y Axis": first_name = "Y Axis (Up)" second_name = "Y Axis (Down)" first_value = self.binding(widget, param, device, first_name, controller, False) if first_value[1] != None: if first_value[0] != "" and first_value[1] != "empty": if first_value[1] == "Naxis" or first_value[1] == "Paxis": input_type = "axis" else: input_type = first_value[1] different = True while different: # We give some time to the user to reset the control stick to zero. time.sleep(0.2) second_value = self.binding(widget, param, device, second_name, controller, False) if second_value[1] == "Naxis" or second_value[ 1] == "Paxis": input_type2 = "axis" elif second_value[1] == None or second_value[ 1] == "empty": break else: input_type2 = second_value[1] if input_type == input_type2: different = False if second_value[1] != None: if second_value[0] != "" and second_value[1] != "empty": if input_type == "axis": store = input_type + "(" + first_value[0] + ("+" if first_value[1] == "Paxis" else "-") + "," + second_value[0] + \ ("+" if second_value[1] == "Paxis" else "-") + ")" widget.set_label(store) self.parent.m64p_wrapper.ConfigSetParameter( param, store) elif input_type == "button": store = input_type + "(" + first_value[ 0] + "," + second_value[0] + ")" widget.set_label(store) self.parent.m64p_wrapper.ConfigSetParameter( param, store) elif input_type == "key": widget.set_label( "(" + sdl.SDL_GetKeyName( int(first_value[0])).decode("utf-8") + ", " + sdl.SDL_GetKeyName( int(second_value[0])).decode("utf-8") + ")") else: widget.set_label("(empty)") self.parent.m64p_wrapper.ConfigSetParameter( param, second_value[0]) else: widget.set_label("(empty)") self.parent.m64p_wrapper.ConfigSetParameter( param, first_value[0]) else: self.binding(widget, param, device, name, controller, True)
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)