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.
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:
Why is my Button's command executed immediately when I create the Button, and not when I click it? [duplicate]
(5 answers)
Closed 2 years ago.
So i am trying to get an text input for my program but tkinter doesn't seem to register it,
and i don't know what i have done wrong
window = self.newWindow(value)
label = tk.Label(window, text="Intfix to Postfix Convert")
label.place(x=0, y=20)
e1 = tk.Entry(window)
text = e1.get()
e1.place(x=0, y=50)
rezultat = tk.Text(window, width=20, height=3)
rezultat.place(x=0, y=80)
button = tk.Button(window, text="Enter")
button.place(x=127, y=46)
button.bind("<Double-Button-1>", self.passValue(rezultat, text))
My code looks something like this. Everything else is working the self.newWindow(value) is just
a function that creates a new window from the main one
so i said text=e1.get() but i ran the debbuger and it says it is an empty string and i want to pass this text through the function passValue()(a function that passes the value to the controller), i used button.bind() to do that. Is that ok?
I tested it by putting a default value at text like text="My name" and it did pass the value so that should be in order but i don't know why doesn't it get it from the entry box like it should.
I even tried to do e1.insert(0,"some random thing") and text= e1.get() and it did get it so i think there's a problem with the input.
Do i need to use some special kind of input function?
The whole code:
class Gui:
def __init__(self, controller):
self.main = tk.Tk()
self.main.title("DSA Quiz Helper")
self.__controller = controller
def IntFixPostExecute(self, event):
widget = event.widget
selection = widget.curselection()
value = widget.get(selection[0])
self.IntfixPostfixWindow(value)
def mainWindow(self):
self.main.geometry("800x500")
# to do scrollbar
lb = tk.Listbox(self.main, width=50, height=30)
lb.insert(1, "Intfix and Postfix Calculator")
lb.insert(2, "Something else")
lb.bind("<Double-Button-1>", self.IntFixPostExecute)
lb.pack()
def IntfixPostfixWindow(self, value):
window = self.newWindow(value)
label = tk.Label(window, text="Intfix to Postfix Convert")
label.place(x=0, y=20)
e1 = tk.Entry(window)
text = e1.get()
e1.place(x=0, y=50)
rezultat = tk.Text(window, width=20, height=3)
rezultat.place(x=0, y=80)
button = tk.Button(window, text="Enter")
button.place(x=127, y=46)
button.bind("<Double-Button-1>", self.passValue(rezultat, text))
print(text)
def passValue(self, rezultat, value):
returnValue = self.__controller.InfixToPostC(rezultat, value)
rezultat.insert(tk.INSERT, returnValue)
def newWindow(self, msg):
newwind = tk.Toplevel(self.main)
q1 = tk.Frame(newwind)
q1.pack()
newwind.geometry("500x230")
return newwind
def run(self):
self.mainWindow()
self.main.mainloop()
if i set this manually it works. I don't understand why i doesn't work from entrybox input
text = tk.StringVar()
e1 = tk.Entry(window, textvariable=text)
text.set("x+y*2")
text = e1.get()
e1.place(x=0, y=50)
I think i figured it out (correct me if i am wrong). I think there is a problem
with the button because as soon as a newwindow is open, the button automatically clicks itself, when at first in the entry box there is no text written yet(so it sends to the controller with the initial text(which is empty)). The problem is why the button auto-clicks itself( or anyway auto-runs the function passValue) and why after i input the text and click the button again it does nothing(so as i understand it works only one time and auto-runs itself, at first there is no text in entrybox and the button auto-runs itself,therefore passing an empty string
You should use entryname.get() to get the text that is inside that entry instead of declaring stringVar() and making that much more unreadable and hard to comprehend and to work with. But this is my point of view! – Tiago Oliveira 48 mins ago
I think what is happening is that u use the method right after declaring the entry widget wich means u are going to get a "" empty string because that's nothing that was written there, u need to replace on the command parameter with entryname.get() instead of declaring variable = entryname.get() and passing that as parameter wich will always be empty! Hope this helps!
My function is not returning any value which i entered into entry-box on button click:
from tkinter import *
import os
def onclick2():
val = box.get()
return(val)
root = Tk()
box = Entry(root)
box.pack()
buttonfortxtbox = Button(root, text="Check Result", font=('Cooper Black', 9), bd=10, width=20,command=onclick2)
buttonfortxtbox.pack(anchor = S)
print(onclick2())
root.mainloop()
Nothing is wrong with your code. But your understanding is not exact:
When you run this line: print(onclick2()) you are trying to print the content of the entry widget which is empty when the program first starts. So that is perfectly normal you are not getting anything visible on the Terminal.
To see the content of the entry widget, modify slightly your callback as follows:
def onclick2():
val = box.get()
print(val)
# You can remove "return val" as it is completely useless for your context
With this simple change, you will see None printed on the Terminal, which thing corresponds to what I described above. Now type something within the entry widget and click on the button: what you type appears finally on the output (Terminal).
You are trying to print the content of your entry widget but you are using the variable for your entry to receive the content which is wrong. Also call print the content in your entry widget to the your terminal .
from tkinter import *
def onclick2():
val = boss.get()
print(val)
root = Tk()
boss = StringVar() # to receive the content in the entry
box = Entry(root , textvariable=boss)
box.pack()
buttonfortxtbox = Button(root, text="Check Result", font=('Cooper Black',
9), bd=10, width=20,command=onclick2)
buttonfortxtbox.pack(anchor = S)
#print(onclick2()) # don't need to call this
root.mainloop()
In the program I made, the user presses enter and the text typed is then shown as a label in the program. So the label keeps getting updated and then written on the next line. The problem is that in the textbox the previous line the user typed stays there, which means u have to keep manually deleting the string in the textbox to write a new line. How can I make it so that you start out with a cleared textbox? Also, the enter button works but it seems that when i click on the "Return" button it gives me an error:
TypeError: evaluate() missing 1 required positional argument: 'event'
Here's the code:
from tkinter import *
window = Tk()
window.geometry("200x300")
def evaluate(event):
thetext = StringVar()
labeloutput = Label(app, textvariable = thetext)
n = e.get()
thetext.set(n)
labeloutput.grid()
app = Frame(window)
app.pack()
e = Entry(window)
e.pack()
b= Button(window, text="Return", command=evaluate)
b.pack()
window.bind("<Return>", evaluate)
mainloop()
Since you bind evaluate as a callback and you use it as a button command, when you use it in the button you have to use a lambda and pass None to the event. event argument is needed because of the binding, but there is no event when you call it from button click, so just pass None to get rid of the error. You can delete by doing entry.delete(0, 'end').
from tkinter import *
window = Tk()
window.geometry("200x300")
def evaluate(event):
thetext = StringVar()
labeloutput = Label(app, textvariable = thetext)
n = e.get()
thetext.set(n)
labeloutput.grid()
e.delete(0, 'end') # Here we remove text inside the entry
app = Frame(window)
app.pack()
e = Entry(window)
e.pack()
b = Button(window, text="Return", command=lambda: evaluate(None)) # Here we have a lambda to pass None to the event
b.pack()
window.bind("<Return>", evaluate)
mainloop()
Of course, if you want to prevent the lambda from being used, you would have to create a function to handle the key binding, and a separate one for the button click.