Creating an Entry formatted for CPF in Python (Tkinter)

Asked

Viewed 221 times

-2

I would like to know how would be the application of a formatting in a field Entry, using the tkinter, for the format of CPF numbers, in the case of, XXX.XXX.XXX-XX. I found nothing similar, only finding solutions in Java or Javascript.

Example:

inserir a descrição da imagem aqui

Grateful!

2 answers

0

I adapted the program from here and worked well.

Note 1 Imagining this CPF 11.111.111-11 - The same would have to be entered as 011.111.111-11

Note 2 ENTER must be given for the field to be "read"

from __future__ import print_function

try:
    import Tkinter as tk
except ImportError:
    import tkinter as tk


class CpfEntry(tk.Frame):
    def __init__(self, master, frame_look={}, **look):
        args = dict(relief=tk.SUNKEN, border=1)
        args.update(frame_look)
        tk.Frame.__init__(self, master, **args)

        args = {'relief': tk.FLAT}
        args.update(look)

        self.entry_1 = tk.Entry(self, width=3, **args)
        self.label_1 = tk.Label(self, text='.', **args)
        self.entry_2 = tk.Entry(self, width=3, **args)
        self.label_2 = tk.Label(self, text='.', **args)
        self.entry_3 = tk.Entry(self, width=3, **args)
        self.label_3 = tk.Label(self, text='-', **args)
        self.entry_4 = tk.Entry(self, width=2, **args)

        self.entry_1.pack(side=tk.LEFT)
        self.label_1.pack(side=tk.LEFT)
        self.entry_2.pack(side=tk.LEFT)
        self.label_2.pack(side=tk.LEFT)
        self.entry_3.pack(side=tk.LEFT)
        self.label_3.pack(side=tk.LEFT)
        self.entry_4.pack(side=tk.LEFT)

        self.entries = [self.entry_1, self.entry_2, self.entry_3, self.entry_4]

        self.entry_1.bind('<KeyRelease>', lambda e: self._check(0, 3))
        self.entry_2.bind('<KeyRelease>', lambda e: self._check(1, 3))
        self.entry_3.bind('<KeyRelease>', lambda e: self._check(2, 3))
        self.entry_4.bind('<KeyRelease>', lambda e: self._check(3, 2))

    def _backspace(self, entry):
        cont = entry.get()
        entry.delete(0, tk.END)
        entry.insert(0, cont[:-1])

    def _check(self, index, size):
        entry = self.entries[index]
        next_index = index + 1
        next_entry = self.entries[next_index] if next_index < len(self.entries) else None
        data = entry.get()

        if len(data) > size or not data.isdigit():
            self._backspace(entry)
        if len(data) >= size and next_entry:
            next_entry.focus()

    def get(self):
        return [e.get() for e in self.entries]


if __name__ == '__main__':
    win = tk.Tk()
    win.title('CPF - demo')

    cpf_entry = CpfEntry(win, font=('Arial', 40, tk.NORMAL), border=0)
    cpf_entry.pack()

    win.bind('<Return>', lambda e: print(cpf_entry.get()))
    win.mainloop()

I hope it helps

0


To format the CPF while the user type it, simply create a function to handle the string and assign an event to the object entry, to perform the function with each button pressed - using the method entry.bind, passing as argument the event and formatting function.

In the function to format the string, you should get the widgtet text - using the method get - and then check if the pressed button was a "BackSpace". This way, the function will only format the string if one more character has been added to the text. This prevents the function to "lock" the text when the user wants to delete some character. It is important to note that the function must have in its signature a parameter to receive the event.

def format_cpf(event = None):
    
    # Obtém o texto, removendo todos os sinais e mantendo apenas os números, 
    # além de estabelecer um limite de 11 caracteres à string.
    text = entry.get().replace(".", "").replace("-", "")[:11]
    new_text = ""

    if event.keysym.lower() == "backspace": return

After that, scroll through all the characters in the text, checking if they are all numbers. It is important to note that using the method isnumeric or isdigit, as in that reply, is not recommended in this case as there are many characters Unicode which are validated by the method. Click here to know which characters are validated or not by the methods.

To validate, check that the character is inside a string containing only numbers, from zero to nine. See the example below:

if char in "0123456789":
    # Código...

After you have checked if the character is a numerical value, add it to a new string, along with the dot and dash signs - by checking the index of the same.

Assuming the number is "92187321245" (Random CPF), you must add a dot after the 3rd and 6th digit, and a dash after the 9th digit, so that the CPF looks like this: "921.873.212-45". This way, you should check the characters in indexes 2, 5 and 8, as in the code below:

if index in [2, 5]: new_text += text[index] + "."  # Índice 2 ou 5 -> Ponto
elif index == 8: new_text += text[index] + "-"     # índice 8 -> Traço
else: new_text += text[index]                      # Outros -> Nenhum sinal

To apply formatting, you should delete all text from entry and add the new formatted string to the widget - using the method delete and insert - as in the code below:

entry.delete(0, "end")
entry.insert(0, new_text)

The whole code would look like this:

def format_cpf(event = None):
    
    text = entry.get().replace(".", "").replace("-", "")[:11]
    new_text = ""

    if event.keysym.lower() == "backspace": return
    
    for index in range(len(text)):
        
        if not text[index] in "0123456789": continue
        if index in [2, 5]: new_text += text[index] + "."
        elif index == 8: new_text += text[index] + "-"
        else: new_text += text[index]

    entry.delete(0, "end")
    entry.insert(0, new_text)


from tkinter import *
screen = Tk()

entry = Entry(screen, font = ("Arial", 20))
entry.bind("<KeyRelease>", format_cpf)
entry.pack()
screen.mainloop()

Browser other questions tagged

You are not signed in. Login or sign up in order to post.