[Python] Trying to make a ghost of whatever is in my clipboard appear where my text cursor is

Discussão em 'Python' iniciado por Stack, Dezembro 9, 2024.

  1. Stack

    Stack Membro Participativo

    I am making a program that can store a clipboard history that you can scroll through to choose what to paste. I want some ghost text to appear (where your text cursor is) of what you currently have in your clipboard. I have tried solving this problem for a week and have hit multiple dead ends.

    This is a test program that is supposed to sort out the ghost display and show it on ctrl where your cursor is but it doesn't work: (Caret position (relative to window) is always (0, 0) not matter what I do)

    class GhostDisplay:
    def __init__(self):
    # Initialize ghost display window
    self.ghost_display = tk.Tk()
    self.ghost_display.overrideredirect(1) # Remove window decorations
    self.ghost_display.attributes('-topmost', True) # Always on top
    self.ghost_display.withdraw() # Start hidden

    self.ghost_label = tk.Label(self.ghost_display, fg='white', bg='black', font=('Arial', 12))
    self.ghost_label.pack()

    # Thread for listening to hotkeys
    self.listener_thread = threading.Thread(target=self._hotkey_listener, daemon=True)

    def _get_text_cursor_position(self):
    """Get the position of the text cursor (caret) in the active window."""
    try:
    hwnd = win32gui.GetForegroundWindow() # Get the handle of the active window
    logging.info(f"Active window handle: {hwnd}")

    caret_pos = win32gui.GetCaretPos() # Get the caret (text cursor) position
    logging.info(f"Caret position (relative to window): {caret_pos}")

    rect = win32gui.GetWindowRect(hwnd) # Get the window's position on the screen
    logging.info(f"Window rectangle: {rect}")

    # Calculate position relative to the screen
    x, y = rect[0] + caret_pos[0], rect[1] + caret_pos[1]
    logging.info(f"Text cursor position: ({x}, {y})")
    return x, y
    except Exception as e:
    logging.error(f"Error getting text cursor position: {e}")
    return None

    def show_ghost(self):
    """Display the ghost near the text cursor with clipboard content."""
    content = pyperclip.paste()
    pos = self._get_text_cursor_position()

    if pos:
    x, y = pos
    logging.info(f"Positioning ghost at: ({x}, {y})")
    self.ghost_label.config(text=content)
    self.ghost_display.geometry(f"+{x+5}+{y+20}") # Position slightly offset from the cursor
    self.ghost_display.deiconify() # Show the ghost window
    else:
    # Fall back to positioning near the mouse
    x, y = win32api.GetCursorPos()
    logging.info(f"Falling back to mouse cursor position: ({x}, {y})")
    self.ghost_label.config(text=f"(Fallback) {content}")
    self.ghost_display.geometry(f"+{x+5}+{y+20}")
    self.ghost_display.deiconify()

    def hide_ghost(self):
    self.ghost_display.withdraw()
    logging.info("Ghost hidden.")

    def _hotkey_listener(self):
    """Listen for hotkey to show/hide the ghost display."""
    def on_press(key):
    try:
    if key in {keyboard.Key.ctrl_l, keyboard.Key.ctrl_r}:
    logging.info("Ctrl pressed. Showing ghost.")
    self.show_ghost()
    except Exception as e:
    logging.error(f"Error in hotkey listener (on_press): {e}")

    def on_release(key):
    try:
    if key in {keyboard.Key.ctrl_l, keyboard.Key.ctrl_r}:
    logging.info("Ctrl released. Hiding ghost.")
    self.hide_ghost()

    # Kill switch is Esc
    if key == keyboard.Key.esc:
    logging.info("ESC pressed. Exiting program.")
    self.stop()
    except Exception as e:
    logging.error(f"Error in hotkey listener (on_release): {e}")

    with keyboard.Listener(on_press=on_press, on_release=on_release) as listener:
    listener.join()

    def run(self):
    self.listener_thread.start() # Start hotkey listener
    self.ghost_display.mainloop()

    def stop(self):
    self.ghost_display.destroy()
    sys.exit(0)
    if __name__ == "__main__":
    open("ghost_display_debug.log", "w").close()
    app = GhostDisplay()
    try:
    app.run()
    except KeyboardInterrupt:
    app.stop()


    The second problem I have is due to not being able to override windows paste functionality. I'm trying to make the ghost appear on ctrl+v (you would scroll through your paste history here) and then paste when ctrl+v is pressed and ctrl or v is released. This is my test code for blocking windows paste functionality on hotkey (F9):

    custom_paste_enabled = True

    def simulate_paste():
    content = pyperclip.paste()
    print(f"Custom Pasted: {content}")

    def toggle_custom_paste():
    global custom_paste_enabled
    custom_paste_enabled = not custom_paste_enabled
    print(f"Custom paste {'enabled' if custom_paste_enabled else 'disabled'}.")

    def custom_paste_handler(e):
    if custom_paste_enabled:
    print("Ctrl+V intercepted. Suppressing OS behavior.")
    simulate_paste() # Perform custom paste
    return False # Suppress this key event
    return True # Allow the normal paste

    # Set up the hotkeys
    keyboard.add_hotkey('F9', toggle_custom_paste) # F9 to toggle custom paste
    keyboard.hook(custom_paste_handler) # Hook into all key events

    print("Listening for keyboard events. F9 to toggle custom paste. Ctrl+V to test.")

    try:
    keyboard.wait('esc') # Kill switch is Esc
    except KeyboardInterrupt:
    print("\nExiting.")


    Any help at all or suggestions with this would be greatly appreciated and extremely helpful. I haven't found much about this online and I'm at the end of my rope here.

    Continue reading...

Compartilhe esta Página