How can I set the text of a ttk.Entry?

Viewed 1679

It appears that I cannot set the text of a ttk.Entry with a root of ttk.Label during an event:

import tkinter
from tkinter import ttk

def modify_label_text(event):
    entry = event.widget
    newvalue = entry.get()
    label = entry.master
    label.configure(text=newvalue)
    entry.unbind("<Return>")
    entry.pack_forget()
    label.focus()

def create_entry(event):
    label = event.widget
    oldvalue = label.cget("text")
    newvalue = tkinter.StringVar()
    entry = ttk.Entry(label, textvariable=newvalue)
    '''
    entry = tkinter.Entry(label, textvariable=newvalue)
    '''
    entry.pack()
    entry.delete(0, "end")
    entry.insert(0, oldvalue)
    entry.bind("<Return>", modify_label_text)
    entry.focus()

root = tkinter.Tk()

clickme = ttk.Label(root, width=16)
clickme.pack()

clickme.bind("<Button-1>", create_entry)
clickme.focus()

root.mainloop()

When I click the empty label and enter a new value, the value is reflected in the label. However, if I click the label again to "edit" the value, the entry field is empty again.

Furthermore, if I use tkinter.Entry rather than ttk.Entry, it appears to work.

Why is the text of the entry only set when using tkinter.Entry? How can I fix this to work with ttk.Entry?

2 Answers

I suggest you create an instance of Entry outside the function and simply pack() and pack_forget() in the function. pack_forget() will not delete your widget it will only hide it from the layout. To delete the widget you might have to use widget.destroy().

import tkinter
from tkinter import ttk

def modify_label_text(event):
    newvalue = entry.get()
    clickme.configure(text=newvalue)
    entry.pack_forget()
    clickme.focus()

def create_entry(event):
    label = event.widget
    entry.pack()
    entry.focus()


root = tkinter.Tk()

newvalue = tkinter.StringVar()

clickme = ttk.Label(root, width=16)
clickme.pack()

clickme.bind("<Button-1>", create_entry)
clickme.focus()

entry = ttk.Entry(clickme, textvariable=newvalue)
entry.bind("<Return>", modify_label_text)

root.mainloop()

Edit(writing my comment as answer)

I suspect that it has to do something with the textvariable being set. Simply removing it or changing the newvalue to global scope seems to work. Also, since you don't seem to be using .set or .get methods you can simply remove it if you want.

This may not an answer, but a bit too large for a comment. Also could someone elaborate this behavior in a different enviorment. I ran that code with:

Mashine:

Windows 10 Home; x64-base,Intel(R) Core(TM) i3-2120 @ 3.30GHz, 3300 MHz, 2Cores

with

Python 3.7.2 and tkinter 8.6

After some research I couldnt find a reason for this behavior. Neither in the docs, nor in the sometimes tricky style_map or in the default bindings.

Can I assume that you create the labels with the entries in a function/loop? You may want to reuse the entry, because for some reason the exact same code works if you adress the entry by winfo_children(). And even if I call .update_idletasks() or just for experience the evil .update() it dosent work for no reason.

import tkinter
from tkinter import ttk

def modify_label_text(event):
    entry = event.widget
    newvalue = entry.get()
    label = entry.master
    label.configure(text=newvalue)
    entry.unbind("<Return>")
    entry.pack_forget()
    label.focus()

def create_entry(event):
    label = event.widget
    oldvalue = label.cget("text")
    newvalue = tkinter.StringVar()
    if len(label.winfo_children()) == 0:
        print('new')
        entry = ttk.Entry(label, textvariable=newvalue)
    '''
    entry = tkinter.Entry(label, textvariable=newvalue)
    '''
    e = label.winfo_children()[0]
    e.pack()
    e.delete(0, "end")
    e.insert(0, oldvalue+' works')
    e.bind("<Return>", modify_label_text)
    e.focus()

root = tkinter.Tk()

clickme = ttk.Label(root, width=16)
clickme.pack()

clickme.bind("<Button-1>", create_entry)
clickme.focus()

root.mainloop()

Another solution could be:

def create_entry(event):
    label = event.widget
    oldvalue = label.cget("text")
    newvalue = tkinter.StringVar()
    entry = ttk.Entry(label, textvariable=newvalue)
    '''
    entry = tkinter.Entry(label, textvariable=newvalue)
    '''
    root.after(100,after_time,entry,oldvalue)

def after_time(entry,oldvalue):    
    entry.pack()
    entry.delete(0, "end")
    entry.insert(0, oldvalue)
    entry.bind("<Return>", modify_label_text)
    entry.focus()
Related