Adding Entries#

Adding the Integer Entry#

tkinter entry integer validation disabled

Create a new integer class based on the previous StringEntry class. Special consideration is needed for the additional passed attributes and changed methods. If a method remains unaltered we need not restate it, therefore to reuse the make_entry method include '%S' even though we are only using '%P'. Base the class IntegerEntry on 11integerfunction.py.

The trickiest part is the init function. The class IntegerEntry inherits from StringEntry, that is class IntegerEntry(StringEntry):, the passed attributes are complete for IntegerEntry, give the self aliases to all the attributes found in IntegerEntry then create an init line for StringEntry with its attributes, but without its default values StringEntry.__init__(self,parent,lf_text,mess_text,def_text,colour,mod). Now give the self aliases for the new attributes l_limit and u_limit.

Proceed with the style colours, not forgetting those for lower and upper limits. Continuation calls are inserted into the construct method and a new method that makes the labels for the limits, another continuation call is needed for make_entry. The methods end_input and is_okay need to be rewritten, otherwise the methods in StringEntry will be used.

Show/Hide Code entry_class_2.py

"""String and Integer Entry classes"""
from tkinter import Tk, StringVar, IntVar
from tkinter.ttk import Entry, Style, Label, Labelframe, Button, Frame,\
    Checkbutton


class StringEntry:
    """String class for entry
        added colour, change state
        integer class added

    Parameters
    ----------
    parent : str
        parent handle
    lf_text : str
        text on LabelFrame
    mess_text : str
        message
    deftext : str
        default text
    colour : str
        frame colour
    mod : bool
        enable or disable state switch

    Returns
    -------
    string
    """
    def __init__(self, parent, lf_text, mess_text, def_inp="", colour='brown',
                 mod=False):
        self.parent = parent
        self.lf_text = lf_text
        self.mess_text = mess_text
        self.mod = mod

        self.out_var = StringVar()
        self.out_var.set(def_inp)

        self.farbe = farbe = {'blue': 'light blue', 'brown': 'brown1',
                              'green': 'light green', 'pink': '#EAAFBF'}

        colour = colour if colour in farbe else 'brown'

        self.colour = colour

        st1 = Style()
        st1.theme_use('default')

        st1.configure(colour+'.TLabelframe', background='#C9B99B')
        st1.configure(colour+'.TLabelframe.Label', background=farbe[colour])
        st1.configure(colour+'.TCheckbutton', background=farbe[colour])
        st1.configure('brown.TLabel', background='#EDEF77')

        self.construct()

    def construct(self):
        """construct of LabelFrame and message

        Parameters
        ----------
        None

        Returns
        -------
        None
        """
        self.lf0 = Labelframe(self.parent, text=self.lf_text,
                              style=self.colour+'.TLabelframe')
        self.lf0.grid(column=0, row=0, padx=10, pady=10)
        self.messlbl = Label(self.lf0, text=self.mess_text, style='brown.TLabel')
        self.messlbl.grid(row=2, column=0, pady=10, padx=10)

        self.make_entry()

    def make_entry(self):
        """construct of Entry

        Parameters
        ----------
        None

        Returns
        -------
        None
        """
        vcmd = self.lf0.register(self.is_okay)

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

        if self.mod in (True, False):
            self.modify()

    def modify(self):
        """construct of state switch

        Parameters
        ----------
        None

        Returns
        -------
        None
        """
        lf_text = self.lf_text
# entry disabled until checkbox is ticked
        self.cb_opt = Checkbutton(self.lf0, command=self.toggle_opt,
                                  style=self.colour+'.TCheckbutton')
        self.lf0['labelwidget'] = self.cb_opt
        if self.mod:
            self.ent1.state(['!disabled'])
            self.cb_opt.state(['!selected'])
            self.cb_opt['text'] = lf_text+' Check to prevent editing '
            self.ent1.focus()
        else:
            self.ent1.state(['disabled'])
            self.cb_opt.state(['selected'])
            self.cb_opt['text'] = lf_text+' Check to modify '

    def toggle_opt(self):
        """state switch logic

        Parameters
        ----------
        None

        Returns
        -------
        None
        """
        lf_text = self.lf_text
# state of entry controlled
# by the state of the check button in Option frame label widget
        if self.cb_opt.instate(['selected']):
            self.ent1.state(['disabled'])
            self.cb_opt['text'] = lf_text+' Check to modify '
        else:
            self.ent1.state(['!disabled'])  # enable option
            self.cb_opt['text'] = lf_text+' Check to prevent editing '
            self.ent1.focus()

    def end_input(self, _evt):
        """limit on string

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

        Returns
        -------
        None
        """
        if len(self.out_var.get()) > 5:
            self.messlbl['text'] = "That's OK"
        else:
            self.messlbl['text'] = "Should be at least 6 characters long"

    def is_okay(self, text, inp, ind):
        """ validation function

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

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


class IntegerEntry(StringEntry):
    """Integer class for entry

    Parameters
    ----------
    parent : str
        parent handle
    lf_text : str
        text on LabelFrame
    mess_text : str
        message
    l_limit : int
        lower limit
    u_limit : int
        upper limit
    deftext : str
        default text
    colour : str
        frame colour
    mod : str
        enable or disable state switch

    Returns
    -------
    integer
    """
    def __init__(self, parent, lf_text, mess_text, l_limit, u_limit, def_inp="",
                 colour='brown', mod=False):
        self.parent = parent
        self.lf_text = lf_text
        self.mess_text = mess_text
        self.mod = mod
        self.colour = colour
        StringEntry.__init__(self, parent, lf_text, mess_text, def_inp,
                             colour, mod)
        self.l_limit = l_limit
        self.u_limit = u_limit

        self.out_var = IntVar()
        self.out_var.set(def_inp)

        self.farbe = farbe = {'blue': 'light blue', 'brown': '#EDEF77',
                              'green': 'light green', 'pink': '#EAAFBF'}

        colour = colour if colour in farbe else 'brown'

        self.colour = colour

        st1 = Style()
        st1.theme_use('default')

        st1.configure(colour+'.TLabelframe', background='#C9B99B')
        st1.configure(colour+'.TLabelframe.Label', background=farbe[colour])
        st1.configure(colour+'.TCheckbutton', background=farbe[colour])
        st1.configure('brownn.TLabel', background='#EDEF77')
        st1.configure('lowr.TLabel', background='lightblue')
        st1.configure('upr.TLabel', background='red')

        self.construct()
        self.limits()

    def limits(self):
        """limit logic

        Parameters
        ----------
        None

        Returns
        -------
        None
        """
        self.ulab = Label(self.lf0, text=str(self.u_limit)+"  upper limit",
                          style='brown.TLabel')
        self.ulab.grid(row=0, column=1, padx=10)
        self.llab = Label(self.lf0, text=str(self.l_limit)+"  lower limit",
                          style='brown.TLabel')
        self.llab.grid(row=2, column=1, padx=10)

        self.make_entry()

    def end_input(self, _evt):
        """limit on integer

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

        Returns
        -------
        None
        """
        self.ulab['style'] = 'brown.TLabel'
        self.llab['style'] = 'brown.TLabel'
        if self.l_limit < int(self.out_var.get()) < self.u_limit:
            self.messlbl['text'] = "That's OK"
        elif self.l_limit >= int(self.out_var.get()):
            self.messlbl['text'] = "Input below or at  lower limit"
            self.llab['style'] = 'lowr.TLabel'
        else:
            self.messlbl['text'] = "Input above or at upper limit"
            self.ulab['style'] = 'upr.TLabel'

    def is_okay(self, text, inp, ind):
        """ validation function

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

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


if __name__ == "__main__":
    root = Tk()
    fra0 = Frame(root)
    fra0.grid(row=0, column=0)
#   LF_TEXT = 'Beer Type'
#   DEF_INP = 'Pilsner'
    COLOUR = 'blue'
    MOD = True
#   MESS_TEXT = 'Start with capital letter, use at least 6 characters'\
#              '<Return> to confirm'
#   v = StringEntry(fr, LF_TEXT, MESS_TEXT, DEF_INP, COLOUR) #,MOD)

    LF_TEXT = 'Number of Coils'
    MESS_TEXT = 'Insert +ve or -ve integer, <Return> to confirm'
    DEF_INP = 10
    L_LIMIT = 1
    U_LIMIT = 100
    v = IntegerEntry(fra0, LF_TEXT, MESS_TEXT, L_LIMIT, U_LIMIT, DEF_INP, COLOUR,
                     MOD)

    b2 = Button(root, text='Click after selection',
                command=lambda: print(v.out_var.get()))
    b2.grid(row=2, column=0)
    root.mainloop()

Adding The Float Entry#

tkinter entry float validation with plit information

The next class is based on 09float_function.py. The new class inherits from IntegerEntry, so there are no new passed arguments, therefore there is no call to IntegerEntry.__init__ ..... The methods ind_input and is_okay need to be rewritten, otherwise the methods in IntegerEntry and by proxy StringEntry will be used. If all the colour and the style configuration are copied to the method construct in StringEntry the size becomes even smaller.

When running IntegerEntry the display breaks up but both StringEntry and FloatEntry behave well. In reality there is too much feedback information, in a larger application the user instructions could have been made once outside the enhanced entry widget. Experimenting with split lines makes the feedback information even more overpowering.

Show/Hide Code entry_class_3.py

"""String, Integer and Float Entry classes"""
from tkinter import Tk, StringVar, IntVar, DoubleVar
from tkinter.ttk import Entry, Style, Label, Labelframe, Button, Frame,\
    Checkbutton


class StringEntry:
    """String class for entry
        added colour, change state
        integer and float classes added

    Parameters
    ----------
    parent : str
        parent handle
    lf_text : str
        text on LabelFrame
    mess_text : str
        message
    def_inp : str
        default text
    colour : str
        frame colour
    mod : str
        enable or disable state switch

    Returns
    -------
    string
    """

    def __init__(self, parent, lf_text, mess_text, def_inp="", colour='brown',
                 mod=False):
        self.parent = parent
        self.lf_text = lf_text
        self.mess_text = mess_text
        self.mod = mod

        self.ent0 = None    #   for entry
        self.cb_opt = None  #   for check option

        self.out_var = StringVar()
        self.out_var.set(def_inp)

        self.farbe = farbe = {'blue': 'light blue', 'brown': 'brown1',
                              'green': 'light green', 'pink': '#EAAFBF'}

        colour = colour if colour in farbe else 'brown'

        self.colour = colour

        st1 = Style()
        st1.theme_use('default')

        st1.configure(colour + '.TLabelframe', background='#C9B99B')
        st1.configure(colour + '.TLabelframe.Label', background=farbe[colour])
        st1.configure(colour + '.TCheckbutton', background=farbe[colour])
        st1.configure('brown.TLabel', background='#EDEF77')

        self.construct()

    def construct(self):
        """construct of LabelFrame and message

        Parameters
        ----------
        None

        Returns
        -------
        None
        """
        self.lf1 = Labelframe(self.parent, text=self.lf_text,
                              style=self.colour + '.TLabelframe')
        self.lf1.grid(column=0, row=0, padx=10, pady=10)
        self.messlbl = Label(
            self.lf1,
            text=self.mess_text,
            style='brown.TLabel')
        self.messlbl.grid(row=2, column=0, pady=10, padx=10)

        self.make_entry()

    def make_entry(self):
        """construct of Entry

        Parameters
        ----------
        None

        Returns
        -------
        None
        """
        vcmd = self.lf1.register(self.is_okay)

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

        if self.mod in (True, False):
            self.modify()

    def modify(self):
        """construct of state switch

        Parameters
        ----------
        None

        Returns
        -------
        None
        """
        lf_text = self.lf_text
        # entry disabled until checkbox is ticked
        self.cb_opt = Checkbutton(self.lf1, command=self.toggle_opt,
                                  style=self.colour + '.TCheckbutton')
        self.lf1['labelwidget'] = self.cb_opt
        if self.mod:
            self.ent0.state(['!disabled'])
            self.cb_opt.state(['!selected'])
            self.cb_opt['text'] = lf_text + '\n Check to prevent editing '
            self.ent0.focus()
        else:
            self.ent0.state(['disabled'])
            self.cb_opt.state(['selected'])
            self.cb_opt['text'] = lf_text + '\n Check to modify '

    def toggle_opt(self):
        """state switch logic

        Parameters
        ----------
        None

        Returns
        -------
        None
        """
        lf_text = self.lf_text
# state of entry controlled
# by the state of the check button in Option frame label widget
        if self.cb_opt.instate(['selected']):
            self.ent0.state(['disabled'])
            self.cb_opt['text'] = lf_text + '\n Check to modify '
        else:
            self.ent0.state(['!disabled'])  # enable option
            self.cb_opt['text'] = lf_text + '\n Check to prevent editing '
            self.ent0.focus()

    def end_input(self, _evt):
        """limit on string

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

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

    def is_okay(self, text, inp, ind):
        """ validation function

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

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


class IntegerEntry(StringEntry):
    """Integer class for entry

    Parameters
    ----------
    parent : str
        parent handle
    lf_text : str
        text on LabelFrame
    mess_text : str
        message
    l_limit : int
        lower limit
    u_limit : int
        upper limit
    def_inp : str
        default text
    colour : str
        frame colour
    mod : str
        enable or disable state switch

    Returns
    -------
    integer
    """

    def __init__(self, parent, lf_text, mess_text, l_limit, u_limit, def_inp="",
                 colour='brown', mod=False):
        self.parent = parent
        self.lf_text = lf_text
        self.mess_text = mess_text
        self.mod = mod
        self.colour = colour
        StringEntry.__init__(
            self,
            parent,
            lf_text,
            mess_text,
            def_inp,
            colour,
            mod)
        self.l_limit = l_limit
        self.u_limit = u_limit

        self.out_var = IntVar()
        self.out_var.set(def_inp)

        self.farbe = farbe = {'blue': 'light blue', 'brown': '#EDEF77',
                              'green': 'light green', 'pink': '#EAAFBF'}

        colour = colour if colour in farbe else 'brown'

        self.colour = colour

        st1 = Style()
        st1.theme_use('default')

        st1.configure(colour + '.TLabelframe', background='#C9B99B')
        st1.configure(colour + '.TLabelframe.Label', background=farbe[colour])
        st1.configure(colour + '.TCheckbutton', background=farbe[colour])
        st1.configure('brownn.TLabel', background='#EDEF77')
        st1.configure('lowr.TLabel', background='lightblue')
        st1.configure('upr.TLabel', background='red')

        self.construct()
        self.limits()

    def limits(self):
        """limit logic

        Parameters
        ----------
        None

        Returns
        -------
        None
        """
        self.ulab = Label(self.lf1, text=str(self.u_limit) + "  upper limit",
                          style='brown.TLabel')
        self.ulab.grid(row=0, column=1, padx=10)
        self.llab = Label(self.lf1, text=str(self.l_limit) + "  lower limit",
                          style='brown.TLabel')
        self.llab.grid(row=2, column=1, padx=10)

        self.make_entry()

    def end_input(self, _evt):
        """limit on integer

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

        Returns
        -------
        None
        """
        self.ulab['style'] = 'brown.TLabel'
        self.llab['style'] = 'brown.TLabel'
        if self.l_limit < int(self.out_var.get()) < self.u_limit:
            self.messlbl['text'] = "That's OK"
        elif self.l_limit >= int(self.out_var.get()):
            self.messlbl['text'] = "Input below or at lower limit"
            self.llab['style'] = 'lowr.TLabel'
        else:
            self.messlbl['text'] = "Input above or at upper limit"
            self.ulab['style'] = 'upr.TLabel'

    def is_okay(self, text, inp, ind):
        """ validation function

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

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


class FloatEntry(IntegerEntry):
    """Float class for entry

    Parameters
    ----------
    parent : str
        parent handle
    lf_text : str
        text on LabelFrame
    mess_text : str
        message
    l_limit : float
        lower limit
    u_limit : float
        upper limit
    deftext : str
        default text
    colour : str
        frame colour
    mod : str
        enable or disable state switch

    Returns
    -------
    float
    """

    def __init__(self, parent, lf_text, mess_text, l_limit, u_limit, def_inp="",
                 colour='brown', mod=False):
        self.parent = parent
        self.lf_text = lf_text
        self.mess_text = mess_text
        self.mod = mod
        self.colour = colour
        self.l_limit = l_limit
        self.u_limit = u_limit
        IntegerEntry.__init__(self, parent, lf_text, mess_text, l_limit, u_limit,
                              def_inp=None, colour='brown', mod=False)
        self.out_var = DoubleVar()
        self.out_var.set(def_inp)

        self.farbe = farbe = {'blue': 'light blue', 'brown': '#EDEF77',
                              'green': 'light green', 'pink': '#EAAFBF'}

        colour = colour if colour in farbe else 'brown'

        self.colour = colour

        st1 = Style()
        st1.theme_use('default')

        st1.configure(colour + '.TLabelframe', background='#C9B99B')
        st1.configure(colour + '.TLabelframe.Label', background=farbe[colour])
        st1.configure(colour + '.TCheckbutton', background=farbe[colour])
        st1.configure('brownn.TLabel', background='#EDEF77')
        st1.configure('lowr.TLabel', background='lightblue')
        st1.configure('upr.TLabel', background='red')

        self.construct()
        self.limits()

    def end_input(self, _evt):
        """limit on float

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

        Returns
        -------
        None
        """
        self.ulab['style'] = 'brown.TLabel'
        self.llab['style'] = 'brown.TLabel'
        if self.l_limit < float(self.out_var.get()) < self.u_limit:
            self.messlbl['text'] = "That's OK"
        elif self.l_limit >= float(self.out_var.get()):
            self.messlbl['text'] = "Input below lower limit"
            self.llab['style'] = 'lowr.TLabel'
        else:
            self.messlbl['text'] = "Input above upper limit"
            self.ulab['style'] = 'upr.TLabel'

    def is_okay(self, text, inp, ind):
        """ validation function

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

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


if __name__ == "__main__":
    root = Tk()
    fr = Frame(root)
    fr.grid(row=0, column=0)
#    LF_TEXT = 'Beer Type'
#    DEF_TEXT = 'Pilsner'
    COLOUR = ''
    MOD = True
    '''
    MESS_TEXT = 'Start with capital letter,\n use at least 6 characters\n <Return> to confirm'
    v = StringEntry(fr,LF_TEXT,MESS_TEXT,DEF_TEXT,COLOUR,MOD)

    LF_TEXT = 'Number of Coils'
    MESS_TEXT = 'Insert +ve or -ve integer,\n <Return> to confirm'
    DEF_TEXT = 10
    L_LIMIT = 1
    U_LIMIT= 100
    v = IntegerEntry(fr,LF_TEXT,MESS_TEXT,L_LIMIT,U_LIMIT,DEF_TEXT,COLOUR,MOD)
    '''
    LF_TEXT = 'Beer Strength v/v % alcohol'
    DEF_TEXT = 5.0
    L_LIMIT = 0.5
    U_LIMIT = 10.5
    MESS_TEXT = 'Insert +ve or -ve float,\n <Return> to confirm'
    v = FloatEntry(
        fr,
        LF_TEXT,
        MESS_TEXT,
        L_LIMIT,
        U_LIMIT,
        DEF_TEXT,
        COLOUR,
        MOD)

    b2 = Button(root, text='Click after selection',
                command=lambda: print(v.out_var.get()))
    b2.grid(row=2, column=0)
    root.mainloop()