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?
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()
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.
Related
def entering(a):
value=entry1.get() #entry1.get()is used to get values which entered in entry box
label1=Label(root, text =value, height=10)
label1.pack()
entry1.delete(0, END) # used to clear entry box
root.bind('<Return>',entering)
how do I remove the widget that I created in the function call entering?
I know about the destroy function. I don't want to destroy it after a particular time.
I want to destroy it or overwrite it into the widget when I call the function again
I think this is what you expect:
import tkinter as tk
root = tk.Tk()
myentry = tk.Entry(root)
myentry.pack()
var = tk.StringVar(root)
mylabel = tk.Label(root, textvariable= var)
mylabel.pack()
def entering(event):
text = myentry.get()
var.set(text)
myentry.bind('<Return>', entering)
root.mainloop()
I made a new object of tk.IntVar and called it pwHardness
but its value is something random like 140358607937898IntVar.
i want the radio buttons to set the value of the variable pwHardness 1 or 2
import tkinter as tk
pwHardness = tk.IntVar
window = tk.Tk()
window.geometry("1600x500")
window.configure(bg="#323e52")
Label = tk.Label(text="Password Gen", background="#323e52", foreground="#fafafa")
Label.config(width=200)
Label.config(font=("Courier", 44))
setPwRadioButtonEasy = tk.Radiobutton(
text="Easy PW",
padx = 20,
var=pwHardness,
variable=pwHardness,
value=1,
)
setPwRadioButtonHard = tk.Radiobutton(
text="Hard PW",
padx = 20,
var=pwHardness,
variable=pwHardness,
value=2,
)
label1 = tk.Label(text=pwHardness)
Label.pack()
setPwRadioButtonEasy.pack()
setPwRadioButtonHard.pack()
label1.pack()
window.mainloop()
FYI This is going to be a Password Generator.
You aren't initializing the variable, you're just taking the IntVar object.
pwHardness = tk.IntVar()
would initialize a new IntVar – you're missing the parentheses.
Additionally, you're passing the var as a "string" value to text.
You'd want
label1 = tk.Label(text=pwHardness.get())
to "read" the variable into the label. However, it won't refresh automatically with that configuration.
You are missing parentheses after pwHardness = tk.IntVar. It should be pwHardness = tk.IntVar(). Additionally, change label1 = tk.Label(text=pwHardness) to label1 = tk.Label(textvar=pwHardness), so the label gets automatically updated. And, tk.IntVar must be initiated with a parent, e.g. the toplevel window. Example :
import tkinter as tk
root = tk.Tk()
var = tk.IntVar(root)
label = tk.Label(root, textvar=var)
label.pack()
while True:
inp = input("enter new value ")
if inp != "quit":
var.set(int(inp))
else:
break
simpledialog or filedialog are widgets very convenient to use.
I would like to do the same :
modal window which pops up on screen like these simpledialogs
combo box inside
and when I select a value in combo, return this value without needing a button
Something like:
def askComboValue():
root = Tk() #how to pops up this window?
label = ttk.Label(root, text = "select your value")
label.pack()
box_value = ''
combo = ttk.Combobox(root, textvariable=box_value, values=['bla', 'bli', 'blo'])
combo.current(0)
combo.pack()
combo.bind("<<ComboboxSelected>>", returnValue) #how to catch this value?
root.grab_set_global() #is it the right way to make it modal?
root.mainloop()
return box_value #how to return this value?
Does anyone know how to deal with it?
Thanks for your help
If the function is called when there is already a tkinter window, then better use Toplevel() instead of Tk(). Also box_value should be instance of StringVar() instead. grab_set() is used instead of grab_set_global() as well.
Below is an example based on your code:
import tkinter as tk
from tkinter import ttk
def askComboValue(*values):
top = tk.Toplevel() # use Toplevel() instead of Tk()
tk.Label(top, text='Select your value').pack()
box_value = tk.StringVar()
combo = ttk.Combobox(top, textvariable=box_value, values=values)
combo.pack()
combo.bind('<<ComboboxSelected>>', lambda _: top.destroy())
top.grab_set()
top.wait_window(top) # wait for itself destroyed, so like a modal dialog
return box_value.get()
def test():
result = askComboValue('bla', 'bli', 'blo')
print(result)
root = tk.Tk()
tk.Button(root, text='Test', command=test).pack()
root.mainloop()
I thought it would be good to get my Tkinter widget names out of the global namespace so I put them all in a def main() and called main(). But when I did this, the ttk.Entry stopped displaying the default text. But it works if I change it to a tk.Entry.
This can be done with a button and entry.get() as shown but in my larger application I do need the stringvar so as far as I know I need tk.StringVar()'s set() method.
I tried using a textvariable in a config() method on the entry name and it did the same thing.
I added a ttk.Combobox since it's part ttk.Entry and it has the same problem.
QUESTION: Is there anything wrong with the two globals declared for StringVar()s in Example 3? Why or why not? I don't know when to draw the line on globals. All the yammering against them makes me never want to use a single one, ever, but maybe that's impossible in procedural coding?
EXAMPLE 1: default text displays in entry with tk.Entry or ttk.Entry
import tkinter as tk
from tkinter import ttk
root = tk.Tk()
root.geometry('200x200+500+300')
def get_txt():
lab2.config(text=ent.get())
x = tk.StringVar()
x.set("default entry text")
y = tk.StringVar()
y.set("default combo option")
ent = ttk.Entry(root, textvariable=x) # either tk or ttk can be used here and default text will show
lab = ttk.Label(root, textvariable=x)
lab2 = ttk.Label(root)
buttn = ttk.Button(root, text='GET TEXT', command=get_txt)
combo = ttk.Combobox(root, values=['dog', 'cat', 'goldfish'], textvariable=y)
lab3 = ttk.Label(root, textvariable=y)
ent.grid()
lab.grid()
lab2.grid()
buttn.grid()
combo.grid()
lab3.grid()
root.mainloop()
EXAMPLE 2: default text displays in entry with tk.Entry, not ttk.Entry
import tkinter as tk
from tkinter import ttk
def main():
def get_txt():
lab2.config(text=ent.get())
x = tk.StringVar()
x.set("default entry text")
y = tk.StringVar()
y.set("default combo option")
ent = tk.Entry(root) # this is where the ttk has to be changed to tk for the default text to show up
ent.config(textvariable=x)
lab = ttk.Label(root, textvariable=x)
lab2 = ttk.Label(root)
buttn = ttk.Button(root, text='GET TEXT', command=get_txt)
combo = ttk.Combobox(root, values=['dog', 'cat', 'goldfish'], textvariable=y) # there's no tk.Combobox
lab3 = ttk.Label(root, textvariable=y)
ent.grid()
lab.grid()
lab2.grid()
buttn.grid()
combo.grid()
lab3.grid()
root = tk.Tk()
main()
root.mainloop()
EXAMPLE 3: global reference to StringVar()--back to globals??
import tkinter as tk
from tkinter import ttk
def main():
def get_txt():
lab2.config(text=ent.get())
# x = tk.StringVar()
x.set("default entry text")
# y = tk.StringVar()
y.set("default combo option")
ent = ttk.Entry(root) # this is where the ttk has to be changed to tk for the default text to show up
ent.config(textvariable=x)
lab = ttk.Label(root, textvariable=x)
lab2 = ttk.Label(root)
buttn = ttk.Button(root, text='GET TEXT', command=get_txt)
combo = ttk.Combobox(root, values=['dog', 'cat', 'goldfish'], textvariable=y) # there's no tk.Combobox
lab3 = ttk.Label(root, textvariable=y)
ent.grid()
lab.grid()
lab2.grid()
buttn.grid()
combo.grid()
lab3.grid()
root = tk.Tk()
x = tk.StringVar()
y = tk.StringVar()
main()
root.mainloop()
I have seen references to this problem. Must be a bug in tkinter.ttk - looks like variable x goes out of scope, although why it only happens with ttk.Entry and ttk.Label, and not with their tk counterparts is beyond me.
In any case, the following is a neater solution and works fine:
import tkinter as tk
from tkinter import ttk
class main(tk.Tk):
def get_txt(self):
self.lab2.config(text=self.ent.get())
def __init__(self):
super().__init__()
self.x = tk.StringVar()
self.x.set("default entry text")
self.y = tk.StringVar()
self.y.set("default combo option")
self.ent = ttk.Entry(self, textvariable=self.x)
lab = ttk.Label(self, textvariable=self.x)
self.lab2 = ttk.Label(self)
buttn = ttk.Button(self, text='GET TEXT', command=self.get_txt)
combo = ttk.Combobox(self, values=['dog', 'cat', 'goldfish'], textvariable=self.y)
lab3 = ttk.Label(self, textvariable=self.y)
self.ent.grid()
lab.grid()
self.lab2.grid()
buttn.grid()
combo.grid()
lab3.grid()
root = main()
root.mainloop()
In need to make a generic function that on event it would return the focused-out widget to its default value. Is there a way to accomplish this?
Example:
entry1 = Tkinter.Entry()
entry1.grid(..)
entry1.insert(0,"hello")
entry1.bind("<FocusIn>", EntryFocusedIn)
entry1.bind("<FocusOut>", EntryFocusedOut)
entry2 = Tkinter.Entry()
entry2.grid(..)
entry2.insert(0,"again")
entry2.bind("<FocusIn>", EntryFocusedIn)
entry2.bind("<FocusOut>", EntryFocusedOut)
def EntryFocusedIn(params):
params.widget.delete(0, Tkinter.END)
def EntryFocusedOut(params):
# return widget to its default value
# which in case of entry1 its "hello"
# and in case of entry2 its "again"
You could subclass the Entry widget to add an attribute to store a default value, and reference that attribute in the event handler. However, there's nothing stopping you from simply adding your own attribute to each Entry widget directly, e.g. entry1.default_value = 'hello', entry1.default_value = 'again':
import Tkinter
def EntryFocusedIn(params):
params.widget.delete(0, Tkinter.END)
def EntryFocusedOut(params):
# restore default value
params.widget.delete(0, Tkinter.END)
params.widget.insert(0, params.widget.default_value)
root = Tkinter.Tk()
entry1 = Tkinter.Entry()
entry1.default_value = 'hello'
entry1.pack()
entry1.insert(0, entry1.default_value)
entry1.bind("<FocusIn>", EntryFocusedIn)
entry1.bind("<FocusOut>", EntryFocusedOut)
entry2 = Tkinter.Entry()
entry2.default_value = 'again'
entry2.pack()
entry2.insert(0, entry2.default_value)
entry2.bind("<FocusIn>", EntryFocusedIn)
entry2.bind("<FocusOut>", EntryFocusedOut)
root.mainloop()