Arun
Arun

Reputation: 67

Lambda function in for loop to bind keywords in tkinter

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.

Upvotes: 0

Views: 604

Answers (3)

salihbugra
salihbugra

Reputation: 1

It should work:

l = []

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

Upvotes: 0

Axe319
Axe319

Reputation: 4365

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()

Upvotes: 1

rizerphe
rizerphe

Reputation: 1390

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!

Upvotes: 0

Related Questions