以程序方式将占位符添加到 tkinter Entry 小部件
Adding placeholders to tkinter Entry widget in a procedural way
我知道这个问题已经在这个网站上得到了回答,但我正在寻找一个更简单的答案,我以前见过一个,但后来这个问题被删除了,我找不到了。希望有人有更好更简单的方法来解决它。与 class 相关的东西可能会更好,因为我可以轻松地将它与更多 Entry
小部件一起使用
这是一个片段:
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()
是的,一旦失去焦点并再次获得焦点,这将删除框中的所有其他项目,包括我们首先输入的文本。无论如何要解决这个问题并使用 tkinter 拥有一个完美的占位符,我知道没有内置的方法。
提前致谢:D
我试过这个:
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()
通过这样做,只有当 Text
中存在默认值时,您才会清除,而且,如果该字段留空,占位符将返回到 Text
。
我不是很清楚你在问什么,所以我猜你是在问如何知道条目小部件何时有占位符文本,何时没有,以便你知道何时清除它以及何时不清除它。
最简单的解决方案是用替换文本向条目添加一个属性,然后在删除之前将其与内容进行比较。
使用函数
首先,让我们创建一个函数来初始化小部件的占位符文本。这个函数做了一些简单的事情:它在小部件上添加了一个 placeholder
属性,并建立了绑定。如果小部件为空,它还会插入占位符:
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)
现在让我们调整您的 remove
函数,使其更通用一些。由于它是通过事件调用的,因此它可以使用 event.widget
而不是对特定小部件的 hard-coded 引用。它还使用我们添加到小部件的 placeholder
属性。这两项技术让它可以被多个小部件使用。
def remove_placeholder(event):
placeholder_text = getattr(event.widget, "placeholder", "")
if placeholder_text and event.widget.get() == placeholder_text:
event.widget.delete(0, "end")
最后我们需要实现add_placeholder
功能。当小部件失去焦点并且用户没有输入任何内容时,此函数将添加占位符。它需要检查条目小部件是否有占位符,如果有并且小部件为空,则添加占位符。与 remove_placeholder
一样,它使用 event.widget
和 placeholder
属性:
def add_placeholder(event):
placeholder_text = getattr(event.widget, "placeholder", "")
if placeholder_text and event.widget.get() == "":
event.widget.insert(0, placeholder_text)
我已经修改了您的程序,为两个条目小部件中的每一个使用不同的占位符文本,以表明这些功能是通用的,并且不依赖于特定的条目小部件。
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()
使用自定义 class
可以说,更好的实现方式是创建自定义 class。这样一切都被封装在一个地方。这是一个例子:
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)
您可以像使用 Entry
小部件一样使用此 class,但您可以指定一个占位符:
e3 = EntryWithPlaceholder(root, placeholder="Address")
e3.pack()
这是一个非常简单的例子。在此示例中,我们包括几个 features/caveats:
- 占位符的幻影文本
entry.input
将 return None
如果文本是占位符或为空
entry.input
应该用来代替 .get()
和 .insert()
。 .input
逻辑旨在为您提供此类小部件的正确结果。 .get()
不够智能,无法 return 正确的数据,并且 .insert()
已被重新配置为 .input
的代理
- 占位符在您键入时变戏法
- placeholder可以用
.insert()
覆盖~不需要用.delete()
。您仍然应该使用 entry.input
而不是
#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'
用法示例:
#__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()
不,这不是直接的,可以用 tkinter
。您可能想使用 类 和 OOP。
我知道这个问题已经在这个网站上得到了回答,但我正在寻找一个更简单的答案,我以前见过一个,但后来这个问题被删除了,我找不到了。希望有人有更好更简单的方法来解决它。与 class 相关的东西可能会更好,因为我可以轻松地将它与更多 Entry
小部件一起使用
这是一个片段:
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()
是的,一旦失去焦点并再次获得焦点,这将删除框中的所有其他项目,包括我们首先输入的文本。无论如何要解决这个问题并使用 tkinter 拥有一个完美的占位符,我知道没有内置的方法。
提前致谢:D
我试过这个:
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()
通过这样做,只有当 Text
中存在默认值时,您才会清除,而且,如果该字段留空,占位符将返回到 Text
。
我不是很清楚你在问什么,所以我猜你是在问如何知道条目小部件何时有占位符文本,何时没有,以便你知道何时清除它以及何时不清除它。
最简单的解决方案是用替换文本向条目添加一个属性,然后在删除之前将其与内容进行比较。
使用函数
首先,让我们创建一个函数来初始化小部件的占位符文本。这个函数做了一些简单的事情:它在小部件上添加了一个 placeholder
属性,并建立了绑定。如果小部件为空,它还会插入占位符:
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)
现在让我们调整您的 remove
函数,使其更通用一些。由于它是通过事件调用的,因此它可以使用 event.widget
而不是对特定小部件的 hard-coded 引用。它还使用我们添加到小部件的 placeholder
属性。这两项技术让它可以被多个小部件使用。
def remove_placeholder(event):
placeholder_text = getattr(event.widget, "placeholder", "")
if placeholder_text and event.widget.get() == placeholder_text:
event.widget.delete(0, "end")
最后我们需要实现add_placeholder
功能。当小部件失去焦点并且用户没有输入任何内容时,此函数将添加占位符。它需要检查条目小部件是否有占位符,如果有并且小部件为空,则添加占位符。与 remove_placeholder
一样,它使用 event.widget
和 placeholder
属性:
def add_placeholder(event):
placeholder_text = getattr(event.widget, "placeholder", "")
if placeholder_text and event.widget.get() == "":
event.widget.insert(0, placeholder_text)
我已经修改了您的程序,为两个条目小部件中的每一个使用不同的占位符文本,以表明这些功能是通用的,并且不依赖于特定的条目小部件。
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()
使用自定义 class
可以说,更好的实现方式是创建自定义 class。这样一切都被封装在一个地方。这是一个例子:
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)
您可以像使用 Entry
小部件一样使用此 class,但您可以指定一个占位符:
e3 = EntryWithPlaceholder(root, placeholder="Address")
e3.pack()
这是一个非常简单的例子。在此示例中,我们包括几个 features/caveats:
- 占位符的幻影文本
entry.input
将 returnNone
如果文本是占位符或为空entry.input
应该用来代替.get()
和.insert()
。.input
逻辑旨在为您提供此类小部件的正确结果。.get()
不够智能,无法 return 正确的数据,并且.insert()
已被重新配置为.input
的代理
- 占位符在您键入时变戏法
- placeholder可以用
.insert()
覆盖~不需要用.delete()
。您仍然应该使用entry.input
而不是
#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'
用法示例:
#__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()
不,这不是直接的,可以用 tkinter
。您可能想使用 类 和 OOP。