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.

Karl Knechtel
  • 62,466
  • 11
  • 102
  • 153
Arun
  • 37
  • 1
  • 5
  • Does this answer your question? [tkinter creating buttons in for loop passing command arguments](https://stackoverflow.com/questions/10865116/tkinter-creating-buttons-in-for-loop-passing-command-arguments) – Karl Knechtel Aug 14 '22 at 20:57

3 Answers3

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()
Axe319
  • 4,255
  • 3
  • 15
  • 31
  • 1
    That worked! Thank you very much. I saw many posts where the community is divided between using lamda and functools.partial. Thanks again. – Arun May 11 '20 at 17:26
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!

rizerphe
  • 1,340
  • 1
  • 14
  • 24
  • 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? – Arun May 11 '20 at 16:44
  • `for key, value in letters.items(): text.bind(key, lambda value=key: print_letter(letters.get(value)))` – rizerphe May 11 '20 at 16:45
0

It should work:

l = []

for _ in range(10):
    l.append(lambda v=_: print(v))
Thomas
  • 1,164
  • 13
  • 41