Related
I know this Q has been answered in this site, but im looking for a more simpler answer, and ive seen one before but then the question has been deleted or something, I cant find it. Hopefully someone is having a better and easier way around to it. Something related to class might be better as I could use it easily with more Entry widgets
Here is a snippet:
from tkinter import *
root = Tk()
def remove(event):
e.delete(0, END)
e = Entry(root)
e.insert(0, 'PLACEHOLDER')
e.pack(padx=100,pady=(30,0))
e.bind('<FocusIn>', remove)
e2 = Entry(root)
e2.pack( pady=(20,100))
root.mainloop()
Yes, this will remove all the other items inside of the box once it looses focus and gains it again, including the text we enter at first. Anyway to get around that and have a perfect placeholder with tkinter, I'm aware there is no inbuilt way.
Thanks in advance :D
I'm not really clear on what you're asking, so I'm guessing you're asking how to know when the entry widget has placeholder text and when it doesn't so that you know when to clear it and when not to clear it.
The easiest solution is to add an attribute to the entry with the replacement text, and then compare it to the contents before deleting.
Using functions
First, let's create a function to initialize the placeholder text for a widget. This function does a few simple things: it adds a placeholder attribute on the widget, and it establishes the bindings. It also inserts the placeholder if the widget is empty:
def init_placeholder(widget, placeholder_text):
widget.placeholder = placeholder_text
if widget.get() == "":
widget.insert("end", placeholder_text)
# set up a binding to remove placeholder text
widget.bind("<FocusIn>", remove_placeholder)
widget.bind("<FocusOut>", add_placeholder)
Now let's tweak your remove function to be a bit more generic. Since it's called via an event, it can use event.widget rather than a hard-coded reference to a specific widget. It also uses the placeholder attribute which we added to the widget. These two techniques lets it be used by more than one widget.
def remove_placeholder(event):
placeholder_text = getattr(event.widget, "placeholder", "")
if placeholder_text and event.widget.get() == placeholder_text:
event.widget.delete(0, "end")
Finally we need to implement the add_placeholder function. This function will add the placeholder when the widget loses focus and the user hasn't typed anything. It needs to check if the entry widget has a placeholder, and if it does and the widget is empty, it adds the placeholder. Like remove_placeholder it uses event.widget and the placeholder attribute:
def add_placeholder(event):
placeholder_text = getattr(event.widget, "placeholder", "")
if placeholder_text and event.widget.get() == "":
event.widget.insert(0, placeholder_text)
I've modified your program to use different placeholder text for each of the two entry widgets to show that the functions are generic and not tied to a specific entry widget.
from tkinter import *
root = Tk()
def remove_placeholder(event):
"""Remove placeholder text, if present"""
placeholder_text = getattr(event.widget, "placeholder", "")
if placeholder_text and event.widget.get() == placeholder_text:
event.widget.delete(0, "end")
def add_placeholder(event):
"""Add placeholder text if the widget is empty"""
placeholder_text = getattr(event.widget, "placeholder", "")
if placeholder_text and event.widget.get() == "":
event.widget.insert(0, placeholder_text)
def init_placeholder(widget, placeholder_text):
widget.placeholder = placeholder_text
if widget.get() == "":
widget.insert("end", placeholder_text)
# set up a binding to remove placeholder text
widget.bind("<FocusIn>", remove_placeholder)
widget.bind("<FocusOut>", add_placeholder)
e = Entry(root)
e.pack(padx=100,pady=(30,0))
e2 = Entry(root)
e2.pack( pady=(20,100))
init_placeholder(e, "First Name")
init_placeholder(e2, "Last Name")
root.mainloop()
Using a custom class
Arguably, a better way to implement this would be to create a custom class. That way everything is encapsulated in one place. Here's an example:
class EntryWithPlaceholder(Entry):
def __init__(self, *args, **kwargs):
self.placeholder = kwargs.pop("placeholder", "")
super().__init__(*args, **kwargs)
self.insert("end", self.placeholder)
self.bind("<FocusIn>", self.remove_placeholder)
self.bind("<FocusOut>", self.add_placeholder)
def remove_placeholder(self, event):
"""Remove placeholder text, if present"""
if self.get() == self.placeholder:
self.delete(0, "end")
def add_placeholder(self,event):
"""Add placeholder text if the widget is empty"""
if self.placeholder and self.get() == "":
self.insert(0, self.placeholder)
You can use this class just like an Entry widget, but you can specify a placeholder:
e3 = EntryWithPlaceholder(root, placeholder="Address")
e3.pack()
Here is a very simple example. In this example we include a couple of features/caveats:
ghost text for the placeholder
entry.input will return None if it's text is the placeholder or empty
entry.input should be used in place of .get() and .insert(). The .input logic is designed to give you the proper results for this type of widget. .get() is not smart enough to return the proper data, and .insert() has been reconfigured as a proxy to .input
placeholder is juggled while you type
placeholder can be overwritten with .insert() ~ no need to use .delete(). You should still use entry.input instead
#widgets.py
import tkinter as tk
class PlaceholderEntry(tk.Entry):
'''
All Of These Properties Are For Convenience
'''
#property
def input(self):
return self.get() if self.get() not in [self.__ph, ''] else None
#input.setter
def input(self, value):
self.delete(0, 'end')
self.insert(0, value)
self.configure(fg = self.ghost if value == self.__ph else self.normal)
#property
def isempty(self) -> bool:
return self.get() == ''
#property
def isholder(self) -> bool:
return self.get() == self.__ph
def __init__(self, master, placeholder, **kwargs):
tk.Entry.__init__(self, master, **{'disabledforeground':'#BBBBBB', **kwargs})
self.normal = self['foreground']
self.ghost = self['disabledforeground']
self.__ph = placeholder
self.input = placeholder
vcmd = self.register(self.validate)
self.configure(validate='all', validatecommand=(vcmd, '%S', '%s', '%d'))
self.bind('<FocusIn>' , self.focusin)
self.bind('<FocusOut>', self.focusout)
self.bind('<Key>' , self.check)
#rewire .insert() to be a proxy of .input
def validate(self, action_text, orig_text, action):
if action == '1':
if orig_text == self.__ph:
self.input = action_text
return True
#removes placeholder if necessary
def focusin(self, event=None):
if self.isholder:
self.input = ''
#adds placeholder if necessary
def focusout(self, event=None):
if self.isempty:
self.input = self.__ph
#juggles the placeholder while you type
def check(self, event):
if event.keysym == 'BackSpace':
if self.input and len(self.input) == 1:
self.input = self.__ph
self.icursor(0)
return 'break'
elif self.isholder:
if event.char:
self.input = ''
else:
return 'break'
usage example:
#__main__.py
import tkinter as tk
import widgets as ctk #custom tk
if __name__ == "__main__":
root = tk.Tk()
root.title("Placeholder Entry")
root.grid_columnconfigure(2, weight=1)
#init some data
entries = [] #for storing entry references
label_text = ['email', 'name']
entry_text = ['you#mail.com', 'John Smith']
#create form
for n, (label, placeholder) in enumerate(zip(label_text, entry_text)):
#make label
tk.Label(root, text=f'{label}: ', width=8, font='consolas 12 bold', anchor='w').grid(row=n, column=0, sticky='w')
#make entry
entries.append(ctk.PlaceholderEntry(root, placeholder, width=14, font='consolas 12 bold'))
entries[-1].grid(row=n, column=1, sticky='w')
#form submit function
def submit():
for l, e in zip(label_text, entries):
if e.input:
print(f'{l}: {e.input}')
#form submit button
tk.Button(root, text='submit', command=submit).grid(column=1, sticky='e')
root.mainloop()
I have tried this:
from tkinter import *
root = Tk()
def remove(event):
if e.get() == 'PLACEHOLDER': #Check default value
e.delete(0, END)
def add(event):
if not e.get(): #Check if left empty
e.insert(0, 'PLACEHOLDER')
e = Entry(root)
e.insert(0, 'PLACEHOLDER')
e.pack(padx=100,pady=(30,0))
e.bind('<FocusIn>', remove)
e.bind('<FocusOut>', add)
e2 = Entry(root)
e2.pack( pady=(20,100))
root.mainloop()
By doing this you will be clearing only if the default value is present in the Text and also, if the field is left empty, the placeholder gets back into the Text.
No, this is not directly, possible with tkinter. You might want to use classes and OOP.
This question already has answers here:
How to add placeholder to an Entry in tkinter?
(10 answers)
Closed 2 years ago.
Can you help me to display a text in my entry, which disappears after clicking on the entry?
For this I have the following example in the entry with the text "Search". I would like to have exactly this text displayed in my entry.
Thanks a lot!
What your looking for is called placeholders. Unfortunately there is no default option with tkinter on placeholders, but a simple placeholder would look like:
import tkinter as tk
root = tk.Tk()
placeholder = 'Your text here'
def erase(event=None):
if e.get() == placeholder:
e.delete(0,'end')
def add(event=None):
if e.get() == '':
e.insert(0,placeholder)
e = tk.Entry(root)
e.pack(padx=10,pady=10)
dummy = tk.Entry(root) #dummy widget just to see other widget lose focus
dummy.pack(padx=10,pady=10)
add()
e.bind('<FocusIn>',erase)
e.bind('<FocusOut>',add)
root.mainloop()
But this code has many downfalls as the entry methods wont work properly if your looking to manipulate with more data, so what I prefer to do is to make a class and use it, instead of using the default Entry class.
This is a code that I had made to work for similar situations, I dont make any claim that this is any how perfect, but this could get your job done.
import tkinter as tk
from tkinter import ttk as ttk
class PlaceholderEntry(ttk.Entry):
'''
Custom modern Placeholder Entry box, takes positional argument master and placeholder along with\n
textcolor(default being black) and placeholdercolor(default being grey).\n
Use acquire() for getting output from entry widget\n
Use shove() for inserting into entry widget\n
Use remove() for deleting from entry widget\n
Use length() for getting the length of text in the widget\n
BUG 1: Possible bugs with binding to this class\n
BUG 2: Anomalous behaviour with config or configure method
'''
def __init__(self, master, placeholder,textcolor='black',placeholdercolor='grey', **kwargs):
self.text = placeholder
self.__has_placeholder = False # placeholder flag
self.placeholdercolor = placeholdercolor
self.textcolor = textcolor
# style for ttk widget
self.s = ttk.Style()
# init entry box
ttk.Entry.__init__(self, master, style='my.TEntry', **kwargs)
self.s.configure('my.TEntry',forground=self.placeholdercolor)
# add placeholder if box empty
self._add()
# bindings of the widget
self.bind('<FocusIn>', self._clear)
self.bind('<FocusOut>', self._add)
self.bind_all('<Key>', self._normal)
self.bind_all('<Button-1>', self._cursor)
def _clear(self, *args): # method to remove the placeholder
if self.get() == self.text and self.__has_placeholder: # remove placeholder when focus gain
self.delete(0, tk.END)
self.s.configure('my.TEntry', foreground='black',
font=(0, 0, 'normal'))
self.__has_placeholder = False #set flag to false
def _add(self, *args): # method to add placeholder
if self.get() == '' and not self.__has_placeholder: # if no text add placeholder
self.s.configure('my.TEntry', foreground=self.placeholdercolor,
font=(0, 0, 'bold'))
self.insert(0, self.text) # insert placeholder
self.icursor(0) # move insertion cursor to start of entrybox
self.__has_placeholder = True #set flag to true
def _normal(self, *args): #method to set the text to normal properties
self._add() # if empty add placeholder
if self.get() == self.text and self.__has_placeholder: # clear the placeholder if starts typing
self.bind('<Key>', self._clear)
self.icursor(-1) # keep insertion cursor to the end
else:
self.s.configure('my.TEntry', foreground=self.textcolor,
font=(0, 0, 'normal')) # set normal font
def acquire(self):
"""Custom method to get the text"""
if self.get() == self.text and self.__has_placeholder:
return 'None'
else:
return self.get()
def shove(self, index, string):
"""Custom method to insert text into entry"""
self._clear()
self.insert(index, string)
def remove(self, first, last):
"""Custom method to remove text from entry"""
if self.get() != self.text:
self.delete(first, last)
self._add()
elif self.acquire() == self.text and not self.__has_placeholder:
self.delete(first, last)
self._add()
def length(self):
"""Custom method to get the length of text in the entry widget"""
if self.get() == self.text and self.__has_placeholder:
return 0
else:
return len(self.get())
def _cursor(self, *args): # method to not allow user to move cursor when placeholder exists
if self.get() == self.text and self.__has_placeholder:
self.icursor(0)
#usage
if __name__ == '__main__':
root = tk.Tk()
e = PlaceholderEntry(root,placeholder='Your text')
e.pack(padx=10,pady=10)
dummy = tk.Entry(root) #dummy widget just to see other widget lose focus
dummy.pack(padx=10,pady=10)
root.mainloop()
Though I recommend you use the latter example once you understand whats going on behind the hood.
PS:- The example with class is faulty when making more than one instance of the class.
I know this Q has been answered in this site, but im looking for a more simpler answer, and ive seen one before but then the question has been deleted or something, I cant find it. Hopefully someone is having a better and easier way around to it. Something related to class might be better as I could use it easily with more Entry widgets
Here is a snippet:
from tkinter import *
root = Tk()
def remove(event):
e.delete(0, END)
e = Entry(root)
e.insert(0, 'PLACEHOLDER')
e.pack(padx=100,pady=(30,0))
e.bind('<FocusIn>', remove)
e2 = Entry(root)
e2.pack( pady=(20,100))
root.mainloop()
Yes, this will remove all the other items inside of the box once it looses focus and gains it again, including the text we enter at first. Anyway to get around that and have a perfect placeholder with tkinter, I'm aware there is no inbuilt way.
Thanks in advance :D
I'm not really clear on what you're asking, so I'm guessing you're asking how to know when the entry widget has placeholder text and when it doesn't so that you know when to clear it and when not to clear it.
The easiest solution is to add an attribute to the entry with the replacement text, and then compare it to the contents before deleting.
Using functions
First, let's create a function to initialize the placeholder text for a widget. This function does a few simple things: it adds a placeholder attribute on the widget, and it establishes the bindings. It also inserts the placeholder if the widget is empty:
def init_placeholder(widget, placeholder_text):
widget.placeholder = placeholder_text
if widget.get() == "":
widget.insert("end", placeholder_text)
# set up a binding to remove placeholder text
widget.bind("<FocusIn>", remove_placeholder)
widget.bind("<FocusOut>", add_placeholder)
Now let's tweak your remove function to be a bit more generic. Since it's called via an event, it can use event.widget rather than a hard-coded reference to a specific widget. It also uses the placeholder attribute which we added to the widget. These two techniques lets it be used by more than one widget.
def remove_placeholder(event):
placeholder_text = getattr(event.widget, "placeholder", "")
if placeholder_text and event.widget.get() == placeholder_text:
event.widget.delete(0, "end")
Finally we need to implement the add_placeholder function. This function will add the placeholder when the widget loses focus and the user hasn't typed anything. It needs to check if the entry widget has a placeholder, and if it does and the widget is empty, it adds the placeholder. Like remove_placeholder it uses event.widget and the placeholder attribute:
def add_placeholder(event):
placeholder_text = getattr(event.widget, "placeholder", "")
if placeholder_text and event.widget.get() == "":
event.widget.insert(0, placeholder_text)
I've modified your program to use different placeholder text for each of the two entry widgets to show that the functions are generic and not tied to a specific entry widget.
from tkinter import *
root = Tk()
def remove_placeholder(event):
"""Remove placeholder text, if present"""
placeholder_text = getattr(event.widget, "placeholder", "")
if placeholder_text and event.widget.get() == placeholder_text:
event.widget.delete(0, "end")
def add_placeholder(event):
"""Add placeholder text if the widget is empty"""
placeholder_text = getattr(event.widget, "placeholder", "")
if placeholder_text and event.widget.get() == "":
event.widget.insert(0, placeholder_text)
def init_placeholder(widget, placeholder_text):
widget.placeholder = placeholder_text
if widget.get() == "":
widget.insert("end", placeholder_text)
# set up a binding to remove placeholder text
widget.bind("<FocusIn>", remove_placeholder)
widget.bind("<FocusOut>", add_placeholder)
e = Entry(root)
e.pack(padx=100,pady=(30,0))
e2 = Entry(root)
e2.pack( pady=(20,100))
init_placeholder(e, "First Name")
init_placeholder(e2, "Last Name")
root.mainloop()
Using a custom class
Arguably, a better way to implement this would be to create a custom class. That way everything is encapsulated in one place. Here's an example:
class EntryWithPlaceholder(Entry):
def __init__(self, *args, **kwargs):
self.placeholder = kwargs.pop("placeholder", "")
super().__init__(*args, **kwargs)
self.insert("end", self.placeholder)
self.bind("<FocusIn>", self.remove_placeholder)
self.bind("<FocusOut>", self.add_placeholder)
def remove_placeholder(self, event):
"""Remove placeholder text, if present"""
if self.get() == self.placeholder:
self.delete(0, "end")
def add_placeholder(self,event):
"""Add placeholder text if the widget is empty"""
if self.placeholder and self.get() == "":
self.insert(0, self.placeholder)
You can use this class just like an Entry widget, but you can specify a placeholder:
e3 = EntryWithPlaceholder(root, placeholder="Address")
e3.pack()
Here is a very simple example. In this example we include a couple of features/caveats:
ghost text for the placeholder
entry.input will return None if it's text is the placeholder or empty
entry.input should be used in place of .get() and .insert(). The .input logic is designed to give you the proper results for this type of widget. .get() is not smart enough to return the proper data, and .insert() has been reconfigured as a proxy to .input
placeholder is juggled while you type
placeholder can be overwritten with .insert() ~ no need to use .delete(). You should still use entry.input instead
#widgets.py
import tkinter as tk
class PlaceholderEntry(tk.Entry):
'''
All Of These Properties Are For Convenience
'''
#property
def input(self):
return self.get() if self.get() not in [self.__ph, ''] else None
#input.setter
def input(self, value):
self.delete(0, 'end')
self.insert(0, value)
self.configure(fg = self.ghost if value == self.__ph else self.normal)
#property
def isempty(self) -> bool:
return self.get() == ''
#property
def isholder(self) -> bool:
return self.get() == self.__ph
def __init__(self, master, placeholder, **kwargs):
tk.Entry.__init__(self, master, **{'disabledforeground':'#BBBBBB', **kwargs})
self.normal = self['foreground']
self.ghost = self['disabledforeground']
self.__ph = placeholder
self.input = placeholder
vcmd = self.register(self.validate)
self.configure(validate='all', validatecommand=(vcmd, '%S', '%s', '%d'))
self.bind('<FocusIn>' , self.focusin)
self.bind('<FocusOut>', self.focusout)
self.bind('<Key>' , self.check)
#rewire .insert() to be a proxy of .input
def validate(self, action_text, orig_text, action):
if action == '1':
if orig_text == self.__ph:
self.input = action_text
return True
#removes placeholder if necessary
def focusin(self, event=None):
if self.isholder:
self.input = ''
#adds placeholder if necessary
def focusout(self, event=None):
if self.isempty:
self.input = self.__ph
#juggles the placeholder while you type
def check(self, event):
if event.keysym == 'BackSpace':
if self.input and len(self.input) == 1:
self.input = self.__ph
self.icursor(0)
return 'break'
elif self.isholder:
if event.char:
self.input = ''
else:
return 'break'
usage example:
#__main__.py
import tkinter as tk
import widgets as ctk #custom tk
if __name__ == "__main__":
root = tk.Tk()
root.title("Placeholder Entry")
root.grid_columnconfigure(2, weight=1)
#init some data
entries = [] #for storing entry references
label_text = ['email', 'name']
entry_text = ['you#mail.com', 'John Smith']
#create form
for n, (label, placeholder) in enumerate(zip(label_text, entry_text)):
#make label
tk.Label(root, text=f'{label}: ', width=8, font='consolas 12 bold', anchor='w').grid(row=n, column=0, sticky='w')
#make entry
entries.append(ctk.PlaceholderEntry(root, placeholder, width=14, font='consolas 12 bold'))
entries[-1].grid(row=n, column=1, sticky='w')
#form submit function
def submit():
for l, e in zip(label_text, entries):
if e.input:
print(f'{l}: {e.input}')
#form submit button
tk.Button(root, text='submit', command=submit).grid(column=1, sticky='e')
root.mainloop()
I have tried this:
from tkinter import *
root = Tk()
def remove(event):
if e.get() == 'PLACEHOLDER': #Check default value
e.delete(0, END)
def add(event):
if not e.get(): #Check if left empty
e.insert(0, 'PLACEHOLDER')
e = Entry(root)
e.insert(0, 'PLACEHOLDER')
e.pack(padx=100,pady=(30,0))
e.bind('<FocusIn>', remove)
e.bind('<FocusOut>', add)
e2 = Entry(root)
e2.pack( pady=(20,100))
root.mainloop()
By doing this you will be clearing only if the default value is present in the Text and also, if the field is left empty, the placeholder gets back into the Text.
No, this is not directly, possible with tkinter. You might want to use classes and OOP.
In another answer here, a user created an inherited TextWithVar class that provides instances of the Tkinter.Text element but with textvariable functionality like Tkinter.Entry has. However, in testing, this new class behaves differently than a Text element when when using the Grid manager. To demonstrate, this code is copied from that answer, with the addition of some test calls at the end:
import Tkinter as tk
class TextWithVar(tk.Text):
'''A text widget that accepts a 'textvariable' option'''
def __init__(self, parent, *args, **kwargs):
try:
self._textvariable = kwargs.pop("textvariable")
except KeyError:
self._textvariable = None
tk.Text.__init__(self, *args, **kwargs)
# if the variable has data in it, use it to initialize
# the widget
if self._textvariable is not None:
self.insert("1.0", self._textvariable.get())
# this defines an internal proxy which generates a
# virtual event whenever text is inserted or deleted
self.tk.eval('''
proc widget_proxy {widget widget_command args} {
# call the real tk widget command with the real args
set result [uplevel [linsert $args 0 $widget_command]]
# if the contents changed, generate an event we can bind to
if {([lindex $args 0] in {insert replace delete})} {
event generate $widget <<Change>> -when tail
}
# return the result from the real widget command
return $result
}
''')
# this replaces the underlying widget with the proxy
self.tk.eval('''
rename {widget} _{widget}
interp alias {{}} ::{widget} {{}} widget_proxy {widget} _{widget}
'''.format(widget=str(self)))
# set up a binding to update the variable whenever
# the widget changes
self.bind("<<Change>>", self._on_widget_change)
# set up a trace to update the text widget when the
# variable changes
if self._textvariable is not None:
self._textvariable.trace("wu", self._on_var_change)
def _on_var_change(self, *args):
'''Change the text widget when the associated textvariable changes'''
# only change the widget if something actually
# changed, otherwise we'll get into an endless
# loop
text_current = self.get("1.0", "end-1c")
var_current = self._textvariable.get()
if text_current != var_current:
self.delete("1.0", "end")
self.insert("1.0", var_current)
def _on_widget_change(self, event=None):
'''Change the variable when the widget changes'''
if self._textvariable is not None:
self._textvariable.set(self.get("1.0", "end-1c"))
root = tk.Tk()
text_frame = TextWithVar(root)
text_frame.grid(row=1, column=1)
test_button = tk.Button(root, text='Test')
test_button.grid(row=1, column=1, sticky='NE')
root.mainloop()
root2 = tk.Tk()
frame = tk.Frame(root2)
frame.grid(row=1, column=1)
text_frame2 = TextWithVar(frame)
text_frame2.grid(row=1, column=1)
test_button2 = tk.Button(frame, text='Test')
test_button2.grid(row=1, column=1, sticky='NE')
root2.mainloop()
In this example, when the TextWithVar element is directly inside root, it acts like it should - the Button element is placed on top of it in the corner. However, when both are inside a Frame, the Button element is nowhere to be seen. Now change both the TextWithVar calls to tk.Text. Both of them work the way they should, with the Button in plain view. According to Bryan, who made the new class, these should work the exact same way, which I tend to agree with. So why do they work differently?
It's a bug in TextWithVar, in this line of code:
tk.Text.__init__(self, *args, **kwargs)
The code needs to include the parent parameter:
tk.Text.__init__(self, parent, *args, **kwargs)
I have been following the validation for Entry boxes from here. The code below is from the answer with the added condition that if the entered value is 'Q' then the program adds 'test' to the beginning of the Entry value.
However once this value is inserted, all validation appears to go out of the window and the Entry allows upper-case values. Some testing on my program shows the validation command (OnValidate in this case) isn't being called upon any further events of the Entry (key, focusin/out etc).
import Tkinter as tk
class MyApp():
def __init__(self):
self.root = tk.Tk()
vcmd = (self.root.register(self.OnValidate),
'%d', '%i', '%P', '%s', '%S', '%v', '%V', '%W')
self.entry = tk.Entry(self.root, validate="key",
validatecommand=vcmd)
self.entry.pack()
self.root.mainloop()
def OnValidate(self, d, i, P, s, S, v, V, W):
if S == "Q":
self.entry.insert(0,"test")
# only allow if the string is lowercase
return (S.lower() == S)
app=MyApp()
The reason for me doing this is I want the Entry to display a default value if its value is left empty after any changes that are made by a user. (i.e. my condition would be if not P on focusout)
Any ideas how to implement this or what's going wrong in the above much appreciated.
The validatecommand option is designed for validation only, not for doing other sorts of things. The behavior you see is the documented behavior.
According to the official tk documentation on entry validation:
... The validate option will also set itself to none when you edit the entry widget from within either the validateCommand or the invalidCommand.
(note: tkinter is little more than a wrapper around the tcl implementation of tk. For this reason, the tcl/tk documentation can be used as the definitive guide to the behavior of Tkinter)
I'm going to answer completely based on:
I want the Entry to display a default
value if its value is left empty after any changes that are made by a
user.
and hope that this example points you to what you want:
import tkinter
def analyze(event=None):
content = entry_contents.get()
if content == "":
entry_contents.set("default")
root = tkinter.Tk()
entry_contents = tkinter.StringVar()
entry = tkinter.Entry(root, textvariable=entry_contents)
entry.grid()
text = tkinter.Text(root, font=("Georgia", "12"))
text.grid()
entry.bind("<FocusOut>", analyze)
root.mainloop()
or if a control variable wont be of any use to you:
import tkinter
def analyze(event=None):
content = entry.get()
if content == "":
entry.insert(0, "default")
root = tkinter.Tk()
entry = tkinter.Entry(root)
entry.grid()
text = tkinter.Text(root, font=("Arial", "12", "bold"))
text.grid()
entry.bind("<FocusOut>", analyze)
root.mainloop()