Tkinter Optionmenu callback not working - python

For some reason I can't get this optionmenu so call the callback function. Is there some special treatment those widgets require? (The function itself works and I can call it from i.e. a button.)
self.shapemenu=Tkinter.OptionMenu(self.frame,self.shape,"rectangle", "circular", command=self.setshape)
self.shape is a Tkinter.StringVar and obviously setshape is the callback function.
What am I doing wrong here?

The optionmenu is designed to set a value, not perform an action. You can't assign a command to it, and if you do, you will break its default behavior of setting the value -- it uses the command option internally to manage its values .
If you want something to happen when the value changes, add a trace on the StringVar.

Related

What's the importance of lambda functions in tkinter?

Can anyone explain to me the importance of the lambda function when creating interface with Tkinter?
I am building a super simple interface to get familiar with Tkinter and I wanted to make so that when I press the "Return" key on keyboard, it would have the same effect as dragging the mouse to the "Submit" button on the screen.
I had some problems with it because nothing seemed to work. That's what I was doing:
self.master.bind("<Return>", self.concluir_return)
Where self.concluir_return is the function responsible for making what I want after pressing the "Submit" button. But it was giving me a TypeError:
Exception in Tkinter callback
Traceback (most recent call last):
File "/usr/lib/python3.6/tkinter/__init__.py", line 1705, in __call__
return self.func(*args)
TypeError: concluir_return() takes 1 positional argument but 2 were given
Which I don't understand at all, since only one argument was being given (self)
But all was solved when I looked into web and modified the line of code with the lambda function.
self.master.bind("<Return>", lambda event: self.concluir_return())
It works perfectly, but I don't quite understand what is happening. I would appreciate if someone could explain.
Also, I hope this is not too generic of a question.
Can anyone explain to me the importance of the lambda function when creating interface with Tkinter?
Arguably, they aren't important at all. They are just a tool, one of several that can be used when binding widgets to functions.
The problem with the binding in your question is due to the fact that when you use bind to bind an event to a function, tkinter will automatically pass an event object to that function you must define a function that accepts that object.
This is where lambda comes in. The command needs to be a callable. One form of a callable is simply a reference to a function such as the one you're using (eg: command=self.concluir_return). If you don't want to modify your function to accept the parameter you can use lambda to create an anonymous function -- a callable without a name.
So, for your specific case, you can define a lambda that accepts the argument, and then the lambda can call your function without the argument.
But all was solved when I looked into web and modified the line of code with the lambda function.
self.master.bind("<Return>", lambda event: self.concluir_return())
This works because the code is effectively the same as if you did this:
def i_dont_care_what_the_name_is(event):
self.concluir_return()
self.master.bind("<Return>", i_dont_care_what_the_name_is)
As you can see, lamda isn't required, it's just a convenient tool that lets you create a simple function on the fly that calls another function.
The bind method takes two arguments, sequence and handler, and will call f(event) when the specified event occurs.
In your case, concluir_return wasn't expecting any argument other than self, so your code raised an error when it was called with event.
The lambda function you used is the equivalent of:
def f(event):
return concluir_handler()
so it bypasses the problem by just ignoring the event argument.
Another way of doing this would be to add an argument to concluir_return.

Function executes correctly as a tkinter button command but not in the script

This is my first question on StackOverflow. So far lurking was enough to solve all my problems.
I'm a python newbie and I don't fully understand the meaning behind 'self' yet.
I defined a function (not a method. It's not inside a class) as
def pcal_thresh(self):
p_th = p_thresh.get()
print('p_th')
I am trying to use it in 2 separate conditions. First as a command for Tkinter
p_thresh = tk.Scale(calibration, from_=255, to=1, length=int(y_height*1.2), command=pcal_thresh)
Second, inside another function
def confirm():
if not top_distance == 0:
pcal_thresh()
In this exact configuration the function "pcal_thresh()" executes correctly as a Tkinter command, but not inside another function. If I remove 'self' from the declaration, it's the opposite. Works fine when used inside a function, but not as a Tkinter command. What can be the issue here?
self does not have a default value, so even if you don't use it, you still need to provide a value when you call pcal_thresh. As a callback, it receives the new scale value when called.
Either provide a dummy argument
def confirm():
if not top_distance == 0:
pcal_thresh(None)
or provide a default value:
def pcal_thresh(self=None):
p_th = p_thresh.get()
print('p_th')

python checkbutton appearance does not update after setting to 1

I have a class with a member self.checkbutton (using the TkInter CheckButton), created in the init function with:
self.checkbutton = Checkbutton(self.frame, variable=self.value, command=self.get_entry_and_execute, onvalue="1", offvalue="0")
Now, instead of clicking on the checkbutton in my frame, I want to set it in my code. So, from somewhere in my code, I call setvalue("1") calling this function:
def setvalue(self, value):
if (value == "1"):
print "SELECTING CHECKBUTTON"
self.checkbutton.select()
# self.checkbutton.set(value=1)
Now, when I do this, actually, I can see that the associated "self.get_entry_and_execute" function is called and it even changes some background color. But, the checkbutton remains unchecked (i.e. an empty field without the "V" symbol).
Weirly, when I add the command
self.checkbutton.set(value=1)
, the code complains: AttributeError: Checkbutton instance has no attribute 'set'
but now the checkbutton does get checked!
I am assuming that because of this error, python puts the checkbutton in the correct state (=1) even though the "set" function does not exist. My question is: how can I correctly make python put the "V" inside the checkbutton box? (I.e, something like a "redraw" function).
According to your code, self.value is an instance of a variable class, so all what you need to do is to replace self.checkbutton.set(value=1) by self.value.set(value=1)

Destroying widget in tkinter

I have managed to get a widget to appear by calling a function, then make it disappear by using the destroy method.
Unfortunately, the only way I managed to do that is by making the object global, which I understand is a bad way of doing things. I have tried several times to destroy the object without using global, but none of them worked.
Here's the relevant code:
def hayHijos():
print("ii")
z=var9.get()
if z==1:
var10 = StringVar()
global numHijosMat
numHijosMat=OptionMenu(app,var10,1,2,3,4,5,6,7,8,9,10)
numHijosMat.grid(column=2)
elif z==0:
print("aa")
numHijosMat.destroy()
var9 = IntVar()
hijosEntrePartes = Checkbutton(app, text="Hijos entre las partes", variable=var9, command=hayHijos)
hijosEntrePartes.var=var9
hijosEntrePartes.grid()
Two general possibilities.
Either you create a class context to keep track of elements such as widgets using class variables (self.widget for example). Therefor have a look at the class documentation
You return and pass the widget to / from your function.
This is not very suitable for callbacks but a general approach
def function(var, widget=None):
""" Widget is an optional argument. If not passed, it is initialized with None """
if var.get()==1:
# some code to create your widget
# make sure to use 'widget = ...' to create it
# to have the reference for your return
# call at the end of the function
else:
# some code to destroy your widget
# e.g. widget.destroy()
return widget
Using this code makes it easy for you to use the widget without making it global. Another variable is used in a global behaviour in your code. "var9". You should also pass this one on.
You would need to adapt your callback using maybe a lambda to pass both.
More recommended would be the class approach over here as lambdas often lack in scope of readability of code and reusability of code. (Could call it also "bad habit" but IMHO this is highly influenced by use case)
If you want to reuse the widget and only want to make it appear / disappear as the use of a Checkbutton suggests, I would rather recommend grid_remove method instead of widget.destroy method

tkinter text entry validation

I'm trying to validate the entry of text using Python/tkInter
def validate_text():
return False
text = Entry(textframe, validate="focusout", validatecommand=validate_text)
where validate_text is the function - I've tried always returning False and always returning True and there's no difference in the outcome..? Is there a set of arguments in the function that I need to include?
Edit - changed from NONE to focusout...still not working
You should register your validate function.
def validate_text():
return False
textframe.register(validate_text)
text = Entry(textframe, validate="focusout", validatecommand=validate_text)
I think the only thing your missing is an invalidcommand (or invcmd). What are you expecting validatecommand (or vcmd) to do if it returns false? According to the Tk Manual (see below), if vcmd returns False and validate is not set to none then invcmd will be called. The typical command for invcmd is Tkinter.bell, which makes a ding sound. Also note that vcmd and invcmd are very touchy, and will turn validate to 'none' if they encounter an exception, if the widget is changed in anyway inside the vcmd or invcmd functions or if vcmd does not return a valid Tcl boolean. In particular textvariable is notorious for causing issues, and a section in Entry called valdation specifically deals with that.
Here are the relevant portions from Tk Command Entry (same for Spinbox). See below for more references.
Command-Line Name: -validatecommand or -vcmd
Database Name: validateCommand
Database Class: ValidateCommand
Specifies a script to eval when you want to validate the input into the entry widget. Setting it to {} disables this feature (the default). This command must return a valid Tcl boolean value. If it returns 0 (or the valid Tcl boolean equivalent) then it means you reject the new edition and it will not occur and the invalidCommand will be evaluated if it is set. If it returns 1, then the new edition occurs. See Validation below for more information.
Command-Line Name: -invalidcommand or -invcmd
Database Name: invalidCommand
Database Class: InvalidCommand
Specifies a script to eval when validateCommand returns 0. Setting it to {} disables this feature (the default). The best use of this option is to set it to bell. See Validation below for more information.
Have a look at this SO answer, the Tk commands and epydoc-Tkinter for more references.
There are so many duplicates to this question.
Python tkInter Entry fun
Restricting the value in Tkinter Entry widget
focusout means validatecommand will be only be invoked when you take the focus out from the entrywidget.
You could try 'key' for validating while typing.
Tcl manual:
Validation works by setting the validateCommand option to a script which will be evaluated according to the validate option as follows:
none
Default. This means no validation will occur.
focus
validateCommand will be called when the entry receives or loses focus.
focusin
validateCommand will be called when the entry receives focus.
focusout
validateCommand will be called when the entry loses focus.
key
validateCommand will be called when the entry is edited.
all
validateCommand will be called for all above conditions.
"Note that this option [validatecommand] is only used if the validate option is not NONE"
From http://effbot.org/tkinterbook/entry.htm#entry.Entry.config-method

Categories

Resources