2017-03-10 52 views
0

我遇到了驗證spinbox輸入的問題。我有一個解決方法似乎工作;然而,這很尷尬。假設這不是一個錯誤,是否有正確的方法來做到這一點?我在Windows 10Python Tkinter Spinbox驗證失敗

使用Python的蟒蛇3.6(TK 8.6)問題是validate設置爲None如果從驗證函數返回False當紡紗器項的值是tofrom之間。這隻發生在單擊向上或向下按鈕時,而不是直接編輯文本時發生。

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.然後點擊幾次。請注意驗證命令停止被調用。輸出是:

01 
014 
0149 
01499 
014999 
0149999 
15000 
15001 

現在,根據docs,使用textVariablevalidateCommand一起是危險的;事實上,我有crashed Python/Tkinter不止一種。但是,在這種情況下,是否使用textVariable並不重要;問題是一樣的。

一個可能的解決方案可能是編輯驗證功能中的tofrom選項。即使這樣做,對我來說也有點問題,因爲我正在將spinbox值同步到嵌入的Matplotlib圖中。我需要計算tofrom併爲每個Matplotlib Artist和spinbox轉換單位。

由於您不能在驗證功能中編輯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 
+0

這種說法是錯誤的:_「問題是,驗證是如果返回從驗證函數假設爲無...」 _ - 驗證功能將不僅僅因爲你返回'False'而被設置爲'None'。實際上,唯一有效的返回值是'True'和'False'。請閱讀並遵循以下建議:[如何創建最小,完整和可驗證示例](http://www.stackoverflow.com/help/mcve) –

+0

重現問題需要相當多的時間和精力。並不是我懶得去做;我認爲這可能是spinbox的預期行爲。我不想投入時間尋找一個不存在的問題。但現在我知道了,在這裏。 – Todd

回答

1

在與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)) 
+0

可能有很多方法。問題在於它是否按照預期行事,或者我們只是在做錯事。 – Todd