Активация кода путем нажатия клавиши мыши (Python)

код на питоне, пример: мне нужно чтобы, когда я нажимал в реальной жизни, на левую кнопку мыши - выводилось "Hello World", а при отпускании этой кнопки писалось "Goodbye world". Не могу решить прошу помогите!


Ответы (2 шт):

Автор решения: Amgarak

Решил вам немного помочь, пример довольно простой (немного избыточный наверное), устанавливаем Hook -> обрабатываем event от мышки -> через генератор обрабатываем строку -> посылаем нажатия клавиши через winAPI. Если сами не разберётесь, спрашивайте ?:

import ctypes
import time
import string
import win32con
import win32api
import win32gui
import atexit
from collections import namedtuple
import threading
import queue

class InputSimulator:
    def __init__(self):
        # Константы для ввода мыши
        self.WHEEL_DELTA = 120
        self.XBUTTON1 = 0x0001
        self.XBUTTON2 = 0x0002
        self.MOUSEEVENTF_ABSOLUTE = 0x8000
        self.MOUSEEVENTF_HWHEEL = 0x01000
        self.MOUSEEVENTF_MOVE = 0x0001
        self.MOUSEEVENTF_MOVE_NOCOALESCE = 0x2000
        self.MOUSEEVENTF_LEFTDOWN = 0x0002
        self.MOUSEEVENTF_LEFTUP = 0x0004
        self.MOUSEEVENTF_RIGHTDOWN = 0x0008
        self.MOUSEEVENTF_RIGHTUP = 0x0010
        self.MOUSEEVENTF_MIDDLEDOWN = 0x0020
        self.MOUSEEVENTF_MIDDLEUP = 0x0040
        self.MOUSEEVENTF_VIRTUALDESK = 0x4000
        self.MOUSEEVENTF_WHEEL = 0x0800
        self.MOUSEEVENTF_XDOWN = 0x0080
        self.MOUSEEVENTF_XUP = 0x0100

        # Константы для ввода клавиатуры
        self.KEYEVENTF_EXTENDEDKEY = 0x0001
        self.KEYEVENTF_KEYUP = 0x0002
        self.KEYEVENTF_SCANCODE = 0x0008
        self.KEYEVENTF_UNICODE = 0x0004

        # Константы для типов ввода
        self.INPUT_MOUSE = 0
        self.INPUT_KEYBOARD = 1
        self.INPUT_HARDWARE = 2

        # Коды клавиш
        self.VK_LBUTTON = 0x01               
        self.VK_RBUTTON = 0x02               
        self.VK_CANCEL = 0x03                
        self.VK_MBUTTON = 0x04               
        self.VK_XBUTTON1 = 0x05              
        self.VK_XBUTTON2 = 0x06              
        self.VK_BACK = 0x08                  
        self.VK_TAB = 0x09                  
        self.VK_CLEAR = 0x0C                 
        self.VK_RETURN = 0x0D                
        self.VK_SHIFT = 0x10                 
        self.VK_CONTROL = 0x11               
        self.VK_MENU = 0x12                  
        self.VK_PAUSE = 0x13                 
        self.VK_CAPITAL = 0x14                
        self.VK_KANA = 0x15                  
        self.VK_HANGUL = 0x15                
        self.VK_JUNJA = 0x17                 
        self.VK_FINAL = 0x18                 
        self.VK_HANJA = 0x19                 
        self.VK_KANJI = 0x19                 
        self.VK_ESCAPE = 0x1B                
        self.VK_CONVERT = 0x1C              
        self.VK_NONCONVERT = 0x1D            
        self.VK_ACCEPT = 0x1E                
        self.VK_MODECHANGE = 0x1F            
        self.VK_SPACE = 0x20                 
        self.VK_PRIOR = 0x21                 
        self.VK_NEXT = 0x22                  
        self.VK_END = 0x23                   
        self.VK_HOME = 0x24                 
        self.VK_LEFT = 0x25                  
        self.VK_UP = 0x26                    
        self.VK_RIGHT = 0x27                 
        self.VK_DOWN = 0x28                  
        self.VK_SELECT = 0x29                
        self.VK_PRINT = 0x2A                 
        self.VK_EXECUTE = 0x2B               
        self.VK_SNAPSHOT = 0x2C              
        self.VK_INSERT = 0x2D                
        self.VK_DELETE = 0x2E                
        self.VK_HELP = 0x2F                  
        self.VK_LWIN = 0x5B                  
        self.VK_RWIN = 0x5C                  
        self.VK_APPS = 0x5D                  
        self.VK_SLEEP = 0x5F                 
        self.VK_NUMPAD0 = 0x60               
        self.VK_NUMPAD1 = 0x61               
        self.VK_NUMPAD2 = 0x62               
        self.VK_NUMPAD3 = 0x63               
        self.VK_NUMPAD4 = 0x64               
        self.VK_NUMPAD5 = 0x65               
        self.VK_NUMPAD6 = 0x66               
        self.VK_NUMPAD7 = 0x67               
        self.VK_NUMPAD8 = 0x68               
        self.VK_NUMPAD9 = 0x69               
        self.VK_MULTIPLY = 0x6A              
        self.VK_ADD = 0x6B                   
        self.VK_SEPARATOR = 0x6C             
        self.VK_SUBTRACT = 0x6D              
        self.VK_DECIMAL = 0x6E               
        self.VK_DIVIDE = 0x6F                
        self.VK_F1 = 0x70                    
        self.VK_F2 = 0x71                    
        self.VK_F3 = 0x72                    
        self.VK_F4 = 0x73                    
        self.VK_F5 = 0x74                    
        self.VK_F6 = 0x75                    
        self.VK_F7 = 0x76                    
        self.VK_F8 = 0x77                    
        self.VK_F9 = 0x78                   
        self.VK_F10 = 0x79                   
        self.VK_F11 = 0x7A                   
        self.VK_F12 = 0x7B                   
        self.VK_F13 = 0x7C                   
        self.VK_F14 = 0x7D                   
        self.VK_F15 = 0x7E                   
        self.VK_F16 = 0x7F                   
        self.VK_F17 = 0x80                   
        self.VK_F18 = 0x81                   
        self.VK_F19 = 0x82                   
        self.VK_F20 = 0x83                   
        self.VK_F21 = 0x84                   
        self.VK_F22 = 0x85                   
        self.VK_F23 = 0x86                   
        self.VK_F24 = 0x87                   
        self.VK_NUMLOCK = 0x90               
        self.VK_SCROLL = 0x91                
        self.VK_LSHIFT = 0xA0                
        self.VK_RSHIFT = 0xA1                
        self.VK_LCONTROL = 0xA2              
        self.VK_RCONTROL = 0xA3              
        self.VK_LMENU = 0xA4                 
        self.VK_RMENU = 0xA5                 
        self.VK_BROWSER_BACK = 0xA6          
        self.VK_BROWSER_FORWARD = 0xA7       
        self.VK_BROWSER_REFRESH = 0xA8       
        self.VK_BROWSER_STOP = 0xA9          
        self.VK_BROWSER_SEARCH = 0xAA        
        self.VK_BROWSER_FAVORITES = 0xAB     
        self.VK_BROWSER_HOME = 0xAC          
        self.VK_VOLUME_MUTE = 0xAD           
        self.VK_VOLUME_DOWN = 0xAE           
        self.VK_VOLUME_UP = 0xAF             
        self.VK_MEDIA_NEXT_TRACK = 0xB0      
        self.VK_MEDIA_PREV_TRACK = 0xB1      
        self.VK_MEDIA_STOP = 0xB2            
        self.VK_MEDIA_PLAY_PAUSE = 0xB3      
        self.VK_LAUNCH_MAIL = 0xB4           
        self.VK_LAUNCH_MEDIA_SELECT = 0xB5   
        self.VK_LAUNCH_APP1 = 0xB6           
        self.VK_LAUNCH_APP2 = 0xB7           
        self.VK_OEM_1 = 0xBA                 
                                        
        self.VK_OEM_PLUS = 0xBB              
        self.VK_OEM_COMMA = 0xBC             
        self.VK_OEM_MINUS = 0xBD             
        self.VK_OEM_PERIOD = 0xBE            
        self.VK_OEM_2 = 0xBF                 
                                        
        self.VK_OEM_3 = 0xC0                 
                                        
        self.VK_OEM_4 = 0xDB                
                                        
        self.VK_OEM_5 = 0xDC                 
                                       
        self.VK_OEM_6 = 0xDD                 
                                        
        self.VK_OEM_7 = 0xDE                 
                                        
        self.VK_OEM_8 = 0xDF                 
        self.VK_OEM_102 = 0xE2               
        self.VK_PROCESSKEY = 0xE5            
        self.VK_PACKET = 0xE7                
        self.VK_ATTN = 0xF6                  
        self.VK_CRSEL = 0xF7                 
        self.VK_EXSEL = 0xF8                 
        self.VK_EREOF = 0xF9                 
        self.VK_PLAY = 0xFA                  
        self.VK_ZOOM = 0xFB                  
        self.VK_PA1 = 0xFD                   
        self.VK_OEM_CLEAR = 0xFE             

        self.KEYEVENTF_EXTENDEDKEY = 0x0001
        self.KEYEVENTF_KEYUP = 0x0002
        self.KEYEVENTF_SCANCODE = 0x0008
        self.KEYEVENTF_UNICODE = 0x0004

        self.KEY_0 = 0x30
        self.KEY_1 = 0x31
        self.KEY_2 = 0x32
        self.KEY_3 = 0x33
        self.KEY_4 = 0x34
        self.KEY_5 = 0x35
        self.KEY_6 = 0x36
        self.KEY_7 = 0x37
        self.KEY_8 = 0x38
        self.KEY_9 = 0x39
        self.KEY_A = 0x41
        self.KEY_B = 0x42
        self.KEY_C = 0x43
        self.KEY_D = 0x44
        self.KEY_E = 0x45
        self.KEY_F = 0x46
        self.KEY_G = 0x47
        self.KEY_H = 0x48
        self.KEY_I = 0x49
        self.KEY_J = 0x4A
        self.KEY_K = 0x4B
        self.KEY_L = 0x4C
        self.KEY_M = 0x4D
        self.KEY_N = 0x4E
        self.KEY_O = 0x4F
        self.KEY_P = 0x50
        self.KEY_Q = 0x51
        self.KEY_R = 0x52
        self.KEY_S = 0x53
        self.KEY_T = 0x54
        self.KEY_U = 0x55
        self.KEY_V = 0x56
        self.KEY_W = 0x57
        self.KEY_X = 0x58
        self.KEY_Y = 0x59
        self.KEY_Z = 0x5A

        # Объявление структур
        class MOUSEINPUT(ctypes.Structure):
            _fields_ = [
                ('dx', ctypes.c_long),
                ('dy', ctypes.c_long),
                ('mouseData', ctypes.c_ulong),
                ('dwFlags', ctypes.c_ulong),
                ('time', ctypes.c_ulong),
                ('dwExtraInfo', ctypes.POINTER(ctypes.c_ulong))
            ]

        class KEYBDINPUT(ctypes.Structure):
            _fields_ = [
                ('wVk', ctypes.c_ushort),
                ('wScan', ctypes.c_ushort),
                ('dwFlags', ctypes.c_ulong),
                ('time', ctypes.c_ulong),
                ('dwExtraInfo', ctypes.POINTER(ctypes.c_ulong))
            ]

        class HARDWAREINPUT(ctypes.Structure):
            _fields_ = [
                ('uMsg', ctypes.c_ulong),
                ('wParamL', ctypes.c_ushort),
                ('wParamH', ctypes.c_ushort)
            ]

        class _INPUTunion(ctypes.Union):
            _fields_ = [
                ('mi', MOUSEINPUT),
                ('ki', KEYBDINPUT),
                ('hi', HARDWAREINPUT)
            ]

        class INPUT(ctypes.Structure):
            _fields_ = [
                ('type', ctypes.c_ulong),
                ('union', _INPUTunion)
            ]

        self.MOUSEINPUT = MOUSEINPUT
        self.KEYBDINPUT = KEYBDINPUT
        self.HARDWAREINPUT = HARDWAREINPUT
        self._INPUTunion = _INPUTunion
        self.INPUT = INPUT

    def SendInput(self, *inputs):
        nInputs = len(inputs)
        LPINPUT = self.INPUT * nInputs
        pInputs = LPINPUT(*inputs)
        cbSize = ctypes.c_int(ctypes.sizeof(inputs[0]))
        return ctypes.windll.user32.SendInput(nInputs, pInputs, cbSize)

    def MouseInput(self, flags, x, y, data):
        return self.MOUSEINPUT(x, y, data, flags, 0, None)

    def KeybdInput(self, code, flags):
        return self.KEYBDINPUT(code, code, flags, 0, None)

    def HardwareInput(self, message, parameter):
        return self.HARDWAREINPUT(message & 0xFFFFFFFF, parameter & 0xFFFF, parameter >> 16 & 0xFFFF)

    def Mouse(self, flags, x=0, y=0, data=0):
        return self.INPUT(self.INPUT_MOUSE, self._INPUTunion(mi=self.MouseInput(flags, x, y, data)))

    def Keyboard(self, code, flags=0):
        return self.INPUT(self.INPUT_KEYBOARD, self._INPUTunion(ki=self.KeybdInput(code, flags)))

    def Hardware(self, message, parameter=0):
        return self.INPUT(self.INPUT_HARDWARE, self._INPUTunion(hi=self.HardwareInput(message, parameter)))


class InputListener:
    """
    https://gist.github.com/Amgarak/5df8477bad67dabbc491322e74ce1c2c
    
    This class is designed for listening to keyboard and mouse events in a Windows environment. 
    It utilizes low-level hooks to capture events such as key presses, 
    key releases, mouse movements, and mouse button clicks. 
    The class provides functionalities to register event handlers and filters, 
    allowing custom processing of events. 
    It also supports the control of threads for concurrent event handling.
    """
    def __init__(self):
        self.flagRun=False
        self.signal = queue.Queue()
        
        self._key_down = set()
        
        self.if_handlers = set()
        self.handlers = set()
        
        self.listener_queues = {}
        self.listeners =[]

        self.variable_key_down = {}

        self.event_types = {
                    win32con.WM_KEYDOWN: 'key_down', 
                    win32con.WM_KEYUP: 'key_up', 
                    0x104: 'key_down',  
                    0x105: 'key_up'}  
        
        self.mouse_types={
                    0x200: 'move',
                    0x20A: 'wheel',
                    0x20E: 'H_wheel', 
                    0x204: 'key_down', 
                    0x205: 'key_up', 
                    0x201: 'key_down', 
                    0x202: 'key_up', 
                    0x207: 'key_down', 
                    0x208: 'key_up', 
                    0x20B: 'key_down', 
                    0x20C: 'key_up'} 

        self.mouse_key={
                    0x200: 'move',  
                    0x20A: 'wheel', 
                    7864320: 'wheel_top', 
                    4287102976: 'wheel_bot', 
                    0x20E: 'H_wheel',  
                    0x204: 'right', 
                    0x205: 'right', 
                    0x201: 'left', 
                    0x202: 'left', 
                    0x207: 'middle', 
                    0x208: 'middle', 
                    131072: 'X_Button_1', 
                    65536: 'X_Button_2'}       

        self.DeviceEvent = namedtuple('DeviceEvent', ['inputDevice', 'event_type', 'key_code', 'x', 'y', 'scan_code', 'key_down'])
        self.DeviceEvent.__new__.__defaults__ = (None, None, None, None, None, None, None)
        
        self.default_key = "Value not defined"
        
    def key_event(self, event):
        while not self.signal.empty():
            self.signal_handler(self.signal.get())

        for listener_queue in self.listener_queues.values():
            listener_queue.put(event)
            
    def key_down(self, event_type, key_code):
        key = str(key_code)

        if key not in self._key_down and event_type == 'key_down':
            self._key_down.add(key)
            return True
        
        elif key in self._key_down and event_type == 'key_up':
            self._key_down.remove(key)
            return True
        
    def if_block(self, event):
        for if_handler in self.if_handlers:
            result = if_handler(event)
            if result is not None and not result:
                return False
        return True

    def keyboard_low_level_handler(self, nCode, wParam, lParam):
        lParam = ctypes.cast(lParam, ctypes.POINTER(ctypes.c_ulong)).contents.value
        
        rezult = self.key_down(self.event_types[wParam], lParam)
        event = self.DeviceEvent('Keyboard', self.event_types[wParam], lParam, key_down=self._key_down)
        
        if rezult: # !!! - Отсекаем повторяющийся ивент key_down
            self.key_event(event)
        
        if self.if_block(event):   
            return ctypes.windll.user32.CallNextHookEx(self.keyboard_hook_id, nCode, wParam, lParam)
        else:
            return -1
               
    def mouse_low_level_handler(self, nCode, wParam, lParam):
        point = ctypes.cast(lParam, ctypes.POINTER(ctypes.c_long * 2)).contents
        
        x = point[0]
        y = point[1]
        
        castomParam = self.mouse_key.get(lParam[1], self.default_key) if lParam[1] is not None else self.mouse_key[wParam]
        self.key_down(self.mouse_types[wParam], castomParam)
        
        event = self.DeviceEvent('Mouse', self.mouse_types[wParam], castomParam, x, y, lParam[1], self._key_down)
        self.key_event(event)
        
        if self.if_block(event):   
            return ctypes.windll.user32.CallNextHookEx(self.mouse_hook_id, nCode, wParam, lParam)
        else:
            return -1
    
    def listen(self):
        CMPFUNC = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.POINTER(ctypes.c_void_p))
        ctypes.windll.user32.SetWindowsHookExW.argtypes = (
            ctypes.c_int,
            CMPFUNC,
            ctypes.c_void_p,
            ctypes.c_uint
        )

        keyboard_pointer = CMPFUNC(self.keyboard_low_level_handler)
        self.keyboard_hook_id = ctypes.windll.user32.SetWindowsHookExW(win32con.WH_KEYBOARD_LL, keyboard_pointer,
                                                                       win32api.GetModuleHandle(None), 0)

        mouse_pointer = CMPFUNC(self.mouse_low_level_handler)
        self.mouse_hook_id = ctypes.windll.user32.SetWindowsHookExW(win32con.WH_MOUSE_LL, mouse_pointer,
                                                                    win32api.GetModuleHandle(None), 0)

        atexit.register(ctypes.windll.user32.UnhookWindowsHookEx, self.keyboard_hook_id)
        atexit.register(ctypes.windll.user32.UnhookWindowsHookEx, self.mouse_hook_id)

        win32gui.GetMessage(None, 0, 0)
        print(f"Hooks завершил свою работу..")
            
    def add_if_handler(self, func): 
        self.if_handlers.add(func) 
            
    def add_handler(self, func): 
        self.handlers.add(func) 
        
    def remove_if_handler(self, func): 
        self.if_handlers.discard(func) 
            
    def remove_handler(self, func): 
        self.handlers.discard(func) 
        self.variable_key_down.pop(func.__name__, None)
        
        
    def create_listener_queues(self):
        self.listener_queues = {func.__name__: queue.Queue() for func in self.handlers}
     
    def create_listener(self):        
        self.listeners = [
        threading.Thread(target=self.wrapper, args=(self.listener_queues[func.__name__], func), name=func.__name__)
        for func in self.handlers
    ]
        
    def start_listener(self):
        for listener_thread in self.listeners:
            listener_thread.daemon = True
            listener_thread.start()
      
    def wrapper(self, listener_queue, func):
        self.variable_key_down[func.__name__] = set()
        while True:
            event = listener_queue.get()
            if event is None:
                break
            
            self.variable_key_down[func.__name__].clear()
            self.variable_key_down[func.__name__].update(event.key_down)
            event = event._replace(key_down=self.variable_key_down.get(func.__name__, None))
            
            func(event) 
            
        print(f"Демон {func.__name__} завершил свою работу..")
            
    def start(self, use_daemon = False):
        self.create_listener_queues()
        self.create_listener()
        self.start_listener()
        
        self.flagRun=True
        
        thread = threading.Thread(target=self.listen, name="Hooks")
        thread.daemon = use_daemon  
        thread.start() 
        self.thread_id = thread.native_id 
   
    def stop(self): 
        user32 = ctypes.windll.user32
        WM_QUIT = 0x0012 
        user32.PostThreadMessageW(self.thread_id, WM_QUIT, 0, 0) 
                                                             
        ctypes.windll.user32.UnhookWindowsHookEx(self.keyboard_hook_id)
        ctypes.windll.user32.UnhookWindowsHookEx(self.mouse_hook_id)
        
        for listener_queue in self.listener_queues.values():
            listener_queue.put(None)
        
        self.listener_queues.clear()
        self.listeners.clear()
        self._key_down.clear()
        self.variable_key_down.clear()
        
        self.flagRun=False     

    def hot_removal_handler(self, func): 
        signal = {"hot_removal_handler": func}
        self.signal.put(signal)
    
    def hot_plugging_handler(self, func):
        signal = {"hot_plugging_handler": func}
        self.signal.put(signal)
    
    def hot_removal_if_handler(self, func): 
        signal = {"hot_removal_if_handler": func}
        self.signal.put(signal)
    
    def hot_plugging_if_handler(self, func): 
        signal = {"hot_plugging_if_handler": func}
        self.signal.put(signal)

    def signal_handler(self, handler_dict):
        match handler_dict:
            case {"hot_removal_handler": func}:
                
                self.handlers.discard(func) 
                stop_signal_queue = self.listener_queues.get(func.__name__)
                stop_signal_queue.put(None)
                self.variable_key_down.pop(func.__name__, None)
                self.listener_queues.pop(func.__name__, None)
                print(f"Handling hot removal for function {func.__name__}")
                
            case {"hot_plugging_handler": func}:
                
                self.handlers.add(func) 
                self.listener_queues.update({func.__name__: queue.Queue()})
                listener_thread=threading.Thread(target=self.wrapper, args=(self.listener_queues[func.__name__], func), name=func.__name__)
                self.listeners.append(listener_thread)
                listener_thread.daemon = True
                listener_thread.start()
                print(f"Handling hot plugging for function {func.__name__}")
                
            case {"hot_removal_if_handler": func}:
                
                self.if_handlers.discard(func) 
                print(f"Handling conditional hot removal for function {func.__name__}")
                
            case {"hot_plugging_if_handler": func}:
                
                self.if_handlers.add(func) 
                print(f"Handling conditional hot plugging for function {func.__name__}")
                
            case _:
                print("Unknown case")
                
    def get_if_handlers(self, objekt=None):
        if objekt == "__name__":
            return set(if_handler.__name__ for if_handler in self.if_handlers)
        else:
             return self.if_handlers

    def get_handlers(self, objekt=None):
        if objekt == "__name__":
            return set(handler.__name__ for handler in self.handlers)
        else:
             return self.handlers

    def get_status_hooks(self):
        return self.flagRun
    

input_simulator = InputSimulator()

UPPER = frozenset('~!@#$%^&*()_+QWERTYUIOP{}|ASDFGHJKL:"ZXCVBNM<>?')
LOWER = frozenset("`1234567890-=qwertyuiop[]\\asdfghjkl;'zxcvbnm,./")
ORDER = string.ascii_letters + string.digits + ' \b\r\t'
ALTER = dict(zip('!@#$%^&*()', '1234567890'))
OTHER = {'`': input_simulator.VK_OEM_3,
         '~': input_simulator.VK_OEM_3,
         '-': input_simulator.VK_OEM_MINUS,
         '_': input_simulator.VK_OEM_MINUS,
         '=': input_simulator.VK_OEM_PLUS,
         '+': input_simulator.VK_OEM_PLUS,
         '[': input_simulator.VK_OEM_4,
         '{': input_simulator.VK_OEM_4,
         ']': input_simulator.VK_OEM_6,
         '}': input_simulator.VK_OEM_6,
         '\\': input_simulator.VK_OEM_5,
         '|': input_simulator.VK_OEM_5,
         ';': input_simulator.VK_OEM_1,
         ':': input_simulator.VK_OEM_1,
         "'": input_simulator.VK_OEM_7,
         '"': input_simulator.VK_OEM_7,
         ',': input_simulator.VK_OEM_COMMA,
         '<': input_simulator.VK_OEM_COMMA,
         '.': input_simulator.VK_OEM_PERIOD,
         '>': input_simulator.VK_OEM_PERIOD,
         '/': input_simulator.VK_OEM_2,
         '?': input_simulator.VK_OEM_2}

def keyboard_stream(string):
    mode = False
    for character in string.replace('\r\n', '\r').replace('\n', '\r'):
        if mode and character in LOWER or not mode and character in UPPER:
            yield input_simulator.Keyboard(input_simulator.VK_SHIFT, mode and input_simulator.KEYEVENTF_KEYUP)
            mode = not mode
        character = ALTER.get(character, character)
        if character in ORDER:
            code = ord(character.upper())
        elif character in OTHER:
            code = OTHER[character]
        else:
            continue
        yield input_simulator.Keyboard(code)
        yield input_simulator.Keyboard(code, input_simulator.KEYEVENTF_KEYUP)
    if mode:
        yield input_simulator.Keyboard(input_simulator.VK_SHIFT, input_simulator.KEYEVENTF_KEYUP)
        
def get_layout():
    u = ctypes.windll.LoadLibrary("user32.dll")
    pf = getattr(u, "GetKeyboardLayout")
    if hex(pf(0)) == '0x4190419':
        return 'ru'
    if hex(pf(0)) == '0x4090409':
        return 'en'


if __name__ == '__main__':  
    if get_layout() == 'ru':
        input_simulator.SendInput(input_simulator.Keyboard(input_simulator.VK_MENU))
        input_simulator.SendInput(input_simulator.Keyboard(input_simulator.VK_SHIFT, input_simulator.KEYEVENTF_EXTENDEDKEY))
        input_simulator.SendInput(input_simulator.Keyboard(input_simulator.VK_SHIFT, input_simulator.KEYEVENTF_KEYUP | input_simulator.KEYEVENTF_EXTENDEDKEY))
        input_simulator.SendInput(input_simulator.Keyboard(input_simulator.VK_MENU, input_simulator.KEYEVENTF_KEYUP))
    
    def left_mouse_down(event):     
        if event.key_code == "left" and event.event_type == 'key_down':   
            for event in keyboard_stream('Hello World'):
                input_simulator.SendInput(event)
                time.sleep(0.01)
                
    def left_mouse_up(event):     
        if event.key_code == "left" and event.event_type == 'key_up':   
            for event in keyboard_stream('Goodbye world'):
                input_simulator.SendInput(event)
                time.sleep(0.01)
            
    input_listener = InputListener()
    
    input_listener.add_handler(left_mouse_down)
    input_listener.add_handler(left_mouse_up)

    input_listener.start()
→ Ссылка
Автор решения: ArseniyRybasov

Могу предложить способ только с использованием библиотеки Tkinter:

from tkinter import *

def printhello(event):
    print("Hello World")
def printgoodbye(event):
    print("Goodbye World")

if __name__ == '__main__':
    window = Tk()
    window.bind("<Button-1>", printhello)
    window.bind("<Button-3>", printgoodbye)
    window.mainloop()
→ Ссылка