Change background color in specific (not all) ttk Entry widget

56 views Asked by At

In my application, I have many Entry widgets. I would like to change the background color of the widget to make it obvious to the user that he or she have entered data in that widget and it has been captured by the application. This is easy with tk.Entry using the configure(background="color") command.

However, I have not figured out how to do it for ttk.Entry using styles. Suggestions very welcome.

After searching the web, I understood how to use styles to change the background color of all the ttk.Entry widgets in a form or frame. I could not find any information for doing that for a specific widget.

2

There are 2 answers

2
milanbalazs On

I have written a working script for it and you can find below it. Actually you should define 2 TTK styles (One for empty and another for filled states) and that styles should be changed with a callback function if the Entry widget (Basically the value of StringVariable object) is filled or it's empty. (I have written many comments in the test code for the better understanding.)

from tkinter import *
from tkinter import ttk

root_window = Tk()

estyle = ttk.Style()
# Create a new element (Eg.: https://stackoverflow.com/questions/45389166/how-to-know-all-style-options-of-a-ttk-widget)
estyle.element_create("plain.field", "from", "clam")
# Create the layout for the style.
# It defines what the custom widget will look like.
estyle.layout(
    "EntryStyle.Empty",  # This name will be used for the style parameter
    [
        (
            "Entry.plain.field",
            {
                "children": [
                    (
                        "Entry.background",
                        {
                            "children": [
                                (
                                    "Entry.padding",
                                    {
                                        "children": [("Entry.textarea", {"sticky": "nswe"})],
                                        "sticky": "nswe",
                                    },
                                )
                            ],
                            "sticky": "nswe",
                        },
                    )
                ],
                "border": "2",
                "sticky": "nswe",
            },
        )
    ],
)
# Define another style for the "filled" state.
estyle.layout(
    "EntryStyle.Filled",
    [
        (
            "Entry.plain.field",
            {
                "children": [
                    (
                        "Entry.background",
                        {
                            "children": [
                                (
                                    "Entry.padding",
                                    {
                                        "children": [("Entry.textarea", {"sticky": "nswe"})],
                                        "sticky": "nswe",
                                    },
                                )
                            ],
                            "sticky": "nswe",
                        },
                    )
                ],
                "border": "2",
                "sticky": "nswe",
            },
        )
    ],
)

# Register both of styles and configure the background/foreground/field-background parameters.
estyle.configure(
    "EntryStyle.Filled", background="green", foreground="black", fieldbackground="green"
)
estyle.configure("EntryStyle.Empty", background="red", foreground="black", fieldbackground="red")

# Creating 2 String variables.
string_var_1 = StringVar()
string_var_2 = StringVar()

# Creating the 2 TTK entries with the default Empty style (by-default the String variable is empty)
entry_1 = ttk.Entry(root_window, style="EntryStyle.Empty", textvariable=string_var_1)
entry_2 = ttk.Entry(root_window, style="EntryStyle.Empty", textvariable=string_var_2)

entry_1.pack(padx=10, pady=10)
entry_2.pack(padx=10, pady=10)

# Entry and Variable connection. It's needed to connect the String variables with Entry widgets.
# The Style of Entry will be changed based on this structure. (I didn't fine solution to get the reference of
# Entry Widget based on StringVar object.)
entry_and_var = {str(string_var_1): entry_1, str(string_var_2): entry_2}


def callback(string_var_wid):
    """
    Callback function to change the style of Entry based on the StringVar object.
    If there is value of StringVar (There is something in the Entry field), then the style will be
    changed to "EntryStyle.Filled". In another case (The deletion is also handled)
    the style is set to "EntryStyle.Empty" (When the entry field is really empty.)
    :param string_var_wid: Reference ob StringVar object.
    :return:
    """

    if not string_var_wid.get():
        entry_and_var[str(string_var_wid)].configure(style="EntryStyle.Empty")
    else:
        entry_and_var[str(string_var_wid)].configure(style="EntryStyle.Filled")


# Set the trace for the StringVar object. If something in written (or delete) in the Entry field then these
# traces will call the callback function.
string_var_1.trace("w", lambda *_, string_variable_1=string_var_1: callback(string_var_1))
string_var_2.trace("w", lambda *_, string_variable_1=string_var_2: callback(string_var_2))

root_window.mainloop()

GUI:

GUI

0
acw1668 On

You need to create a custom style for the entry box when something is input into the entry box. To check whether something is input into the entry box, one of the way is using the validatecommand option.

Below is a simple example using custom ttk.Entry widget:

import tkinter as tk
from tkinter import ttk

class MyEntry(ttk.Entry):
    def __init__(self, master=None, **kwargs):
        self.bg_filled = kwargs.pop("bg_filled", "cyan")
        super().__init__(master, **kwargs)
        # use widget internal name as the custom style name
        self.style_filled = f"{self._name}.TEntry"
        # configure the custom style to what you want
        ttk.Style().configure(self.style_filled, background=self.bg_filled, fieldbackground=self.bg_filled)
        # setup validation command used to change style
        vcmd = (self.register(self.validate), "%P")
        self.configure(validate="key", validatecommand=vcmd)

    def validate(self, value):
        # use custom style if something input, otherwise use default style
        self.configure(style=self.style_filled if value.strip() else "TEntry")
        return True

root = tk.Tk()

s = ttk.Style()
# choose a theme that supports changing background color
s.theme_use("default")

MyEntry(root).pack(padx=10, pady=10)
MyEntry(root, bg_filled="yellow").pack(padx=10, pady=10)
MyEntry(root).pack(padx=10, pady=10)

root.mainloop()

Use the bg_filled option to set the required background color when something is input.

Result:

enter image description here