confused
confused

Reputation: 1323

Python detect two keys at once in tkinter

Finally figured out how to scroll the screen using only the keyboard in tkinter, took getting on the right website to show me the answer. Now I have one other small, but rather important problem that I'm experiencing.

The program is setup to scroll the underlying image using the cursor keys. If I push two keys(up/left) at the same time it will scroll either up one and left forever or up one and left forever instead of constantly switching back and forth.

How do get it to recognize that I'm pushing both and holding them down? It only recognizes one of the two, no matter which two keys I'm holding down.

import tkinter as tk
import random

class Example(tk.Frame):
    def __init__(self, parent):
        tk.Frame.__init__(self, parent)

        self.canvas = tk.Canvas(self, background="bisque", width=400, height=400)
        self.canvas.pack(fill="both", expand=True)
        self.canvas.configure(scrollregion=(-1000, -1000, 1000, 1000))

        self.canvas.bind("<Left>", self.keyleft)
        self.canvas.bind("<Right>", self.keyright)
        self.canvas.bind("<Up>", self.keyup)
        self.canvas.bind("<Down>", self.keydown)
        self.canvas.focus_set()

        # the following two values cause the canvas to scroll
        # one pixel at a time
        self.canvas.configure(xscrollincrement=1, yscrollincrement=1)

        # finally, draw something on the canvas so we can watch it move
        for i in range(1000):
            x = random.randint(-1000, 1000)
            y = random.randint(-1000, 1000)
            color = random.choice(("red", "orange", "green", "blue", "violet"))
            self.canvas.create_oval(x, y, x+20, y+20, fill=color)

    def keyup(self,event):
        self.canvas.yview_scroll(-1,'units')
    def keydown(self,event):
        self.canvas.yview_scroll(1,'units')
    def keyleft(self,event):
        self.canvas.xview_scroll(-1,'units')
    def keyright(self,event):
        self.canvas.xview_scroll(1,'units')

if __name__ == "__main__":
    root = tk.Tk()
    Example(root).pack(fill="both", expand=True)
    root.mainloop()

Upvotes: 0

Views: 1002

Answers (1)

Novel
Novel

Reputation: 13729

This isn't a tkinter problem; it's the way your OS handles the long pressing a key. Go to a text editor and press some keys (text keys, not arrows) and you'll see the same behavior. You OS probably has some settings to modify that behavior.

You could take over the press and hold behavior in tkinter and handle multiple keys that way, but that would require disabling this feature in your OS first. How you do that is OS-specific, and I doubt it's possible to disable it for your application only.

Edit: if you are ok with shutting off the OS key repeat feature either manually or programatically, you could use this code to have tkinter take over the key repeat:

import tkinter as tk
import random

class Example(tk.Frame):
    def __init__(self, parent):
        tk.Frame.__init__(self, parent)

        self.keys = dict.fromkeys(('Left', 'Right', 'Up', 'Down'))

        self.canvas = tk.Canvas(self, background="bisque", width=400, height=400)
        self.canvas.pack(fill="both", expand=True)
        self.canvas.configure(scrollregion=(-1000, -1000, 1000, 1000))

        parent.bind("<KeyPress>", self.keypress)
        parent.bind("<KeyRelease>", self.keypress)
        self.canvas.focus_set()

        # the following two values cause the canvas to scroll
        # one pixel at a time
        self.canvas.configure(xscrollincrement=1, yscrollincrement=1)

        # finally, draw something on the canvas so we can watch it move
        for i in range(1000):
            x = random.randint(-1000, 1000)
            y = random.randint(-1000, 1000)
            color = random.choice(("red", "orange", "green", "blue", "violet"))
            self.canvas.create_oval(x, y, x+20, y+20, fill=color)

        self.looper() # start the looping

    def keypress(self,event):
        if event.keysym in self.keys:
            # event type 2 is key down, type 3 is key up
            self.keys[event.keysym] = event.type == '2'

    def looper(self):
        if self.keys['Up']:
            self.canvas.yview_scroll(-1,'units')
        if self.keys['Down']:
            self.canvas.yview_scroll(1,'units')
        if self.keys['Left']:
            self.canvas.xview_scroll(-1,'units')
        if self.keys['Right']:
            self.canvas.xview_scroll(1,'units')

        self.after(20, self.looper) # set the refresh rate here ... ie 20 milliseconds. Smaller number means faster scrolling

if __name__ == "__main__":
    root = tk.Tk()
    Example(root).pack(fill="both", expand=True)
    root.mainloop()

Edit edit: Some googling suggests that some OS's send repeated 'press' signals rather than the press - release - press - release cycle that I see in Linux Mint. If your OS does then you may be able to use this code without disabling the autorepeat.

Upvotes: 1

Related Questions