2017-03-10 3 views
0

Ich habe ein Problem bei der Validierung der Spinbox-Eingabe. Ich habe einen Workaround unter diesem scheint zu arbeiten; es ist jedoch peinlich. Angenommen, dies ist kein Fehler, gibt es einen richtigen Weg, dies zu tun? Ich bin mit Anaconda Python 3.6 (tk 8.6) unter Windows 10.Python Tkinter Spinbox Validierung fehlgeschlagen

Das Problem ist, dass validate-None gesetzt, wenn Sie False von der Validierungsfunktion, wenn der Wert in der Spinnbox Eintrag Rückkehr ist zwischen to und from. Dies geschieht nur, wenn Sie auf die Nach-oben- oder Nach-unten-Schaltfläche klicken und nicht, wenn Sie den Text direkt bearbeiten.

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() 

Um zu reproduzieren, markieren Sie 0 und geben Sie 149999 ein. Klicken Sie dann ein paar Mal nach oben. Beachten Sie, dass der Validierungsbefehl nicht mehr aufgerufen wird. Die Ausgabe ist:

01 
014 
0149 
01499 
014999 
0149999 
15000 
15001 

nun nach dem docs, mit textVariable und validateCommand zusammen ist gefährlich; tatsächlich habe ich crashed Python/Tkinter in mehr als einer Hinsicht. In diesem Fall ist es jedoch egal, ob Sie textVariable verwenden oder nicht; Das Problem ist das gleiche.

Eine mögliche Lösung könnte darin bestehen, die Optionen to und from in der Validierungsfunktion zu bearbeiten. Auch wenn das funktioniert, ist es für mich etwas problematisch, weil ich Spinbox-Werte mit einem eingebetteten Matplotlib-Plot synchronisiere. Ich müsste to und from berechnen und Einheiten für jeden Matplotlib Artist und Spinbox konvertieren.

Da Sie die textVariable in der Validierungsfunktion nicht bearbeiten können, habe ich Folgendes herausgefunden. Vielleicht kann jemand das verbessern.

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

Diese Aussage ist falsch: _ „Das Problem ist, dass validate auf None gesetzt ist, wenn Sie falsch von der Validierungsfunktion zurückkehren ...“ _ - die Funktion Validierung wird nicht auf 'None' gesetzt werden, nur weil Sie' False' zurückgeben. Die einzigen gültigen Rückgabewerte sind "True" und "False". Lesen und befolgen Sie den folgenden Hinweis: [Erstellen eines minimalen, vollständigen und überprüfbaren Beispiels] (http://www.stackoverflow.com/help/mcve) –

+0

Es ist ein erheblicher Zeit- und Arbeitsaufwand, ein Problem zu reproduzieren. Es ist nicht so, dass ich zu faul bin, das zu tun. Ich dachte, dies könnte das beabsichtigte Verhalten der Spinbox sein. Ich wollte nicht die Zeit investieren, um ein nicht existierendes Problem zu jagen. Aber jetzt, wo ich informiert bin, ist es hier. – Todd

Antwort

1

Nachdem ich mit dem Validierungsmechanismus in Spinbox gekämpft hatte, gab ich es auf. Vielleicht funktioniert es so, wie es beabsichtigt war, aber ich finde es kontraintuitiv, dass es nur einmal aufgerufen wird. Meine Anwendung verwendet spinbox, um ein Matplotlib-Diagramm zu aktualisieren, und ich brauche die Daten eine ganze Zahl in einem angegebenen Bereich. Ich brauchte den Code, um nicht-ganzzahlige Einträge sowie ganze Zahlen außerhalb des Bereichs abzufangen. Die Lösung, die ich gefunden habe, war die Verwendung von Tastenbindungen anstelle des Validierungsmechanismus, um das gewünschte Ergebnis zu erzielen. Hier ist der relevante Teil des Codes:

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

Es gibt wahrscheinlich zahlreiche Möglichkeiten. Die Frage ist, ob es sich wie beabsichtigt verhält oder wir nur etwas falsch machen. – Todd