0

I am very new to Python an tkinter. I am developing a keyboard using tkinter. I have created a text box and I am trying to bind keys to it so that users can use keyboard for input. I want to print different keys when a key is pressed. For an example, I want to print P when 'p' is pressed, A when 'a' is pressed. I know I can manually type in each key and add a function to print the required value but that would be time consuming so I decided to use a dictionary.

Below is the code for this.

def print_letter(letter):
    if letter == "backspace":
        text.configure(state="normal")
        value = text.get("1.0", "end")
        text.delete("1.0", "end")
        text.insert("1.0", value[:-2])
        text.configure(state="disabled")
    else:        
        text.configure(state="normal")
        text.insert("end", letter)
        text.configure(state="disabled")

    letters = {
        'a': 'A',
        'b': 'B',
        'c': 'C',
        'd': 'D',
        'e': 'E',
    }

    root = tk.Tk()
    root.geometry("800x415")
    root.resizable(False, False)

    text = tk.Text(text_frame, width=97, height=15)
    text.configure(state="disabled")
    text.grid(row=0, column=0, sticky="EW")
    text.focus_set()

    for key, value in letters.items():
        text.bind(key, lambda value: print_letter(letters.get(key)))

But only the last value in the dictionary is bounded to any key I press. I saw a few posts on SO and they all suggested the below options:

  1. text.bind(key, lambda value=value: print_letter(letters.get(key)))
  2. text.bind(key, lambda args=value: print_letter(letters.get(key)))

All the above gave me the same result.

Any help is appreciated.

1

3 Answers 3

1

My personal preference is to use functools.partial rather than lambda since it freezes the arguments. Here is an example of it in practice.

import tkinter as tk
from functools import partial

# here we add an *args argument 
# since tkinter will also send the keystroke
# as an argument
def print_letter(letter, *args):
    if letter == "backspace":
        text.configure(state="normal")
        value = text.get("1.0", "end")
        text.delete("1.0", "end")
        text.insert("1.0", value[:-2])
        text.configure(state="disabled")
    else:      
        text.configure(state="normal")
        text.insert("end", letter)
        text.configure(state="disabled")

letters = {
    'a': 'A',
    'b': 'B',
    'c': 'C',
    'd': 'D',
    'e': 'E',
}
root = tk.Tk()
root.geometry("800x415")
root.resizable(False, False)
text = tk.Text(root, width=97, height=15)
text.configure(state="disabled")
text.grid(row=0, column=0, sticky="EW")
text.focus_set()
for key, value in letters.items():
    # partial takes unlimited args.
    # The first being the function name 
    # and subsequent ones being the arguments
    text.bind(key, partial(print_letter, value))

root.mainloop()
Sign up to request clarification or add additional context in comments.

1 Comment

That worked! Thank you very much. I saw many posts where the community is divided between using lamda and functools.partial. Thanks again.
0

If I've understood your question correctly, you need sth. like this:

l = []

for _ in range(10):
    l.append(lambda v=_: print(v))

Hope that's helpful!

2 Comments

Thanks for the comment. So this would create a list with lambda functions. How would I bind keys to individual lambda functions in the list?
for key, value in letters.items(): text.bind(key, lambda value=key: print_letter(letters.get(value)))
0

It should work:

l = []

for _ in range(10):
    l.append(lambda v=_: print(v))

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.