I’ve created a KeyBinder object which is basically a button that, when pressed, will listen for a keypress event and then capture the next keypress. Now I’ve modified it to test what info inside the event object i have to bind to the root window to actually use the keybind:
import tkinter as tk
class KeyBinder(tk.Button):
key_code: str = None
bind_list: set[int] = {}
def __init__(self,bind_list=None,*args,**kwargs):
super().__init__(*args,**kwargs)
#self.config(command=self.folder_select())
self.key_code = None
if "text" not in kwargs: self.config(text="Choose key")
self.bind("<1>",lambda event:self.start_keybind())
def start_keybind(self):
self.winfo_toplevel().bind("<Key>",self.bind_key)
self.config(text="Press a key...")
def bind_key(self,event:tk.Event):
if event.keysym == "Escape":
self.key_code = None
self.config(text="Choose key")
elif not self.bind_list or event.keycode not in self.bind_list:
self.key_code = event.keysym
self.config(text=event.keysym)
else:
self.key_code = None
self.config(text="Key already bound")
self.winfo_toplevel().unbind("<Key>")
self.winfo_toplevel().bind(self.key_code,add_a)
###########
def add_a(_):
l = label
text = l.cget("text")
l.config(text+"a")
############
window = tk.Tk()
btn = KeyBinder()
btn.pack()
label = tk.Label(text="b")
label.pack()
window.mainloop()
I’ve tried using event.key, .keysym, .keycode and converting all of them into strings encapsulated in < > as the first window.bind() argument, but the bound function doesn’t fire in any of those cases.
What’s the intended way to capture user input and use it as a keybind?
I just realized that bind events are case sensitive (e.g. Right vs space ), so to avoid having to check for special cases I’ll just restrict the binding to letters and numbers.
Regarding your correction: does type hinting syntax have an effect at runtime?
Humm, not sure how to answer this, as your skill set is above mine. All I know is that without my mod, I couldn’t get you code to run; it through an error…
Traceback (most recent call last):
File "./untitled-2.py", line 5, in <module>
class KeyBinder(tk.Button):
File "./untitled-2.py", line 7, in KeyBinder
bind_list: set[int] = {}
TypeError: 'type' object is not subscriptable
Indeed, it is not. I suggest you read PEP 526 to familiarize yourself with the typing syntax for variable annotations, as your suggestion indicates you are almost certainly not unfamiliar with it.
Specifically, the annotation set[int] annotates the variable bind_list as being a set composed of int members (set is a generic collection, which is parameterized by the type of its elements). The proposed alteration actually instantiates a set composed of one member, the inttype, which of course is not what is intended.
You are running into an issue because you are running the code on Python <3.9, which means that the builtin set cannot be subscripted with a type for use in type checking (rather, you’d have to from typing import Set and use that instead for annotation purposes), and without using from __future__ import annotations, which work around that issue by treating annotations as strings rather than evaluating them at runtime.
To avoid this, instead of mangling the type hints, just add from __future__ import annotations at the top of the module (assuming you’re using a supported Python version, i.e. 3.7+) or upgrade to Python 3.9+.
In this case, yes, because from __future__ import annotations isn’t used. See PEP 563 for more details.