问题描述
我知道此问题已在本站点得到解答,但我正在寻找一个更简单的答案,并且我之前见过一个问题,但随后该问题已删除或无法找到,我找不到它。希望有人能够找到更好,更轻松的方法。与类有关的东西可能会更好,因为我可以在更多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
解决方法
我不清楚您要问的是什么,所以我猜您是在问如何知道输入窗口小部件何时有占位符文本,何时没有占位符文本,以便您知道何时清除它以及何时不清除它。
最简单的解决方案是将属性添加到带有替换文本的条目中,然后将其与内容进行比较,然后再删除。
使用功能
首先,让我们创建一个函数来初始化小部件的占位符文本。这个函数做一些简单的事情:在小部件上添加一个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
而不是对特定小部件的硬编码引用。它还使用了我们添加到小部件中的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,"")
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,"end")
def add_placeholder(event):
"""Add placeholder text if the widget is empty"""
placeholder_text = getattr(event.widget,placeholder_text)
def init_placeholder(widget,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 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
小部件一样使用此类,但可以指定一个占位符:
e3 = EntryWithPlaceholder(root,placeholder="Address")
e3.pack()
,
这是一个非常简单的示例。在此示例中,我们包含了几个功能/界面:
- 占位符的幽灵文本
-
entry.input
如果文本为占位符或为空,则将返回None
-
entry.input
应该代替.get()
和.insert()
使用。.input
逻辑旨在为您提供此类小部件的正确结果。.get()
不够聪明,无法返回正确的数据,并且.insert()
已重新配置为.input
的代理 - 您输入时占位符被弄乱了 可以使用
- 占位符〜无需使用
.delete()
。您仍应改为使用entry.input
.insert()
覆盖#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,**{'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 = ['[email protected]','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,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()
,
我已经尝试过了:
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,0))
e.bind('<FocusIn>',remove)
e.bind('<FocusOut>',add)
e2 = Entry(root)
e2.pack( pady=(20,100))
root.mainloop()
这样做只会清除Text
中存在默认值的情况,而且如果该字段保留为空,则占位符将返回Text
。
否,tkinter
不能直接做到这一点。您可能要使用类和OOP。