Basically I have a bunch of checkbuttons, with some on and some off by default. I'm having an issue where if I put the checkbuttons inside the function they aren't on by default like they should be.
Here's the working code:
from tkinter import *
root = Tk()
integer = IntVar(value=1)
Checkbutton(root, text="Should be on by default", variable=integer).grid()
root.mainloop()
Here's the not working code:
from tkinter import *
root = Tk()
def main():
integer = IntVar(value=1)
Checkbutton(root, text="Should be on by default", variable=integer).grid()
main()
root.mainloop()
Can anyone explain to me why this is?
By the time you see the window, integer no longer exists and the checkbox shows as unchecked for lack of a variable to store its state.
Compare:
from tkinter import *
root = Tk()
def main():
global integer
integer = IntVar(value=1)
Checkbutton(root, text="Should be on by default", variable=integer).grid()
main()
root.mainloop()
The global integer tells Python that this integer should be defined at the 'global' level and thus it stays around after the function.
By the way, it's bad practice to name a variable after its type - try picking a name that represents what its value means, instead of describing its type.
You shared some additional code with a similar problem (only repeating the elements that matter):
from tkinter import *
def change_job_skills(name):
top_window = Toplevel(root)
# ..
skill_dictionary = {}
# ..
row_ = 2
column_ = 0
# ..
job_focuses_dictionary = {}
for key in sorted(job_focuses_dictionary.keys()):
Checkbutton(top_window, text=key.strip(""),
variable=job_focuses_dictionary[key]).grid(row=row_, column=column_, sticky=W)
# ..
# no definition was provided of actually_change_job_skills, but it's not important here
Button(top_window, text='Submit',
command=lambda: [actually_change_job_skills(skill_dictionary, name),
top_window.destroy()]).grid(row=0, column=0, sticky=W)
# no reference is made to `job_focuses_dictionary` in a way that survives the function
root = Tk()
change_job_skills("Community Engagement")
root.mainloop()
Although both skill_dictionary and job_focuses_dictionary are used in the code of change_job_skills, skill_dictionary is used in the definition of a lambda function, which is then passed as the command argument for Button. Since the button will need to call that function later, a reference to the lambda is saved inside it and since the function body of the lambda references skill_dictionary, the dictionary survives the function returning.
However, job_focuses_dictionary is only referenced as job_focuses_dictionary[key], retrieving a value from it - the dictionary itself isn't passed to anything that maintains a reference to it outside the function, so when the function returns, the dictionary is garbage-collected.
Same problem, but a bit harder to spot. (#acw1668 pointed it out in the comments as well)
Note that I also renamed your parameter Name to name, you should reserve capitalised names for types and lowercase names for variables, in line with Python standard naming, which helps future you and others more quickly read and understand your code. Nothing to do with the problem though.
Related
Tkinter dynamically made variables are not working properly in checkbutton of menu. They are displaying the wrong image as they were supposed to.
Here's my code:
def checkbutton(self,index,var=None):
self.popup_menu.add_checkbutton(label=self.btns[index]['text'], command = lambda : self.menu(index) , variable=IntVar().set(1))
I'm using direct method variable=IntVar().set(1). I aslo tried making variable like :
currentVar=IntVar()
currentVar.set(1)
But I encountered the same problem.
First variable=IntVar().set(1) will assign None, result of set(1) to variable option. Second dynamically created variable will be garbage collected after the function completes.
You need to create an instance variable:
def checkbutton(self,index,var=None):
var1 = IntVar(value=1)
self.popup_menu.add_checkbutton(label=self.btns[index]['text'], command=lambda: self.menu(index), variable=var1)
# self.varlist should be already created in __init__()
self.varlist.append(var1)
I wrote some code using tkinter and some nested functions (see code below) and get the warning in pycharm "Global variable 'value' is undefined at the module level". The program works as intended (the window title is renamed to 'text 0') but i still get the warning... What do i have to change to get rid of this warning? (the program is more complex, i need to start it like it does, with window.after and i also need all the functions)
from tkinter import *
def function_1():
global value
window.title("text " + value)
def function_2():
def function_3():
global value
value = ent.get()
if value == '0':
function_1()
ent = Entry()
ent.pack()
button = Button(text="ok", command=function_3)
button.pack()
window = Tk()
window.after(0, function_2)
window.mainloop()
The global keyword is used inside a function to control assignment to a variable. Normally within a function value = "bar" creates a "value" in the local function namespace. global value in that same function tells python to use the global namespace instead.
In your current code, the module level value variable will not come into existence until function_3 is called. Anything that tries to use value before function_3 is called will get a NameError. function_1 is a case in point; if it is called before function_3, you have an error. It may be that your code can never call function_1 before function_3 but that is difficult for a linter or future maintiainers of the code to know. That's why you get the warning.
The solution is to set a default for value that is sensible for the program. I don't know what works for you, but here is an example that throws up a message. BTW, global value is not needed in function_1. Its job is to control assignment, you can read global variables without any additional annotation.
from tkinter import *
value = None
def function_1():
if value is not None:
window.title("text " + value)
else:
import tkinter.messagebox
tkinter.messagebox.showerror("Error", "Do the thing before the thing")
def function_2():
def function_3():
global value
value = ent.get()
if value == '0':
function_1()
ent = Entry()
ent.pack()
button = Button(text="ok", command=function_3)
button.pack()
window = Tk()
window.after(0, function_2)
window.mainloop()
I have made a variable called 'localtime2' within my def in the code and then have a label which says 'textvariable=localtime2.' the problem is that it does not display the information about the variable.
localtime2 = time.asctime(time.localtime(time.time()))
tk.Label(roots, font=('arial', 16, 'bold'), textvariable=localtime2, bd=16, anchor="w").grid(row=2, column=0)
This is all I have in the code about this variable and it is not coming up with any error in the terminal. It just doesnt show at all.
Edit: The solution to the original post was using text=localtime2.get() instead of textvariable=localtime2 in the label widget (for some strange reason). However, my original answer is still correct as tkinter variables should be used and so I will keep it up.
You must use tkinter variables in tkinter widgets and not normal python variables. Tkinter variables are slightly different to normal variables and must first be defined as a specific data type. For example, a variable which contains a string must be first created like so:
string_variable = tk.StringVar()
likewise a boolean would be created like so:
boolean_variable = tk.BooleanVar()
Read here for more information on tkinter variables.
These variables are in fact classes so in order to set them use must call the .set() method. Therefore to set a tkinter String variable to "a string" you would use the following code:
string_variable = tk.StringVar() # Create the variable
string_variable.set("a string") # Set the value of the variable
Thus to fix your code you need to make localtime2 a tkinter variable and set it to the time using the .set() method.
Example:
localtime2 = tk.StringVar() # Create the localtime2 string variable
localtime2.set(time.asctime(time.localtime(time.time()))) # Set the variable
tk.Label(roots, font=('arial', 16, 'bold'), textvariable=localtime2, bd=16, anchor="w").grid(row=2, column=0)
Whenever there is a change in a tkinter variable, the update is automatically reflected everywhere. Because of this property you cannot use a normal python variable here.
Try using StringVar() and then setting the variable content using set()
my code looks like this
root = Tk()
a = IntVar(root)
later in my code i cannot access 'a' but i can access 'root'
I tried
root.getvar('a')
root.children
root.client()
root.slaves()
root.getint(0)
and none of them is or contains 'a'
and I need value from 'a'
how can I get it
You cannot get a tkinter variable given only the root window, or the master of a widget. At least, not without a lot of work. Tkinter simply doesn't keep track of these variables for you.
To gain access to the variable, you must do the same with it as you do with any other python variable or object: you need to make it global, or a class or instance variable, or you need to pass it to the function that needs access.
How can I get a returned object from a function that is executed as a Tkinter callback?
import Tkinter as Tk
from functools import partial
def square(x):
return x*x
root = Tk.Tk()
var = Tk.IntVar(root, value=0) #the variable the gets passed to the class call
menu = Tk.OptionMenu(root, var, *[0,1,2,3,4,5]) #a drop-down list to choose a value for the variable
menu.pack()
button = Tk.Button(root, text='click', command = partial(square,var.get())) #a button that calls the class
button.pack()
root.mainloop()
Obviously this is a simplified example. In reality the function called by the button will return objects, which I wish to append to a list of objects that will be held in the main Python namespace for further operations.
Anyway, here the user is able to choose an argument for the function using a GUI, and press a button that will execute the function. The return value of the function, however, seems doomed to be lost to the aether, since the callback won't accept returns. Can this be overcome without the use of an ugly global in the definition of square(x)?
The notion of "returning" values from callbacks doesn't make sense in the context of an event driven program. Callbacks are called as the result of an event, so there's nowhere to return a value to.
As a general rule of thumb, your callbacks should always call a function, rather than using functools.partial or lambda. Those two are fine when needed, but if you're using an object-oriented style of coding they are often unnecessary, and lead to code that is more difficult to maintain than it needs to be.
For example:
def compute():
value = var.get()
result = square(value)
list_of_results.append(result)
button = Tk.Button(root, text='click', command = compute)
...
This becomes much easier, and you can avoid global variables, if you create your application as a class:
class App(...):
...
def compute():
...
result = self.square(self.var.get())
self.results.append(result)
Sorry for being 6 years late, but recently I figured out a good way to do this without making your code messy and hard to maintain.
This is pretty much what DaveTheScientist has said, but I just want to expand on it a little.
Usually, in Tkinter, if you want to have a button call a function, you do the following:
exampleButton = Button(root, text = 'Example', command = someFunc)
This will simply call someFunc whenever the button is pressed. If this function, however, takes arguments, you need to use lambdas and do something like this:
exampleButton = Button(root, text = 'Example', command = lambda: someFunc(arg1, arg2))
The above line of code will run someFunc and use the variables arg1 and arg2 as arguments for that function. Now, what you could do in a program where, a lot of the times, you would need the functions run by buttons to return values, is create a new function which is called by every button.
This function takes the function you want your button to run as a first argument, and that function's arguments afterwards.
def buttonpress(function, *args):
value = function(*args)
Then when you create the button, you do:
exampleButton = Button(root, text = 'Example', command = lambda: buttonpress( someFunc, arg1, arg2 ))
This will run the given function (in this case, someFunc) and store the returned value in the value variable. It also has the advantage that you can use as many arguments as you want for the function your button runs.
Just create an actual function that is called by your button, instead of putting it all inline like that.
button=Tk.Button(parent, text='click', command=someFxn)
def someFxn(): your code
Then in your function just call the var.get(), do your calculation, and then do something with the value.