Python Tkinter Spinbox验证Fai

2024-06-26 00:27:13 发布

您现在位置:Python中文网/ 问答频道 /正文

我在验证数字调整框输入时遇到问题。下面我有一个解决方法,似乎可以工作;但是,这很尴尬。假设这不是一个bug,有正确的方法吗?我在Windows10上使用AnacondaPython3.6(Tk8.6)。

问题是,当spinbox项中的值介于to和{}之间时,如果从验证函数返回{},则{}被设置为None。仅当单击“向上”或“向下”按钮时才会发生这种情况,而不是直接编辑文本时。

import tkinter as tk

class SpinboxGui:

    def __init__(self):
        self.root = tk.Tk()
        vcmd = (self.root.register(self.validate_spin), '%W', '%P')
        self.spin = tk.Spinbox(self.root, from_=0, to=50000)
        self.spin.config(validate="key", validatecommand=vcmd)
        self.spin.pack()

    def validate_spin(self, name, nv):
        try:
            print(nv)
            n = int(nv)
        except:
            return False
        if n <= 15000:
            return True
        return False

if __name__ == "__main__":
    SpinboxGui()
    tk.mainloop()

若要复制,请突出显示0并键入149999。然后点击几下。请注意,validation命令将停止被调用。输出为:

^{pr2}$

现在,根据docs,同时使用textVariable和{}是很危险的;实际上,我有{a2}Python/Tkinter不止一种。但是,在本例中,是否使用textVariable并不重要;问题是相同的。

一种可能的解决方案是编辑验证函数中的tofrom选项。即使这样做也有点问题,因为我要将数字调整框值同步到嵌入式Matplotlib绘图。{cd4}和plot{cd4}都需要转换。

由于您不能在validation函数中编辑textVariable,所以我得出如下结论。也许有人可以改进一下。

def __init__(self):
    # http://stackoverflow.com/a/4140988/675216
    vcmd= (self.root.register(self.validate_spin), '%W', '%P')
    # Rest of code left out
    self.spin.config(validate="key", validatecommand=vcmd)
    self.spin.bind("<<ResetValidate>>", self.on_reset_validate)

def on_reset_validate(self, event):
    # Turn validate back on and set textVariable
    self.spin.config(validate="key")

def validate_spin(self, name, nv):
    # Do validation ...
    if not valid:
        self.spin.event_generate("<<ResetValidate>>", when="tail")
    return valid

Tags: tokey函数selfconfig编辑returndef
2条回答

在与spinbox中的验证机制进行了斗争之后,我放弃了它。也许它的工作方式与预期的一样,但我认为它只被调用一次是违反直觉的。我的应用程序使用spinbox更新matplotlib图形,我需要数据是指定范围内的整数。我需要代码来捕捉非整数项以及超出范围的整数。我想出的解决方案是使用密钥绑定而不是验证机制来获得所需的结果。以下是代码的相关部分:

class IntSpinbox(ttk.Frame):

    def __init__(self, parent, **kwargs):
        ttk.Frame.__init__(self,
                   parent,
                   borderwidth=kwargs.get('frameborderwidth', 2),
                   relief=kwargs.get('framerelief', tk.GROOVE))
        self.valuestr = tk.StringVar()
        self.valuestr2 = tk.StringVar()
        self.minvalue = kwargs.get('minvalue', 0)
        self.maxvalue = kwargs.get('maxvalue', 99)
        self.initval = kwargs.get('initvalue', self.minvalue)
        self.valuestr.set(str(self.initval))
        self.valuestr2.set(str(self.initval))
        self.label = ttk.Label(self,
                   text=kwargs.get('labeltext', 'No label'),
                   anchor='w',
                   width=kwargs.get('labelwidth', 20))
        self.spinbox = tk.Spinbox(self,
                   from_=self.minvalue,
                   to=self.maxvalue,
                   increment=1,
                   textvariable=self.valuestr)
        self.spinbox.bind('<Return>', self.updateSpinbox)
        self.spinbox.bind('<FocusOut>', self.updateSpinbox)
        self.spinbox.bind('<FocusIn>', self.storeSpinbox)
        self.spinbox.bind('<Button-1>', self.storeSpinbox)
        self.spinbox.bind('<Button-2>', self.storeSpinbox)

        self.label.pack(side=tk.TOP, fill=tk.X, expand=True, padx=5)
        self.spinbox.pack(side=tk.BOTTOM, fill=tk.X, expand=True, padx=2, pady=5)
        self.onChange = kwargs.get('onchange', self.doNothing)

    def storeSpinbox(self, event):
        tmpstr = self.valuestr.get()
        try:
            tmpval = int(tmpstr)
        except:
            tmpval = -1000
        if tmpval < self.minvalue:
            tmpval = self.minvalue
        elif tmpval > self.maxvalue:
            tmpval  = self.maxvalue
        self.valuestr2.set(str(tmpval))        

    def updateSpinbox(self, event):
        tmpstr = self.valuestr.get()
        try:
            tmpval = int(tmpstr)
        except:
            tmpstr = self.valuestr2.get()
            self.valuestr.set(tmpstr)
            return
        if tmpval < self.minvalue:
            tmpval = self.minvalue
        elif tmpval > self.maxvalue:
            tmpval  = self.maxvalue
        tmpstr = str(tmpval)
        self.valuestr.set(tmpstr)
        self.valuestr2.set(tmpstr)
        self.onChange()

    def doNothing(self):
        pass

    def getValue(self):
        tmpstr = self.valuestr.get()
        return(int(tmpstr))

    def setValue(self, value):
        self.valuestr.set(str(value))

我可能要迟到了,但我会把它留在这里,以防有人需要。在类似的情况下,我所做的就是在回调中做我需要的所有事情,以写入链接到spinbox的变量。比如:

import Tkinter as tk

root = tk.Tk()
my_var = tk.IntVar() # or whatever you need
spin = tk.Spinbox(root, from_=0, to=100, textvariable=my_var)
spin.pack()

def do_whatever_I_need(*args):
    # here I can access the Spinbox value using spin.get()
    # I can do whatever check I 

my_var.trace('w', whatever) #'w' for "after writing"

trace方法创建的回调调用给定函数,其中有两个参数:回调模式(在本例中是'w')和变量名(这是我从未使用过的某个内部tkinter标识符)。这就是do_wahtever_I_need的签名是*args的原因。在

相关问题 更多 >