def test_evbit_string(self): self.assertEqual(evbit('EV_SYN'), libevdev.EV_SYN) self.assertEqual(evbit('EV_KEY'), libevdev.EV_KEY) self.assertEqual(evbit('EV_REL'), libevdev.EV_REL) for t in libevdev.types: self.assertEqual(evbit(t.name), t)
def __key2seq(self, key, downup=1): """ 构建事件序列。 """ if downup == 1 or downup == 0: pass else: raise ValueError("param: downup chioce 1 or 0.") shiftdown = [ InputEvent(ev.EV_MSC.MSC_SCAN, value=4), InputEvent(evbit("KEY_LEFTSHIFT"), value=1), InputEvent(ev.EV_SYN.SYN_REPORT, value=0) ] shiftup = [ InputEvent(ev.EV_MSC.MSC_SCAN, value=4), InputEvent(evbit("KEY_LEFTSHIFT"), value=0), InputEvent(ev.EV_SYN.SYN_REPORT, value=0) ] key_seq = [ InputEvent(ev.EV_MSC.MSC_SCAN, value=4), InputEvent(evbit("KEY_" + key.upper()), value=downup), InputEvent(ev.EV_SYN.SYN_REPORT, value=0) ] if key.isupper(): event_seq = shiftdown + key_seq + shiftup elif key.islower(): event_seq = key_seq elif key.isdigit(): event_seq = key_seq return event_seq
def __init__(self, uuid): self._events_state = { "ABS_X": None, "ABS_Y": None, "ABS_RX": None, "ABS_RY": None, "BTN_NORTH": None, "BTN_SOUTH": None, "BTN_EAST": None, "BTN_WEST": None } self._axis_min = -32768 self._axis_max = 32767 self.device = evd.Device() self.device.name = f"gamepad-{uuid}" for ev in list(self._events_state.keys())[:4]: self.device.enable( evd.evbit(ev), evd.InputAbsInfo(minimum=self._axis_min, maximum=self._axis_max)) for ev in list(self._events_state.keys())[4:]: self.device.enable(evd.evbit(ev)) self.uinput_node = self.device.create_uinput_device() print("New device at {} ({})".format(self.uinput_node.devnode, self.uinput_node.syspath)) time.sleep( 1) # Give everything time to register that the device exists
def pipe_device(args, remote_device, local_device): """ Pipe events from a remote device to a local device. Args: args: argparse arguments remote_device (paramiko.ChannelFile): read-only stream of input events local_device: local virtual input device to write events to """ import libevdev # While debug mode is active, we log events grouped together between # SYN_REPORT events. Pending events for the next log are stored here pending_events = [] while True: e_time, e_millis, e_type, e_code, e_value = struct.unpack( '2IHHi', remote_device.read(16)) e_bit = libevdev.evbit(e_type, e_code) event = libevdev.InputEvent(e_bit, value=e_value) local_device.send_events([event]) if args.debug: if e_bit == libevdev.EV_SYN.SYN_REPORT: event_repr = ', '.join( '{} = {}'.format(event.code.name, event.value) for event in pending_events) log.debug('{}.{:0>6} - {}'.format(e_time, e_millis, event_repr)) pending_events = [] else: pending_events += [event]
def __init__(self, main_instance, *args, **kw): super().__init__(*args, daemon=True, **kw) self.main_instance = main_instance args = main_instance.args self.event_queue = main_instance.event_queue self.device = libevdev.Device() self.device.name = "Dualkeys uinput" for i in range(libevdev.EV_KEY.max): self.device.enable(libevdev.evbit(1, i)) self.ui = self.device.create_uinput_device() time.sleep(0.5) self.do_print = main_instance.args.print # print(args.pre_emptive_mods) self.pre_emptive_mods = set(args.pre_emptive_mods) self.do_pre_emptive = len(self.pre_emptive_mods) > 0 self.kill_switches = args.kill_switch self.handle_repeat = args.repeat_timeout is not None self.repeat_timeout = float( args.repeat_timeout) / 1000 if self.handle_repeat else None self.do_timing = main_instance.args.timing self.repeat_keys = args.repeat_keys self.handle_idle = args.idle_timeout is not None self.idle_timeout = float( args.idle_timeout) / 1000 if self.handle_idle else None self.idle_keys = args.idle_keys self.angry_keys = args.angry_keys self.angry_key_directory = args.angry_key_directory self.ignore_keys = args.ignore if len(self.angry_keys) > 0 or args.save_history: self.history = deque(maxlen=args.angry_key_history) else: self.history = None # Define registered keys self.registered_keys = {} if args.key is not None: for keys in args.key: self.registered_keys[keys[0]] = DualKey(*keys) self.print_registered_keys() self.swap_keys = {} if args.swap_key is not None: for keys in args.swap_key: self.swap_keys[keys[0]] = SwapKey(*keys) print(self.swap_keys) # Main status indicator self.event_list = DLList() self.conflict_list = {} # TODO: not a good name if it's a dict self.key_counter = {} self.resolution_dict = {} # Remember currently unresolved keys self.pre_emptive_dict = {} self.back_links = {} self.last_pressed = {} self.last_pressed_key = None self.error_queue = self.main_instance.error_queue
def __init__(self, hid, evdev=None): self.hid = hid.lower() if evdev is None: evdev = f'ABS_{hid.upper()}' self.evdev = libevdev.evbit('EV_ABS', evdev)
def event_loop_inner(self, event): if event.matches(libevdev.EV_SYN): # logging.info(str(event)) if event.matches(libevdev.EV_SYN.SYN_DROPPED): # if True: logging.info("SYN_DROPPED received") if event.matches(libevdev.EV_KEY): key_event = KeyEvent(event, grab=self.grab) scancode = key_event.code if False: if scancode not in self.pressed_dict: self.pressed_dict[scancode] = 0 if key_event.keystate == 0: self.last_pressed_up[scancode] = event self.pressed_dict[scancode] -= 1 elif key_event.keystate == 1: self.pressed_dict[scancode] += 1 elif key_event.keystate == 2: pass logging.info("[" + ",".join([f"({libevdev.evbit(1,k)}, {v})" \ for k, v in self.pressed_dict.items() \ if v != 0 and libevdev.evbit(1,k) is not None]) + "]") # elif key_event.keystate == 2: # device_wrapper.last_pressed_repeat[key_event.scancode] = key_event # future = self.loop.create_task(self.clear_on_timeout(device_wrapper = device_wrapper, # key_event = key_event, timeout = self.keyrepeat_timeout)) # future.add_done_callback(self.future_callback_error_logger) self.event_queue.put((self.device_node, key_event))
def watch(self): """ return: callback function """ while True: for key, event_ in self._selector.select(): logger.debug("self._selector.select()") devfd = key.data hotkey = self._hotkey_seq_dict try: for e in devfd.events(): if e.matches(ev.evbit("EV_KEY")): logger.debug( f"key: {e.code.name} value: {e.value}") logger.debug(f"hotkey: {hotkey}") logger.debug("-" * 60) # 这个事件在hotkey seq if e.value == 1 and e.code in hotkey: hotkey = hotkey.get(e.code) if hasattr(hotkey, "__call__"): return hotkey elif e.value == 0 and e.code in hotkey: hotkey = self._hotkey_seq_dict except EventsDroppedException: logger.warning("EventsDroppedException") for e in devfd.sync(): logger.debug(e) logger.debug("从这里返回的????")
def generate_keysym_line(code, kernel, kver_list=[]): """ Generate the line to append to the keysym file. This format is semi-ABI, scripts rely on the format of this line (e.g. in xkeyboard-config). """ evcode = libevdev.evbit(libevdev.EV_KEY.value, code) if not evcode.is_defined: # codes without a #define in the kernel return None if evcode.name.startswith("BTN_"): return None name = "".join( [s.capitalize() for s in evcode.name[4:].lower().split("_")]) keysym = f"XF86XK_{name}" tabs = 4 - len(keysym) // 8 kver = kernel.introduced_in_version(evcode.name) or " " if kver_list: from fnmatch import fnmatch allowed_kvers = [v.strip() for v in kver_list.split(",")] for allowed in allowed_kvers: if fnmatch(kver, allowed): break else: # no match return None return f"#define {keysym}{' ' * tabs}_EVDEVK(0x{code:03X}) /* {kver:5s} {evcode.name} */"
def _sync(self): events = [] for ev in self._events_state.keys(): if self._events_state[ev] is not None: events.append(InputEvent(evd.evbit(ev), self._events_state[ev])) events.append(InputEvent(evd.EV_SYN.SYN_REPORT, 0)) self.uinput_node.send_events(events)
def _read_config(self, config_path): if not os.path.exists(config_path): raise Exception(f'Missing config file {config_path}') config = configparser.ConfigParser() # disable the lowercase conversion config.optionxform = lambda option: option config.read(config_path) if ('General' not in config.sections() or 'Macros' not in config.sections()): raise Exception('Invalid Config file, sections missing') entry = config['General'].get('ModeKey') if entry is None: raise Exception(f'Missing entry ModeKey') mode_key = libevdev.evbit(f'KEY_{entry}') if mode_key is None: raise Exception(f'Unable to map ModeKey={entry}') macros = {} for key, value in config['Macros'].items(): keybit = libevdev.evbit(f'KEY_{key}') if keybit is None: raise Exception(f'Unable to map key {key}') macro = [] for m in value.split(' '): keyname = m press = True release = True if m[0] == '+': # press only keyname = m[1:] release = False elif m[0] == '-': # release only keyname = m[1:] press = False bit = libevdev.evbit(f'KEY_{keyname}') if bit is None: raise Exception(f'Unable to map key {keyname}') if press: macro.append((bit, 1)) if release: macro.append((bit, 0)) macros[keybit] = macro return mode_key, macros
def read_tablet(rm_inputs, *, orientation, monitor_num, region, threshold, mode): """Loop forever and map evdev events to mouse Args: rm_inputs (dictionary of paramiko.ChannelFile): dict of pen, button and touch input streams orientation (str): tablet orientation monitor_num (int): monitor number to map to region (boolean): whether to selection mapping region with region tool threshold (int): pressure threshold mode (str): mapping mode """ from pynput.mouse import Button, Controller mouse = Controller() monitor = get_monitor(region, monitor_num, orientation) log.debug('Chose monitor: {}'.format(monitor)) x = y = 0 while True: _, _, e_type, e_code, e_value = struct.unpack( '2IHHi', rm_inputs['pen'].read(16)) e_bit = libevdev.evbit(e_type, e_code) e = libevdev.InputEvent(e_bit, value=e_value) if e.matches(EV_ABS): # handle x direction if e.matches(EV_ABS.ABS_Y): log.debug(e.value) x = e.value # handle y direction if e.matches(EV_ABS.ABS_X): log.debug('\t{}'.format(e.value)) y = e.value # handle draw if e.matches(EV_KEY.BTN_TOUCH): log.debug('\t\t{}'.format(e.value)) if e.value == 1: log.debug('PRESS') mouse.press(Button.left) else: log.debug('RELEASE') mouse.release(Button.left) if e.matches(EV_SYN): mapped_x, mapped_y = remap(x, y, wacom_width, wacom_height, monitor.width, monitor.height, mode, orientation) mouse.move(monitor.x + mapped_x - mouse.position[0], monitor.y + mapped_y - mouse.position[1])
def test_hat_switch(self, hat_value, expected_evdev, evdev_value): uhdev = self.uhdev r = uhdev.event(hat_switch=hat_value) events = uhdev.next_sync_events() self.debug_reports(r, uhdev, events) assert libevdev.InputEvent( libevdev.evbit('EV_ABS', expected_evdev), evdev_value) in events
def parse_single_key(k): k = k.strip().upper() if not k.startswith("KEY_") and not k.startswith("BTN_"): k = "KEY_" + k event = libevdev.evbit(k) if event is None: raise argparse.ArgumentTypeError( f"{k} is not a valid key identifier, you might want to try Dualkeys.py -p " "to obtain valid identifiers.") return event.value
def read_tablet(rm_inputs, *, orientation, monitor, threshold, mode): """Pipe rM evdev events to local device Args: rm_inputs (dictionary of paramiko.ChannelFile): dict of pen, button and touch input streams orientation (str): tablet orientation monitor (int): monitor number to map to threshold (int): pressure threshold mode (str): mapping mode """ local_device = create_local_device() log.debug("Created virtual input device '{}'".format(local_device.devnode)) configure_xinput( orientation=orientation, monitor=monitor, threshold=threshold, mode=mode, ) import libevdev pending_events = [] # loop inputs forever # for input_name, stream in cycle(rm_inputs.items()): stream = rm_inputs['pen'] while True: try: data = stream.read(16) except TimeoutError: continue e_time, e_millis, e_type, e_code, e_value = struct.unpack( '2IHHi', data) e_bit = libevdev.evbit(e_type, e_code) event = libevdev.InputEvent(e_bit, value=e_value) local_device.send_events([event]) # While debug mode is active, we log events grouped together between # SYN_REPORT events. Pending events for the next log are stored here if log.level == logging.DEBUG: if e_bit == libevdev.EV_SYN.SYN_REPORT: event_repr = ', '.join( '{} = {}'.format(event.code.name, event.value) for event in pending_events) log.debug('{}.{:0>6} - {}'.format(e_time, e_millis, event_repr)) pending_events = [] else: pending_events.append(event)
def addhotkey(self, keyseq=(), callback=print): """ keyseq: ["alt", "f"] callback: function() """ if not isinstance(keyseq, tuple): raise HotKeyError("键序列必须是 tuple !") # 预处理 seq = [] for key in keyseq: key = key.upper() if key == "ALT": key = "LEFTALT" elif key == "CTRL": key = "LEFTCTRL" key = ev.evbit("KEY_" + key) seq.append(key) seq_len = len(seq) # 判断是否是最后一个键 last_key = 0 current_keyseq = self._hotkey_seq_dict for key in seq: last_key += 1 if hasattr(current_keyseq, "__call__"): raise HotKeyError(f"{seq} 的父键已存在!") if key in current_keyseq: if last_key < seq_len: current_keyseq = current_keyseq[key] elif last_key == seq_len and key in current_keyseq: raise HotKeyError(f"{seq} 按键序列已存在!") else: current_keyseq[key] = callback self._hotkey_list.append(seq) else: if last_key == seq_len: current_keyseq[key] = callback self._hotkey_list.append(seq) else: # 一个新的快捷键序列 current_keyseq[key] = {} current_keyseq = current_keyseq[key]
def test_dual_buttons(self): """check for button reliability when pressing 2 buttons""" uhdev = self.uhdev evdev = uhdev.get_evdev() syn_event = self.syn_event # can change intended b1 b2 values b1 = uhdev.buttons[0] key1 = libevdev.evbit(uhdev.buttons_map[b1]) b2 = uhdev.buttons[1] key2 = libevdev.evbit(uhdev.buttons_map[b2]) buttons = {b1: True, b2: True} r = uhdev.event(buttons=buttons) expected_event0 = libevdev.InputEvent(key1, 1) expected_event1 = libevdev.InputEvent(key2, 1) events = uhdev.next_sync_events() self.debug_reports(r, uhdev, events) self.assertInputEventsIn( (syn_event, expected_event0, expected_event1), events) assert evdev.value[key1] == 1 assert evdev.value[key2] == 1 buttons = {b1: False, b2: None} r = uhdev.event(buttons=buttons) r = uhdev.event(buttons={b1: False, b2: None}) expected_event = libevdev.InputEvent(key1, 0) events = uhdev.next_sync_events() self.debug_reports(r, uhdev, events) self.assertInputEventsIn((syn_event, expected_event), events) assert evdev.value[key1] == 0 assert evdev.value[key2] == 1 buttons = {b1: None, b2: False} r = uhdev.event(buttons=buttons) expected_event = libevdev.InputEvent(key2, 0) events = uhdev.next_sync_events() self.debug_reports(r, uhdev, events) self.assertInputEventsIn((syn_event, expected_event), events) assert evdev.value[key1] == 0 assert evdev.value[key2] == 0
def events(): """ Yields the next event in the recording """ for event in device["events"]: for evdev in event.get("evdev", []): yield libevdev.InputEvent( code=libevdev.evbit(evdev[2], evdev[3]), value=evdev[4], sec=evdev[0], usec=evdev[1], )
def from_name(cls, name: str, type: str) -> "Device": """ Create a Device from the given name with the given type (pen, pad, finger). This method iterates through the test/devices/*.yml files and finds the file for the device with the given name, then loads the matching event node for that type. """ type = type.lower() assert type.lower() in ("pen", "pad", "finger") for ymlfile in Path("test/devices").glob("*.yml"): with open(ymlfile) as fd: yml = yaml.safe_load(fd) logger.debug(f"Found device: {yml['name']}") if yml["name"].upper() != name.upper(): continue for d in yml["devices"]: if d["type"] != type: continue name = d["name"] id = InputId.from_list([int(i, 16) for i in d["id"]]) bits = [libevdev.evbit(b) for b in d["bits"]] abs = { libevdev.evbit(n): libevdev.InputAbsInfo(*v) for n, v in d["abs"].items() } props = [libevdev.propbit(p) for p in d["props"]] return Device(name=name, id=id, bits=bits, absinfo=abs, props=props) raise ValueError( f"Device '{name}' does not have type '{type}'") raise ValueError(f"Device '{name}' does not exist")
def scale(self, device: Device) -> Ev: value = self.value if self.name.startswith("ABS_") and self.name not in [ "ABS_MT_SLOT", "ABS_MT_TRACKING_ID", ]: evbit = libevdev.evbit(self.name) absinfo = device.absinfo[evbit] value = ( absinfo.minimum + (absinfo.maximum - absinfo.minimum + 1) * self.value / 100.0) return Ev(self.name, int(value))
def test_evbit(self): self.assertEqual(evbit(0, 0), libevdev.EV_SYN.SYN_REPORT) self.assertEqual(evbit(1, 30), libevdev.EV_KEY.KEY_A) self.assertEqual(evbit(2, 1), libevdev.EV_REL.REL_Y) self.assertEqual(evbit(0), libevdev.EV_SYN) self.assertEqual(evbit(1), libevdev.EV_KEY) self.assertEqual(evbit(2), libevdev.EV_REL) for t in libevdev.types: self.assertEqual(evbit(t.value), t)
def main(args): path = args[1] code_from = libevdev.evbit(args[2]) code_to = libevdev.evbit(args[3]) print('Remapping {} to {}'.format(code_from, code_to)) fd = open(path, 'rb') d = libevdev.Device(fd) d.grab() # create a duplicate of our input device d.enable(code_to) # make sure the code we map to is available uidev = d.create_uinput_device() print('Device is at {}'.format(uidev.devnode)) while True: for e in d.events(): # change any event with our event code to # the one we want to map to, but pass all other events # through if e.code == code_from: e = libevdev.InputEvent(code_to, e.value) uidev.send_events([e])
def create(device): evdev = fetch(device, "evdev") d = libevdev.Device() d.name = fetch(evdev, "name") ids = fetch(evdev, "id") if len(ids) != 4: raise YamlException("Invalid ID format: {}".format(ids)) d.id = dict(zip(["bustype", "vendor", "product", "version"], ids)) codes = fetch(evdev, "codes") for evtype, evcodes in codes.items(): for code in evcodes: data = None if evtype == libevdev.EV_ABS.value: values = fetch(evdev, "absinfo")[code] absinfo = libevdev.InputAbsInfo( minimum=values[0], maximum=values[1], fuzz=values[2], flat=values[3], resolution=values[4], ) data = absinfo elif evtype == libevdev.EV_REP.value: if code == libevdev.EV_REP.REP_DELAY.value: data = 500 elif code == libevdev.EV_REP.REP_PERIOD.value: data = 20 d.enable(libevdev.evbit(evtype, code), data=data) properties = fetch(evdev, "properties") for prop in properties: d.enable(libevdev.propbit(prop)) uinput = d.create_uinput_device() check_udev_properties(device, uinput) return uinput
def collect_events(frame): evs = [] events_skipped = False for (sec, usec, evtype, evcode, value) in frame: if evtype == libevdev.EV_KEY.value and value == 2: # key repeat events_skipped = True continue e = libevdev.InputEvent(libevdev.evbit(evtype, evcode), value=value, sec=sec, usec=usec) evs.append(e) # If we skipped some events and now all we have left is the # SYN_REPORTs, we drop the SYN_REPORTs as well. if events_skipped and all( e for e in evs if e.matches(libevdev.EV_SYN.SYN_REPORT)): return [] else: return evs
def replay(device, verbose): events = fetch(device, "events") if events is None: return uinput = device["__uinput"] # The first event may have a nonzero offset but we want to replay # immediately regardless. When replaying multiple devices, the first # offset is the offset from the first event on any device. offset = time.time() - device["__first_event_offset"] if offset < 0: error( "WARNING: event time offset is in the future, refusing to replay") return # each 'evdev' set contains one SYN_REPORT so we only need to check for # the time offset once per event for event in events: try: evdev = fetch(event, "evdev") except YamlException: continue (sec, usec, evtype, evcode, value) = evdev[0] evtime = sec + usec / 1e6 + offset now = time.time() if evtime - now > 150 / 1e6: # 150 µs error margin time.sleep(evtime - now - 150 / 1e6) evs = [ libevdev.InputEvent(libevdev.evbit(e[2], e[3]), value=e[4], sec=e[0], usec=e[1]) for e in evdev ] uinput.send_events(evs) if verbose: print_events(uinput.devnode, device["__index"], evs)
def sync(self, force=False): """ Returns an iterator with events pending to re-sync the caller's view of the device with the one from libevdev. :param force: if set, the device forces an internal sync. This is required after changing the fd of the device when the device state may have changed while libevdev was not processing events. """ if self._libevdev.fd is None: return [] if force: flags = READ_FLAG_FORCE_SYNC else: flags = READ_FLAG_SYNC ev = self._libevdev.next_event(flags) while ev is not None: code = libevdev.evbit(ev.type, ev.code) yield InputEvent(code, ev.value, ev.sec, ev.usec) ev = self._libevdev.next_event(flags)
def assert_button(self, button): uhdev = self.uhdev syn_event = self.syn_event buttons = {} key = libevdev.evbit(uhdev.buttons_map[button]) buttons[button] = True r = uhdev.event(buttons=buttons) expected_event = libevdev.InputEvent(key, 1) events = uhdev.next_sync_events() self.debug_reports(r, uhdev, events) self.assertInputEventsIn((syn_event, expected_event), events) assert uhdev.evdev.value[key] == 1 buttons[button] = False r = uhdev.event(buttons=buttons) expected_event = libevdev.InputEvent(key, 0) events = uhdev.next_sync_events() self.debug_reports(r, uhdev, events) self.assertInputEventsIn((syn_event, expected_event), events) assert uhdev.evdev.value[key] == 0
def events(self): """ Returns an iterable with currently pending events. Event processing should look like this:: fd = open("/dev/input/event0", "rb") ctx = libevdev.Device(fd) while True: for e in ctx.events(): print(e): This function detects if the file descriptor is in blocking or non-blocking mode and adjusts its behavior accordingly. If the file descriptor is in nonblocking mode and no events are available, this function returns immediately. If the file descriptor is blocking, this function blocks if there are no events available. :returns: an iterable with the currently pending events """ if self._libevdev.fd is None: return [] if os.get_blocking(self._libevdev.fd.fileno()): flags = READ_FLAG_BLOCKING else: flags = READ_FLAG_NORMAL ev = self._libevdev.next_event(flags) while ev is not None: code = libevdev.evbit(ev.type, ev.code) yield InputEvent(code, ev.value, ev.sec, ev.usec) if code == libevdev.EV_SYN.SYN_DROPPED: raise EventsDroppedException() ev = self._libevdev.next_event(flags)
def __add_mouse_keyboard_events(self): """ 为虚拟键鼠添加鼠标事件。 """ self.device.enable(ev.EV_MSC.MSC_SCAN) # 鼠标事件类型 mouse0 = [ evbit(0, i) for i in range(15) ] mouse1 = [ evbit(1, i) for i in range(272, 276+1) ] mouse2 = [ evbit(2, 0), evbit(2, 1), evbit(2, 8), evbit(2, 11) ] mouse = mouse0 + mouse1 + mouse2 # 键盘事件类型 keyboard = [ evbit(1, i) for i in range(1, 128+1) ] self.events = mouse + keyboard # LEDs #led = [ evbit(17, 0), evbit(17, 1), evbit(17, 2) ] for e in self.events: self.device.enable(e)
def __init__(self): self._delay = 0.01 # 鼠标事件类型 mouse0 = [evbit(0, i) for i in range(15)] mouse1 = [evbit(1, i) for i in range(272, 276 + 1)] mouse2 = [evbit(2, 0), evbit(2, 1), evbit(2, 8), evbit(2, 11)] mouse = mouse0 + mouse1 + mouse2 # 键盘事件类型 keyboard = [evbit(1, i) for i in range(1, 128 + 1)] self.events = mouse + keyboard # key event 可输出的字符元组 self.keyevents2strings = [] for e in self.events: if e.name.startswith("KEY_"): self.keyevents2strings.append(e.name.lstrip("KEY_")) elif e.name.startswith("BTN_"): self.keyevents2strings.append(e.name.lstrip("BTN_"))