try:
from Tkinter import Entry, Frame, Label, StringVar
from Tkconstants import *
except ImportError:
from tkinter import Entry, Frame, Label, StringVar
from tkinter.constants import *
def hex2rgb(str_rgb):
try:
rgb = str_rgb[1:]
if len(rgb) == 6:
r, g, b = rgb[0:2], rgb[2:4], rgb[4:6]
elif len(rgb) == 3:
r, g, b = rgb[0] * 2, rgb[1] * 2, rgb[2] * 2
else:
raise ValueError()
except:
raise ValueError("Invalid value %r provided for rgb color."% str_rgb)
return tuple(int(v, 16) for v in (r, g, b))
class Placeholder_State(object):
__slots__ = 'normal_color', 'normal_font', 'placeholder_text', 'placeholder_color', 'placeholder_font', 'contains_placeholder'
def add_placeholder_to(entry, placeholder, color="grey", font=None):
normal_color = entry.cget("fg")
normal_font = entry.cget("font")
if font is None:
font = normal_font
state = Placeholder_State()
state.normal_color=normal_color
state.normal_font=normal_font
state.placeholder_color=color
state.placeholder_font=font
state.placeholder_text = placeholder
state.contains_placeholder=True
def on_focusin(event, entry=entry, state=state):
if state.contains_placeholder:
entry.delete(0, "end")
entry.config(fg = state.normal_color, font=state.normal_font)
state.contains_placeholder = False
def on_focusout(event, entry=entry, state=state):
if entry.get() == '':
entry.insert(0, state.placeholder_text)
entry.config(fg = state.placeholder_color, font=state.placeholder_font)
state.contains_placeholder = True
entry.insert(0, placeholder)
entry.config(fg = color, font=font)
entry.bind('<FocusIn>', on_focusin, add="+")
entry.bind('<FocusOut>', on_focusout, add="+")
entry.placeholder_state = state
return state
class SearchBox(Frame):
def __init__(self, master, entry_width=30, entry_font=None, entry_background="white", entry_highlightthickness=1, button_text="Search", button_ipadx=10, button_background="#009688", button_foreground="white", button_font=None, opacity=0.8, placeholder=None, placeholder_font=None, placeholder_color="grey", spacing=3, command=None):
Frame.__init__(self, master)
self._command = command
self.entry = Entry(self, width=entry_width, background=entry_background, highlightcolor=button_background, highlightthickness=entry_highlightthickness)
self.entry.pack(side=LEFT, fill=BOTH, ipady=1, padx=(0,spacing))
if entry_font:
self.entry.configure(font=entry_font)
if placeholder:
add_placeholder_to(self.entry, placeholder, color=placeholder_color, font=placeholder_font)
self.entry.bind("<Escape>", lambda event: self.entry.nametowidget(".").focus())
self.entry.bind("<Return>", self._on_execute_command)
opacity = float(opacity)
if button_background.startswith("#"):
r,g,b = hex2rgb(button_background)
else:
# Color name
r,g,b = master.winfo_rgb(button_background)
r = int(opacity*r)
g = int(opacity*g)
b = int(opacity*b)
if r <= 255 and g <= 255 and b <=255:
self._button_activebackground = '#%02x%02x%02x' % (r,g,b)
else:
self._button_activebackground = '#%04x%04x%04x' % (r,g,b)
self._button_background = button_background
self.button_label = Label(self, text=button_text, background=button_background, foreground=button_foreground, font=button_font)
if entry_font:
self.button_label.configure(font=button_font)
self.button_label.pack(side=LEFT, fill=Y, ipadx=button_ipadx)
self.button_label.bind("<Enter>", self._state_active)
self.button_label.bind("<Leave>", self._state_normal)
self.button_label.bind("<ButtonRelease-1>", self._on_execute_command)
def get_text(self):
entry = self.entry
if hasattr(entry, "placeholder_state"):
if entry.placeholder_state.contains_placeholder:
return ""
else:
return entry.get()
else:
return entry.get()
def set_text(self, text):
entry = self.entry
if hasattr(entry, "placeholder_state"):
entry.placeholder_state.contains_placeholder = False
entry.delete(0, END)
entry.insert(0, text)
def clear(self):
self.entry_var.set("")
def focus(self):
self.entry.focus()
def _on_execute_command(self, event):
text = self.get_text()
self._command(text)
def _state_normal(self, event):
self.button_label.configure(background=self._button_background)
def _state_active(self, event):
self.button_label.configure(background=self._button_activebackground)
if __name__ == "__main__":
try:
from Tkinter import Tk
from tkMessageBox import showinfo
except ImportError:
from tkinter import Tk
from tkinter.messagebox import showinfo
def command(text):
showinfo("search command", "searching:%s"%text)
root = Tk()
SearchBox(root, command=command, placeholder="Type and press enter", entry_highlightthickness=0).pack(pady=6, padx=3)
root.mainloop()
Diff to Previous Revision
--- revision 8 2017-04-04 18:14:50
+++ revision 9 2017-04-08 12:27:36
@@ -21,7 +21,7 @@
return tuple(int(v, 16) for v in (r, g, b))
class Placeholder_State(object):
- __slots__ = 'normal_color', 'normal_font', 'placeholder_text', 'placeholder_color', 'placeholder_font', 'with_placeholder'
+ __slots__ = 'normal_color', 'normal_font', 'placeholder_text', 'placeholder_color', 'placeholder_font', 'contains_placeholder'
def add_placeholder_to(entry, placeholder, color="grey", font=None):
normal_color = entry.cget("fg")
@@ -36,21 +36,21 @@
state.placeholder_color=color
state.placeholder_font=font
state.placeholder_text = placeholder
- state.with_placeholder=True
+ state.contains_placeholder=True
def on_focusin(event, entry=entry, state=state):
- if state.with_placeholder:
+ if state.contains_placeholder:
entry.delete(0, "end")
entry.config(fg = state.normal_color, font=state.normal_font)
- state.with_placeholder = False
+ state.contains_placeholder = False
def on_focusout(event, entry=entry, state=state):
if entry.get() == '':
entry.insert(0, state.placeholder_text)
entry.config(fg = state.placeholder_color, font=state.placeholder_font)
- state.with_placeholder = True
+ state.contains_placeholder = True
entry.insert(0, placeholder)
entry.config(fg = color, font=font)
@@ -68,9 +68,7 @@
self._command = command
- self.entry_var = StringVar()
-
- self.entry = Entry(self, textvariable=self.entry_var, width=entry_width, background=entry_background, highlightcolor=button_background, highlightthickness=entry_highlightthickness)
+ self.entry = Entry(self, width=entry_width, background=entry_background, highlightcolor=button_background, highlightthickness=entry_highlightthickness)
self.entry.pack(side=LEFT, fill=BOTH, ipady=1, padx=(0,spacing))
if entry_font:
@@ -78,10 +76,10 @@
if placeholder:
add_placeholder_to(self.entry, placeholder, color=placeholder_color, font=placeholder_font)
-
- if command is not None:
- self.entry.bind("<Return>",self._execute_command)
-
+
+ self.entry.bind("<Escape>", lambda event: self.entry.nametowidget(".").focus())
+ self.entry.bind("<Return>", self._on_execute_command)
+
opacity = float(opacity)
if button_background.startswith("#"):
@@ -110,13 +108,25 @@
self.button_label.bind("<Enter>", self._state_active)
self.button_label.bind("<Leave>", self._state_normal)
- self.button_label.bind("<ButtonRelease-1>", self._execute_command)
+ self.button_label.bind("<ButtonRelease-1>", self._on_execute_command)
+
+ def get_text(self):
+ entry = self.entry
+ if hasattr(entry, "placeholder_state"):
+ if entry.placeholder_state.contains_placeholder:
+ return ""
+ else:
+ return entry.get()
+ else:
+ return entry.get()
- def get(self):
- return self.entry_var.get()
-
- def set(self, text):
- self.entry_var.set(text)
+ def set_text(self, text):
+ entry = self.entry
+ if hasattr(entry, "placeholder_state"):
+ entry.placeholder_state.contains_placeholder = False
+
+ entry.delete(0, END)
+ entry.insert(0, text)
def clear(self):
self.entry_var.set("")
@@ -124,10 +134,10 @@
def focus(self):
self.entry.focus()
- def _execute_command(self, event):
- text = self.entry_var.get()
+ def _on_execute_command(self, event):
+ text = self.get_text()
self._command(text)
-
+
def _state_normal(self, event):
self.button_label.configure(background=self._button_background)