Instead of using two colors for active background and normal background, I use only one color and opacity parameter.
I trigger the feeling of a button using different colors when the mouse is and isn't over. Many modern HTML search boxes uses the same approach.
Command function receives text of entry box when button is pressed.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 | 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()
|