Here's my solution.
- remove
.
from pricevar.get()
and turn it into a list
- get the
char
of the key that was pressed and check if it is numeric
- if it is numeric,
append
it to the list
insert
the decimal back into the list 2 places from the end
join
the list
without the 0 index and assign it to pricevar.set
import tkinter as tk
class App(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.pricevar = tk.StringVar()
self.pricevar.set('0.00')
dflt = dict(width=5, font='Consolas 16 bold', insertontime=0)
self.priceEntry = tk.Entry(self, textvariable=self.pricevar, **dflt)
self.priceEntry.grid()
self.priceEntry.bind('<Key>', self.price)
def price(self, event):
o = list(self.pricevar.get().replace('.', ''))
c = event.char
if c.isnumeric():
o.append(c)
o.insert(-2, '.')
self.pricevar.set(''.join(o[1:]))
return 'break'
if __name__ == '__main__':
app = App()
app.mainloop()
If you want to turn it into it's own widget it would look something like the below. In this example I removed the StringVar
and set the Entry
text directly. I also included a reset button and a label. Price also has a lock on it so that once it is 'full' no more text can be entered unless it is reset. If you change the value
in the constructor (ex value='00.00'
) it will all still work and the label will be automatically sized to fit the value
import tkinter as tk
class PriceEntry(tk.Frame):
#focus in/out colors
FOC = '#FFFFFF'
FIC = '#DDDDFF'
def __init__(self, master, row=0, column=0, text='undefined', value='0.00'):
tk.Frame.__init__(self, master)
self.grid(row=row, column=column)
#price label
tk.Label(self, text=text, font='Calibri 12 bold').grid(row=0, column=0, padx=(6, 2), pady=2)
#price entry
self.price = tk.Entry(self, background=PriceEntry.FOC, width=len(value), font='Consolas 16 bold', insertontime=0)
self.price.grid(row=0, column=1, padx=2, pady=2)
#insert start value
self.value = value
self.price.insert('end', value)
#capture all keypresses
self.price.bind('<Key>', self.keyHandler)
#since we turned the cursor off, use a different way to indicate focus
self.price.bind('<FocusOut>', self.indicate)
self.price.bind('<FocusIn>', self.indicate)
#price reset button
tk.Button(self, text=chr(10226), relief='flat', bd=0, font='none 12 bold', command=self.reset).grid(row=0, column=2)
def reset(self):
self.price.delete(0, 'end') #delete old text
self.price.insert('end', self.value) #insert init value
def keyHandler(self, event):
o = list(self.price.get().replace('.', '')) #remove decimal and return text as a list
if (c := event.char).isnumeric() and not int(o[0]): #if character is numeric and price isn't "full"
o.append(c) #append character to list
o.insert(-2, '.') #replace decimal
self.price.delete(0, 'end') #delete old text
self.price.insert('end', ''.join(o[1:])) #insert new text
return 'break' #stop further propagation
def indicate(self, event):
if str(event) == '<FocusOut event>':
self.price['background'] = PriceEntry.FOC
elif str(event) == '<FocusIn event>':
self.price['background'] = PriceEntry.FIC
class App(tk.Tk):
WIDTH, HEIGHT, TITLE = 800, 600, 'Price Busters'
def __init__(self):
tk.Tk.__init__(self)
#init widget at row0 column0
self.minimum = PriceEntry(self, 0, 0, 'min')
#init widget at row0 column1 with a higher price potential
self.maximum = PriceEntry(self, 0, 1, 'max', '000.00')
if __name__ == '__main__':
app = App()
app.title(App.TITLE)
app.geometry(f'{App.WIDTH}x{App.HEIGHT}')
app.resizable(width=False, height=False)
app.mainloop()