限制在tkinter中输入,以便可以输入价格,按键位置以百分之一美元为单位,小数点为固定

问题描述

我知道关于这方面的文档并不多,但是我想限制tkinter输入框中的输入。遵循在here上找到的一些代码,我目前正在这样做:

    def float_only(self,S,d):
        if d == '1': #insert
            if not S in ['.','0','1','2','3','4','5','6','7','8','9']:
                return False
        return True

及更高版本:

        mod_Box = tk.Toplevel(self.root)
        self.priceVar = tk.StringVar(mod_Box)
        self.priceVar.set('0.00')
        
        vcmd = (mod_Box.register(self.float_only),'%s','%d')
        priceEntry = tk.Entry(mod_Box,textvariable=self.priceVar,validate='key',validatecommand=vcmd)

这只允许使用小数和数字,但是我真的很想如果我能拥有它,那么按每个数字都将输入的数字放在最后一个小数位,然后将其余的数字向上移动,同时仍然只允许输入数字,例如收银机呢。例如,如果我输入2然后4然后0,则输入框将显示

0.00-> 0.02-> 0.24-> 2.40

那么我就不需要允许小数点了(因此它们不能多次输入),这将是一个更加顺畅的体验。当然,当我说到现在的时候,我几乎不知道自己在做什么,所以将不胜感激。

解决方法

这是我的解决方法。

  • .中删除pricevar.get()并将其变成list
  • 获取所按下键的char,并检查其是否为数字
  • 如果是数字,请append到列表中
  • insert小数从末尾回到列表的第二位
  • join list(不带0索引)并将其分配给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()

如果您想将其变成自己的小部件,则外观如下所示。在此示例中,我删除了StringVar并直接设置了Entry文本。我还包括一个重置按钮和一个标签。价格也有锁定,因此一旦“已满”,就不能再输入其他文本,除非将其重置。如果您在构造函数中更改了value(例如value='00.00'),它仍然可以正常工作,并且标签的大小将自动调整为适合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,padx=(6,2),pady=2)
        
        #price entry
        self.price = tk.Entry(self,background=PriceEntry.FOC,width=len(value),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,'min') 
      
        #init widget at row0 column1 with a higher price potential
        self.maximum = PriceEntry(self,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()