Python Forum
[Tkinter] Validating and modifying a Spinbox
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
[Tkinter] Validating and modifying a Spinbox
#1
Below is a code snippet of a program that uses the SpinBox control. I want to the control to allow numbers between 1 and 100 as set in from_ and to.

I borrowed some code to validate the control when it focuses out. Problem is that I am not able to reset the control. Returning false does not seem to have any impact on the control.

Below I placed a short video in which I write letters into the control. I detect the error but am not able to reset it.

Thanks.


from tkinter import *


class GUI:
    """
    Handles the GUI interface
    """   
    
    def __init__(self):
        """_summary_
        """        
        # root window
        self.root = Tk()
        self.root.title("Password Generator")
        self.root.geometry("800x300")
        self.root.resizable(width=FALSE, height=FALSE)

        # registering validation command
        vldt_ifnum_cmd = (self.root.register(self.ValidateIfNum),'%P', '%W')
 
        # pass length information
        self.passTitle = Label(self.root, text = "Passowrd Length: ").grid(row=0, column=0, padx=5, pady=5, sticky=E)
        self.passLen = IntVar(value=20) 
        self.passLenSb = Spinbox(
            self.root, 
            from_=1, 
            to=100, 
            textvariable=self.passLen, 
            width=10, 
            justify=CENTER, 
            bd=3, 
            validate='focusout', 
            validatecommand=vldt_ifnum_cmd
            ).grid(row=0, column=1, padx=5, pady=5)

        self.Note = Label(self.root, text = "Press TAB to lose focus on Spinbox").grid(row=8, column=2)
      
        
    def ValidateIfNum(self, user_input, widget_name):
        # disallow anything but numbers in the input
        valid = user_input.isdigit()
        # now that we've ensured the input is only integers, range checking!
        if valid:
            # get minimum and maximum values of the widget to be validated
            minval = int(self.root.nametowidget(widget_name).config('from')[4])
            maxval = int(self.root.nametowidget(widget_name).config('to')[4])
            # check if it's in range
            if int(user_input) not in range (minval, maxval):
                valid = False
        if not valid:
            # reset the value to the original 
            self.passLen = IntVar(value=20) 
        return valid
Reply
#2
After going through the documentation Tcl8.6.13/Tk8.6.13 Documentation > Tk Commands > spinbox I found out that:

Quote:In general, the -textvariable and -validatecommand can be dangerous to mix. Any problems have been overcome so that using the -validatecommand will not interfere with the traditional behavior of the spinbox widget. Using the -textvariable for read-only purposes will never cause problems. The danger comes when you try set the -textvariable to something that the -validatecommand would not accept, which causes -validate to become none (the -invalidcommand will not be triggered). The same happens when an error occurs evaluating the -validatecommand.

Primarily, an error will occur when the -validatecommand or -invalidcommand encounters an error in its script while evaluating or -validatecommand does not return a valid Tcl boolean value. The -validate option will also set itself to none when you edit the spinbox widget from within either the -validatecommand or the -invalidcommand. Such editions will override the one that was being validated. If you wish to edit the value of the widget during validation and still have the -validate option set, you should include the command

I was overthinking the problem.

The code below worked:

  def ValidateIfNum(self, user_input, widget_name):
        """
        Args:
            user_input (IntVal): The value typed into the spinbox
            widget_name (): The widget name

        Returns:
            Boolean: Whether the value is valid
        """
        
        valid = user_input.isdigit()
        # now that we've ensured the input is only integers, range checking!
        if valid:
            # get minimum and maximum values of the widget to be validated
            minval = int(self.root.nametowidget(widget_name).config('from')[4])
            maxval = int(self.root.nametowidget(widget_name).config('to')[4])
            # check if it's in range
            if int(user_input) not in range (minval, maxval):
                valid = False
            
        return valid
Reply
#3
Hi,

Would like to share a link to the complete solution: PasswordGeneratorGUI.

The SpinBox control proved particularly challenging to fine tune it to where I wanted to get it.

The up and down arrows always worked as expected but I was never able to easily manage if someone typed in an invalid string in the box. I was expecting that if someone typed in, dd, I could capture an onChange or onFocusLost event out of the box and take corrective action.

I found that pressing Tab did not cause the SpinBox to loose focus (even though the cursor would jump).

The lines below seem to have done the trick.

        self.root.bind('<Return>', self.reset_focus)
        self.root.bind('<Tab>', self.reset_focus)
Still not sure whether it could have been achieved without the need to code them in (they are built into the tool).



Another observation is that the name of the spinbox I define in python is different from that in Tcl/tk (widget) and I can't figure out how to get from the python name to the internal equivalent. Can one get to this via
(self.root.register(self.ValidateIfNum),'%P', '[b]%W[/b]')
?

With one spinbox the solution is manageable.

Thanks for any comments
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  [PyQt] [Solved]Add a SpinBox to MsgBox or Carry Variable Over? Extra 6 1,831 Jun-05-2022, 09:32 PM
Last Post: Extra
  [Tkinter] how to celect com port from spinbox and make connect button 00alkskodi00 0 2,441 Apr-20-2020, 02:26 PM
Last Post: 00alkskodi00

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020