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