This is the most commonly asked and answered question in Python. You weren't able to find the answer because you don't know how to ask the question right. Don't feel bad. If you knew how to ask the question you would already know the answer. Yeah, it is one of those questions.
The question is easier to answer with a simpler example:
choice = 0
def set_choice_one():
choice = 1
def set_choice_n(n):
choice = n
def print_choice():
print(choice)
print_choice()
set_choice_one()
print_choice()
set_choice_n(2)
print_choice()
Output:
0
0
0
The answer to your question about why is "choice" always zero is; well, it depends on what choice you are talking about.
There are three variables named "choice" in the example above. Think of these variables as module.choice, set_choice_one.choice and set_choice_n.choice. At the top of the program, when Python executes
choice = 0
, the variable module.choice is created. When python sees a name and an assignment (=), it starts looking for a variable. Because the assignment is occurring outside a function or class, Python starts looking for the variable "choice" in something called the "module namespace". All modules (files) get their own namespace where their variables can hide. If Python didn't hide module variables in the module namespace it would be impossible to write programs. Every time you needed a variable or function you would have to search your entire Python distribution to find a new, unique name. The module namespace hides all those other variable names used in other modules so you can use almost any variable name you want.
Modules aren't the only thing to have a namespace. Functions have a namespace too. When set_choice_one() executes
choice = 1
, Python does the same thing it did for
choice = 0
, but this time it does it in the function namespace. Python looks around for "choice" in the set_choice_one() namespace, doesn't find a variable with that name and creates a variable named "choice".
You may be wondering about print_choice(). print_choice() uses a variable named "choice", and since it prints "0" it must be using the module.choice. How does it find that?
Unlike assignment (=), getting the value of a variable does not automatically create a new variable if the name is not found. Instead, Python looks starts looking outside at other namespaces to see if they define the variable. First Python looks in the function for the variable. If not found it looks in the enclosing function's namespace for the variable. Since we didn't wrap print_choice() inside another function there are no enclosing functions to check. Next Python looks for the variable in the module. "choice" is defined in the module, so print_choice() uses that variable. If Python didn't find choice in the module it looks one last place before giving up; the built-ins. This last step is why I can use
print(choice)
in my program without getting "NameError: print not found".
Sometimes you want your function to not get a module variable's value, but set a module variable's value. That is what "global" is used for. "global" tells Python to look outside the immediate namespace before creating a new variable.
Another way to accomplish the same thing is to change a value inside a container object. That is what Larz60+ is doing with the IntVar. His callback1() does not change the value of "choice", so Python does not create a local variable. His function changes the value that "choice" contains. This same idea could be implemented using a list or a dictionary.
Well, you did ask how to "pass on variables", so I'll try to answer that question.
There are lots of ways you can pass values around. This example reuses the "choice" example with a spin. Instead of writing a unique callback for every button it reuses a callback and passes along a variable. Actually these examples don't reuse the callback. They create a new function that calls the same callback.
from tkinter import *
from functools import partial
master = Tk()
choice = 0
def set_choice(value):
global choice
choice = value
b = Button(master, text="Choice 1", command=partial(set_choice, 1))
b.grid()
b = Button(master, text="Choice 2", command=lambda x=2: set_choice(x))
b.grid()
b = Button(master, text="Let's show what choice you made!", command=lambda : print(choice))
b.grid()
master.mainloop()
Output:
0
1
2
The "Choice 1" button uses a partial function to create a new function that calls setChoice(1). The "Choice 2" button uses a lambda function to call set_choice() and passes the variable "x" which was assigned the value 2. The print button reuses the builtin print() function and passes the module variable "choice".
Another way to pass information around is use tkinter variables. You tried to do this in your example, and Larz60+ did this successfully in his example. I don't write a lot of tkinter code, but when I do I almost always use tkinter variables instead of talking to the widgets directly.
Yet another way to pass information around is to write classes. Classes are a bit like modules in that they have a namespace and can have variables and functions (called methods when talking about a class). Classes can be a good way to organize variables and functions when your program starts growing large.