Lambda function in for loop to bind keywords in tkinter

Question:

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.

Asked By: Arun

||

Answers:

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!

Answered By: rizerphe

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()
Answered By: Axe319

it will works

l = []

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

Answered By: salihbugra
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.