Rodney Boyd
Rodney Boyd

Reputation: 39

tkinter event binding not handled correctly

I have a function in a tkinter application that does some operations on the content of the clipboard, pastes the result to a Text widget, and then should return to the previous application. For the last part I'm generating an Alt-Tab using pynput.

def invert_clipboard(e=None):
    txt = root.clipboard_get()
    if not txt:
        return
    tokens = re.split('\s+', txt.strip())
    if len(tokens) >= 2:
        out = tokens[len(tokens)-1].strip() + ', ' 
        for i in range(len(tokens)-1):
            out += tokens[i]
            if i < len(tokens) - 2:
                out += ' '
    else:
        out = txt.strip()
    out += '#'
    input_text.insert(tk.INSERT, out)
    global dirty
    dirty = True
    thread = Thread(target=switch_window)
    thread.start()

def switch_window():
    root.update_idletasks()
    with keyboard.pressed(Key.alt):
        keyboard.press(Key.tab)
        keyboard.release(Key.tab)
        keyboard.release(Key.alt)

The function can be accessed via a menu item:

macros_menu.add_command(
        label='Invert',
        command=invert_clipboard,
        accelerator="Ctrl+M"

or via a keyboard shortcut:

root.bind('<Control-m>', lambda event:invert_clipboard(None))

When called from the menu, everything works as intended; when called from the keyboard shortcut, the return to previous doesn't quite work. I see the group of thumbnails of the open apps, with the expected app highlighted, but pressing Enter is required to maximize it.

I assume I'm not handling the keyboard event correctly in the bind, but I have only a murky understanding of how to do so. Any suggestions? Thanks.

Upvotes: 2

Views: 490

Answers (1)

Thingamabobs
Thingamabobs

Reputation: 8062

The Problem that you are facing is that you have an additional and unwanted modifier key pressed during the event and that changes the behavior. You can check this by just trying the key combination CTRL+ALT+TAB without running any script. You could solve it by changing your bind in a manner that it works without CTRL or you release that key before you invoke your desired key combination.

import tkinter as tk
import ctypes

ALT = 0x12
TAB = 0x09
SHIFT = 0x10
CTRL = 0x11
PRESS = 0x0000
RELEASE = 0x0002

keyevent = ctypes.windll.user32.keybd_event

def show_next(event=None):
    keyevent(CTRL,0,RELEASE,0)
    keyevent(ALT,0,PRESS,0)
    keyevent(TAB,0,PRESS,0)
    keyevent(TAB,0,RELEASE,0)
    keyevent(ALT,0,RELEASE,0)
    

def show_prev(event=None):
    keyevent(CTRL,0,RELEASE,0)
    keyevent(ALT,0,PRESS,0)
    keyevent(SHIFT,0,PRESS,0)
    keyevent(TAB,0,PRESS,0)
    keyevent(TAB,0,RELEASE,0)
    keyevent(SHIFT,0,RELEASE,0)
    keyevent(ALT,0,RELEASE,0)

root = tk.Tk()
btn1 = tk.Button(root, text='Next', command= show_next)
btn1.pack(side=tk.RIGHT)
btn2 = tk.Button(root, text='Prev', command= show_prev)
btn2.pack(side=tk.LEFT)
root.bind('<Control-n>', show_next)
root.bind('<Control-p>', show_prev)
root.mainloop()

Upvotes: 1

Related Questions