自动将破折号添加到Tkinter中的输入框

时间:2020-05-31 16:54:45

标签: python tkinter phone-number tkinter-entry

有人在键入电话号码时(例如说电话号码为5551111234),是否可以自动在电话号码中添加破折号,但是当他们在输入框中键入该号码时,该号码应显示为连字符b / w自动像 555-1111234

3 个答案:

答案 0 :(得分:1)

我同时使用了this example和跟踪{k}的组合,并将它们与this answer结合使用,我不确定这是否是正确的美国格式,因为我住在英国,并且我们格式化这里的情况有所不同,但这是如何工作的粗略示例:

(while (search-forward "\n" nil t) 
  (unless (looking-at-p "\n")  (replace-match "" nil t)))

替代


# Python program to trace
# variable in tkinter


from tkinter import *
import re

root = Tk()

my_var = StringVar()

# defining the callback function (observer)


def phone_format(phone_number):
    try:
        clean_phone_number = re.sub('[^0-9]+', '', phone_number)
        formatted_phone_number = re.sub(
            r"(\d)(?=(\d{3})+(?!\d))", r"\1-", "%d" % int(clean_phone_number[:-1])) + clean_phone_number[-1]
        return formatted_phone_number
    except ValueError:
        return phone_number


def my_callback(var, indx, mode):
    my_var.set(phone_format(my_var.get()))
    label.configure(text=my_var.get())


my_var.trace_add('write', my_callback)

label = Label(root)
label.pack(padx=5, pady=5)

Entry(root, textvariable=my_var).pack(padx=5, pady=5)

root.mainloop()

这是我的解决方案,使用了PyPi的# Python program to trace # variable in tkinter from tkinter import * import phonenumbers import re root = Tk() my_var = StringVar() # defining the callback function (observer) # def phone_format(phone_number): # try: # clean_phone_number = re.sub('[^0-9]+', '', phone_number) # formatted_phone_number = re.sub( # r"(\d)(?=(\d{3})+(?!\d))", r"\1-", "%d" % int(clean_phone_number[:-1])) + clean_phone_number[-1] # return formatted_phone_number # except ValueError: # return phone_number def phone_format(n): # return format(int(n[:-1]), ",").replace(",", "-") + n[-1] # return phonenumbers.format_number(n, phonenumbers.PhoneNumberFormat.NATIONAL) formatter = phonenumbers.AsYouTypeFormatter("US") for digit in re.findall(r'\d', n)[:-1]: formatter.input_digit(digit) return formatter.input_digit(re.findall(r'\d', n)[-1]) def my_callback(var, indx, mode): print(my_var.get()) my_var.set(phone_format(my_var.get())) label.configure(text=my_var.get()) def callback(event): entry.icursor(END) my_var.trace_add('write', my_callback) label = Label(root) label.pack(padx=5, pady=5) entry = Entry(root, textvariable=my_var) entry.bind("<Key>", callback) entry.pack(padx=5, pady=5) root.mainloop() ,它似乎可以正常工作。

答案 1 :(得分:1)

这是一个复杂的示例,但它不仅处理电话号码。它被评论至死。

#widgets.py

import tkinter as tk, re
from dataclasses import dataclass, field
from typing import List, Pattern, Iterable
from copy import deepcopy

Char: Pattern = re.compile('[a-z0-9]', re.I)


''' FormEntryFormat_dc
    this serves as a configuration for the behavior of FormEntry
'''
@dataclass
class FormEntryFormat_dc:
    valid      :Pattern        = None                        #pattern to validate text by
    separator  :str            = None                        #the separator to use
    marks      :List           = field(default_factory=list) #list of positions to apply separator
    strict     :bool           = False                       #True|False strict typing
        
    def config(self, ascopy:bool=True, **data):
        c = deepcopy(self) if ascopy else self
        for key in c.__dict__:
            if key in data:
                c.__dict__[key] = data[key]                  #assign new value
        return c
    
    
#prepare a few formats        
TimeFormat   = FormEntryFormat_dc(re.compile('^(\d{1,2}(:(\d{1,2}(:(\d{1,2})?)?)?)?)?$'      ), ':' , [2, 5])
DateFormat   = FormEntryFormat_dc(re.compile('^(\d{1,2}(\\\\(\d{1,2}(\\\\(\d{1,4})?)?)?)?)?$'), '\\', [2, 5])
PhoneFormat  = FormEntryFormat_dc(re.compile('^(\d{1,3}(-(\d{1,3}(-(\d{1,4})?)?)?)?)?$'      ), '-' , [3, 7], True)   
PhoneFormat2 = FormEntryFormat_dc(re.compile('^(\d{1,3}(-(\d{1,7})?)?)?$'                    ), '-' , [3]   , True)   


''' FormEntry
    an entry with format behavior
'''
class FormEntry(tk.Entry):
    @property
    def input(self) -> str:
        return self.get()
        
    def offset(self, separator:str, marks:Iterable):
        sep_marks = [] #cache for positions of already inserted separators
        offset    = 0  #the overall offset between inserted and expected separator marks
        
        #get a mark for every current separator
        for i, c in enumerate(self.input):
            if c == separator:
                sep_marks.append(i)
        
        #if any sep_marks ~ subtract the value of sep_marks last index 
        #~from the value of the corresponding index in marks
        n = len(sep_marks)
        if n:       
            offset = max(0, marks[n-1]-sep_marks[-1])
            
        return offset
    
    def __init__(self, master, frmt:FormEntryFormat_dc, **kwargs):
        tk.Entry.__init__(self, master, **kwargs)
        
        self.valid = frmt.valid
        if self.valid:
            #register validatecommand and assign to options
            vcmd = self.register(self.validate)
            self.configure(validate="all", validatecommand=(vcmd, '%P'))
            
        if frmt.marks and frmt.separator:
            #bind every key to formatting
            self.bind('<Key>', lambda e: self.format(e, frmt.separator, frmt.marks, frmt.strict))
        
    def validate(self, text:str):      
        return not (self.valid.match(text) is None) #validate with regex

    def format(self, event, separator:str, marks:Iterable, strict:bool):
        if event.keysym != 'BackSpace':             #allow backspace to function normally
            i = self.index('insert')                #get current index
            if Char.match(event.char) is None and (i in marks or not strict):
                event.char = separator              #overwrite with proper separator
            else:
                #automatically add separator
                if i+self.offset(separator, marks) in marks:
                    event.char = f'{separator}{event.char}'
                    
            self.insert(i, event.char)              #validation will check if this is allowed
            
            return 'break'

#main.py (OOP style)

import widgets as ctk #custom tk
import tkinter as tk

class Main(tk.Tk):
    def __init__(self):
        tk.Tk.__init__(self)
        
        self.title("Formatted Entry")

        self.grid_columnconfigure(2, weight=1)

        #create labels
        self.labels = ['time', 'date', 'phone', 'phone2']
        for n, label in enumerate(self.labels):
            tk.Label(self, text=f'{label}: ', width=14, font='consolas 12 bold', anchor='w').grid(row=n, column=0, sticky='w')

        #create entries
        self.entries = []
        for n, format in enumerate([ctk.TimeFormat, ctk.DateFormat, ctk.PhoneFormat, ctk.PhoneFormat2]):
            self.entries.append(ctk.FormEntry(self, format, width=14, font='consolas 12 bold'))
            self.entries[-1].grid(row=n, column=1, sticky='w')
        
        #form submit button        
        tk.Button(self, text='submit', command=self.submit).grid(column=1, sticky='e')
            
    def submit(self):
        for l, e in zip(self.labels, self.entries):
            print(f'{l}: {e.input}')
        


Main().mainloop() if __name__ == "__main__" else None

#main.py (procedural style)

import widgets as ctk #custom tk
import tkinter as tk

if __name__ == "__main__":
    root = tk.Tk()
    root.title("Formatted Entry")
    root.grid_columnconfigure(2, weight=1)

    #create labels
    labels = ['time', 'date', 'phone', 'phone2']
    for n, label in enumerate(labels):
        tk.Label(root, text=f'{label}: ', width=14, font='consolas 12 bold', anchor='w').grid(row=n, column=0, sticky='w')

    #create entries
    entries = []
    for n, format in enumerate([ctk.TimeFormat, ctk.DateFormat, ctk.PhoneFormat, ctk.PhoneFormat2]):
        entries.append(ctk.FormEntry(root, format, width=14, font='consolas 12 bold'))
        entries[-1].grid(row=n, column=1, sticky='w')
     
    def submit():
        for l, e in zip(labels, entries):
            print(f'{l}: {e.input}')
            
    #form submit button        
    tk.Button(root, text='submit', command=submit).grid(column=1, sticky='e')
    
    root.mainloop()

答案 2 :(得分:1)

这是一个程序示例。该示例已受到严重评论。

import tkinter as tk, re
from dataclasses import dataclass, field
from typing import List, Pattern, Iterable
from copy import deepcopy

Char: Pattern = re.compile('[a-z0-9]', re.I)


''' FormField_dc
    this serves as a configuration for the behavior of form_field
'''
@dataclass
class FormEntryFormat_dc:
    valid      :Pattern        = None                        #pattern to validate text by
    separator  :str            = None                        #the separator to use
    marks      :List           = field(default_factory=list) #list of positions to apply separator
    strict     :bool           = False                       #True|False strict typing
        
    def config(self, ascopy:bool=True, **data):
        c = deepcopy(self) if ascopy else self
        for key in c.__dict__:
            if key in data:
                c.__dict__[key] = data[key]                  #assign new value
        return c
    
    
#prepare a few formats        
TimeFormat   = FormEntryFormat_dc(re.compile('^(\d{1,2}(:(\d{1,2}(:(\d{1,2})?)?)?)?)?$'      ), ':' , [2, 5])
DateFormat   = FormEntryFormat_dc(re.compile('^(\d{1,2}(\\\\(\d{1,2}(\\\\(\d{1,4})?)?)?)?)?$'), '\\', [2, 5])
PhoneFormat  = FormEntryFormat_dc(re.compile('^(\d{1,3}(-(\d{1,3}(-(\d{1,4})?)?)?)?)?$'      ), '-' , [3, 7], True)   
PhoneFormat2 = FormEntryFormat_dc(re.compile('^(\d{1,3}(-(\d{1,7})?)?)?$'                    ), '-' , [3]   , True)   


''' FormField
    An entry field intended to force a specific format while the user types
'''
def form_field(master, f:FormEntryFormat_dc, **kwargs) -> tk.Entry:
    entry = tk.Entry(master, **kwargs)
    
    def offset(separator:str, marks:Iterable):
        sep_marks = [] #cache for positions of already inserted separators
        offset    = 0  #the overall offset between inserted and expected separator marks
        
        #get a mark for every current separator
        for i, c in enumerate(entry.get()):
            if c == separator:
                sep_marks.append(i)
        
        #if any sep_marks ~ subtract the value of sep_marks last index 
        #~from the value of the corresponding index in marks
        n = len(sep_marks)
        if n:       
            offset = max(0, marks[n-1]-sep_marks[-1])
            
        return offset
        
    #test text against validity conditions
    def validate(text):
        #if numeric check is True and len(text) > 0 
        return not (f.valid.match(text) is None) #validate with regex
        
    if f.valid:
        #register validatecommand and assign to options
        vcmd = entry.register(validate)
        entry.configure(validate="all", validatecommand=(vcmd, '%P'))
            
    #add separators when entry "insert" index equals a mark  
    #~and separator isn't already present
    def format(event, separator:str, marks:Iterable, strict:bool):
        #allow backspace to function normally
        if event.keysym != 'BackSpace':
            i = entry.index('insert')                #get current index
            if Char.match(event.char) is None and (i in marks or not strict):
                event.char = separator              #overwrite with proper separator
            else:
                #automatically add separator
                if i+offset(separator, marks) in marks:
                    event.char = f'{separator}{event.char}'
                    
            entry.insert(i, event.char)              #validation will check if this is allowed
            
            return 'break'
                
    
    if f.marks and f.separator:           
        #bind every keypress to formatting
        entry.bind('<Key>', lambda e: format(e, f.separator, f.marks, f.strict))
    
    
    return entry
    

##USAGE EXAMPLE
if __name__ == "__main__":
    root = tk.Tk()
    root.title("Formatted Entry")
    root.grid_columnconfigure(2, weight=1)

    #create labels
    labels = ['time', 'date', 'phone', 'phone2']
    for n, label in enumerate(labels):
        tk.Label(root, text=f'{label}: ', width=14, font='consolas 12 bold', anchor='w').grid(row=n, column=0, sticky='w')

    #create entries
    entries = []
    for n, format in enumerate([TimeFormat, DateFormat, PhoneFormat, PhoneFormat2]):
        entries.append(form_field(root, format, width=14, font='consolas 12 bold'))
        entries[-1].grid(row=n, column=1, sticky='w')
     
    def submit():
        for l, e in zip(labels, entries):
            print(f'{l}: {e.get()}')
            
    #form submit button        
    tk.Button(root, text='submit', command=submit).grid(column=1, sticky='e')
    
    root.mainloop()