I'm coding a little tool that displays the key presses on the screen with Tkinter, useful for screen recording.
Is there a way to get a listener for all key presses of the system globally with Tkinter? (for every keystroke including F1, CTRL, ..., even when the Tkinter window does not have the focus)
I currently know a solution with pyHook.HookManager(), pythoncom.PumpMessages(), and also solutions from Listen for a shortcut (like WIN+A) even if the Python script does not have the focus but is there a 100% tkinter solution?
Indeed, pyhook is only for Python 2, and pyhook3 seems to be abandoned, so I would prefer a built-in Python3 / Tkinter solution for Windows.
Solution 1: if you need to catch keyboard events in your current window, you can use:
from tkinter import *
def key_press(event):
key = event.char
print(f"'{key}' is pressed")
root = Tk()
root.geometry('640x480')
root.bind('<Key>', key_press)
mainloop()
Solution 2: if you want to capture keys regardless of which window has focus, you can use keyboard
As suggested in tkinter using two keys at the same time, you can detect all key pressed at the same time with the following:
history = []
def keyup(e):
print(e.keycode)
if e.keycode in history :
history.pop(history.index(e.keycode))
var.set(str(history))
def keydown(e):
if not e.keycode in history :
history.append(e.keycode)
var.set(str(history))
root = Tk()
root.bind("<KeyPress>", keydown)
root.bind("<KeyRelease>", keyup)
Related
I have 6 macro keys on my keyboard, G1 through to G6. My question is simple, how can I use:
from tkinter import *
master = Tk()
master.bind('<G1>', #trigger some event>
To actually trigger an event?
Obviously, at the moment an error appears as "G1" isnt recognised.
You can see if you can get the key code by binding <Key> as shown below. If this doesn't produce anything then your window system is not handling these keys and there is nothing tkinter can do. On my system, holding AltGr and O together generates an ΓΈ and I see oslash as the printed output. Adding a new binding for <oslash> then works for that key input.
If this doesn't show a keysym for your keys, you will need to specify the windowing system in use as getting inputs from special keys will be different on X Windows, MacOS and Windows. Tk relies on the window system input queue to provide these keyboard inputs.
import tkinter as tk
root = tk.Tk()
e = ttk.Entry(root)
e.place(x=1,y=1)
e.bind('<Key>', lambda ev: print(ev.keysym))
root.mainloop()
I am trying to get keypresses in Python (2.7.10), bit I had no luck with getch(), as ord(getch()) was returning 255 constantly, so I am now using Tkinter. (I believe that Tkinter is also cross-platform so that should help as i plan to run this script on a Linux device).
I need to be able to get keypresses, even if they are not pressed while the Tkinter window is not active.
Here is my code:
from Tkinter import *
import time, threading
x = "Hi!"
def callback(event):
x = "key: " + event.char
print(x)
def doTk():
root = Tk()
root.bind_all("<Key>", callback)
root.withdraw()
root.mainloop()
thread1 = threading.Thread(target=doTk)
thread1.deamon = True
thread1.start()
I am not reveiving any errors, it is not not registering keypresses. I have also tried this without using threading, but it still does not work.
Please also note that I cannot use raw_input() as I need this to be able to run in the background and still get keypresses.
I am aware that this does not produce a frame, I do not want it to.
Thanks in advance for any help :)
PS: I have looked to other answers on StackOverflow and other sites, but they all either don't work or give solutions where keypresses are only registered when the tkinter frame is active.
I've been searching for information on the following Tkinter window features without success. Platform is Windows, Python 2.7. At the end of this post is code that can be used to explore Tkinter window events.
How can one detect window minimize/maximize events? The event object returned by binding to a window's <Configure> event does contain any information about these events. I've searched for protocols (like WM_DELETE_WINDOW) that might expose these events without success.
How can one determine window frame border sizes (not Tkinter frames, the frame the OS places around the container where Tkinter places its widgets)? Is there a non-platform specific way to discover these windows properties or do I need to use platform specific solutions like the win32 api under Windows?
How can one determine a window's visibility, eg. whether a window is invisible or not as set by .withdraw()?
Is it possible to cancel a window event, eg. if one wanted to constrain a window to a certain location on a user's desktop? Returning 'break' from a window's <Configure> event does not cancel window move or resize events.
Here's sample code for experimenting with Tkinter window events.
from __future__ import print_function
try:
import Tkinter as tk
except ImportError:
import tkinter as tk
def onFormEvent(event):
for key in dir(event):
if not key.startswith('_'):
print('%s=%s' % (key, getattr(event, key)))
print()
root = tk.Tk()
root.geometry('150x50')
lblText = tk.Label(root, text='Form event tester')
lblText.pack()
root.bind('<Configure>', onFormEvent)
root.mainloop()
Updat:e Here's what I learned about the following events:
event.type == 22 (one or more of following changed: width, height, x, y)
event.type == 18 (minimized) event.widget.winfo_viewable() = 0 (invisible)
event.type == 19 (restore after minimized)
event.type == 2 (maximize)
event.type == 22 (restore after maximized due to change in width and height)
Determining window visibility is done with a .winfo_viewable() call. Returns 1 if visible, 0 if not.
If you want to prevent the window from resizing, set up your window the way you want, then use
self.root.minsize(self.root.winfo_reqwidth(), self.root.winfo_reqheight())
self.root.maxsize(self.root.winfo_reqwidth(), self.root.winfo_reqheight())
at the end of your __init__ call.
To completely disable the window from being moved, then you probably just want to remove the title bar and frame with self.root.overrideredirect(True).
I'm trying to capture key presses so that when a given combination is pressed I trigger an event.
I've searched around for tips on how to get started and the simplest code snippet I can find is in Python - I grabbed the code below for it from here. However, when I run this from a terminal and hit some keys, after the "Press a key..." statement nothing happens.
Am I being stupid? Can anyone explain why nothing happens, or suggest a better way of achieving this on Linux (any language considered!)?
import Tkinter as tk
def key(event):
if event.keysym == 'Escape':
root.destroy()
print event.char
root = tk.Tk()
print "Press a key (Escape key to exit):"
root.bind_all('<Key>', key)
# don't show the tk window
root.withdraw()
root.mainloop()
Tk does not seem to get it if you do not display the window. Try:
import Tkinter as tk
def key(event):
if event.keysym == 'Escape':
root.destroy()
print event.char
root = tk.Tk()
print "Press a key (Escape key to exit):"
root.bind_all('<Key>', key)
# don't show the tk window
# root.withdraw()
root.mainloop()
works for me...
What you're doing is reading /dev/tty in "raw" mode.
Normal Linux input is "cooked" -- backspaces and line endings have been handled for you.
To read a device like your keyboard in "raw" mode, you need to make direct Linux API calls to IOCTL.
Look at http://code.activestate.com/recipes/68397/ for some guidance on this. Yes, the recipe is in tcl, but it gives you a hint as to how to proceed.
Alternatively (a non-Python option) use XBindKeys.
Well, turns out there is a much simpler answer when using GNOME which doesn't involve any programming at all...
http://www.captain.at/howto-gnome-custom-hotkey-keyboard-shortcut.php
Archived on Wayback
Just create the script/executable to be triggered by the key combination and point the 'keybinding_commands' entry you create in gconf-editor at it.
Why didn't I think of that earlier?
tkinter 'bind' method only works when tkinter window is active.
If you want binding keystrokes combinations that works in all desktop (global key/mouse binding) you can use bindglobal (install with pip install bindglobal). It works exactly like standard tkinter 'bind'.
Example code:
import bindglobal
def callback(e):
print("CALLBACK event=" + str(e))
bg = bindglobal.BindGlobal()
bg.gbind("<Menu-1>",callback)
bg.start()
I'm building a code in which I'd like to be able to generate an event when the user changes the focus of the cursor from an Entry widget to anywhere, for example another entry widget, a button...
So far i only came out with the idea to bind to TAB and mouse click, although if i bind the mouse click to the Entry widget i only get mouse events when inside the Entry widget.
How can I accomplish generate events for when a widget loses cursor focus?
Thanks in advance!
The events <FocusIn> and <FocusOut> are what you want. Run the following example and you'll see you get focus in and out bindings whether you click or press tab (or shift-tab) when focus is in one of the entry widgets.
from Tkinter import *
def main():
global text
root=Tk()
l1=Label(root,text="Field 1:")
l2=Label(root,text="Field 2:")
t1=Text(root,height=4,width=40)
e1=Entry(root)
e2=Entry(root)
l1.grid(row=0,column=0,sticky="e")
e1.grid(row=0,column=1,sticky="ew")
l2.grid(row=1,column=0,sticky="e")
e2.grid(row=1,column=1,sticky="ew")
t1.grid(row=2,column=0,columnspan=2,sticky="nw")
root.grid_columnconfigure(1,weight=1)
root.grid_rowconfigure(2,weight=1)
root.bind_class("Entry","<FocusOut>",focusOutHandler)
root.bind_class("Entry","<FocusIn>",focusInHandler)
text = t1
root.mainloop()
def focusInHandler(event):
text.insert("end","FocusIn %s\n" % event.widget)
text.see("end")
def focusOutHandler(event):
text.insert("end","FocusOut %s\n" % event.widget)
text.see("end")
if __name__ == "__main__":
main();
This isn't specific to tkinter, and it's not focus based, but I got an answer to a similar question here:
Detecting Mouse clicks in windows using python
I haven't done any tkinter in quite a while, but there seems to be "FocusIn" and "FocusOut" events. You might be able to bind and track these to solve your issue.
From:
http://effbot.org/tkinterbook/tkinter-events-and-bindings.htm