Welcome, guest | Sign In | My Account | Store | Cart
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)
 

History