tkinter callbacks stop working
Peter Otten
__peter__ at web.de
Fri Oct 21 10:45:39 EDT 2016
namenobodywants at gmail.com wrote:
> hello pythonistas
>
> the script below plays tictactoe; everything works; but if i replace the
> block at the bottom
>
> if True:
> <tkinter code>
>
> with this instead
>
> def function():
Below I use main() instead.
> <tkinter code>
> function()
>
> then the tkinter callbacks don't work anymore; can anybody make sense of
> this? thanks if you can help
>
> peace
> stm
>
> ps: here's the code...
When you invoke your script on the command line you'll get detailed
information about the error, e. g.:
$ python3 tictactoe.py
Traceback (most recent call last):
File "tictactoe.py", line 113, in <module>
main()
File "tictactoe.py", line 108, in main
newgame()
File "tictactoe.py", line 57, in newgame
show(board)
File "tictactoe.py", line 70, in <lambda>
show = lambda board: status.set(getstatus(board)) or
[squares[i].set(board[i]) for i in range(9)]
NameError: name 'status' is not defined
This is called "traceback" and the thing that you should study carefully
when debugging and always provide when you are asking for help.
The NameError is a hint that the show callback cannot find a variable called
status. This is of course because after your change status is bound inside a
function and thus local to that function. Some ways to fix the problem are
(1) make status (and squares) global by putting the declaration
global status, squares
into your function. This is the beginner's approach and for more complex
programs you tend to end up in a big mess where everything is global.
(2) make the lambda (which doesn't have to be a lambda because it's not
acually used as an expression) local
def main():
global show
def show(board):
...
...
so that it sees the names local to the enclosing function. We still need to
declare the function as global because it will be invoked by other global
functions (of course you can try and make those local, too).
(4) Pass the dependencies explicitly as additional parameters. show() would
become
def show(board, status, squares):
...
and you have to change all places where it is called and provide the extra
arguments. This gets tedious when there are multiple levels of function
calls because the calling function must often be changed in a similar way.
(5) Use a class and turn all functions that implicitly access global state
into methods. Store what used to be global state as instance attributes of
the class. This seems to be the most popular approach for GUI applications.
It allows relatively clean designs if you are careful that your classes
don't become too big.
More information about the Python-list
mailing list