Entry Layouts#

Layout String#

string tkinter entry validation and layout

The Entry widget is going to be placed within a Labelframe that will contain labels for user information and two optional labels for limits. This allows us to use it as a standalone class or function, which can be used as an enhanced entry. We need to know when the user is finished with input, which can be tied into an event such as the Return key.

Import the extra widgets StringVar, Labelframe and Label. Set up the Labelframe with the text giving the entry a title. Create the function end_input that is bound to the Return key, use a simple length check to ensure the input has been made. The callback function remains unaltered. Tie the StringVar to the entry widget and give the entry focus. Add a message label with a starting text, this will be altered by the event function.

Run the program trying a full length input and a short input:

from tkinter import Tk, font, StringVar
from tkinter.ttk import Entry, Style, Label, Labelframe
....
LF_TEXT = 'Beer Type'
lf0 = Labelframe(root, text=LF_TEXT)
lf0.grid(column=0, row=0)

def end_input(evt):
print('evt', entsv.get())
if len(entsv.get()) > 5:
    mess_lbl['text'] = "That's OK"
else:
    mess_lbl['text'] = "Should be at least 6 characters long"
......
entsv = StringVar()
ent0 = Entry(lf0, validate='key', validatecommand=(vcmd, '%P', '%S'),
             textvariable=entsv)
ent0.bind("<Return>", endinput)
ent0.grid(row=1, column=0, padx=10)
ent0.focus()

mess_lbl = Label(lf0, text='Start with capital letter, <Return> to confirm')
mess_lbl.grid(row=2, column=0, pady=10, padx=10)

Show/Hide Code 06layout_string.py

"""entry for string with layout"""
from tkinter import Tk, StringVar
from tkinter.ttk import Entry, Style, Label, Labelframe

root = Tk()
style = Style()
style.theme_use('default')

LF_TEXT = 'Beer Type'
lf0 = Labelframe(root, text=LF_TEXT)
lf0.grid(column=0, row=0)

def end_input(evt):
    """limit on string

    Parameters
    ----------
    evt : str
        bind handle

    Returns
    -------
    None
    """
    print('evt', entsv.get())
    if len(entsv.get()) > 5:
        mess_lbl['text'] = "That's OK"
    else:
        mess_lbl['text'] = "Should be at least 6 characters long"

def is_okay(text, inp, index):
    """ validation function

    Parameters
    ----------
    text : str
        text if allowed
    input : str
        current input

    Returns
    -------
    boolean
    """
    print(text)
    index = int(index)
    if text.isupper() and index == 0:
        return True
    if input in (",", ".", "'", " ") and index > 0:
        return True
    if inp.isalnum() and index > 0:
        return True
    if text == "":
        return True
    else:
        return False

vcmd = root.register(is_okay)

entsv = StringVar()
ent0 = Entry(lf0, validate='key', validatecommand=(vcmd, '%P', '%S', '%i'),
             textvariable=entsv)
ent0.bind("<Return>", end_input)
ent0.grid(row=1, column=0, padx=10)
ent0.focus()

mess_lbl = Label(lf0, text='Start with capital letter, <Return> to confirm')
mess_lbl.grid(row=2, column=0, pady=10, padx=10)

root.mainloop()

Layout Integer#

tkinter entry integer validation and layout

In concept it is similar to our string layout, but now the widget title may contain units (as appropriate), upper and lower limits may be present that affect the logic of the event function. First define the lower limits, then alter the event function to confirm the value and show when the limit has been exceeded. The rest of the script is the same as 04entry_negative_integers.py Float Entry

L_LIMIT = 0
U_LIMIT = 100

def end_input(evt):
    print('evt',ensv.get())
    if L_LIMIT < int(ensv.get()) < U_LIMIT:
        mess_lbl['text'] = "That's OK"
    elif L_LIMIT >= int(ensv.get()):
        mess_lbl['text'] = "Input below lower limit"
    else:
        mess_lbl['text'] = "Input above upper limit"

This works but the user has no idea of limits, so the limits will be shown in two separate labels, (just after the limit definition):

ulab = Label(lf0, text=str(U_LIMIT)+"  upper limit")
ulab.grid(row=0, column=1, padx=10)
llab = Label(lf0, text=str(L_LIMIT)+"  lower limit")
llab.grid(row=2, column=1, padx=10)

The enhanced widget looks rather grey with black lettering, colour it up a bit. Make the labelframe pale brown and the labels pale yellow:

s.configure('brown.TLabelframe',background='#C9B99B')
s.configure('brown.TLabelframe.Label',background='#EDEF77')
s.configure('brown.TLabel',background='#EDEF77')
....
lf0 = Labelframe(root, text=lftext, style='brown.TLabelframe')
...
ulab = Label(lf0, text=str(U_LIMIT)+"  upper limit", style='brown.TLabel')
...
llab = Label(lf0, text=str(L_LIMIT)+"  lower limit", style='brown.TLabel')
...
mess_lbl = Label(lf0,text='Start with capital letter, <Return> to confirm', style='brown.TLabel')

It should be relatively easy to add warning colours to the limits, trigger these when the limit is exceeded. Keep the warning colour there until the Return event, when we can reset the warning colours before retesting:

s.configure('lowr.TLabel', background='lightblue')
s.configure('upr.TLabel', background='red')
....
def end_input(evt):
    print('evt',ensv.get())
    ulab['style'] = 'brown.TLabel'
    llab['style'] = 'brown.TLabel'
    if L_LIMIT < int(ensv.get()) < U_LIMIT:
        mess_lbl['text'] = "That's OK"
    elif L_LIMIT >= int(ensv.get()):
        mess_lbl['text'] = "Input below lower limit"
        llab['style'] = 'lowr.TLabel'
    else:
        mess_lbl['text'] = "Input above upper limit"
        ulab['style'] = 'upr.TLabel'

That seems to function properly and gives the user a good feedback to the user.

Show/Hide Code 07layout_integer.py

"""String layout that determines the finish of input from a Return event.
    the integer is checked against limits before confirming that all is OK

"""
from tkinter import Tk, StringVar
from tkinter.ttk import Entry, Style, Label, Labelframe

root = Tk()
style = Style()
style.theme_use('default')

style.configure('brown.TLabelframe', background='#C9B99B')
style.configure('brown.TLabelframe.Label', background='#EDEF77')
style.configure('brown.TLabel', background='#EDEF77')
style.configure('lowr.TLabel', background='lightblue')
style.configure('upr.TLabel', background='red')

LFTEXT = 'Number of Coils'
lf0 = Labelframe(root, text=LFTEXT, style='brown.TLabelframe')
lf0.pack(padx=10, pady=10)
L_LIMIT = 0
U_LIMIT = 100

ulab = Label(lf0, text=str(U_LIMIT)+"  upper limit", style='brown.TLabel')
ulab.grid(row=0, column=1, padx=10)
llab = Label(lf0, text=str(L_LIMIT)+"  lower limit", style='brown.TLabel')
llab.grid(row=2, column=1, padx=10)


def end_input(evt):
    """limit on integer

    Parameters
    ----------
    evt : str
        bind handle

    Returns
    -------
    None
    """
    print('evt', entsv.get())
    ulab['style'] = 'brown.TLabel'
    llab['style'] = 'brown.TLabel'
    if L_LIMIT < int(entsv.get()) < U_LIMIT:
        mess_lbl['text'] = "That's OK"
    elif L_LIMIT >= int(entsv.get()):
        mess_lbl['text'] = "Input below lower limit"
        llab['style'] = 'lowr.TLabel'
    else:
        mess_lbl['text'] = "Input above upper limit"
        ulab['style'] = 'upr.TLabel'

def is_okay(text):
    """ validation function

    Parameters
    ----------
    text : str
        text if allowed

    Returns
    -------
    boolean
    """
    print(text)
    if text in("", "-"):
        return True
    try:
        int(text)
    except ValueError:
        return False
    return True

vcmd = root.register(is_okay)

entsv = StringVar()
ent0 = Entry(lf0, validate='key', validatecommand=(vcmd, '%P'), textvariable=entsv)
ent0.bind("<Return>", end_input)
ent0.grid(row=1, column=0, padx=10)
ent0.focus()

mess_lbl = Label(lf0, text='Insert +ve or -ve integer, <Return> to confirm',
                style='brown.TLabel')
mess_lbl.grid(row=2, column=0, pady=10, padx=10)

root.mainloop()

Layout Float#

tkinter entry float validation and layout, lower limit

Float Entry, Lower Limit Activated#

Use most of the new construct from 07layout_integer.py, only needing to change to float and copying the feedback function from 05entry_float.py entry-float

if L_LIMIT < float(ensv.get()) < U_LIMIT:
...
elif L_LIMIT > float(ensv.get()):
...
def isOkay(text):
    print(text)
    if text in ("", "-", ".", "-."):
        return True
    try:
        float(text)
    except ValueError:
        return False
    return True

Show/Hide Code 08layout_float.py

"""Float layout that determines the finish of input from a Return event.
the float is checked against limits before confirming that all is OK

"""

from tkinter import Tk, StringVar
from tkinter.ttk import Entry, Style, Label, Labelframe

root = Tk()
style = Style()
style.theme_use('default')

style.configure('brown.TLabelframe', background='#C9B99B')
style.configure('brown.TLabelframe.Label', background='#EDEF77')
style.configure('brown.TLabel', background='#EDEF77')
style.configure('lowr.TLabel', background='lightblue')
style.configure('upr.TLabel', background='red')

LFTEXT = 'Beer Strength % v/v'
lf0 = Labelframe(root, text=LFTEXT, style='brown.TLabelframe')
lf0.pack(padx=10, pady=10)
L_LIMIT = 0.0
U_LIMIT = 10.0

ulab = Label(lf0, text=str(U_LIMIT)+"  upper limit", style='brown.TLabel')
ulab.grid(row=0, column=1, padx=10)
llab = Label(lf0, text=str(L_LIMIT)+"  lower limit", style='brown.TLabel')
llab.grid(row=2, column=1, padx=10)


def end_input(evt):
    """limit on float

    Parameters
    ----------
    evt : str
        bind handle

    Returns
    -------
    None
    """
    print('evt', entsv.get())
    ulab['style'] = 'brown.TLabel'
    llab['style'] = 'brown.TLabel'
    if L_LIMIT < float(entsv.get()) < U_LIMIT:
        mess_lbl['text'] = "That's OK"
    elif L_LIMIT >= float(entsv.get()):
        mess_lbl['text'] = "Input below lower limit"
        llab['style'] = 'lowr.TLabel'
    else:
        mess_lbl['text'] = "Input above upper limit"
        ulab['style'] = 'upr.TLabel'

def is_okay(text):
    """ validation function

    Parameters
    ----------
    text : str
        text if allowed

    Returns
    -------
    boolean
    """
    print(text)
    if text in ("", "-", ".", "-."):
        return True
    try:
        float(text)
    except ValueError:
        return False
    return True

vcmd = root.register(is_okay)

entsv = StringVar()
ent0 = Entry(lf0, validate='key', validatecommand=(vcmd, '%P'),
           textvariable=entsv)
ent0.bind("<Return>", end_input)
ent0.grid(row=1, column=0, padx=10)
ent0.focus()

mess_lbl = Label(lf0, text='Insert +ve or -ve float, <Return> to confirm',
                style='brown.TLabel')
mess_lbl.grid(row=2, column=0, pady=10, padx=10)

root.mainloop()