How to add placeholder to an Entry in tkinter?
Asked Answered
G

11

37

I have created a login window in tkinter which has two Entry field, first one is Username and second one is Password.
code

from tkinter import *

ui = Tk()

e1 = Entry(ui)
#i need a placeholder "Username" in the above entry field
e1.pack()

ui.mainloop()

I want a placeholder called "Username" in the Entry, but if you click inside the entry box, the text should disappear.

Guenon answered 7/1, 2015 at 13:4 Comment(4)
Post your code please.Fermanagh
What have you tried? Stackoverflow isn't intended to be a code writing service.Dagnydago
See #45217557Dissolute
you can use this package: github.com/dorukwagle/tkinter-input-box hope this helps...Meiny
F
24

You need to set a default value for this entry. Like this:

from tkinter import *

ui = Tk()

e1 = Entry(ui)
e1.insert(0, 'username')
e1.pack()

ui.mainloop()

Then if you want to delete the content when you click the entry, then you have to bind a mouse click event with an event handler method to update content of this entry. Here is a link for you.

Fermanagh answered 7/1, 2015 at 13:54 Comment(4)
Instead of binding to a click, you should bind to <FocusIn>, since it's possible to switch focus to an entry widget via the keyboard. Your answer would be a little better if you actually showed how to do that.Dagnydago
Here's a simple one liner for future readers based on Bryan's suggestion: e1.bind("<FocusIn>", lambda args: e1.delete('0', 'end')).Gabriella
@Gabriella please can you explain this for lambda args: i want to understand betterCleocleobulus
@ADWAN bind passes event values to its callback button by default that's why there is args. The reason I use lambda is that I want to pass some values to the actual method, delete, I want to use.Gabriella
G
41

You can create a class that inherits from Entry like below:

import tkinter as tk

class EntryWithPlaceholder(tk.Entry):
    def __init__(self, master=None, placeholder="PLACEHOLDER", color='grey'):
        super().__init__(master)

        self.placeholder = placeholder
        self.placeholder_color = color
        self.default_fg_color = self['fg']

        self.bind("<FocusIn>", self.foc_in)
        self.bind("<FocusOut>", self.foc_out)

        self.put_placeholder()

    def put_placeholder(self):
        self.insert(0, self.placeholder)
        self['fg'] = self.placeholder_color

    def foc_in(self, *args):
        if self['fg'] == self.placeholder_color:
            self.delete('0', 'end')
            self['fg'] = self.default_fg_color

    def foc_out(self, *args):
        if not self.get():
            self.put_placeholder()

if __name__ == "__main__": 
    root = tk.Tk() 
    username = EntryWithPlaceholder(root, "username")
    password = EntryWithPlaceholder(root, "password", 'blue')
    username.pack()
    password.pack()  
    root.mainloop()
Gabriella answered 21/12, 2017 at 15:49 Comment(2)
This could further be improved by defining a custom class tag to be used with bindtags, bind_class.Gabriella
also, it would probably be wise to accept *args and **kwargs on __init__, although you might need to handle the textvariable keyword in the FocusIn and FocusOut events to prevent the placeholder from being sent to the StringVarDoha
F
24

You need to set a default value for this entry. Like this:

from tkinter import *

ui = Tk()

e1 = Entry(ui)
e1.insert(0, 'username')
e1.pack()

ui.mainloop()

Then if you want to delete the content when you click the entry, then you have to bind a mouse click event with an event handler method to update content of this entry. Here is a link for you.

Fermanagh answered 7/1, 2015 at 13:54 Comment(4)
Instead of binding to a click, you should bind to <FocusIn>, since it's possible to switch focus to an entry widget via the keyboard. Your answer would be a little better if you actually showed how to do that.Dagnydago
Here's a simple one liner for future readers based on Bryan's suggestion: e1.bind("<FocusIn>", lambda args: e1.delete('0', 'end')).Gabriella
@Gabriella please can you explain this for lambda args: i want to understand betterCleocleobulus
@ADWAN bind passes event values to its callback button by default that's why there is args. The reason I use lambda is that I want to pass some values to the actual method, delete, I want to use.Gabriella
B
14

Updated (Improved Answer):

  • Use the on_focus_out function to reinsert the placeholder if the text field is empty (if you don't want this to happen, you can use the method from the older code)
import tkinter as tk


def on_focus_in(entry):
    if entry.cget('state') == 'disabled':
        entry.configure(state='normal')
        entry.delete(0, 'end')


def on_focus_out(entry, placeholder):
    if entry.get() == "":
        entry.insert(0, placeholder)
        entry.configure(state='disabled')


root = tk.Tk()

entry_x = tk.Entry(root, width=50)
entry_x.pack(pady=10)
entry_x.insert(0, "Place Holder X")
entry_x.configure(state='disabled')

entry_y = tk.Entry(root, width=50)
entry_y.pack(pady=10)
entry_y.insert(0, "Place Holder Y")
entry_y.configure(state='disabled')

x_focus_in = entry_x.bind('<Button-1>', lambda x: on_focus_in(entry_x))
x_focus_out = entry_x.bind(
    '<FocusOut>', lambda x: on_focus_out(entry_x, 'Place Holder X'))

y_focus_in = entry_y.bind('<Button-1>', lambda x: on_focus_in(entry_y))
y_focus_out = entry_y.bind(
    '<FocusOut>', lambda x: on_focus_out(entry_y, 'Place Holder Y'))

root.mainloop()

Note:

  • It is discouraged to import *, so we should import like this import tkinter as tk.
  • I have created two Entry widgets to depict the changes.

Old (Not Recommended):

This will work for any placeholder you want.

from tkinter import *
root = Tk()

my_entry = Entry(root, width=50)
my_entry.pack()
my_entry.insert(0, "Place Holder")
my_entry.configure(state=DISABLED)

def on_click(event):
    my_entry.configure(state=NORMAL)
    my_entry.delete(0, END)

    # make the callback only work once
    my_entry.unbind('<Button-1>', on_click_id)

on_click_id = my_entry.bind('<Button-1>', on_click)

root.mainloop()
Bustos answered 16/11, 2019 at 9:39 Comment(0)
J
5

My solution is to subclass the tk.Entry and control the content and color, binding the <FocusIn> and <FocusOut> events to methods that fill and clear the text as necessary. This is the behavior:

enter image description here

Here the complete example code:

import tkinter as tk

class PlaceholderEntry(tk.Entry):
    def __init__(self, master=None, placeholder='', cnf={}, fg='black',
                 fg_placeholder='grey50', *args, **kw):
        super().__init__(master=None, cnf={}, bg='white', *args, **kw)
        self.fg = fg
        self.fg_placeholder = fg_placeholder
        self.placeholder = placeholder
        self.bind('<FocusOut>', lambda event: self.fill_placeholder())
        self.bind('<FocusIn>', lambda event: self.clear_box())
        self.fill_placeholder()

    def clear_box(self):
        if not self.get() and super().get():
            self.config(fg=self.fg)
            self.delete(0, tk.END)

    def fill_placeholder(self):
        if not super().get():
            self.config(fg=self.fg_placeholder)
            self.insert(0, self.placeholder)
    
    def get(self):
        content = super().get()
        if content == self.placeholder:
            return ''
        return content

class App(tk.Frame):
    def __init__(self, master=None):
        self.root = master
        super().__init__(master, borderwidth=0, relief=tk.RAISED)
        
        self.root.title('Placeholder example')
        self.pack_propagate(False)
        self.pack()
        
        self.entry = PlaceholderEntry(self.root, placeholder='This text is a placeholder')
        self.entry.pack()
        
        self.btn = tk.Button(self.root, text='Nothing', highlightcolor='cyan')
        self.btn.pack()
        

root = tk.Tk()
app = App(master=root)

app.root.mainloop()
Jandel answered 14/7, 2021 at 10:51 Comment(0)
R
3

A working placeholder class. What this does is that it binds to <FocusIn> and <FocusOut> so that when you put focus on it if there is no text it will insert your placeholder into it. You can also change the color on if it is selected or not.

class Placeholder:
    def __init__(self,master,placeholder='',placeholdercolor='grey',color='black',**kwargs):
        self.e = Entry(master,fg=placeholdercolor,**kwargs)
        self.e.bind('<FocusIn>',self.focus_in)
        self.e.bind('<FocusOut>',self.focus_out)
        self.e.insert(0, placeholder)
        self.placeholder = placeholder
        self.placeholdercolor=placeholdercolor
        self.color = color

    def pack(self,side=None,**kwargs):
        self.e.pack(side=side,**kwargs)

    def place(self,side=None,**kwargs):
        self.e.place(side=side,**kwargs)

    def grid(self,column=None,**kwargs):
        self.e.grid(column=column,**kwargs)

    def focus_in(self,e):
        if self.e.get() == self.placeholder:
            self.e.delete(0,END)
        self.e.configure(fg=self.color)

    def focus_out(self,e):
        if self.e.get() == '':
            self.e.configure(fg=self.placeholdercolor)
            self.e.delete(0,END)
            self.e.insert(0,self.placeholder)
Rena answered 14/11, 2020 at 11:58 Comment(0)
B
1

Joining Nae and Stephen Lins solutions with text field

text = Text(ui)
text.pack()
text.insert('1.0', 'text placeholder')
text.bind("<FocusIn>", lambda args: text.delete('1.0', 'end'))
Basset answered 25/9, 2022 at 12:56 Comment(0)
D
0
from tkinter import *

root=Tk()
root.geometry("300x200+600+250")
root.config(background="#E0FFFF")
root.resizable(False,False)


def userText(event):
    e1.delete(0,END)
    usercheck=True

def passText(event):
    e2.delete(0, END)
    passcheck=True



a=StringVar()
b=StringVar()
usercheck=False
passcheck=False


Label(root,text="User name",bg="#E0FFFF").place(x=20,y=50)
e1= Entry(root,textvariable=a)
e1.place(x=100,y=50)
e1.insert(0,"Enter username")
e1.bind("<Button>",userText)


Label(root,text="Password",bg="#E0FFFF").place(x=20,y=95)
e2= Entry(root,textvariable=b)
e2.place(x=100,y=95)
e2.insert(0,"Enter password")
e2.bind("<Button>",passText)


root.mainloop()
Disrespect answered 27/6, 2018 at 15:58 Comment(0)
C
0

For a more compact solution than the above listed, I suggest that you create a function that would erase the text box on a click event (lambda), as shown here.

from tkinter import *   

def clear_entry(event, entry):
    entry.delete(0, END)
    entry.unbind('<Button-1>', click_event)

ui = Tk()

entry = Entry(ui)

entry.pack()
placeholder_text = '<enter-placeholder>'
entry.insert(0, placeholder_text)

entry.bind("<Button-1>", lambda event: clear_entry(event, entry))

ui.mainloop()

The "<Button-1>" stands for when you left-click the entry box, so do not alter it, and once you click on the box, it will trigger the event and run the function clear_entry. You have to declare the function and the entry element before defining placeholder_text and using entry.insert. Hopefully, this is a viable solution to this problem.

Carri answered 6/1, 2021 at 1:8 Comment(0)
U
0

# This is a easy way to add placeholder to an Entry

from tkinter import *

w = Tk()
w.title("Demo")
w.geomerty("500x500")

EntrySetText = StringVar()
EntryText = Entry(w)
EntryText.pack()
EntrySetText.set("Hello World!")

w.mainloop()
Utah answered 27/1, 2023 at 7:13 Comment(2)
If you click inside the entry box, will the text disappear like what OP wants?Dissuade
As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.Gaselier
T
0

Based on the answer here I derived a different solution, because I didn't like the idea that the tk.DISABLED state was used for two reasons:

  1. it can collide with existing code that need this entry to be disabled regardless of the placeholder text bindings
  2. it is deceiving to end users because they might not know that this entry is actually not disabled

For this purpose, I created an extension class that can pass on the configure_placeholder method via inheritance. This also requires less code than implementing a custom Entry widget from scratch.

# file: extensions.py
import tkinter as tk

class TkExtensions(tk.Frame):
    @staticmethod
    def __on_focus_in(event: tk.Event, entry: tk.Entry) -> None:
        entry.configure(fg="black")
        entry.delete(0, tk.END)

    @staticmethod
    def __on_focus_out(event: tk.Event, entry: tk.Entry, placeholder: str) -> None:
        if entry.get() == "":
            entry.insert(0, placeholder)
            entry.configure(fg="grey")

    @staticmethod
    def configure_placeholder(entry: tk.Entry, placeholder: str) -> None:
        entry.insert(0, placeholder)
        # set color to grey instead of entering a disabled state
        entry.configure(fg="grey")
        entry.bind("<FocusIn>", lambda event: TkExtensions.__on_focus_in(event, entry))
        # restore placeholder text if there was no user input after focus in
        entry.bind("<FocusOut>", lambda event: TkExtensions.__on_focus_out(event, entry, placeholder))

Example usage in a consuming class:


import tkinter as tk
from tkinter import N, W, X
from typing import Self

from extensions import TkExtensions

class Window(TkExtensions, tk.Frame):
   # stuff and things

   def build_settings(self: Self, ...) -> None:
        # ... some code
        origin_lon = tk.Entry(self.settings)
        origin_lon.pack(side=tk.LEFT, expand=True, fill=X, anchor=N+W)
        Window.configure_placeholder(origin_lon, "placeholder")
        # ... more code
Triennium answered 30/7, 2023 at 16:59 Comment(0)
M
-1

If you combine the entry field with a string variable all of this becomes a lot easier

from tkinter import *
from tkinter import ttk
from typing import Optional


class PlaceholderEntry:
    
    def __init__(self, root: Tk | Frame | ttk.Frame | ttk.LabelFrame | Toplevel, string_variable: Optional[StringVar] = None, style: str = '', width: int = 25, placeholder_text: str = '', text_color: str = 'black', placeholder_color: str = 'grey50'):
        self._placeholder_text = placeholder_text
        self._placeholder_color = placeholder_color
        self._text_color = text_color
        self._text_var = string_variable if string_variable else StringVar(root)
        self.__text_var.set(placeholder_text)
        self.__entry = ttk.Entry(root, textvariable=self.__text_var, style=style, width=width)
        self._entry.bind('<FocusOut>', self._focus_out)
        self._entry.bind('<FocusIn>', self._focus_in)
        self._change_color(False)

    @property
    def entry(self) -> ttk.Entry:
        return self.__entry

    @property
    def string_var(self) -> StringVar:
        return self.__text_var

    def _change_color(self, placeholder: bool) -> None:
        if placeholder:
            self.__entry.configure(foreground=self._text_color)
        else:
            self.__entry.configure(foreground=self._placeholder_color)

    def _focus_in(self, event: Event) -> None:
        if self.__text_var.get() == self._placeholder_text:
            self.__text_var.set('')
        self._change_color(True)

    def _focus_out(self, event: Event) -> None:
        if not self._entry.get():
            self.__text_var.set(self._placeholder_text)
        if self.__entry.get() == self._placeholder_text:
            self._change_color(False)
        else:
            self._change_color(True)

Macaw answered 22/12, 2022 at 12:44 Comment(3)
Attribute names __like_this__ are considered special by Python. Please do not use them for whatever arbitrary data held by the instances. This risks overwriting something important (like, say, __init__) and communicates something misleading about the code. (Python will not do any "magic" interpretation of self.__entry__, but it looks as if that were the intent. It does treat the similar __enter__ specially.)Dissuade
Attribute names __like_this get special "name-mangling" handling, but it's important to note that this does not make them "private" or "protected" in any meaningful sense. Python lacks those concepts. The generally accepted best practice is to use names _like_this for internal, undocumented implementation details; while there is nothing you can to to prevent other people from using them, they will clearly know that they are not supposed to, that this is not part of the intended API, and that it could break without warning in future versions of your code.Dissuade
Corrected as suggestedMacaw

© 2022 - 2024 — McMap. All rights reserved.