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()
Related
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)
I would like to enable ctrl+a to select the text within a combobox. Instead of selecting all it does <end> (more or less at least).
minimal example:
#!/usr/bin/env python3
import tkinter as tk
from tkinter import ttk
from tkinter.messagebox import showinfo
root = tk.Tk()
def month_changed(event):
msg = f'You selected {month_cb.get()}!'
showinfo(title='Result', message=msg)
# month of year
months = ['Jan', 'Feb']
# create a combobox
selected_month = tk.StringVar()
month_cb = ttk.Combobox(root, textvariable=selected_month)
month_cb['values'] = months
month_cb.pack(fill='x', padx=5, pady=5)
month_cb.bind('<<ComboboxSelected>>', month_changed)
month_cb.bind('<Control-a>', doSomeSelecting) #doSomeSelcting tbd
root.mainloop()
I stole the example and minimized it from here to get a quick example: https://www.pythontutorial.net/tkinter/tkinter-combobox/
So what you are doing is overriding the default bindings for your platform. On X11 Tk sets up a default binding for Control-Key-slash to generate the <<SelectAll>> virtual event. On Win32 this is extended with Control-Key-a as well. On X11 Control-a is bound to <<LineStart>>.
So the platfrom correct thing is to leave it alone and learn to use Control-slash to select all. To override this you need to bind Control-a to a function that generates the SelectAll virtual event and also prevents the default event handler from then moving the insertion point to the start of the line. For that:
def selall(ev):
ev.widget.event_generate('<<SelectAll>>')
return 'break'
month_cb.bind('<Control-a>', selall)
The return 'break' is important here otherwise the event handlers will continue being called and our selection will be undone when something generates the <<LineStart>> event after our <<SelectAll>>.
This can be investigated in IDLE using month_cb.bindtags() to find that it's class bindings are TCombobox. Then month_cb.bind_class('TCombobox') to see all the events that are bound to this class. For the virtual events, root.event_info('<<SelectAll>>') shows the set of events that cause this virtual event to be raised.
Say if i wish to bind my space bar and the key "w" to any random function in my code, how would i do this? Should i be using if "w" and if "" then perform or can you bind multiple keys to one function?
#if statement way. idk how to do this tho
if "w" == Pressed:
if "<space>" == Pressed:
#perform function
#or
self._master.bind("<space>", "w", lambda e: function)
In tkinter you can put string with all keys "<space>w" and you can do: press space, (release space or not), press w and it will run function.
import tkinter as tk
def test(event):
print('test')
root = tk.Tk()
root.bind('<space>w', test)
root.mainloop()
By just adding the and, I was able to bind this Text entry box to both the tab and enter keys. I suppose if you wanted each key to run a different function, you could.
Text.bind("<Tab>", AddText) and Text.bind("<Return>", AddText)
I'm developing a GUI using python 3.6 but I need the user to double-click on a tkinter Entry widget to allow input (to prevent modification of fields by accident), instead of just press any key to input text.
Is there any way to prevent the user input by keystroke until double
click on Entry?
I've tried by first to override events with the methods below to unbind keystrokes but none of them worked, so re-definition of new bind (double click) is not yet implemented.
Entry.unbind_all('<Key>')
Entry.unbind_all('<KeyPress>')
Entry.unbind_all('<KeyRelease>')
One simple way of doing preventing the user input by keystrokes until a double-click, is simply manipulating the state of an Entry with double click and focus out events. As in by default every widget is read-only, when double-clicked a single one gets enabled, and when lost focus it's read-only again:
try: # In order to be able to import tkinter for
import tkinter as tk # either in python 2 or in python 3
except ImportError:
import Tkinter as tk
def on_double_click(widget):
widget['state'] = 'normal'
def on_lose_focus(widget):
widget['state'] = 'readonly'
def main():
root = tk.Tk()
entries = list()
for i in range(3):
entries.append(tk.Entry(root, state='readonly'))
entries[-1].bind('<Double-Button-1>',
lambda e, w=entries[-1]: on_double_click(w))
entries[-1].bind('<FocusOut>',
lambda e, w=entries[-1]: on_lose_focus(w))
entries[-1].pack()
tk.mainloop()
if __name__ == '__main__':
main()
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()