tkinter Checkbutton widget returning wrong boolean value - python

I have a simple GUI here that's suppose to return a boolean value depending on whether the check button is checked or not. I've set the boolean variable to False hence the empty check button. What I don't understand is that when I check the button, the function binded to that widget returns a False instead of True. Why is that?
Here's the code...
from tkinter import *
from tkinter import ttk
def getBool(event):
print(boolvar.get())
root = Tk()
boolvar = BooleanVar()
boolvar.set(False)
cb = Checkbutton(root, text = "Check Me", variable = boolvar)
cb.bind("<Button-1>", getBool)
cb.pack()
root.mainloop()
when checking the empty button the function outputs...
False
Shouldn't it return True now that the button is checked?

The boolean value is changed after the bind callback is made. To give you an example, check this out:
from tkinter import *
def getBool(event):
print(boolvar.get())
root = Tk()
boolvar = BooleanVar()
boolvar.set(False)
boolvar.trace('w', lambda *_: print("The value was changed"))
cb = Checkbutton(root, text = "Check Me", variable = boolvar)
cb.bind("<Button-1>", getBool)
cb.pack()
root.mainloop()
When you presses the Checkbutton, the first output is False then it's "The value was changed", which means that the value was changed after the getBool callback is completed.
What you should do is to use the command argument for the setting the callback, look:
from tkinter import *
def getBool(): # get rid of the event argument
print(boolvar.get())
root = Tk()
boolvar = BooleanVar()
boolvar.set(False)
boolvar.trace('w', lambda *_: print("The value was changed"))
cb = Checkbutton(root, text = "Check Me", variable = boolvar, command = getBool)
cb.pack()
root.mainloop()
The output is first "The value was changed" then True.
For my examples, I used boolvar.trace, it runs the lambda callback when the boolean value changes ('w')

Related

display not changing despite variables being changed on tkinter

I want to write a program where after a user enters text and clicks a button, the text becomes a label and the button text is changed. My code is:
# Imports
import os, sys
import tkinter
"""
Tkinter program 1
text box + button + label
"""
# Button Entry
def enter(inputtedinfo, randvar, EnterMessage):
randvar = inputtedinfo.get()
EnterMessage = "Submitted!"
# Main Function
def main():
something = tkinter.Tk()
something.title("My First Tkinter Window")
something.geometry("600x400")
randvar = ""
EnterMessage = "Enter"
inputtedinfo = tkinter.StringVar()
userLabel = tkinter.Label(something, text = randvar)
userEntry = tkinter.Entry(something, textvariable = inputtedinfo)
userButton = tkinter.Button(something, text = EnterMessage, command = enter(inputtedinfo, randvar, EnterMessage))
userEntry.grid(row=0,column=0)
userLabel.grid(row=0,column=1)
userButton.grid(row=0,column=2)
something.mainloop()
sys.exit(0)
if(__name__ == "__main__"):
main()
The user input works, but clicking the button does nothing despite the fact that it is supposed to change the variables for the button and label displays. Did I mess up somewhere?
The command argument takes the name of a function. If you write the complete call with arguments, it's not the name of the function but whatever is returned by this exact function call. So, your button will not work. It will have the command None.
In order to do what you want to do, you have to make the StringVar()s accessible to the function you are calling. So, you can both get the contents of the entry and change the values of the button and the label. To do this, best add the string variables and the widgets as attributes to the toplevel you already created (something). So, they stay available to all functions and you can get and change information:
# Button Entry
def enter():
something.randvar.set(something.inputtedinfo.get())
something.userButton["text"] = "Submitted!"
# Main Function
def main():
global something
something = tkinter.Tk()
something.title("My First Tkinter Window")
something.geometry("600x400")
something.randvar = tkinter.StringVar()
something.randvar.set("")
EnterMessage = "Enter"
something.inputtedinfo = tkinter.StringVar()
userLabel = tkinter.Label(something, textvariable = something.randvar)
something.userEntry = tkinter.Entry(something, textvariable = something.inputtedinfo)
something.userButton = tkinter.Button(something, text = EnterMessage, command = enter)
something.userEntry.grid(row=0,column=0)
userLabel.grid(row=0,column=1)
something.userButton.grid(row=0,column=2)
something.mainloop()
if(__name__ == "__main__"):
main()
There are few issues in your code:
assign string to textvariable, should use StringVar instead
command=enter(...) will execute enter(...) immediately and then assign None to command option, should use lambda instead
updating strings inside enter() does not automatically update the label and the button, should use .set() on the StirngVar instead
Below is modified code:
def enter(inputtedinfo, randvar, EnterMessage):
# used .set() to update StringVar
randvar.set(inputtedinfo.get())
EnterMessage.set("Submitted!")
def main():
something = tkinter.Tk()
something.title("My First Tkinter Window")
something.geometry("600x400")
randvar = tkinter.StringVar() # changed to StringVar()
EnterMessage = tkinter.StringVar(value="Enter") # changed to StringVar()
inputtedinfo = tkinter.StringVar()
userLabel = tkinter.Label(something, textvariable=randvar) # used textvariable instead of text option
userEntry = tkinter.Entry(something, textvariable=inputtedinfo)
userButton = tkinter.Button(something, textvariable=EnterMessage, command=lambda: enter(inputtedinfo, randvar, EnterMessage))
userEntry.grid(row=0,column=0)
userLabel.grid(row=0,column=1)
userButton.grid(row=0,column=2)
something.mainloop()

Determining what radiobutton was selected tkinter

I am trying to figure out how to use tkinter radio-buttons properly.
I have used this question as a guideline: Radio button values in Python Tkinter
For some reason I can't figure out how to return a variable that is indicative of what the user selected.
Code:
def quit_loop():
global selection
selection = option.get()
root.quit()
return selection
def createWindow():
root = Tk()
root.geometry=('400x400')
option = StringVar()
option.set('none')
R1 = Radiobutton(root, text='Compile', value = 'Compile', var=option)
R2 = Radiobutton(root, text='Create', value = 'Create', var=option)
button = Button(root, text='ok', command=quit_loop)
R1.pack()
R2.pack()
button.pack()
root.mainloop()
when I call createWindow() I would expect the radio-button box to pop up, and after making my selection and pressing 'ok' I expected it to return me a variable selection which relates to the selected button. Any advice? Tkinter stuff is particularly challenging to me because it seems so temperamental.
You need to make option global if you want to access outside of createWindow
Here's an example of your code that will print out the value of the selected radiobutton and then quit when you click the button. I simply had to declare root and options as global:
from tkinter import *
def quit_loop():
global selection
selection = option.get()
root.quit()
return selection
def createWindow():
global option, root
root = Tk()
root.geometry=('400x400')
option = StringVar()
option.set('none')
R1 = Radiobutton(root, text='Compile', value = 'Compile', var=option)
R2 = Radiobutton(root, text='Create', value = 'Create', var=option)
button = Button(root, text='ok', command=quit_loop)
R1.pack()
R2.pack()
button.pack()
root.mainloop()
createWindow()
As far as I know one needs to do two things to communicate with tkinter
widgets: pass a variable, and pass a command. When user interacts with
widgets, tkinter will do two things: update value of variable and call the
function passed in as command. It is up to us to access the value of the
variable inside the command function.
import tkinter as tk
from tkinter import StringVar, Radiobutton
def handle_radio():
print(option.get())
root = tk.Tk()
option = StringVar()
option.set('none')
R1 = Radiobutton(root, text='Compile', value = 'Compile', var=option, command=handle_radio)
R2 = Radiobutton(root, text='Create', value = 'Create', var=option, comman=handle_radio)
R1.pack()
R2.pack()
root.mainloop()
The code prints 'Create' and 'Compile' when user selects the appropriate
radio option.
Hope this helps.
Regards,
Prasanth

Python 3 entry digits only with error print in another label

I am new to codding in general. I have found a code for a function which restricts users of entering anything but digits in entry field but I can't assign the function to entry object.
I have tree problems.
I can't assign function to entry.
I want error print message to show in label "A".
Pressing "Enter" multiple times execute "def doit(FFF)" multiple times on top of each other, so I want to restrict pressing it more than one time.
I have been trying for the past 3 days but I keep failing.
from tkinter import *
def doit(FFF):
...
def val():
API = IN.get()
while True:
try:
API = int(input())
except ValueError:
print('Not an integer')
continue
else:
return API
break
root = Tk()
root.geometry("300x200")
IN = Entry(root)
IN.bind("<Return>", val, lambda _:doit(FFF))
IN.pack(side=LEFT, fill="both", expand=True)
A = Label(root, fg="red")
A.pack(side=LEFT)
B = Button(root, text="START", fg="black", command=lambda:doit(FFF))
B.pack(side=RIGHT)
root.mainloop()
Example
is_integer() checks only if text is integer number and returns True/False so it can be used in many places.
<KeyRelease> executes validate() after every keypress (or rather key release) and validate() uses is_integer() to check text in Entry (event.widget gives access to widget to which was bind() event) and changes text in Label.
Button executes function run_it() which uses is_integer() to check if Entry has correct integer (to make sure) or you have to check if Entry is not empty. If Entry has no correct integer then it changes text in Label and ends function.
import tkinter as tk
# --- functions ---
def run_it():
# you can check it again to make sure
text = e.get()
if not is_integer(text):
l['text'] = "You can't run it"
return
# do what you want
l['text'] = 'Running ...'
def validate(event):
#text = e.get()
text = event.widget.get()
if is_integer(text):
l['text'] = ''
else:
l['text'] = 'Not an integer'
def is_integer(text):
try:
value = int(text)
return True
except ValueError:
return False
# --- main ---
root = tk.Tk()
e = tk.Entry(root)
e.pack()
#e.bind("<Return>", validate)
e.bind("<KeyRelease>", validate)
l = tk.Label(root)
l.pack()
b = tk.Button(root, text="START", command=run_it)
b.pack()
root.mainloop()

Updating label keeps previous text

In the program I made, the user presses enter and the text typed is then shown as a label in the program. So the label keeps getting updated and then written on the next line. The problem is that in the textbox the previous line the user typed stays there, which means u have to keep manually deleting the string in the textbox to write a new line. How can I make it so that you start out with a cleared textbox? Also, the enter button works but it seems that when i click on the "Return" button it gives me an error:
TypeError: evaluate() missing 1 required positional argument: 'event'
Here's the code:
from tkinter import *
window = Tk()
window.geometry("200x300")
def evaluate(event):
thetext = StringVar()
labeloutput = Label(app, textvariable = thetext)
n = e.get()
thetext.set(n)
labeloutput.grid()
app = Frame(window)
app.pack()
e = Entry(window)
e.pack()
b= Button(window, text="Return", command=evaluate)
b.pack()
window.bind("<Return>", evaluate)
mainloop()
Since you bind evaluate as a callback and you use it as a button command, when you use it in the button you have to use a lambda and pass None to the event. event argument is needed because of the binding, but there is no event when you call it from button click, so just pass None to get rid of the error. You can delete by doing entry.delete(0, 'end').
from tkinter import *
window = Tk()
window.geometry("200x300")
def evaluate(event):
thetext = StringVar()
labeloutput = Label(app, textvariable = thetext)
n = e.get()
thetext.set(n)
labeloutput.grid()
e.delete(0, 'end') # Here we remove text inside the entry
app = Frame(window)
app.pack()
e = Entry(window)
e.pack()
b = Button(window, text="Return", command=lambda: evaluate(None)) # Here we have a lambda to pass None to the event
b.pack()
window.bind("<Return>", evaluate)
mainloop()
Of course, if you want to prevent the lambda from being used, you would have to create a function to handle the key binding, and a separate one for the button click.

Tkinter: Link an Entry Widget to a Button to a Function

I am new to Tkinter and not to sure how to proceed. I am trying to link a function that I define to a entry widget that is activated by a button. but I can't figure out how to get the three to communicate to each other. I would like it to print as well as return to the script so that I can be used in another function. This is what I have so far:
import Tkinter as tk
def TestMath(x):
calculate = x + 4
print calculate
return calculate
root = tk.Tk()
entry = tk.Entry(root)
value = entry.get()
number = int(value)
button = tk.Button(root, text="Calculate")
calculation = TestMath(number)
root.mainloop()
Button calls function assigned to command= (it has to be "function name" without () and arguments - or lambda function)
TestMath assigns calculation to global variable result and other functions can have access to that value.
import Tkinter as tk
def TestMath():
global result # to return calculation
result = int(entry.get())
result += 4
print result
result = 0
root = tk.Tk()
entry = tk.Entry(root)
entry.pack()
button = tk.Button(root, text="Calculate", command=TestMath)
button.pack()
root.mainloop()
Function called by button don't have to return value because there is no object which could receive that value.

Categories